README.TXT

The \Mssdk\Samples\Internet directory contains samples
(written in C/C++, Visual Basic, Java, and/or HTML) demonstrating
different internet or intranet related technologies as described
below. See also the \Mssdk\Samples\NetDS samples for additional
networking sample code.

* IE         - Internet Explorer capabilities and extensions
               (including wininet)
* IIS        - Internet Information Server; ISAPI and Active Server
* Index      - programmatic access to the Content Indexing catalogs
* MSAgent    - demonstrating "interactive personalities" from web pages
* MSChat     - ActiveX chat control and various things you can do with it
* NetMeeting - NetMeeting ActiveX control and related tools
* Scripting  - ActiveX scripting
* WebPost    - APIs for updating content on Web Sites

The build environment (headers, libraries, and minimum build
tools) for the Platform SDK must be installed in order to build
any of the samples in the \Mssdk\Samples\Internet directory. The
most recent version of the build environment is available at
http://www.microsoft.com/msdn/sdk/platform.htm.
Some components may also require you to install runtimes, depending
on the platform on which you are trying to run the sample.
Redistributables are available in the \Mssdk\Redist subdirectory
on the Platform SDK compact disc.

Documentation for the components listed above can be installed
from the Platform SDK compact disc, or can be viewed online at
http://premium.microsoft.com/msdn/library/. This is a FREE service;
registration is necessary.

If you do not see any subdirectories in the \Mssdk\Samples\Internet
node, you'll need to install the Internet, Intranet, and Extranet
Samples component from the Platform SDK compact disc or download and
install it from http://www.microsoft.com/msdn/sdk/platform.htm.


README.TXT

The files contained in this directory (..\Samples\Channel) can be used
to subscribe to a sample channel. To make the channel work properly
these files must be placed on an HTTP server, and the path names in the
color.cdf and scrnsave.htm files must reflect their actual URL. To
subscribe to the channel, either double-click the color.cdf file or
right-click the color.cdf file and select "subscribe" on the context
menu. The "subscribe" option will only appear if the local machine has
Internet Explorer 4.0 installed.

The small *.gif files (red, green, and blue) are displayed in the
Channels Explorer bar to represent various channel items. The
Homepage.htm file is the top-level page assigned to the sample color
channel. The browser displays this page whenever the channel is
launched. Page1.htm, Page2.htm, and Page3.htm are displayed in the
browser whenever the corresponding red, green, or blue page is clicked
in the Channels Explorer bar. The Logo_big.gif file is displayed in the
Browser Channels Explorer bar and the Logo_med.gif file is displayed in
the desktop Channel Bar (if the "View as Web page" option is enabled).
Color.cdf is the Channel Definition Format file that defines the sample
color channel.


README.TXT

======================================================
Notes for "COLBTN" Samples
======================================================

The COLBTN example shows a control that accesses and changes the HTML document in which it is contained.

COLBTN is written using ATL2.0 and was originally built using VC++5. The control subclasses a button and causes the background color of the document to change to pink when pressed. Loading the button.htm file into IE4demonstrates the control.

The control actually imnplents the equivalent of the follwing line of script to make use of the Dynamic HTML support of IE4:
document.bgcolor = "pink"

When clicked the button follows these steps to access its container and the document and are shown in the button.h file.

1) Call getContainer on the client site, to obtain the container's IOleContainer interface
2) Calls QueryInterface for IHTMLDocument2, this interface gives access to the document.
3) Call get_Body to obtain the IHTMLBodyElement interface
4) call put_bgColor


BUTTON.H

// Button.h : Declaration of the CButton

#ifndef __BUTTON_H_
#define __BUTTON_H_

#include "resource.h"       // main symbols
#include <mshtml.h>

/////////////////////////////////////////////////////////////////////////////
// CButton
class ATL_NO_VTABLE CButton :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CButton, &CLSID_Button>,
public CComControl<CButton>,
public IDispatchImpl<IButton, &IID_IButton, &LIBID_COLBTNLib>,
public IProvideClassInfo2Impl<&CLSID_Button, NULL, &LIBID_COLBTNLib>,
public IPersistStreamInitImpl<CButton>,
public IPersistStorageImpl<CButton>,
public IQuickActivateImpl<CButton>,
public IOleControlImpl<CButton>,
public IOleObjectImpl<CButton>,
public IOleInPlaceActiveObjectImpl<CButton>,
public IViewObjectExImpl<CButton>,
public IOleInPlaceObjectWindowlessImpl<CButton>,
public IDataObjectImpl<CButton>,
public ISpecifyPropertyPagesImpl<CButton>
{
public:
CContainedWindow m_ctlButton;

CButton() :
m_ctlButton(_T("Button"), this, 1)
{

m_bWindowOnly = TRUE;
}

DECLARE_REGISTRY_RESOURCEID(IDR_BUTTON)

BEGIN_COM_MAP(CButton)
COM_INTERFACE_ENTRY(IButton)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY_IMPL(IOleControl)
COM_INTERFACE_ENTRY_IMPL(IOleObject)
COM_INTERFACE_ENTRY_IMPL(IQuickActivate)
COM_INTERFACE_ENTRY_IMPL(IPersistStorage)
COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
COM_INTERFACE_ENTRY_IMPL(ISpecifyPropertyPages)
COM_INTERFACE_ENTRY_IMPL(IDataObject)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
END_COM_MAP()

BEGIN_PROPERTY_MAP(CButton)
// Example entries
// PROP_ENTRY("Property Description", dispid, clsid)
END_PROPERTY_MAP()

BEGIN_MSG_MAP(CButton)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
ALT_MSG_MAP(1)
 MESSAGE_HANDLER( WM_LBUTTONUP, OnClick)
END_MSG_MAP()

LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
RECT rc;
GetWindowRect(&rc);
rc.right -= rc.left;
rc.bottom -= rc.top;
rc.top = rc.left = 0;
m_ctlButton.Create(m_hWnd, rc );
::SendMessage(m_ctlButton.m_hWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)"Pink" );

return 0;
}
STDMETHOD(SetObjectRects)(LPCRECT prcPos,LPCRECT prcClip)
{
IOleInPlaceObjectWindowlessImpl<CButton>::SetObjectRects(prcPos, prcClip);
int cx, cy;
cx = prcPos->right - prcPos->left;
cy = prcPos->bottom - prcPos->top;
::SetWindowPos(m_ctlButton.m_hWnd, NULL, 0,
0, cx, cy, SWP_NOZORDER | SWP_NOACTIVATE);
return S_OK;
}

// IViewObjectEx
STDMETHOD(GetViewStatus)(DWORD* pdwStatus)
{
ATLTRACE(_T("IViewObjectExImpl::GetViewStatus\n"));
*pdwStatus = VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE;
return S_OK;
}

LRESULT OnClick(UINT, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
HRESULT hr = E_FAIL;
// Get the document
CComPtr<IOleContainer> spContainer;
m_spClientSite->GetContainer(&spContainer);
CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> spDoc(spContainer);
if (spDoc)
hr = spDoc->put_bgColor( CComVariant(CComBSTR(_T("pink") ) ) );
m_ctlButton.DefWindowProc(WM_LBUTTONUP, wParam, lParam);
return 0;
}

// IButton
public:
HRESULT OnDraw(ATL_DRAWINFO& di);

};

#endif //__BUTTON_H_


COLBTN.CPP

// colbtn.cpp : Implementation of DLL Exports.

// You will need VC 4.2 and the full 4.2b patch (http://msvc/vc42b/vc42b.htm) in
// order to build this project.  This patch includes the final NT 4.0 SDK.
// You will also need MIDL 3.00.75, which is included with ATL 2.0.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f colbtnps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "colbtn.h"

#include "colbtn_i.c"
#include "Button.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_Button, CButton)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
_Module.UnregisterServer();
#if _WIN32_WINNT >= 0x0400
UnRegisterTypeLib(LIBID_COLBTNLib, 1, 0, LOCALE_USER_DEFAULT, SYS_WIN32);
#endif
return S_OK;
}


COLBTN.IDL

#include <olectl.h> // colbtn.idl : IDL source for colbtn.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (colbtn.tlb) and marshalling code.
import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(7022DA42-930B-11D0-AF9A-00A0C909FE5F),
dual,
helpstring("IButton Interface"),
pointer_default(unique)
]
interface IButton : IDispatch
{
};
[
uuid(7022DA40-930B-11D0-AF9A-00A0C909FE5F),
version(1.0),
helpstring("colbtn 1.0 Type Library")
]
library COLBTNLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

[
uuid(7022DA43-930B-11D0-AF9A-00A0C909FE5F),
helpstring("Button Class")
]
coclass Button
{
[default] interface IButton;
};
};


BUTTON.CPP

// Button.cpp : Implementation of CButton
#include "stdafx.h"
#include "colbtn.h"
#include "Button.h"

/////////////////////////////////////////////////////////////////////////////
// CButton

HRESULT CButton::OnDraw(ATL_DRAWINFO& di)
{
return S_OK;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by colbtn.rc
//
#define IDS_PROJNAME                    100
#define IDR_BUTTON                      101

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           102
#endif
#endif


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//  stdafx.pch will be the pre-compiled header
//  stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

#ifdef _ATL_STATIC_REGISTRY
#include <statreg.h>
#include <statreg.cpp>
#endif

#include <atlimpl.cpp>
#include <atlctl.cpp> #include <atlwin.cpp>


STDAFX.H

// stdafx.h : include file for standard system include files,
//      or project specific include files that are used frequently,
//      but are changed infrequently

#define STRICT

#define _WIN32_WINNT 0x0400
#define _ATL_APARTMENT_THREADED

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>
#include <atlctl.h>


README.TXT

======================================================
Notes for "Drill" Samples
======================================================

There are two Drill samples, both do the same thing, however one is written using MFC and the other is written using Visual Basic.

OVERVIEW
========
The drill samples demonstrate hosting the WebBrowser control as part of another application. The buttons and address input are supplied by the hosting application and commands are sent to the WebBrowser control on the form. Entering a URL in the address field on the form will result in the WebBrowser control navigating to that page.

Additionally the Drill samples show how a hosting application can "drill" into the WebBrowser control and investigate the loaded HTML document. In this case the host walks the ALL collection of the document object of the loaded html page using Dynamic HTML and populate a list box with each element encountered.

NOTES on Driller
================
The sample is an MFC based ActiveX control container and is a dialog based application with control container support enabled.

The sample is a VC4.2b sample. It has not been tested with VC5 although it will most probably work.

The two functions of interest are OnBtnGo() and
OnBtnDrill(). Both are in the file drillerDlg.cpp.

OnBtnGo:
The function uses the Navigate2 method of the IWebBrowser2
interface to navigate to the URL.

OnBtnDrill()
This function shows how to walk the object model for
an HTML page. It does the equivalent of the following
in script:

cnt = document.all.length;
for (i=0;i<cnt;i++)
{
 elem = document.all.item(i);
 output(elem.tagName);
 if (elem.tagName == "IMG")
    output(elem.href);
}

The code in C++ wil be as follows:

   IDispatch* pDisp = pBrowser->GetDocument();

The document property gives access to the object model
for the HTML document. From here on one can walk
down the dynamic HTML object model.

For example, getting the IHTMLDocument2 interface
is equivalent to getting to the document object
in the object model. The document object exposes
the HTML docuement through a number of collections
and properties.

All the interfaces for the object model are defined
in MSHTML.h which is included the INetSDK\include
directory.

Some of the methods of the IHTMLDocument2 interface are:

         get_all( IHTMLElementCollection   *p)
         get_images( IHTMLElementCollection   *p)
         get_applets( IHTMLElementCollection   *p)
         get_links( IHTMLElementCollection   *p)
         get_forms( IHTMLElementCollection   *p)
         get_anchors( IHTMLElementCollection   *p)
         get_scripts( IHTMLElementCollection   *p)
         get_frames( IHTMLFramesCollection2   *p)
         get_embeds( IHTMLElementCollection   *p)
         get_plugins( IHTMLElementCollection   *p)

Most of these methods returns a collection which corresponds to the
collection
in the object model. For example get_all returns the all collection,
which is
a collection of all the elements in the document. Similarly get_images
returns
the images collection, get_links the links collection, get_forms the
forms
collection and so on.

IDispatch* pDisp = pBrowser->GetDocument();

if (pDisp != NULL )
{
IHTMLDocument2* pHTMLDocument2;
HRESULT hr;
hr = pDisp->QueryInterface( IID_IHTMLDocument2,
(void**)&pHTMLDocument2 );
if (hr == S_OK)
{

IHTMLElementCollection* pColl;
hr = pHTMLDocument2->get_all( &pColl );

Using get_all we get the all collection of the object model.
This is the same as doing allColl = document.all from within
script.

Below is how to walk the all collection. The same
technique can be used to walk any collection. The idea
is to get the length using get_length and then
interate through each element.

if (hr == S_OK)
{
LONG celem;
hr = pColl->get_length( &celem );

if ( hr == S_OK )
{
for ( int i=0; i< celem; i++ )
{
VARIANT varIndex;
varIndex.vt = VT_UINT;
varIndex.lVal = i;
VARIANT var2;
VariantInit( &var2 );
IDispatch* pDisp;

hr = pColl->item( varIndex,
var2, &pDisp );

The item method of the collection gives the element's dispatch
interface. Each element implements the IHTMLElement interface which
can be used to access the elements methods and properties.

Again look at the IHTMLElement interface in MSHTML.h for the
different methods that is has. There is a corresponding method
for every element property in the object model. So for example
if you want to get the tagName of the element use the get_tagName
method. The tagName is a the HTML tag of the element. For example
the tagName of the following line of HTML code:
<H1> This is Heading 1 </H1>
will be H1.

if ( hr == S_OK )
{

IHTMLElement* pElem;

hr =
pDisp->QueryInterface( IID_IHTMLElement, (void **)&pElem );

if ( hr == S_OK )
{

BSTR bstr;
hr =
pElem->get_tagName(&bstr);
CString strTag =
bstr;

An element could implement other interfaces depending on the
type of the element. For example if the element is an
image it will implement the IHTMLImgElement interface. The
IHTMLImgElement interface gives more information about
the IMG element. In this sample we use the IHTMLImgElement
to get the URL for the image.

IHTMLImgElement*
pImgElem;
hr =
pDisp->QueryInterface( IID_IHTMLImgElement, (void **)&pImgElem );
if ( hr == S_OK
)
{

pImgElem->get_href(&bstr);
strTag
+= " - ";
strTag
+= bstr;

pImgElem->Release();
}

m_listTags.AddString( strTag );

pElem->Release();
}
pDisp->Release();
}
}
}

pColl->Release();
}
pHTMLDocument2->Release();
}
pDisp->Release();
}

Notes on DrillVB
================
The DrillVB sample is written using Visual Basic 4.0, and will also run in VB5.0. It populates a listbox with the elements that it finds on the all collection which is obtained using
   Set allCol = WebBrowser1.Document.All


README.TXT

======================================================
Notes for Driller
======================================================

Driller is an MFC based control host, that hosts the
WebBrowser Control. Built Using VC++5.0.

The functionality of drilling into the document hosted
and listing the tags found in the listbox is discussed
in the readme file for Drill which additionally covers
the DrillVB sample.

Addtional functionality that is present in the Driller
sample is providing extra control through the implementation
of the IDocHostUIHandler interface. This interface is
documented in the SDK, this sample shows how this
interface can be used to control the context menu's
and extend the Dynamic HTML Object Model.

The Context menu's that appear as standard for a
right mouse click are disabled within the Driller
sample. This is achieved by returning S_OK from the
ShowContextMenu method of the IDocHostUIHandler
interface, indicating that the host has handled the
call and the IE4 components need not perform the
standard processing.

The Dynamic HTML Object Model is also extended in the
Driller sample by providing an IDispatch interface to
the getExternal method of the IDocHostUIhandler
interface. This IDispatch is used whenever the script
within the HTML document refers to window.external,
whatever follows this will be handed to the GetIDsof
Names member function of the IDispatch interface to be
resolved.
This can be seen by loading the extend.htm file supplied
into Driller and pressing the Extend button.

To implement the IDocHostUIHandler, the client site needs
to implement the interface. In MFC, the class COleControlSite
encapsulates the client site. In this example we are subclassing
MFC, a class CCustomControlSite, is derived from COleControlSite
and CCustomControlSite implements IDocHostUIHandler. To hook
in the CCustomControlSite, a class CCustomOccManager is derived
from COccManager. Subclassing COccManager and
COleControlSite in such a manner is implementation specific
to MFC. So, if future versions of MFC change the implementation
of COleControlSite or COccManager, this sample (and your code
if you use this technique) might not work. We are looking at
possible ways to have MFC expose the client site for
customization. If in future MFC does expose the client
site, then we will modify this sample to use that functionality.

If you are using this sample, to design your code please be
advised that you may have to change your code in the future.

It is possible that this sample will not compile because
an include directory has not been identified.  The program
looks for the file called occimpl.h.  Prepended to
the header file is the directory where that file
was located on the test machine.  This maybe different
than most users.  The solution is to delete the prepended
directory and under project==> settings, go to the C/C++ tab,
look under Preprocessor and then add the
include directory to the additional include directories.  An
example of what that might look like is:
c:\program files\devstudio\vc\mfc.


CUSTSITE.H

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
#ifndef __CUSTOMSITEH__
#define __CUSTOMSITEH__

#include "idispimp.h"
#include <mshtmhst.h>

//
// NOTE:
// Some of the code in this file is MFC implementation specific.
// Changes in future versions of MFC implementation may require
// the code to be changed. Please check the readme of this
// sample for more information
//

class CCustomControlSite:public COleControlSite
{
public:
CCustomControlSite(COleControlContainer *pCnt):COleControlSite(pCnt){}

protected:

DECLARE_INTERFACE_MAP();
BEGIN_INTERFACE_PART(DocHostUIHandler, IDocHostUIHandler)
STDMETHOD(ShowContextMenu)(/* [in] */ DWORD dwID,
            /* [in] */ POINT __RPC_FAR *ppt,
            /* [in] */ IUnknown __RPC_FAR *pcmdtReserved,
            /* [in] */ IDispatch __RPC_FAR *pdispReserved);
STDMETHOD(GetHostInfo)(
            /* [out][in] */ DOCHOSTUIINFO __RPC_FAR *pInfo);
STDMETHOD(ShowUI)(
            /* [in] */ DWORD dwID,
            /* [in] */ IOleInPlaceActiveObject __RPC_FAR *pActiveObject,
            /* [in] */ IOleCommandTarget __RPC_FAR *pCommandTarget,
            /* [in] */ IOleInPlaceFrame __RPC_FAR *pFrame,
            /* [in] */ IOleInPlaceUIWindow __RPC_FAR *pDoc);
STDMETHOD(HideUI)(void);
STDMETHOD(UpdateUI)(void);
STDMETHOD(EnableModeless)(/* [in] */ BOOL fEnable);
STDMETHOD(OnDocWindowActivate)(/* [in] */ BOOL fEnable);
STDMETHOD(OnFrameWindowActivate)(/* [in] */ BOOL fEnable);
STDMETHOD(ResizeBorder)(
            /* [in] */ LPCRECT prcBorder,
            /* [in] */ IOleInPlaceUIWindow __RPC_FAR *pUIWindow,
            /* [in] */ BOOL fRameWindow);
STDMETHOD(TranslateAccelerator)(
            /* [in] */ LPMSG lpMsg,
            /* [in] */ const GUID __RPC_FAR *pguidCmdGroup,
            /* [in] */ DWORD nCmdID);
STDMETHOD(GetOptionKeyPath)(
            /* [out] */ LPOLESTR __RPC_FAR *pchKey,
            /* [in] */ DWORD dw);
STDMETHOD(GetDropTarget)(
            /* [in] */ IDropTarget __RPC_FAR *pDropTarget,
            /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget);
    STDMETHOD(GetExternal)(
            /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDispatch);
    STDMETHOD(TranslateUrl)(
            /* [in] */ DWORD dwTranslate,
            /* [in] */ OLECHAR __RPC_FAR *pchURLIn,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut);
    STDMETHOD(FilterDataObject)(
            /* [in] */ IDataObject __RPC_FAR *pDO,
            /* [out] */ IDataObject __RPC_FAR *__RPC_FAR *ppDORet);
END_INTERFACE_PART(DocHostUIHandler)
};

class CCustomOccManager :public COccManager
{
public:
CCustomOccManager(){}
COleControlSite* CreateSite(COleControlContainer* pCtrlCont)
{
CCustomControlSite *pSite = new CCustomControlSite(pCtrlCont);
return pSite;
}
};

#endif


DRILLDLG.CPP

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
// drillerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "driller.h"
#include "drillDlg.h"
#include "WebBro.h"
#include <mshtml.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
CAboutDlg();

// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDrillerDlg dialog

CDrillerDlg::CDrillerDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDrillerDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CDrillerDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CDrillerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDrillerDlg)
DDX_Control(pDX, IDC_LIST1, m_listTags);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CDrillerDlg, CDialog)
//{{AFX_MSG_MAP(CDrillerDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_GO, OnBtnGo)
ON_BN_CLICKED(IDC_BTN_DRILL, OnBtnDrill)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDrillerDlg message handlers

BOOL CDrillerDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// Add "About..." menu item to system menu.

// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}

// Set the icon for this dialog.  The framework does this automatically
//  when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE);// Set big icon
SetIcon(m_hIcon, FALSE);// Set small icon

// TODO: Add extra initialization here

return TRUE;  // return TRUE  unless you set the focus to a control
}

void CDrillerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CDrillerDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting

SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CDrillerDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}

void CDrillerDlg::OnBtnGo()
{
CWebBrowser2* pBrowser = (CWebBrowser2*)GetDlgItem( IDC_EXPLORER1 );
CString strURL;
GetDlgItemText( IDC_EDIT1, strURL );
COleVariant* pvarURL = new COleVariant( strURL );
COleVariant* pvarEmpty = new COleVariant;
pBrowser->Navigate2( pvarURL, pvarEmpty, pvarEmpty, pvarEmpty, pvarEmpty );
delete pvarURL;
delete pvarEmpty;
}

void CDrillerDlg::OnBtnDrill()
{
m_listTags.ResetContent();

CWebBrowser2* pBrowser = (CWebBrowser2*)GetDlgItem( IDC_EXPLORER1 );

IDispatch* pDisp = pBrowser->GetDocument();

if (pDisp != NULL )
{
IHTMLDocument2* pHTMLDocument2;
HRESULT hr;
hr = pDisp->QueryInterface( IID_IHTMLDocument2, (void**)&pHTMLDocument2 );
if (hr == S_OK)
{

IHTMLElementCollection* pColl;
hr = pHTMLDocument2->get_all( &pColl );
if (hr == S_OK)
{
LONG celem;
hr = pColl->get_length( &celem );

if ( hr == S_OK )
{
for ( int i=0; i< celem; i++ )
{
VARIANT varIndex;
varIndex.vt = VT_UINT;
varIndex.lVal = i;
VARIANT var2;
VariantInit( &var2 );
IDispatch* pDisp;

hr = pColl->item( varIndex, var2, &pDisp );

if ( hr == S_OK )
{

IHTMLElement* pElem;

hr = pDisp->QueryInterface( IID_IHTMLElement, (void **)&pElem );
if ( hr == S_OK )
{

BSTR bstr;
hr = pElem->get_tagName(&bstr);
CString strTag = bstr;

IHTMLImgElement* pImgElem;
hr = pDisp->QueryInterface( IID_IHTMLImgElement, (void **)&pImgElem );
if ( hr == S_OK )
{
pImgElem->get_href(&bstr);
strTag += " - ";
strTag += bstr;
pImgElem->Release();
}
else
{
IHTMLAnchorElement* pAnchElem;
hr = pDisp->QueryInterface( IID_IHTMLAnchorElement, (void **)&pAnchElem );
if ( hr == S_OK )
{
pAnchElem->get_href(&bstr);
strTag += " - ";
strTag += bstr;
pAnchElem->Release();
}
}

m_listTags.AddString( strTag );

pElem->Release();
}
pDisp->Release();
}
}
}

pColl->Release();
}
pHTMLDocument2->Release();
}
pDisp->Release();
}

}


DRILLDLG.H

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
// drillerDlg.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CDrillerDlg dialog

class CDrillerDlg : public CDialog
{
// Construction
public:
CDrillerDlg(CWnd* pParent = NULL);// standard constructor

// Dialog Data
//{{AFX_DATA(CDrillerDlg)
enum { IDD = IDD_DRILLER_DIALOG };
CListBoxm_listTags;
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDrillerDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);// DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
HICON m_hIcon;

// Generated message map functions
//{{AFX_MSG(CDrillerDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnBtnGo();
afx_msg void OnBtnDrill();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

};


DRILLER.CPP

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
// driller.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "driller.h"
#include "drillDlg.h"
// NOTE: This line is a hardcoded reference to an MFC header file
//  this path may need to be changed to refer to the location of VC5 install
//  for successful compilation.
#include <..\src\occimpl.h>
#include "custsite.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDrillerApp

BEGIN_MESSAGE_MAP(CDrillerApp, CWinApp)
//{{AFX_MSG_MAP(CDrillerApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
//    DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

// The one and only CDrillerApp object
CDrillerApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CDrillerApp construction

CDrillerApp::CDrillerApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}

CDrillerApp::~CDrillerApp()
{
if ( m_pDispOM != NULL)
{
delete m_pDispOM;
}
}

/////////////////////////////////////////////////////////////////////////////
// CDrillerApp initialization

BOOL CDrillerApp::InitInstance()
{
// Create a custom control manager class so we can overide the site
CCustomOccManager *pMgr = new CCustomOccManager;

// Create an IDispatch class for extending the Dynamic HTML Object Model
m_pDispOM = new CImpIDispatch;

// Set our control containment up but using our control container
// management class instead of MFC's default
AfxEnableControlContainer(pMgr);

// Standard initialization
// If you are not using these features and wish to reduce the size
//  of your final executable, you should remove from the following
//  the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls();                     // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic();       // Call this when linking to MFC statically
#endif

CDrillerDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
//  dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
//  dismissed with Cancel
}

// Since the dialog has been closed, return FALSE so that we exit the
//  application, rather than start the application's message pump.
return FALSE;
}


DRILLER.H

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
// driller.h : main header file for the DRILLER application
//

#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif

#include "resource.h"// main symbols

/////////////////////////////////////////////////////////////////////////////
// CDrillerApp:
// See driller.cpp for the implementation of this class
//
/////////////////////////////////////////////////////////////////////////////
class CDrillerApp : public CWinApp
{
public:
CDrillerApp();
~CDrillerApp();
class CImpIDispatch* m_pDispOM;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDrillerApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL

// Implementation

//{{AFX_MSG(CDrillerApp)
// NOTE - the ClassWizard will add and remove member functions here.
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

};

extern CDrillerApp theApp;
/////////////////////////////////////////////////////////////////////////////


IDISPIMP.CPP

/*
 * idispimp.CPP
 * IDispatch for Extending Dynamic HTML Object Model
 *
 * Copyright (c)1995-1997 Microsoft Corporation, All Rights Reserved
 */

#include "stdafx.h"
#include "idispimp.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// Hardcoded information for extending the Object Model
// Typically this would be supplied through a TypeInfo
// In this case the name "xxyyzz" maps to DISPID_Extend
constWCHAR pszExtend[10]=L"xxyyzz";
#define DISPID_Extend 12345

/*
 * CImpIDispatch::CImpIDispatch
 * CImpIDispatch::~CImpIDispatch
 *
 * Parameters (Constructor):
 *  pSite           PCSite of the site we're in.
 *  pUnkOuter       LPUNKNOWN to which we delegate.
 */

CImpIDispatch::CImpIDispatch( void )
{
    m_cRef = 0;
}

CImpIDispatch::~CImpIDispatch( void )
{
ASSERT( m_cRef == 0 );
}

/*
 * CImpIDispatch::QueryInterface
 * CImpIDispatch::AddRef
 * CImpIDispatch::Release
 *
 * Purpose:
 *  IUnknown members for CImpIDispatch object.
 */

STDMETHODIMP CImpIDispatch::QueryInterface( REFIID riid, void **ppv )
{
    *ppv = NULL;

    if ( IID_IDispatch == riid )
{
        *ppv = this;
}

if ( NULL != *ppv )
    {
        ((LPUNKNOWN)*ppv)->AddRef();
        return NOERROR;
    }

return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CImpIDispatch::AddRef(void)
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) CImpIDispatch::Release(void)
{
    return --m_cRef;
}

//IDispatch
STDMETHODIMP CImpIDispatch::GetTypeInfoCount(UINT* /*pctinfo*/)
{
return E_NOTIMPL;
}

STDMETHODIMP CImpIDispatch::GetTypeInfo(/* [in] */ UINT /*iTInfo*/,
            /* [in] */ LCID /*lcid*/,
            /* [out] */ ITypeInfo** /*ppTInfo*/)
{
return E_NOTIMPL;
}

STDMETHODIMP CImpIDispatch::GetIDsOfNames(
            /* [in] */ REFIID riid,
            /* [size_is][in] */ OLECHAR** rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID* rgDispId)
{
HRESULT hr;
UINTi;

// Assume some degree of success
hr = NOERROR;

// Hardcoded mapping for this sample
// A more usual procedure would be to use a TypeInfo
for ( i=0; i < cNames; i++)
{
if (  2 == CompareString( lcid, NORM_IGNOREWIDTH, (char*)pszExtend, 3, (char*)rgszNames[i], 3 ) )
{
rgDispId[i] = DISPID_Extend;
}
else
{
// One or more are unknown so set the return code accordingly
hr = ResultFromScode(DISP_E_UNKNOWNNAME);
rgDispId[i] = DISPID_UNKNOWN;
}
}
return hr;
}

STDMETHODIMP CImpIDispatch::Invoke(
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID /*riid*/,
            /* [in] */ LCID /*lcid*/,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS* pDispParams,
            /* [out] */ VARIANT* pVarResult,
            /* [out] */ EXCEPINFO* /*pExcepInfo*/,
            /* [out] */ UINT* puArgErr)
{

// For this sample we only support a Property Get on DISPID_Extend
// returning a BSTR with "Wibble" as the value
if ( dispIdMember == DISPID_Extend )
{
if ( wFlags & DISPATCH_PROPERTYGET )
{
if ( pVarResult != NULL )
{
WCHAR buff[10]=L"Wibble";
BSTR bstrRet = SysAllocString( buff );
VariantInit(pVarResult);
V_VT(pVarResult)=VT_BSTR;
V_BSTR(pVarResult) = bstrRet;
}
}
}

return S_OK;
}


IDISPIMP.H

/*
 * IDispimp.H
 * IDispatch
 *
 * Copyright (c)1995-1996 Microsoft Corporation, All Rights Reserved
 */

#ifndef _IDISPIMP_H_
#define _IDISPIMP_H_

class CImpIDispatch : public IDispatch
{
    protected:
        ULONG               m_cRef;

    public:
        CImpIDispatch(void);
        ~CImpIDispatch(void);

        STDMETHODIMP QueryInterface(REFIID, void **);
        STDMETHODIMP_(ULONG) AddRef(void);
        STDMETHODIMP_(ULONG) Release(void);

//IDispatch
        STDMETHODIMP GetTypeInfoCount(UINT* pctinfo);
        STDMETHODIMP GetTypeInfo(/* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo** ppTInfo);
STDMETHODIMP GetIDsOfNames(
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID *rgDispId);
STDMETHODIMP Invoke(
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS  *pDispParams,
            /* [out] */ VARIANT  *pVarResult,
            /* [out] */ EXCEPINFO *pExcepInfo,
            /* [out] */ UINT *puArgErr);

};
#endif //_IDISPIMP_H_


CUSTSITE.CPP

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=

//
// NOTE:
// Some of the code in this file is MFC implementation specific.
// Changes in future versions of MFC implementation may require
// the code to be changed. Please check the readme of this
// sample for more information
//

#include "stdafx.h"
#undef AFX_DATA
#define AFX_DATA AFX_DATA_IMPORT

// NOTE: THis line is a hardcoded reference to an MFC header file
//  this path may need to be changed to refer to the location of VC5 install
//  for successful compilation.

#include <..\src\occimpl.h>
#undef AFX_DATA
#define AFX_DATA AFX_DATA_EXPORT
#include "custsite.h"
#include "driller.h"

BEGIN_INTERFACE_MAP(CCustomControlSite, COleControlSite)
INTERFACE_PART(CCustomControlSite, IID_IDocHostUIHandler, DocHostUIHandler)
END_INTERFACE_MAP()

ULONG FAR EXPORT  CCustomControlSite::XDocHostUIHandler::AddRef()
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
return pThis->ExternalAddRef();
}

ULONG FAR EXPORT  CCustomControlSite::XDocHostUIHandler::Release()
{
    METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
return pThis->ExternalRelease();
}

HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::QueryInterface(REFIID riid, void **ppvObj)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    HRESULT hr = (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObj);
return hr;
}

// * CImpIDocHostUIHandler::GetHostInfo
// *
// * Purpose: Called at initialisation
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::GetHostInfo( DOCHOSTUIINFO* pInfo )
{

METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER;
    pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;

    return S_OK;
}

// * CImpIDocHostUIHandler::ShowUI
// *
// * Purpose: Called when MSHTML.DLL shows its UI
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::ShowUI(
DWORD dwID,
IOleInPlaceActiveObject * /*pActiveObject*/,
IOleCommandTarget * pCommandTarget,
IOleInPlaceFrame * /*pFrame*/,
IOleInPlaceUIWindow * /*pDoc*/)
{

METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
// We've already got our own UI in place so just return S_OK
    return S_OK;
}

// * CImpIDocHostUIHandler::HideUI
// *
// * Purpose: Called when MSHTML.DLL hides its UI
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::HideUI(void)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return S_OK;
}

// * CImpIDocHostUIHandler::UpdateUI
// *
// * Purpose: Called when MSHTML.DLL updates its UI
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::UpdateUI(void)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
// MFC is pretty good about updating it's UI in it's Idle loop so I don't do anything here
return S_OK;
}

// * CImpIDocHostUIHandler::EnableModeless
// *
// * Purpose: Called from MSHTML.DLL's IOleInPlaceActiveObject::EnableModeless
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::EnableModeless(BOOL /*fEnable*/)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return E_NOTIMPL;
}

// * CImpIDocHostUIHandler::OnDocWindowActivate
// *
// * Purpose: Called from MSHTML.DLL's IOleInPlaceActiveObject::OnDocWindowActivate
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::OnDocWindowActivate(BOOL /*fActivate*/)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return E_NOTIMPL;
}

// * CImpIDocHostUIHandler::OnFrameWindowActivate
// *
// * Purpose: Called from MSHTML.DLL's IOleInPlaceActiveObject::OnFrameWindowActivate
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::OnFrameWindowActivate(BOOL /*fActivate*/)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return E_NOTIMPL;
}

// * CImpIDocHostUIHandler::ResizeBorder
// *
// * Purpose: Called from MSHTML.DLL's IOleInPlaceActiveObject::ResizeBorder
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::ResizeBorder(
LPCRECT /*prcBorder*/,
IOleInPlaceUIWindow* /*pUIWindow*/,
BOOL /*fRameWindow*/)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return E_NOTIMPL;
}

// * CImpIDocHostUIHandler::ShowContextMenu
// *
// * Purpose: Called when MSHTML.DLL would normally display its context menu
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::ShowContextMenu(
DWORD /*dwID*/,
POINT* /*pptPosition*/,
IUnknown* /*pCommandTarget*/,
IDispatch* /*pDispatchObjectHit*/)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return S_OK; // We've shown our own context menu. MSHTML.DLL will no longer try to show its own.
}

// * CImpIDocHostUIHandler::TranslateAccelerator
// *
// * Purpose: Called from MSHTML.DLL's TranslateAccelerator routines
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::TranslateAccelerator(LPMSG lpMsg,
            /* [in] */ const GUID __RPC_FAR *pguidCmdGroup,
            /* [in] */ DWORD nCmdID)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return S_FALSE;
}

// * CImpIDocHostUIHandler::GetOptionKeyPath
// *
// * Purpose: Called by MSHTML.DLL to find where the host wishes to store
// *its options in the registry
// *
HRESULT FAR EXPORT  CCustomControlSite::XDocHostUIHandler::GetOptionKeyPath(BSTR* pbstrKey, DWORD)
{

METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
return E_NOTIMPL;
}

STDMETHODIMP CCustomControlSite::XDocHostUIHandler::GetDropTarget(
            /* [in] */ IDropTarget __RPC_FAR *pDropTarget,
            /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return E_NOTIMPL;
}

STDMETHODIMP CCustomControlSite::XDocHostUIHandler::GetExternal(
            /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDispatch)
{
// return the IDispatch we have for extending the object Model
IDispatch* pDisp = (IDispatch*)theApp.m_pDispOM;
pDisp->AddRef();
*ppDispatch = pDisp;
    return S_OK;
}

STDMETHODIMP CCustomControlSite::XDocHostUIHandler::TranslateUrl(
            /* [in] */ DWORD dwTranslate,
            /* [in] */ OLECHAR __RPC_FAR *pchURLIn,
            /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return E_NOTIMPL;
}

STDMETHODIMP CCustomControlSite::XDocHostUIHandler::FilterDataObject(
            /* [in] */ IDataObject __RPC_FAR *pDO,
            /* [out] */ IDataObject __RPC_FAR *__RPC_FAR *ppDORet)
{
METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return E_NOTIMPL;
}


RESOURCE.H

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by driller.rc
//
#define IDM_ABOUTBOX                    0x0010
#define IDD_ABOUTBOX                    100
#define IDS_ABOUTBOX                    101
#define IDD_DRILLER_DIALOG              102
#define IDR_MAINFRAME                   128
#define IDC_EXPLORER1                   1000
#define IDC_BTN_GO                      1001
#define IDC_EDIT1                       1002
#define IDC_BTN_DRILL                   1003
#define IDC_LIST1                       1004
#define IDC_BUTTON1                     1005

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        130
#define _APS_NEXT_COMMAND_VALUE         32771
#define _APS_NEXT_CONTROL_VALUE         1007
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


STDAFX.CPP

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
// stdafx.cpp : source file that includes just the standard includes
//driller.pch will be the pre-compiled header
//stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"


STDAFX.H

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#define VC_EXTRALEAN// Exclude rarely-used stuff from Windows headers

#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions
#include <afxdisp.h>        // MFC OLE automation classes
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>// MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT


WEBBRO.CPP

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.

#include "stdafx.h"
#include "webbro.h"

/////////////////////////////////////////////////////////////////////////////
// CWebBrowser2

IMPLEMENT_DYNCREATE(CWebBrowser2, CWnd)

/////////////////////////////////////////////////////////////////////////////
// CWebBrowser2 properties

/////////////////////////////////////////////////////////////////////////////
// CWebBrowser2 operations

unsigned long CWebBrowser2::AddRef()
{
unsigned long result;
InvokeHelper(0x60000001, DISPATCH_METHOD, VT_I4, (void*)&result, NULL);
return result;
}

unsigned long CWebBrowser2::Release()
{
unsigned long result;
InvokeHelper(0x60000002, DISPATCH_METHOD, VT_I4, (void*)&result, NULL);
return result;
}

void CWebBrowser2::GoBack()
{
InvokeHelper(0x64, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CWebBrowser2::GoForward()
{
InvokeHelper(0x65, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CWebBrowser2::GoHome()
{
InvokeHelper(0x66, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CWebBrowser2::GoSearch()
{
InvokeHelper(0x67, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CWebBrowser2::Navigate(LPCTSTR URL, VARIANT* Flags, VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers)
{
static BYTE parms[] =
VTS_BSTR VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT;
InvokeHelper(0x68, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 URL, Flags, TargetFrameName, PostData, Headers);
}

void CWebBrowser2::Refresh()
{
InvokeHelper(DISPID_REFRESH, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CWebBrowser2::Refresh2(VARIANT* Level)
{
static BYTE parms[] =
VTS_PVARIANT;
InvokeHelper(0x69, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 Level);
}

void CWebBrowser2::Stop()
{
InvokeHelper(0x6a, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

LPDISPATCH CWebBrowser2::GetApplication()
{
LPDISPATCH result;
InvokeHelper(0xc8, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, NULL);
return result;
}

LPDISPATCH CWebBrowser2::GetParent()
{
LPDISPATCH result;
InvokeHelper(0xc9, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, NULL);
return result;
}

LPDISPATCH CWebBrowser2::GetContainer()
{
LPDISPATCH result;
InvokeHelper(0xca, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, NULL);
return result;
}

LPDISPATCH CWebBrowser2::GetDocument()
{
LPDISPATCH result;
InvokeHelper(0xcb, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, NULL);
return result;
}

BOOL CWebBrowser2::GetTopLevelContainer()
{
BOOL result;
InvokeHelper(0xcc, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

CString CWebBrowser2::GetType()
{
CString result;
InvokeHelper(0xcd, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

long CWebBrowser2::GetLeft()
{
long result;
InvokeHelper(0xce, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetLeft(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0xce, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

long CWebBrowser2::GetTop()
{
long result;
InvokeHelper(0xcf, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetTop(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0xcf, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

long CWebBrowser2::GetWidth()
{
long result;
InvokeHelper(0xd0, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetWidth(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0xd0, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

long CWebBrowser2::GetHeight()
{
long result;
InvokeHelper(0xd1, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetHeight(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0xd1, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

CString CWebBrowser2::GetLocationName()
{
CString result;
InvokeHelper(0xd2, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

CString CWebBrowser2::GetLocationURL()
{
CString result;
InvokeHelper(0xd3, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

BOOL CWebBrowser2::GetBusy()
{
BOOL result;
InvokeHelper(0xd4, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

void CWebBrowser2::Quit()
{
InvokeHelper(0x12c, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CWebBrowser2::ClientToWindow(long* pcx, long* pcy)
{
static BYTE parms[] =
VTS_PI4 VTS_PI4;
InvokeHelper(0x12d, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 pcx, pcy);
}

void CWebBrowser2::PutProperty(LPCTSTR szProperty, const VARIANT& vtValue)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x12e, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 szProperty, &vtValue);
}

VARIANT CWebBrowser2::GetProperty(LPCTSTR szProperty)
{
VARIANT result;
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x12f, DISPATCH_METHOD, VT_VARIANT, (void*)&result, parms,
szProperty);
return result;
}

CString CWebBrowser2::GetName()
{
CString result;
InvokeHelper(0x0, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

long CWebBrowser2::GetHwnd()
{
long result;
InvokeHelper(DISPID_HWND, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

CString CWebBrowser2::GetFullName()
{
CString result;
InvokeHelper(0x190, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

CString CWebBrowser2::GetPath()
{
CString result;
InvokeHelper(0x191, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

BOOL CWebBrowser2::GetVisible()
{
BOOL result;
InvokeHelper(0x192, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetVisible(BOOL bNewValue)
{
static BYTE parms[] =
VTS_BOOL;
InvokeHelper(0x192, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 bNewValue);
}

BOOL CWebBrowser2::GetStatusBar()
{
BOOL result;
InvokeHelper(0x193, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetStatusBar(BOOL bNewValue)
{
static BYTE parms[] =
VTS_BOOL;
InvokeHelper(0x193, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 bNewValue);
}

CString CWebBrowser2::GetStatusText()
{
CString result;
InvokeHelper(0x194, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetStatusText(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x194, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

long CWebBrowser2::GetToolBar()
{
long result;
InvokeHelper(0x195, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetToolBar(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0x195, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

BOOL CWebBrowser2::GetMenuBar()
{
BOOL result;
InvokeHelper(0x196, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetMenuBar(BOOL bNewValue)
{
static BYTE parms[] =
VTS_BOOL;
InvokeHelper(0x196, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 bNewValue);
}

BOOL CWebBrowser2::GetFullScreen()
{
BOOL result;
InvokeHelper(0x197, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetFullScreen(BOOL bNewValue)
{
static BYTE parms[] =
VTS_BOOL;
InvokeHelper(0x197, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 bNewValue);
}

void CWebBrowser2::Navigate2(VARIANT* URL, VARIANT* Flags, VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers)
{
static BYTE parms[] =
VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT;
InvokeHelper(0x1f4, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 URL, Flags, TargetFrameName, PostData, Headers);
}

long CWebBrowser2::QueryStatusWB(long cmdID)
{
long result;
static BYTE parms[] =
VTS_I4;
InvokeHelper(0x1f5, DISPATCH_METHOD, VT_I4, (void*)&result, parms,
cmdID);
return result;
}

void CWebBrowser2::ExecWB(long cmdID, long cmdexecopt, VARIANT* pvaIn, VARIANT* pvaOut)
{
static BYTE parms[] =
VTS_I4 VTS_I4 VTS_PVARIANT VTS_PVARIANT;
InvokeHelper(0x1f6, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 cmdID, cmdexecopt, pvaIn, pvaOut);
}

long CWebBrowser2::GetReadyState()
{
long result;
InvokeHelper(DISPID_READYSTATE, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

BOOL CWebBrowser2::GetOffline()
{
BOOL result;
InvokeHelper(0x226, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetOffline(BOOL bNewValue)
{
static BYTE parms[] =
VTS_BOOL;
InvokeHelper(0x226, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 bNewValue);
}

BOOL CWebBrowser2::GetSilent()
{
BOOL result;
InvokeHelper(0x227, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetSilent(BOOL bNewValue)
{
static BYTE parms[] =
VTS_BOOL;
InvokeHelper(0x227, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 bNewValue);
}

BOOL CWebBrowser2::GetRegisterAsBrowser()
{
BOOL result;
InvokeHelper(0x228, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetRegisterAsBrowser(BOOL bNewValue)
{
static BYTE parms[] =
VTS_BOOL;
InvokeHelper(0x228, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 bNewValue);
}

BOOL CWebBrowser2::GetRegisterAsDropTarget()
{
BOOL result;
InvokeHelper(0x229, DISPATCH_PROPERTYGET, VT_BOOL, (void*)&result, NULL);
return result;
}

void CWebBrowser2::SetRegisterAsDropTarget(BOOL bNewValue)
{
static BYTE parms[] =
VTS_BOOL;
InvokeHelper(0x229, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 bNewValue);
}


WEBBRO.H

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
#ifndef __WEBBROWSER2_H__
#define __WEBBROWSER2_H__

// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.

/////////////////////////////////////////////////////////////////////////////
// CWebBrowser2 wrapper class

class CWebBrowser2 : public CWnd
{
protected:
DECLARE_DYNCREATE(CWebBrowser2)
public:
CLSID const& GetClsid()
{
static CLSID const clsid
= { 0x8856f961, 0x340a, 0x11d0, { 0xa9, 0x6b, 0x0, 0xc0, 0x4f, 0xd7, 0x5, 0xa2 } };
return clsid;
}
virtual BOOL Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd, UINT nID,
CCreateContext* pContext = NULL)
{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); }

    BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID,
CFile* pPersist = NULL, BOOL bStorage = FALSE,
BSTR bstrLicKey = NULL)
{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID,
pPersist, bStorage, bstrLicKey); }

// Attributes
public:

// Operations
public:
// method 'QueryInterface' not emitted because of invalid return type or parameter type
unsigned long AddRef();
unsigned long Release();
// method 'GetTypeInfoCount' not emitted because of invalid return type or parameter type
// method 'GetTypeInfo' not emitted because of invalid return type or parameter type
// method 'GetIDsOfNames' not emitted because of invalid return type or parameter type
// method 'Invoke' not emitted because of invalid return type or parameter type
void GoBack();
void GoForward();
void GoHome();
void GoSearch();
void Navigate(LPCTSTR URL, VARIANT* Flags, VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers);
void Refresh();
void Refresh2(VARIANT* Level);
void Stop();
LPDISPATCH GetApplication();
LPDISPATCH GetParent();
LPDISPATCH GetContainer();
LPDISPATCH GetDocument();
BOOL GetTopLevelContainer();
CString GetType();
long GetLeft();
void SetLeft(long nNewValue);
long GetTop();
void SetTop(long nNewValue);
long GetWidth();
void SetWidth(long nNewValue);
long GetHeight();
void SetHeight(long nNewValue);
CString GetLocationName();
CString GetLocationURL();
BOOL GetBusy();
void Quit();
void ClientToWindow(long* pcx, long* pcy);
void PutProperty(LPCTSTR szProperty, const VARIANT& vtValue);
VARIANT GetProperty(LPCTSTR szProperty);
CString GetName();
long GetHwnd();
CString GetFullName();
CString GetPath();
BOOL GetVisible();
void SetVisible(BOOL bNewValue);
BOOL GetStatusBar();
void SetStatusBar(BOOL bNewValue);
CString GetStatusText();
void SetStatusText(LPCTSTR lpszNewValue);
long GetToolBar();
void SetToolBar(long nNewValue);
BOOL GetMenuBar();
void SetMenuBar(BOOL bNewValue);
BOOL GetFullScreen();
void SetFullScreen(BOOL bNewValue);
void Navigate2(VARIANT* URL, VARIANT* Flags, VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers);
long QueryStatusWB(long cmdID);
void ExecWB(long cmdID, long cmdexecopt, VARIANT* pvaIn, VARIANT* pvaOut);
long GetReadyState();
BOOL GetOffline();
void SetOffline(BOOL bNewValue);
BOOL GetSilent();
void SetSilent(BOOL bNewValue);
BOOL GetRegisterAsBrowser();
void SetRegisterAsBrowser(BOOL bNewValue);
BOOL GetRegisterAsDropTarget();
void SetRegisterAsDropTarget(BOOL bNewValue);
};

#endif // __WEBBROWSER2_H__


DRILL.FRM

VERSION 4.00
Begin VB.Form Form1
   Caption         =   "Form1"
   ClientHeight    =   5535
   ClientLeft      =   1140
   ClientTop       =   1515
   ClientWidth     =   8685
   Height          =   5940
   Left            =   1080
   LinkTopic       =   "Form1"
   ScaleHeight     =   5535
   ScaleWidth      =   8685
   Top             =   1170
   Width           =   8805
   Begin VB.CommandButton List
      Caption         =   "List"
      Height          =   375
      Left            =   5520
      TabIndex        =   7
      Top             =   480
      Width           =   1455
   End
   Begin VB.ListBox List1
      Height          =   4155
      Left            =   4800
      TabIndex        =   6
      Top             =   1080
      Width           =   3615
   End
   Begin VB.CommandButton Refresh
      Caption         =   "&Refresh"
      Height          =   375
      Index           =   1
      Left            =   2160
      TabIndex        =   5
      Top             =   600
      Width           =   855
   End
   Begin VB.CommandButton Forward
      Caption         =   "&Forward"
      Height          =   375
      Index           =   0
      Left            =   1080
      TabIndex        =   4
      Top             =   600
      Width           =   855
   End
   Begin VB.CommandButton Back
      Caption         =   "&Back"
      Height          =   375
      Left            =   120
      TabIndex        =   3
      Top             =   600
      Width           =   735
   End
   Begin VB.TextBox Text1
      Height          =   375
      Left            =   120
      TabIndex        =   2
      Top             =   120
      Width           =   3135
   End
   Begin VB.CommandButton Go
      Caption         =   "&Go"
      Height          =   375
      Left            =   3600
      TabIndex        =   1
      Top             =   120
      Width           =   855
   End
   Begin SHDocVwCtl.WebBrowser WebBrowser1
      Height          =   4215
      Left            =   120
      TabIndex        =   0
      Top             =   1080
      Width           =   4335
      ExtentX         =   7435
      ExtentY         =   7646
      ViewMode        =   1
      Offline         =   0
      Silent          =   0
      RegisterAsBrowser=   0
      RegisterAsDropTarget=   1
      AutoArrange     =   -1  'True
      NoClientEdge    =   0   'False
      AlignLeft       =   0   'False
      ViewID          =   "{0057D0E0-3573-11CF-AE69-08002B2E1262}"
   End
End
Attribute VB_Name = "Form1"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Private Sub Command2_Click()

End Sub

Private Sub Command3_Click()

End Sub

Private Sub Command4_Click()

End Sub

Private Sub Back_Click()
WebBrowser1.GoBack
End Sub

Private Sub Form_Load()
WebBrowser1.Navigate2 ("http://www.microsoft.com")
End Sub

Private Sub Forward_Click(Index As Integer)
WebBrowser1.GoForward
End Sub

Private Sub Go_Click()
WebBrowser1.Navigate2 (Text1)
End Sub

Private Sub List_Click()
 Dim allCol
  Dim TagName As String
  List1.Clear
  Set allCol = WebBrowser1.Document.All
  allcount = allCol.length
  For i = 0 To allcount - 1
    TagName = allCol.Item(i).TagName
    If TagName = "IMG" Or TagName = "A" Then
       TagName = TagName & " - " & allCol.Item(i).href
    End If
    List1.AddItem (TagName)
  Next
End Sub

Private Sub Refresh_Click(Index As Integer)
WebBrowser1.Refresh2
End Sub

Private Sub WebBrowser1_NavigateComplete2(ByVal pDisp As Object, URL As Variant)
Text1.Text = URL
List1.Clear
End Sub


README.TXT

=======================================================================

Release notes for the Glass Sample of the Internet Client Software
Development Kit:

Copyright (c) Microsoft Corporation, 1997
=======================================================================

Description:

The glass sample demonstrates some of the new multi-media
infrustructure enhancements made to the Internet Explorer that
Active X control creators can use. These features include CSS filters,
visual filters, timer services, and how to obtain a DirectDraw surfaces
from IE4. It demonstrates this by creating a looking glass which can alter
the contents of what ever is beneath it. In this case, it brightens or
dims depending on the properties set.
The html page, GlassCtl.htm, gives a brief demonstration
of some of its capabilities with just the press of a few buttons.
In this page. The GlassCSS.htm file shows the Glass control attached to
another element as a CSS Filter, which changes the visual representation
for that element.

Building note:

The glass control assumes that the project will be built with VC++ version 5,
with ATL.


CPGLASS.H

////////////////////////////////////////////////////////////////////////////// // CProxy_GlassEvents template <class T> class CProxy_GlassEvents : public IConnectionPointImpl<T, &DIID__GlassEvents, CComDynamicUnkArray> { public: //methods: //_GlassEvents : IDispatch public: void OnMouseDown( short Button, short Shift, long x, long y) { VARIANTARG* pvars = new VARIANTARG[4]; for (int i = 0; i < 4; i++) VariantInit(&pvars[i]); T* pT = (T*)this; pT->Lock(); IUnknown** pp = m_vec.begin(); while (pp < m_vec.end()) { if (*pp != NULL) { pvars[3].vt = VT_I2; pvars[3].iVal= Button; pvars[2].vt = VT_I2; pvars[2].iVal= Shift; pvars[1].vt = VT_I4; pvars[1].lVal= x; pvars[0].vt = VT_I4; pvars[0].lVal= y; DISPPARAMS disp = { pvars, NULL, 4, 0 }; IDispatch* pDispatch = reinterpret_cast<IDispatch*>(*pp); pDispatch->Invoke(DISPID_MOUSEDOWN, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL); } pp++; } pT->Unlock(); delete[] pvars; } void OnMouseMove( short Button, short Shift, long x, long y) { VARIANTARG* pvars = new VARIANTARG[4]; for (int i = 0; i < 4; i++) VariantInit(&pvars[i]); T* pT = (T*)this; pT->Lock(); IUnknown** pp = m_vec.begin(); while (pp < m_vec.end()) { if (*pp != NULL) { pvars[3].vt = VT_I2; pvars[3].iVal= Button; pvars[2].vt = VT_I2; pvars[2].iVal= Shift; pvars[1].vt = VT_I4; pvars[1].lVal= x; pvars[0].vt = VT_I4; pvars[0].lVal= y; DISPPARAMS disp = { pvars, NULL, 4, 0 }; IDispatch* pDispatch = reinterpret_cast<IDispatch*>(*pp); pDispatch->Invoke(DISPID_MOUSEMOVE, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL); } pp++; } pT->Unlock(); delete[] pvars; } void OnMouseUp( short Button, short Shift, long x, long y) { VARIANTARG* pvars = new VARIANTARG[4]; for (int i = 0; i < 4; i++) VariantInit(&pvars[i]); T* pT = (T*)this; pT->Lock(); IUnknown** pp = m_vec.begin(); while (pp < m_vec.end()) { if (*pp != NULL) { pvars[3].vt = VT_I2; pvars[3].iVal= Button; pvars[2].vt = VT_I2; pvars[2].iVal= Shift; pvars[1].vt = VT_I4; pvars[1].lVal= x; pvars[0].vt = VT_I4; pvars[0].lVal= y; DISPPARAMS disp = { pvars, NULL, 4, 0 }; IDispatch* pDispatch = reinterpret_cast<IDispatch*>(*pp); pDispatch->Invoke(DISPID_MOUSEUP, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL); } pp++; } pT->Unlock(); delete[] pvars; }  };


DISPIDS.H

//=--------------------------------------------------------------------------=
// Dispids.H
//=--------------------------------------------------------------------------=
// Copyright  1997  Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// dispids for use in an automation or control object.
//
#ifndef _DISPIDS_H_

//=--------------------------------------------------------------------------=
// for the Glass control
// properties & methods
//
#define DISPID_GLASS_LINEWIDTH     0
#define DISPID_GLASS_BRIGHTNESS    1
#define DISPID_GLASS_DYNAMICFADE   2
#define DISPID_GLASS_FADEINTERVAL  3
#define DISPID_GLASS_TIMESENSITIVE 4
#define DISPID_GLASS_TIMERMODE     5
#define DISPID_GLASS_FREEZE        6
#define DISPID_GLASS_TIMERTYPE     7
#define DISPID_GLASS_NEWREFTIMER   8
#define DISPID_GLASS_ENABLED       9

// events
//

#define _DISPIDS_H_
#endif // _DISPIDS_H_


GLASS.CPP

// Glass.cpp : Implementation of DLL Exports.

// You will need VC 4.2 and the full 4.2b patch (http://msvc/vc42b/vc42b.htm) in
// order to build this project.  This patch includes the final NT 4.0 SDK.
// You will also need MIDL 3.00.75, which is included with ATL 2.0.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f Glassps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "Glass.h"

#include "Glass_i.c"
#include "GlassCtl.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_GlassControl, CGlassControl)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
    LONG    ret=0;
    HKEY    key;

    // register self as a CSS Extension object (CSSFilter)
    // You may want to use RegCreateKeyEx here instead
    ret = RegCreateKey( HKEY_LOCAL_MACHINE,
                        _T("Software\\MICROSOFT\\Windows\\CurrentVersion\\Explorer\\CSSFilters"),
                        &key);
    if ( ERROR_SUCCESS == ret )
    {
        ret = RegSetValueEx( key, _T("glass"), 0, REG_SZ,
                           (LPBYTE)_T("{32F4F485-7F80-11D0-B835-00A0C911837F}"),
                           sizeof(_T("{32F4F485-7F80-11D0-B835-00A0C911837F}")) );
        RegCloseKey(key);
    }
    if ( ret )
        return HRESULT_FROM_WIN32(ret);

// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);

}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
    LONG    ret=0;

    // unregister self as a CSS Extension object (CSSFilter)
    // You may want to use RegDeleteKeyEx here instead
    ret = RegDeleteKey( HKEY_LOCAL_MACHINE,
          _T("Software\\MICROSOFT\\Windows\\CurrentVersion\\Explorer\\CSSFilters\\glass"));

_Module.UnregisterServer();
#if _WIN32_WINNT >= 0x0400
UnRegisterTypeLib(LIBID_GLASSLib, 1, 0, LOCALE_USER_DEFAULT, SYS_WIN32);
#endif
return S_OK;
}


GLASS.IDL

#include <olectl.h>
#include "DispIDs.h"
// Glass.idl : IDL source for Glass.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (Glass.tlb) and marshalling code.
import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(32F4F486-7F80-11D0-B835-00A0C911837F),
dual,
helpstring("IGlassControl Interface"),
pointer_default(unique)
]
interface IGlassControl : IDispatch
{
// properties
//
        [propget, id(DISPID_GLASS_LINEWIDTH)]
            HRESULT LineWidth([out, retval] long *plWidth);
        [propput, id(DISPID_GLASS_LINEWIDTH)]
            HRESULT LineWidth([in] long lWidth);
        [propget, id(DISPID_GLASS_BRIGHTNESS)]
            HRESULT Brightness([out, retval] long *plBrightness);
        [propput, id(DISPID_GLASS_BRIGHTNESS)]
            HRESULT Brightness([in] long lBrightness);
        [propget, id(DISPID_GLASS_DYNAMICFADE)]
            HRESULT DynamicFade([out, retval] long *plDynamicFade);
        [propput, id(DISPID_GLASS_DYNAMICFADE)]
            HRESULT DynamicFade([in] long lDynamicFade);
        [propget, id(DISPID_GLASS_FADEINTERVAL)]
            HRESULT FadeInterval([out, retval] long *plFadeInterval);
        [propput, id(DISPID_GLASS_FADEINTERVAL)]
            HRESULT FadeInterval([in] long lFadeInterval);
        [propget, id(DISPID_GLASS_FREEZE)]
            HRESULT Freeze([out, retval] long *plFreeze);
        [propput, id(DISPID_GLASS_FREEZE)]
            HRESULT Freeze([in] long lFreeze);
        [propget, id(DISPID_GLASS_ENABLED)]
            HRESULT Enabled([out, retval] long *plEnabled);
        [propput, id(DISPID_GLASS_ENABLED)]
            HRESULT Enabled([in] long lEnabled);

        //methods
//
};
[
uuid(32F4F480-7F80-11D0-B835-00A0C911837F),
version(1.0),
helpstring("GlassControl 1.0 Type Library")
]
library GLASSLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

[
uuid(234A2FA2-8039-11d0-B835-00A0C911837F),
helpstring("Event Interface for GlassControl")
]
dispinterface _GlassEvents
{
properties:
methods:
// for some reason OLE_XPOS_PIXELS AND OLE_YPOS_PIXELS cause a compile error
[id(DISPID_MOUSEDOWN)]
void OnMouseDown(short Button, short Shift, long x, long y);
[id(DISPID_MOUSEMOVE)]
void OnMouseMove(short Button, short Shift, long x, long y);
[id(DISPID_MOUSEUP)]
void OnMouseUp(short Button, short Shift, long x, long y);
};

[
uuid(32F4F485-7F80-11D0-B835-00A0C911837F),
helpstring("GlassControl Class")
]
coclass GlassControl
{
[default] interface IGlassControl;
[default,source] dispinterface _GlassEvents;
};
};


GLASSCTL.CPP

// GlassControl.cpp : Implementation of CGlassControl
#include "stdafx.h"
#include "initguid.h"
#include "Glass.h"
#include "GlassCtl.h"
#include <stdio.h>
#include "COMDEF.h"

short _GetShiftState(void);
short _GetButtonState(WPARAM wParam);

/////////////////////////////////////////////////////////////////////////////
// CGlassControl

//=--------------------------------------------------------------------------=
// CGlassControl::CGlassControl
//=--------------------------------------------------------------------------=
//
// Notes:
//
CGlassControl::CGlassControl()
{
    m_state.Brightness      = 64;
    m_state.iWidth          = 10;
    m_state.FadeInterval    = 5;
    m_state.fFreeze         = 0;
    m_state.fEnabled        = TRUE;
    m_state.fInvalid        = TRUE;
    m_pDirectDraw3          = NULL;
    m_pTimer                = NULL;
    m_pTimerSink            = NULL;
    m_TimerCookie           = 0;
    m_timeLastAdvise        = 0;

    m_pSource               = NULL;
    m_pSite                 = NULL;
    SetRect(&m_rc, 0, 0, 0, 0);
    m_pCSSSite              = NULL;
}

//=--------------------------------------------------------------------------=
// CGlassControl::~CGlassControl
//=--------------------------------------------------------------------------=
//
// Notes: Our timer sink is not part of this object to avoid circular
//        references, but the sink must be told that the control is
//        no longer around to be called.
//
CGlassControl::~CGlassControl ()
{
    if ( m_pTimerSink )
        m_pTimerSink->m_pGC = NULL;     // Sink is on its own
    if ( m_pDirectDraw3 )
        m_pDirectDraw3->Release();
    if ( m_pTimer )
        m_pTimer->Release();

    if (m_pSource)
    {
        m_pSource->SetSite(0);
        m_pSource->Release();
    }
    if (m_pSite)
        m_pSite->Release();

    if (m_pCSSSite)
        m_pCSSSite->Release();
}

//=--------------------------------------------------------------------------=
// CGlassControl::OnDraw
//=--------------------------------------------------------------------------=
//
// Parameters:
//    ATL_DRAWINGO       - [in]  HDC, rects, and all that stuff
//
// Output:
//    HRESULT
//
// Notes:
//

HRESULT CGlassControl::OnDraw(ATL_DRAWINFO& di)
{
    HBRUSH              hbrold;
    HPEN                hpen, hpenold;
    IDirectDrawSurface *pDDSurface = NULL;
    DDSURFACEDESC       desc;
    HRESULT             hr = S_OK;
    RECT                rc, rcLens, rcClip;
    HPALETTE            hpal = NULL;
    BYTE                itoi[256];
    VOID               *pBits = NULL;
    long                pitch;
    long                x, y;
int            bpp = 0, i;
    BYTE*               prow;
    BYTE*               pput;

    //
    // draw a thick, green rectangle border
    //
    hpen = CreatePen(PS_INSIDEFRAME, m_state.iWidth, RGB(0, 255, 0));
    if (!hpen) return E_FAIL;
    hpenold = (HPEN)SelectObject(di.hdcDraw, hpen);
    if (!m_bInPlaceActive || m_bWndLess)
        hbrold = (HBRUSH)SelectObject(di.hdcDraw, GetStockObject(HOLLOW_BRUSH));
    Rectangle(di.hdcDraw, di.prcBounds->left, di.prcBounds->top, di.prcBounds->right, di.prcBounds->bottom);
    if (!di.bOptimize) {
        if (!m_bInPlaceActive || m_bWndLess)
            SelectObject(di.hdcDraw, hbrold);
        SelectObject(di.hdcDraw, hpenold);
    }

    rcLens = *(RECT*)di.prcBounds;
    InflateRect(&rcLens, -m_state.iWidth, -m_state.iWidth);

    GetClipBox(di.hdcDraw, &rcClip);
    if (!IntersectRect(&rcLens, &rcClip, &rcLens))
        // no intersection with interior "lens" area; nothing to do
        goto Cleanup;

    // get surface factory via service provider
    if (!m_pDirectDraw3)
    {
        IServiceProvider * serviceProvider = 0;

        if ( m_spClientSite )       // this is if we are an ole object thru the <OBJECT> tag
            m_spClientSite->QueryInterface(
                    IID_IServiceProvider,
    (void**)&serviceProvider);
        else if ( m_pCSSSite )      // this is if we are a CSS Extension (Filter) object
        {
            hr = m_pCSSSite->QueryInterface( IID_IServiceProvider,
                                             (void**)&serviceProvider);
            if ( hr )
                goto ErrorExit;
        }
        else
            goto ErrorExit;

        if (FAILED(hr))
            goto ErrorExit;
        hr = serviceProvider->QueryService(
                SID_SDirectDraw3,
                IID_IDirectDraw3,
                (void**)&m_pDirectDraw3);
        serviceProvider->Release();
        if (FAILED(hr))
            goto ErrorExit;
    }
    // get DDSurface pointer
    hr = m_pDirectDraw3->GetSurfaceFromDC(di.hdcDraw, &pDDSurface);
    if (FAILED(hr))
        goto ErrorExit;

    // map our rect into the surface coords
    rc = rcLens;
    LPtoDP(di.hdcDraw, (POINT*)&rcLens, 2);

    // lock it to get pbits
    desc.dwSize = sizeof(desc);
    hr = pDDSurface->Lock(&rcLens, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
    if (FAILED(hr))
        goto ErrorExit;
    pBits = desc.lpSurface;
    pitch = desc.lPitch;
    bpp = desc.ddpfPixelFormat.dwRGBBitCount;
    if (bpp == 16
        && desc.ddpfPixelFormat.dwRBitMask == 0x7C00
        && desc.ddpfPixelFormat.dwGBitMask == 0x03E0
        && desc.ddpfPixelFormat.dwBBitMask == 0x001F)
    {
        bpp = 15;
    }

    switch (bpp)
    {
    case 8:
        //
        // 8-bpp paletted case
        //

        //
        // modify the bits directly
        //
struct
        {
            WORDpalVersion;
            WORDpalNumEntries;
            PALETTEENTRY    palPalEntry[256];
        } logpal;

        logpal.palNumEntries = 256;
logpal.palVersion = 0x300;
IDirectDrawPalette* pDDPalette;
hr = pDDSurface->GetPalette(&pDDPalette);
if (SUCCEEDED(hr))
{
hr = pDDPalette->GetEntries(0, 0, 256, logpal.palPalEntry);
pDDPalette->Release();
}
        if (SUCCEEDED(hr))
        {
                int     r,g,b;
HPALETTE    hpal;
                hpal = (HPALETTE)GetCurrentObject(di.hdcDraw, OBJ_PAL);

                for (i = 0; i < logpal.palNumEntries; ++i)
                {
                    r = max(0, min(255, logpal.palPalEntry[i].peRed   + m_state.Brightness));
                    g = max(0, min(255, logpal.palPalEntry[i].peGreen + m_state.Brightness));
                    b = max(0, min(255, logpal.palPalEntry[i].peBlue  + m_state.Brightness));
                    itoi[i] = GetNearestPaletteIndex(hpal, RGB(r,g,b));
                }
DeleteObject(hpal);

                // then use the map to translate the pixels
                prow = (BYTE*)pBits;
                for (y = rcLens.top; y < rcLens.bottom; ++y)
                {
                    pput = prow;
                    for (x = rcLens.left; x < rcLens.right; ++x)
                    {
                        *pput = itoi[*pput];
                        pput++;
                    }
                    prow += pitch;
                }
        }
break;

case 24:
        //
        // 24-bpp case
        //
        int p;
        for (i = 0; i <= 255; ++i)
            itoi[i] = max(0, min(255, i + m_state.Brightness));
        prow = (BYTE*)pBits;
        for (y = rcLens.top; y < rcLens.bottom; ++y)
        {
            pput = prow;
            for (x = rcLens.left; x < rcLens.right; ++x)
            {
                for (p = 1; p <= 3; ++p)
                {
                    *pput = itoi[*pput];
                    pput++;
                }
            }
            prow += pitch;
}
        break;

    case 32:
        //
        // 32-bpp case; same as 24-bpp, but 4 bytes per pixel
        //
        for (i = 0; i <= 255; ++i)
            itoi[i] = max(0, min(255, i + m_state.Brightness));
        prow = (BYTE*)pBits;
        for (y = rcLens.top; y < rcLens.bottom; ++y)
        {
            pput = prow;
            for (x = rcLens.left; x < rcLens.right; ++x)
            {
                for (p = 1; p <= 3; ++p)
                {
                    *pput = itoi[*pput];
                    pput++;
                }
                pput++; // do that extra dummy byte
            }
            prow += pitch;
        }
break;

    case 15:
        {
            //
            // 16-bpp 555 case
            //
            typedef struct
            {
                unsigned short r:5;
                unsigned short g:5;
                unsigned short b:5;
            } W555;
            W555* pputw;
            W555 w;
            for (i = 0; i <= 31; ++i)
                itoi[i] = max(0, min(31, ((i << 3) + m_state.Brightness + 3) >> 3));
            prow = (BYTE*)pBits;
            for (y = rcLens.top; y < rcLens.bottom; ++y)
            {
                pputw = (W555*)prow;
                for (x = rcLens.left; x < rcLens.right; ++x)
                {
                    w = *pputw;
                    w.r = itoi[w.r];
                    w.g = itoi[w.g];
                    w.b = itoi[w.b];
                    *pputw++ = w;
                }
                prow += pitch;
            }
        }
break;

    case 16:
        {
            //
            // 16-bpp 565 case
            //
            typedef struct
            {
                unsigned short r:5;
                unsigned short g:6;
                unsigned short b:5;
            } W565;
            W565* pputw;
            W565 w;
            for (i = 0; i <= 31; ++i)
                itoi[i] = max(0, min(31, ((i << 3) + m_state.Brightness + 4) >> 3));
            for (i = 0; i <= 63; ++i)
                itoi[i+32] = max(0, min(63, ((i << 2) + m_state.Brightness + 1) >> 2));
            prow = (BYTE*)pBits;
            for (y = rcLens.top; y < rcLens.bottom; ++y)
            {
                pputw = (W565*)prow;
                for (x = rcLens.left; x < rcLens.right; ++x)
                {
                    w = *pputw;
                    w.r = itoi[w.r];
                    w.g = itoi[w.g+32];
                    w.b = itoi[w.b];
                    *pputw++ = w;
                }
                prow += pitch;
            }
        }
break;

    case 4:
        //
        // 4-bpp case
        //
        prow = (BYTE*)pBits;
        for (y = rcLens.top; y < rcLens.bottom; ++y)
        {
            pput = prow;
            for (x = rcLens.left; x < rcLens.right; ++x)
            {
                // just an inversion, for demo purposes
                if (x & 1)
                {
                    *pput = *pput ^ 0x0F;
                    ++pput;
                }
                else
                {
                    *pput = *pput ^ 0xF0;
                }
            }
            prow += pitch;
        }
break;

    case 1:
        {
            //
            // 1-bpp case
            //
            static BYTE msk[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
            prow = (BYTE*)pBits;
            for (y = rcLens.top; y < rcLens.bottom; ++y)
            {
                pput = prow;
                for (x = rcLens.left; x < rcLens.right; ++x)
                {
                    // just an inversion, for demo purposes
                    *pput = *pput ^ msk[x & 0x7];
                    if ((x & 0x7) == 7)
                        ++pput;
                }
                prow += pitch;
            }
        }
break;

    default:
        _ASSERT("Unsupported Buffer Depth");
break;
}

Cleanup:
    if (pDDSurface)
    {
        // unlock pbits
        if (pBits)
            pDDSurface->Unlock(pBits);
        pDDSurface->Release();
    }
    DeleteObject(hpen);

    return S_OK;

ErrorExit:
    // mark control with big X to let user know something went wrong
    MoveToEx(di.hdcDraw, di.prcBounds->left+m_state.iWidth, di.prcBounds->top+m_state.iWidth, NULL);
    LineTo(  di.hdcDraw, di.prcBounds->right-m_state.iWidth, di.prcBounds->bottom-m_state.iWidth);
    MoveToEx(di.hdcDraw, di.prcBounds->right-m_state.iWidth, di.prcBounds->top+m_state.iWidth, NULL);
    LineTo(  di.hdcDraw, di.prcBounds->left+m_state.iWidth, di.prcBounds->bottom-m_state.iWidth);
    goto Cleanup;
}

//=--------------------------------------------------------------------------=
// CGlassControl::OnMouse
//=--------------------------------------------------------------------------=
// Handles Mouse events and forwards them on.
//
// Parameters:
//     see win32sdk on window procs [sans HWND -- it's in m_hwnd if you have one]
//
// Notes:
//
LRESULT
CGlassControl::OnMouse(UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    switch (msg) {
        case WM_LBUTTONDOWN:
        case WM_RBUTTONDOWN:
        case WM_MBUTTONDOWN:
            OnMouseDown(_GetButtonState(wParam), _GetShiftState(),
                      (long)LOWORD(lParam), (long)HIWORD(lParam));
            m_spInPlaceSite->SetCapture(TRUE);
            break;

        case WM_LBUTTONUP:
        case WM_RBUTTONUP:
        case WM_MBUTTONUP:
            OnMouseUp(_GetButtonState(wParam), _GetShiftState(),
                    (long)LOWORD(lParam), (long)HIWORD(lParam));
            m_spInPlaceSite->SetCapture(FALSE);
            break;
        case WM_MOUSEMOVE:
            OnMouseMove(_GetButtonState(wParam), _GetShiftState(),
                      (long)LOWORD(lParam), (long)HIWORD(lParam));
            break;
    }
    return 0;
}

//=--------------------------------------------------------------------------=
// CGlassControl::get_LineWidth    [IGlass]
//=--------------------------------------------------------------------------=
// returns out current width
//
// Parameters:
//    long *                - [out]
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::get_LineWidth
(
    long *plWidth
)
{
    CHECK_POINTER(plWidth);

    *plWidth = m_state.iWidth;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::put_LineWidth    [IGlass]
//=--------------------------------------------------------------------------=
// sets the current width
//
// Parameters:
//    long              - [in]  new value
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::put_LineWidth
(
    long lWidth
)
{
    // sanity checking!
    //
    if (lWidth < 0 || lWidth > 255)
        return CTL_E_INVALIDPROPERTYVALUE;

    // set the value, tell everybody about it, and force a repaint.
    //
    m_state.iWidth = lWidth;
    SetDirty(TRUE);
    if ( m_pSite )       // if we're a filter, normal ole control invalidation doesn't work
        InvalidateRect(NULL, FALSE);
    else
        FireViewChange();
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::get_Brightness    [IGlass]
//=--------------------------------------------------------------------------=
// returns out current Brightness
//
// Parameters:
//    long *                - [out]
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::get_Brightness
(
    long *plBrightness
)
{
    CHECK_POINTER(plBrightness);

    *plBrightness = m_state.Brightness;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::put_Brightness    [IGlass]
//=--------------------------------------------------------------------------=
// sets the current Brightness
//
// Parameters:
//    long              - [in]  new value
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::put_Brightness
(
    long lBrightness
)
{
    // sanity checking!
    //
    if (lBrightness < -255 || lBrightness > 255)
        return CTL_E_INVALIDPROPERTYVALUE;

    // set the value, tell everybody about it, and force a repaint.
    //
    m_state.Brightness = lBrightness;
    SetDirty(TRUE);
    if ( m_pSite )       // if we're a filter, normal ole control invalidation doesn't work
        InvalidateRect(NULL, FALSE);
    else
        FireViewChange();
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::get_DynamicFade    [IGlass]
//=--------------------------------------------------------------------------=
// returns out current DynamicFade
//
// Parameters:
//    long *                - [out]
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::get_DynamicFade
(
    long *plDynamicFade
)
{
    CHECK_POINTER(plDynamicFade);

    *plDynamicFade = m_state.DynamicFade;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::put_DynamicFade    [IGlass]
//=--------------------------------------------------------------------------=
// sets the current DynamicFade
//
// Parameters:
//    long              - [in]  new value
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::put_DynamicFade
(
    long lDynamicFade
)
{
    VARIANT vtimeMin, vtimeMax, vtimeInt, vtimeGet;

    // set the value, tell everybody about it
    //
    m_state.DynamicFade = (lDynamicFade > 0 ? lDynamicFade:0);
    VariantInit( &vtimeMin );
    VariantInit( &vtimeMax );
    VariantInit( &vtimeInt );
    VariantInit( &vtimeGet );
    V_VT(&vtimeMin) = VT_UI4;
    V_VT(&vtimeMax) = VT_UI4;
    V_VT(&vtimeInt) = VT_UI4;
    V_VT(&vtimeGet) = VT_UI4;

    if ( m_state.DynamicFade > 0 )
    {
        if ( m_TimerCookie > 0 && m_pTimer )
        {       // junk old advise
            m_pTimer->Unadvise( m_TimerCookie );
            m_TimerCookie = 0;
        }
        if ( !m_pTimer )
        {       // get a timer
            HRESULT hr;
            IServiceProvider * serviceProvider = 0;
            ITimerService *pTimerService = 0;

            hr = m_spClientSite->QueryInterface(
                        IID_IServiceProvider,
        (void**)&serviceProvider);
            if (FAILED(hr))
                return hr;

            hr = serviceProvider->QueryService(
                    SID_STimerService,
                    IID_ITimerService,
                    (void**)&pTimerService);
            serviceProvider->Release();

            hr = pTimerService->CreateTimer(NULL, &m_pTimer);
            if (FAILED(hr))
            {
                _ASSERTE(0 && "Timer creation failed" );
                return hr;
            }
            pTimerService->Release();
        }

        // make sure we have our sink ready
        if ( !m_pTimerSink )
        {
            m_pTimerSink = new CGlassControlSink( this );
            if ( !m_pTimerSink )
                return E_OUTOFMEMORY;
        }

        m_timeLastAdvise = 0;       // reset interval counter
        m_pTimer->GetTime(&vtimeGet);
        V_UI4(&vtimeMin) = V_UI4(&vtimeGet);
        V_UI4(&vtimeMax) = 0;
        V_UI4(&vtimeInt) = (unsigned)m_state.DynamicFade;

        m_pTimer->Advise( vtimeMin, vtimeMax, vtimeInt, 0,
                          (ITimerSink *)m_pTimerSink, &m_TimerCookie );

    }
    else
    {
        if ( m_pTimer && m_TimerCookie > 0 )
        {
            m_pTimer->Unadvise( m_TimerCookie );
            m_TimerCookie = 0;
        }
    }
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::get_FadeInterval    [IGlass]
//=--------------------------------------------------------------------------=
// returns out current FadeInterval
//
// Parameters:
//    long *                - [out]
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::get_FadeInterval
(
    long *plFadeInterval
)
{
    CHECK_POINTER(plFadeInterval);

    *plFadeInterval = m_state.FadeInterval;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::put_FadeInterval    [IGlass]
//=--------------------------------------------------------------------------=
// sets the current FadeInterval
//
// Parameters:
//    long              - [in]  new value
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::put_FadeInterval
(
    long lFadeInterval
)
{

    // set the value, tell everybody about it
    //
    m_state.FadeInterval = (lFadeInterval > 0 ? lFadeInterval:0);
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::get_Freeze    [IGlass]
//=--------------------------------------------------------------------------=
// returns out current Freeze
//
// Parameters:
//    long *                - [out]
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::get_Freeze
(
    long *plFreeze
)
{
    CHECK_POINTER(plFreeze);

    *plFreeze = m_state.fFreeze;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::put_Freeze    [IGlass]
//=--------------------------------------------------------------------------=
// sets the current Freeze
//
// Parameters:
//    long              - [in]  new value
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::put_Freeze
(
    long lFreeze
)
{
    m_state.fFreeze = !!lFreeze;
    if ( m_pTimer )
    {
        m_pTimer->Freeze( m_state.fFreeze );
    }
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::get_Enabled    [IGlass]
//=--------------------------------------------------------------------------=
// returns out current Enabled
//
// Parameters:
//    long *                - [out]
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::get_Enabled
(
    long *plEnabled
)
{
    CHECK_POINTER(plEnabled);

    *plEnabled = m_state.fEnabled;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::put_Enabled    [IGlass]
//=--------------------------------------------------------------------------=
// sets the current Enabled
//
// Parameters:
//    long              - [in]  new value
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::put_Enabled
(
    long lEnabled
)
{
    // set the value, tell everybody about it
    // Probably should freeze the timer, but then calling enabled true multiple
    // times, or false multiple times, could get dicy, so we won't bother.
    m_state.fEnabled = !!lEnabled;
    if ( m_pSite )       // if we're a filter, normal ole control invalidation doesn't work
        InvalidateRect(NULL, FALSE);
    else
        FireViewChange();
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::QueryHitPoint    [IViewObjectEx]
//=--------------------------------------------------------------------------=
// asks whether or not the user has clicked on a portion of the control, or
// on a transparent portion
//
// Parameters:
//    DWORD                - [in]  aspect
//    LPCRECT              - [in]  Bounds rectangle
//    POINT                - [in]  hit location client coordinates
//    LONG                 - [in]  what the container considers close
//    DWORD *              - [out] info about the hit
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControl::QueryHitPoint
(
    DWORD    dvAspect,
    LPCRECT  prcBounds,
    POINT    ptLocation,
    LONG     lCloseHint,
    DWORD   *pdwHitResult
)
{
    RECT rcSmall;

    CHECK_POINTER(pdwHitResult);

    rcSmall = *prcBounds;
    InflateRect(&rcSmall, -m_state.iWidth, -m_state.iWidth);

    // if it's within the outer retangle, but not within iWidth pixels of it,
    // then it's not a hit.
    //
    *pdwHitResult = (PtInRect(prcBounds, ptLocation) &&
                    !PtInRect(&rcSmall, ptLocation)) ? HITRESULT_HIT : HITRESULT_TRANSPARENT;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControl::HandleOnTimer         [Helper]
//=--------------------------------------------------------------------------=
// Real handler for OnTimer call on TimerSink help class.
//
// Parameters:
//    TIME              - [in]  scheduled time of event
//
// Output:
//    HRESULT
//
// Notes: Brightness delta is time sensitive, so if some notifications get
//        dropped it won't effect the perceived speed of the fading.
//
STDMETHODIMP CGlassControl::HandleOnTimer
(
    DWORD timeAdvise
)
{
    int cInterval = 1;

    if ( m_timeLastAdvise != 0 && m_state.DynamicFade > 0 &&
        timeAdvise > m_timeLastAdvise )
        {
        cInterval = (timeAdvise - m_timeLastAdvise) / m_state.DynamicFade;
        }
    m_timeLastAdvise = timeAdvise;

    while ( cInterval-- > 0 )
    {
        m_state.Brightness += m_state.FadeInterval;
        if ( m_state.Brightness >= 255 )
        {
            m_state.FadeInterval = -m_state.FadeInterval;
            m_state.Brightness = 255;
        }
        else if ( m_state.Brightness <= -255 )
        {
            m_state.FadeInterval = -m_state.FadeInterval;
            m_state.Brightness = -255;
        }
    }

    // set the value, tell everybody about it
    //
    SetDirty(TRUE);
    if ( m_pSite )       // if we're a filter, normal ole control invalidation doesn't work
        InvalidateRect(NULL, FALSE);
    else
        FireViewChange();

    return S_OK;
}

//=--------------------------------------------------------------------------=
// CGlassControlSink::***           [IUnknown]
//=--------------------------------------------------------------------------=
// Usual IUnknown implementation
//
ULONG
CGlassControlSink::AddRef()
{
    return ++m_cRefs;
}

ULONG
CGlassControlSink::Release()
{
    if ( 0 == --m_cRefs )
    {
        ULONG refCount = m_cRefs;
        delete this;
        return refCount;
    }
    return m_cRefs;
}

HRESULT
CGlassControlSink::QueryInterface(REFIID riid, void **ppv)
{
    if ( !ppv )
        return E_POINTER;

    *ppv = NULL;
    if (IID_IUnknown == riid) {
        *ppv = (void *)(IUnknown *)this;
    } else if (IID_ITimerSink == riid) {
        *ppv = (void *)(ITimerSink *)this;
    }

    if (*ppv)
    {
        ((IUnknown *)*ppv)->AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

//=--------------------------------------------------------------------------=
// CGlassControlSink::OnTimer           [ITimerSink]
//=--------------------------------------------------------------------------=
// callback proc for Timer notifications
//
// Parameters:
//    TIME              - [in]  scheduled time of event
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP CGlassControlSink::OnTimer
(
    VARIANT timeAdvise
)
{
    _ASSERTE( VT_UI4 == V_VT(&timeAdvise) && "Variant type mismatch" );
    return( m_pGC?m_pGC->HandleOnTimer(V_UI4(&timeAdvise)):S_OK );
}

//=--------------------------------------------------------------------------=
// _GetShiftState    [standard helper!]
//=--------------------------------------------------------------------------=
// used by people to get shift state information
//
// Output:
//    short        - mask of shift, ctrl and alt keys
//
// Notes:
//
short _GetShiftState(void)
{
    BOOL bShift = (GetKeyState(VK_SHIFT) < 0);
    BOOL bCtrl  = (GetKeyState(VK_CONTROL) < 0);
    BOOL bAlt   = (GetKeyState(VK_MENU) < 0);

    return (short)(bShift + (bCtrl << 1) + (bAlt << 2));
}

//=--------------------------------------------------------------------------=
// _GetButtonState    [standard helper!]
//=--------------------------------------------------------------------------=
// Returns which button was pressed. Does not handle cords
//
// Output:
//    short        - Left, Middle, Right, or no button

//
// Notes:
//
short _GetButtonState(WPARAM wParam)
{
    return (short)(wParam & MK_LBUTTON) ? LEFT_BUTTON :
                  ((wParam & MK_RBUTTON) ? RIGHT_BUTTON :
                  ((wParam & MK_MBUTTON) ? MIDDLE_BUTTON : 0));
}

//
// IViewFilter and IViewFilterSite interface method implementations
// This is for making the Glass control a VISUAL filter
//

STDMETHODIMP CGlassControl::SetSource(IViewFilter *pFilter)
{
    if (m_pSource)
    {
        m_pSource->SetSite(NULL);
        m_pSource->Release();
    }

    m_pSource = pFilter;
    if (m_pSource)
    {
        m_pSource->AddRef();
        m_pSource->SetSite((IViewFilterSite*)this);
    }
    return S_OK;
}

STDMETHODIMP CGlassControl::GetSource(IViewFilter **ppFilter)
{
    if (ppFilter == 0)
        return E_POINTER;
    *ppFilter = m_pSource;
    if (m_pSource)
    {
        m_pSource->AddRef();
    }
    return S_OK;
}

STDMETHODIMP CGlassControl::SetSite(IViewFilterSite *pSink)
{
    if (m_pSite)
        m_pSite->Release();
    m_pSite = pSink;

    if (m_pSite)
    {
        m_pSite->AddRef();
    }
    return S_OK;
}

STDMETHODIMP CGlassControl::GetSite(IViewFilterSite **ppSink)
{
    if (ppSink == 0)
        return E_POINTER;
    *ppSink = m_pSite;
    if (m_pSite)
        m_pSite->AddRef();
    return S_OK;
}

STDMETHODIMP CGlassControl::GetStatusBits(DWORD *pdwFlags)
{
    HRESULT hr = S_OK;

    if (m_pSource)
    {
        HRESULT hr = m_pSource->GetStatusBits(pdwFlags);
        if (SUCCEEDED(hr))
        {
            *pdwFlags &= ~FILTER_STATUS_OPAQUE;
        }
    }
    return hr;
}

STDMETHODIMP CGlassControl::SetPosition(LPCRECT prc)
{
    m_rc = *prc;

    if (m_pSource)
    {
        return m_pSource->SetPosition(prc);
    }
    return S_OK;
}

STDMETHODIMP CGlassControl::Draw(HDC hdc, LPCRECT prcBounds)
{
    HRESULT         hr;
    ATL_DRAWINFO    di;

    if (m_pSource == 0)
        return VIEW_E_DRAW;

    if (prcBounds == 0)
        return E_POINTER;

    // fill the hdc with the element we are attached to
    hr = m_pSource->Draw(hdc, prcBounds);
    if ( FAILED(hr) || !m_state.fEnabled )
        return hr;

    // Just Call through to OnDraw, but first fill in ATL_DRAWINFO
    // struct with bare info
    di.cbSize = sizeof(di);
    di.hicTargetDev = hdc;
    di.hdcDraw = hdc;
    memcpy( &(di.prcBounds), &prcBounds, sizeof(RECT) );
    memcpy( &(di.prcWBounds), &prcBounds, sizeof(RECT) );
    di.bOptimize = FALSE;
    di.bZoomed = FALSE;
    hr = OnDraw(di);
    return hr;

}

STDMETHODIMP CGlassControl::InvalidateRect(LPCRECT prc, BOOL fErase)
{
    if (m_pSite)
        return m_pSite->InvalidateRect(prc, fErase);
    return S_OK;
}

STDMETHODIMP CGlassControl::InvalidateRgn(HRGN hrgn, BOOL fErase)
{
    if (m_pSite)
        return m_pSite->InvalidateRgn(hrgn, fErase);
    return S_OK;
}

STDMETHODIMP CGlassControl::GetDC(LPCRECT prc, DWORD dwFlags, HDC *phdc)
{
    // THIS SAMPLE IS JUST IGNORING THIS FOR NOW!
    // can either return E_NOTIMPL or go thru site for default behavior
    if (m_pSite)
        return m_pSite->GetDC(prc, dwFlags, phdc);
    return E_NOTIMPL;
}

STDMETHODIMP CGlassControl::ReleaseDC(HDC hdc)
{
    // THIS SAMPLE IS JUST IGNORING THIS FOR NOW!
    // can either return E_NOTIMPL or go thru site for default behavior
    if (m_pSite)
        return m_pSite->ReleaseDC(hdc);
    return E_NOTIMPL;
}

STDMETHODIMP CGlassControl::OnStatusBitsChange(DWORD dwFlags)
{
    HRESULT hr = S_OK;
    if (m_pSite)
    {
        dwFlags &= ~FILTER_STATUS_OPAQUE;
        hr = m_pSite->OnStatusBitsChange(dwFlags);
    }
    return hr;
}

//
// ICSSFilter interface method implementations
// This is for making the Glass control a CSS filter
//

STDMETHODIMP CGlassControl::SetSite(ICSSFilterSite *pSite)
{
    IHTMLElement *pElem = NULL;
    IHTMLControlElement *pCtrlElem = NULL;
    IUnknown *pUnk = NULL;
    HRESULT hr = S_OK;

    if ( m_pCSSSite )
    {
        if ( SUCCEEDED(m_pCSSSite->GetElement( &pElem )) )
        {
            if ( SUCCEEDED(pElem->QueryInterface( IID_IHTMLControlElement,
                                                  (void **)&pCtrlElem )) )
            {
                ControlQueryInterface( IID_IUnknown, (void **)&pUnk );
                hr = pCtrlElem->removeFilter( pUnk );
            }
        }
        m_pCSSSite->Release();
    }

m_pCSSSite = pSite;

if ( m_pCSSSite )
{
m_pCSSSite->AddRef();

        // hook up with IViewFilter stuff now.
        if ( SUCCEEDED(hr=m_pCSSSite->GetElement( &pElem )) )
        {
            if ( SUCCEEDED(hr=pElem->QueryInterface( IID_IHTMLControlElement,
                                                     (void **)&pCtrlElem )) )
            {
                hr = ControlQueryInterface( IID_IUnknown, (void **)&pUnk );
                hr = pCtrlElem->addFilter( pUnk );
            }
        }
        if ( FAILED(hr) )
            goto error;
    }

cleanup:
    if ( pUnk )
        pUnk->Release();
    if ( pCtrlElem )
        pCtrlElem->Release();
    if ( pElem )
        pElem->Release();

return hr;

error:
    // Sorry, if I can't connect with a Site, I don't connect at all
    if ( m_pCSSSite )
    {
        m_pCSSSite->Release();
        m_pCSSSite = NULL;
    }
    goto cleanup;
}

STDMETHODIMP CGlassControl::OnAmbientPropertyChange( DISPID dispid )
{
    return E_NOTIMPL;
}

//
// IPersistPropertyBag overrides
//

STDMETHODIMP CGlassControl::Load( IPropertyBag *pPropBag, IErrorLog *pErrorLog )
{
    _variant_t  vBrightness;
    HRESULT hr = pPropBag->Read(L"brightness", &vBrightness, NULL);
    if (SUCCEEDED(hr))
    {
        m_state.Brightness = (long)vBrightness;
    }

_variant_t vEnabled;
hr = pPropBag->Read(L"enabled", &vEnabled, NULL);
    if (SUCCEEDED(hr))
    {
        m_state.fEnabled = (bool)vEnabled;
    }
    return S_OK;
}

//
// IObjectSafety overrides
//

STDMETHODIMP
CGlassControl::GetInterfaceSafetyOptions( REFIID riid,
                                          DWORD *pdwSupportedOptions,
                                          DWORD *pdwEnabledOptions )
{
    HRESULT hr;

    if ( pdwSupportedOptions == NULL || pdwEnabledOptions == NULL )
        return E_POINTER;

if ( riid == IID_IDispatch )
    {
        *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
        *pdwEnabledOptions = m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
    }
    else if ( riid == IID_IPersistPropertyBag )
    {
        *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
        *pdwEnabledOptions = m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_DATA;
    }
    else
    {
        IUnknown *pUnk;
        *pdwSupportedOptions = 0;
        *pdwEnabledOptions = 0;
        hr = _InternalQueryInterface(riid, (void**)&pUnk);
        if ( SUCCEEDED(hr) ) {
            pUnk->Release();
            hr = E_FAIL;
        }
    }
    return hr;
}

STDMETHODIMP
CGlassControl::SetInterfaceSafetyOptions( REFIID riid,
                                          DWORD dwOptionSetMask,
                                          DWORD dwEnabledOptions )
{
    HRESULT hr = S_OK;
    // If we're being asked to set our safe for initializing or
    // scripting option then oblige
    if ( riid == IID_IDispatch )
    {
        // Store our current safety level to return in GetInterfaceSafetyOptions
        m_dwSafety = dwEnabledOptions & dwOptionSetMask;
    }
    else if ( riid == IID_IPersistPropertyBag )
    {
        // Store our current safety level to return in GetInterfaceSafetyOptions
        m_dwSafety = dwEnabledOptions & dwOptionSetMask;
    }
    else
    {
        IUnknown *pUnk;
        hr = _InternalQueryInterface(riid, (void**)&pUnk);
        if ( SUCCEEDED(hr) ) {
            pUnk->Release();
            hr = E_FAIL;
        }
    }
    return hr;
}


GLASSCTL.H

// GlassControl.h : Declaration of the CGlassControl

#ifndef __GLASSCONTROL_H_
#define __GLASSCONTROL_H_

#include "resource.h"       // main symbols
#include "CPGlass.h"
#include <mshtml.h>         // for ICSSFilter
#include <ocmm.h>
#include <DDraw.H>
#include <DDrawEx.H>

typedef struct tagGLASSSTATE
{

    int         iWidth;
    int         Brightness;
    DWORD       DynamicFade;    // 0 = no fade, otherwise, value = interval
    int         FadeInterval;   // the change in brightness
    unsigned    fFreeze:1;
    unsigned    fEnabled:1;     // CSSFilter state
    unsigned    fInvalid:1;     // get drawn the first time around
} GLASSSTATE;

#define LEFT_BUTTON     0x01
#define RIGHT_BUTTON    0x02
#define MIDDLE_BUTTON   0x04

#define CHECK_POINTER(val) \
    if (!(val) || IsBadWritePtr((void *)(val), sizeof(void *))) return E_POINTER

/////////////////////////////////////////////////////////////////////////////
// CGlassControl
class ATL_NO_VTABLE CGlassControl :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CGlassControl, &CLSID_GlassControl>,
public CComControl<CGlassControl>,
public IDispatchImpl<IGlassControl, &IID_IGlassControl, &LIBID_GLASSLib>,
public IProvideClassInfo2Impl<&CLSID_GlassControl, &DIID__GlassEvents, &LIBID_GLASSLib>,
public IPersistStreamInitImpl<CGlassControl>,
public IPersistStorageImpl<CGlassControl>,
public IPersistPropertyBagImpl<CGlassControl>,
public IQuickActivateImpl<CGlassControl>,
public IOleControlImpl<CGlassControl>,
public IOleObjectImpl<CGlassControl>,
public IOleInPlaceActiveObjectImpl<CGlassControl>,
public IViewObjectExImpl<CGlassControl>,
public IOleInPlaceObjectWindowlessImpl<CGlassControl>,
public IDataObjectImpl<CGlassControl>,
public ISpecifyPropertyPagesImpl<CGlassControl>,
    public CProxy_GlassEvents<CGlassControl>,
    public IConnectionPointContainerImpl<CGlassControl>,
    public IObjectSafetyImpl<CGlassControl>,
public IViewFilter,
public IViewFilterSite,
public ICSSFilter
{
public:
    friend class CGlassControlSink;

    CGlassControl();
    ~CGlassControl();

DECLARE_REGISTRY_RESOURCEID(IDR_GLASSCONTROL)

BEGIN_COM_MAP(CGlassControl)
COM_INTERFACE_ENTRY(IGlassControl)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY_IMPL(IOleControl)
COM_INTERFACE_ENTRY_IMPL(IOleObject)
COM_INTERFACE_ENTRY_IMPL(IQuickActivate)
COM_INTERFACE_ENTRY_IMPL(IPersistStorage)
COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
COM_INTERFACE_ENTRY_IMPL(IPersistPropertyBag)
COM_INTERFACE_ENTRY_IMPL(ISpecifyPropertyPages)
COM_INTERFACE_ENTRY_IMPL(IDataObject)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
COM_INTERFACE_ENTRY_IMPL(IObjectSafety)
COM_INTERFACE_ENTRY(IViewFilter)
COM_INTERFACE_ENTRY(IViewFilterSite)
COM_INTERFACE_ENTRY(ICSSFilter)
END_COM_MAP()

BEGIN_CONNECTION_POINT_MAP(CGlassControl)
CONNECTION_POINT_ENTRY(DIID__GlassEvents)
END_CONNECTION_POINT_MAP()

BEGIN_PROPERTY_MAP(CGlassControl)
// Example entries
// PROP_ENTRY("Property Description", dispid, clsid)
PROP_PAGE(CLSID_StockColorPage)
END_PROPERTY_MAP()

BEGIN_MSG_MAP(CGlassControl)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
    MESSAGE_RANGE_HANDLER(WM_MOUSEMOVE, WM_MBUTTONUP, OnMouse)
END_MSG_MAP()

// IViewObjectEx
#define VIEWSTATUS_SURFACE 16
STDMETHOD(GetViewStatus)(DWORD* pdwStatus)
{
ATLTRACE(_T("IViewObjectExImpl::GetViewStatus\n"));
*pdwStatus = VIEWSTATUS_DVASPECTTRANSPARENT | VIEWSTATUS_SURFACE;
return S_OK;
}

// IGlassControl
public:
HRESULT OnDraw(ATL_DRAWINFO& di);

    // IViewObjectEX overrides
    STDMETHOD(QueryHitPoint)(DWORD dwAspect, LPCRECT pRectBounds, POINT ptlLoc, LONG lCloseHint, DWORD *pHitResult);

    // Event handling methods
    LRESULT OnMouse(UINT msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

    // IGlass properties
    //
    STDMETHOD(get_LineWidth)(long FAR* plWidth);
    STDMETHOD(put_LineWidth)(long lWidth);
    STDMETHOD(get_Brightness)(long FAR* plBrightness);
    STDMETHOD(put_Brightness)(long lBrightness);
    STDMETHOD(get_DynamicFade)(long FAR* plDynamicFade);
    STDMETHOD(put_DynamicFade)(long lDynamicFade);
    STDMETHOD(get_FadeInterval)(long FAR* plFadeInterval);
    STDMETHOD(put_FadeInterval)(long lFadeInterval);
    STDMETHOD(get_Freeze)(long FAR* plFreeze);
    STDMETHOD(put_Freeze)(long lFreeze);
    STDMETHOD(get_Enabled)(long FAR* plEnabled);
    STDMETHOD(put_Enabled)(long lEnabled);

    STDMETHOD(HandleOnTimer)( DWORD timeAdvise );

// IViewFilter
public:
    STDMETHOD(SetSource)(IViewFilter *pSource);
    STDMETHOD(GetSource)(IViewFilter **ppSource);
    STDMETHOD(SetSite)(IViewFilterSite *pSink);
    STDMETHOD(GetSite)(IViewFilterSite **ppSink);
    STDMETHOD(SetPosition)(LPCRECT prc);
    STDMETHOD(Draw)(HDC hdc, LPCRECT prcBounds);
    STDMETHOD(GetStatusBits)(DWORD *pdwFlags);

// IViewFilterSite
public:
    STDMETHOD(GetDC)(LPCRECT prc, DWORD dwFlags, HDC *phdc);
    STDMETHOD(ReleaseDC)(HDC hdc);
    STDMETHOD(InvalidateRect)(LPCRECT prc, BOOL fErase);
    STDMETHOD(InvalidateRgn)(HRGN hrgn, BOOL fErase);
    STDMETHOD(OnStatusBitsChange)(DWORD dwFlags);

// ICSSFilter methods
    STDMETHOD(SetSite)(ICSSFilterSite *pSite);
    STDMETHOD(OnAmbientPropertyChange)(DISPID dispid);

// IPersistPropertyBag methods
STDMETHOD(Load)(IPropertyBag *pPropBag, IErrorLog *pErrorLog);

// IObjectSafety methods
    STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid,
                                         DWORD *pdwSupportedOptions,
                                         DWORD *pdwEnabledOptions);
STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid,
                                         DWORD dwOptionSetMask,
                                         DWORD dwEnabledOptions);

    // private state information.
    //
    GLASSSTATE          m_state;
    IDirectDraw3        *m_pDirectDraw3;
    ITimer              *m_pTimer;
    CGlassControlSink   *m_pTimerSink;
    DWORD               m_TimerCookie;
    DWORD               m_timeLastAdvise;

    IViewFilter         *m_pSource;
    IViewFilterSite     *m_pSite;
    RECT                m_rc;
    ICSSFilterSite      *m_pCSSSite;
};

class CGlassControlSink : public ITimerSink
{

    friend class CGlassControl;

  public:

    CGlassControlSink( CGlassControl *pGC ) { m_pGC = pGC; m_cRefs = 0; }
    ~CGlassControlSink() { if ( m_pGC ) m_pGC->m_pTimerSink = NULL; }

    // IUnknown methods
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();
    STDMETHODIMP QueryInterface (REFIID iid, void **ppvObj);

    // ITimerSink methods
    STDMETHOD(OnTimer)(VARIANT timeAdvise);

  protected:
    ULONG               m_cRefs;
    CGlassControl      *m_pGC;
};

#endif //__GLASSCONTROL_H_


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Glass.rc
//
#define IDS_PROJNAME                    100
#define IDR_GLASSCONTROL                101

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           102
#endif
#endif


ATLBASE.H

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLBASE_H__
#define __ATLBASE_H__

#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifdef _UNICODE
#ifndef UNICODE
#define UNICODE         // UNICODE is used by Windows headers
#endif
#endif

#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE        // _UNICODE is used by C-runtime/MFC headers
#endif
#endif

#ifdef _DEBUG
#ifndef DEBUG
#define DEBUG
#endif
#endif

// namespace and our templates don't quite work with earlier compiler
// Can turn off namespace support directly, also
#if _MSC_VER<1100
#ifndef ATL_NO_NAMESPACE
#define ATL_NO_NAMESPACE
#endif
#endif

///////////////////////////////////////////////////////////////////////////////
// __declspec(novtable) is used on a class declaration to prevent the vtable
// pointer from being initialized in the constructor and destructor for the
// class.  This has many benefits because the linker can now eliminate the
// vtable and all the functions pointed to by the vtable.  Also, the actual
// constructor and destructor code are now smaller.
///////////////////////////////////////////////////////////////////////////////
// This should only be used on a class that is not directly createable but is
// rather only used as a base class.  Additionally, the constructor and
// destructor (if provided by the user) should not call anything that may cause
// a virtual function call to occur back on the object.
///////////////////////////////////////////////////////////////////////////////
// By default, the wizards will generate new ATL object classes with this
// attribute (through the ATL_NO_VTABLE macro).  This is normally safe as long
// the restriction mentioned above is followed.  It is always safe to remove
// this macro from your class, so if in doubt, remove it.
///////////////////////////////////////////////////////////////////////////////

#if _MSC_VER<1100
#define ATL_NO_VTABLE
#else
#ifdef _ATL_DISABLE_NO_VTABLE
#define ATL_NO_VTABLE
#else
#define ATL_NO_VTABLE __declspec(novtable)
#endif
#endif

#ifndef _ATL_NO_PRAGMA_WARNINGS
#pragma warning(disable: 4201) // nameless unions are part of C++
#pragma warning(disable: 4127) // constant expression
#pragma warning(disable: 4512) // can't generate assignment operator (so what?)
#pragma warning(disable: 4514) // unreferenced inlines are common
#pragma warning(disable: 4103) // pragma pack
#pragma warning(disable: 4702) // unreachable code
#pragma warning(disable: 4237) // bool
#pragma warning(disable: 4710) // function couldn't be inlined
#pragma warning(disable: 4355) // 'this' : used in base member initializer list
#pragma warning(disable: 4097) // typedef name used as synonym for class-name
#endif //!_ATL_NO_PRAGMA_WARNINGS

#include <windows.h>
#include <winnls.h>
#include <ole2.h>

#include <stddef.h>
#include <tchar.h>
#include <malloc.h>
#ifndef _ATL_NO_DEBUG_CRT
// Warning: if you define the above symbol, you will have
// to provide your own definition of the _ASSERTE(x) macro
// in order to compile ATL
#include <crtdbg.h>
#endif

#include <olectl.h>
#include <winreg.h>
#include <atliface.h>

#ifndef _ATL_PACKING
#define _ATL_PACKING 8
#endif
#pragma pack(push, _ATL_PACKING)

#include <atlconv.h>

#if defined(_ATL_DLL)
#pragma comment(lib, "atl.lib")
#define ATLAPI extern "C" HRESULT __stdcall
#define ATLAPI_(x) extern "C" x __stdcall
#elif defined(_ATL_DLL_IMPL)
#define ATLAPI extern "C" HRESULT __declspec(dllexport) __stdcall
#define ATLAPI_(x) extern "C" x __declspec(dllexport) __stdcall
#else
#define ATLAPI HRESULT __stdcall
#define ATLAPI_(x) x __stdcall
#endif

#ifndef ATL_NO_NAMESPACE
#ifndef _ATL_DLL_IMPL
namespace ATL
{
#endif
#endif

typedef HRESULT (WINAPI _ATL_CREATORFUNC)(void* pv, REFIID riid, LPVOID* ppv);
typedef HRESULT (WINAPI _ATL_CREATORARGFUNC)(void* pv, REFIID riid, LPVOID* ppv, DWORD dw);
typedef HRESULT (WINAPI _ATL_MODULEFUNC)(DWORD dw);
typedef LPCTSTR (WINAPI _ATL_DESCRIPTIONFUNC)();

struct _ATL_OBJMAP_ENTRY
{
const CLSID* pclsid;
HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister);
_ATL_CREATORFUNC* pfnGetClassObject;
_ATL_CREATORFUNC* pfnCreateInstance;
IUnknown* pCF;
DWORD dwRegister;
_ATL_DESCRIPTIONFUNC* pfnGetObjectDescription;
HRESULT WINAPI RevokeClassObject()
{
return CoRevokeClassObject(dwRegister);
}
HRESULT WINAPI RegisterClassObject(DWORD dwClsContext, DWORD dwFlags)
{
IUnknown* p = NULL;
HRESULT hRes = pfnGetClassObject(pfnCreateInstance, IID_IUnknown, (LPVOID*) &p);
if (SUCCEEDED(hRes))
hRes = CoRegisterClassObject(*pclsid, p, dwClsContext, dwFlags, &dwRegister);
if (p != NULL)
p->Release();
return hRes;
}
};

struct _ATL_REGMAP_ENTRY
{
LPCOLESTR     szKey;
LPCOLESTR     szData;
};

struct _ATL_MODULE
{
// Attributes
public:
UINT cbSize;
HINSTANCE m_hInst;
HINSTANCE m_hInstResource;
HINSTANCE m_hInstTypeLib;
_ATL_OBJMAP_ENTRY* m_pObjMap;
LONG m_nLockCnt;
HANDLE m_hHeap;
CRITICAL_SECTION m_csTypeInfoHolder;
CRITICAL_SECTION m_csWindowCreate;
CRITICAL_SECTION m_csObjMap;
};

//This define makes debugging asserts easier.
#define _ATL_SIMPLEMAPENTRY ((_ATL_CREATORARGFUNC*)1)

struct _ATL_INTMAP_ENTRY
{
const IID* piid;       // the interface id (IID)
DWORD dw;
_ATL_CREATORARGFUNC* pFunc; //NULL:end, 1:offset, n:ptr
};

/////////////////////////////////////////////////////////////////////////////
// QI Support

ATLAPI AtlInternalQueryInterface(void* pThis,
const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject);

/////////////////////////////////////////////////////////////////////////////
// Smart Pointer helpers

ATLAPI_(IUnknown*) AtlComPtrAssign(IUnknown** pp, IUnknown* lp);
ATLAPI_(IUnknown*) AtlComQIPtrAssign(IUnknown** pp, IUnknown* lp, REFIID riid);

/////////////////////////////////////////////////////////////////////////////
// Inproc Marshaling helpers
ATLAPI AtlFreeMarshalStream(IStream* pStream);
ATLAPI AtlMarshalPtrInProc(IUnknown* pUnk, const IID& iid, IStream** ppStream);
ATLAPI AtlUnmarshalPtr(IStream* pStream, const IID& iid, IUnknown** ppUnk);

ATLAPI_(BOOL) AtlWaitWithMessageLoop(HANDLE hEvent);

/////////////////////////////////////////////////////////////////////////////
// Connection Point Helpers

ATLAPI AtlAdvise(IUnknown* pUnkCP, IUnknown* pUnk, const IID& iid, LPDWORD pdw);
ATLAPI AtlUnadvise(IUnknown* pUnkCP, const IID& iid, DWORD dw);

/////////////////////////////////////////////////////////////////////////////
// IDispatch Error handling

ATLAPI AtlSetErrorInfo(const CLSID& clsid, LPCOLESTR lpszDesc,
DWORD dwHelpID, LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes,
HINSTANCE hInst);

/////////////////////////////////////////////////////////////////////////////
// Module

ATLAPI AtlModuleInit(_ATL_MODULE* pM, _ATL_OBJMAP_ENTRY* p, HINSTANCE h);
ATLAPI AtlModuleRegisterClassObjects(_ATL_MODULE* pM, DWORD dwClsContext, DWORD dwFlags);
ATLAPI AtlModuleRevokeClassObjects(_ATL_MODULE* pM);
ATLAPI AtlModuleGetClassObject(_ATL_MODULE* pM, REFCLSID rclsid, REFIID riid, LPVOID* ppv);
ATLAPI AtlModuleTerm(_ATL_MODULE* pM);
ATLAPI AtlModuleRegisterServer(_ATL_MODULE* pM, BOOL bRegTypeLib, const CLSID* pCLSID = NULL);
ATLAPI AtlModuleUnregisterServer(_ATL_MODULE* pM, const CLSID* pCLSID = NULL);
ATLAPI AtlModuleUpdateRegistryFromResourceD(_ATL_MODULE*pM, LPCOLESTR lpszRes,
BOOL bRegister, struct _ATL_REGMAP_ENTRY* pMapEntries, IRegistrar* pReg = NULL);
ATLAPI AtlModuleRegisterTypeLib(_ATL_MODULE* pM, LPCOLESTR lpszIndex);

#ifndef ATL_NO_NAMESPACE
#ifndef _ATL_DLL_IMPL
}; //namespace ATL
#endif
#endif

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

#if defined (_CPPUNWIND) & (defined(_ATL_EXCEPTIONS) | defined(_AFX))
#define ATLTRY(x) try{x;} catch(...) {}
#else
#define ATLTRY(x) x;
#endif

#ifdef _DEBUG
void _cdecl AtlTrace(LPCTSTR lpszFormat, ...);
#ifndef ATLTRACE
#define ATLTRACE            AtlTrace
#endif
#define ATLTRACENOTIMPL(funcname)   ATLTRACE(_T("%s not implemented.\n"), funcname); return E_NOTIMPL
#else
inline void _cdecl AtlTrace(LPCTSTR , ...){}
#ifndef ATLTRACE
#define ATLTRACE            1 ? (void)0 : AtlTrace
#endif
#define ATLTRACENOTIMPL(funcname)   return E_NOTIMPL
#endif //_DEBUG

#define offsetofclass(base, derived) ((DWORD)(static_cast<base*>((derived*)8))-8)

/////////////////////////////////////////////////////////////////////////////
// Master version numbers

#define _ATL     1      // Active Template Library
#define _ATL_VER 0x0200 // Active Template Library version 2.00

/////////////////////////////////////////////////////////////////////////////
// Win32 libraries

#ifndef _ATL_NO_FORCE_LIBS
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")
#pragma comment(lib, "olepro32.lib")
#pragma comment(lib, "uuid.lib")
#pragma comment(lib, "advapi32.lib")
#endif // _ATL_NO_FORCE_LIBS

template <class T>
class CComPtr
{
public:
typedef T _PtrClass;
CComPtr() {p=NULL;}
CComPtr(T* lp)
{
if ((p = lp) != NULL)
p->AddRef();
}
CComPtr(const CComPtr<T>& lp)
{
if ((p = lp.p) != NULL)
p->AddRef();
}
~CComPtr() {if (p) p->Release();}
void Release() {if (p) p->Release(); p=NULL;}
operator T*() {return (T*)p;}
T& operator*() {_ASSERTE(p!=NULL); return *p; }
//The assert on operator& usually indicates a bug.  If this is really
//what is needed, however, take the address of the p member explicitly.
T** operator&() { _ASSERTE(p==NULL); return &p; }
T* operator->() { _ASSERTE(p!=NULL); return p; }
T* operator=(T* lp){return (T*)AtlComPtrAssign((IUnknown**)&p, lp);}
T* operator=(const CComPtr<T>& lp)
{
return (T*)AtlComPtrAssign((IUnknown**)&p, lp.p);
}
#if _MSC_VER>1020
bool operator!(){return (p == NULL);}
#else
BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}
#endif
T* p;
};

//Note: CComQIPtr<IUnknown, &IID_IUnknown> is not meaningful
//      Use CComPtr<IUnknown>
template <class T, const IID* piid>
class CComQIPtr
{
public:
typedef T _PtrClass;
CComQIPtr() {p=NULL;}
CComQIPtr(T* lp)
{
if ((p = lp) != NULL)
p->AddRef();
}
CComQIPtr(const CComQIPtr<T,piid>& lp)
{
if ((p = lp.p) != NULL)
p->AddRef();
}
// If you get an error that this member is already defined, you are probably
// using a CComQIPtr<IUnknown, &IID_IUnknown>.  This is not necessary.
// Use CComPtr<IUnknown>
CComQIPtr(IUnknown* lp)
{
p=NULL;
if (lp != NULL)
lp->QueryInterface(*piid, (void **)&p);
}
~CComQIPtr() {if (p) p->Release();}
void Release() {if (p) p->Release(); p=NULL;}
operator T*() {return p;}
T& operator*() {_ASSERTE(p!=NULL); return *p; }
//The assert on operator& usually indicates a bug.  If this is really
//what is needed, however, take the address of the p member explicitly.
T** operator&() { _ASSERTE(p==NULL); return &p; }
T* operator->() {_ASSERTE(p!=NULL); return p; }
T* operator=(T* lp){return (T*)AtlComPtrAssign((IUnknown**)&p, lp);}
T* operator=(const CComQIPtr<T,piid>& lp)
{
return (T*)AtlComPtrAssign((IUnknown**)&p, lp.p);
}
T* operator=(IUnknown* lp)
{
return (T*)AtlComQIPtrAssign((IUnknown**)&p, lp, *piid);
}
#if _MSC_VER>1020
bool operator!(){return (p == NULL);}
#else
BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}
#endif
T* p;
};

/////////////////////////////////////////////////////////////////////////////
// CComBSTR
class CComBSTR
{
public:
BSTR m_str;
CComBSTR()
{
m_str = NULL;
}
/*explicit*/ CComBSTR(int nSize, LPCOLESTR sz = NULL)
{
m_str = ::SysAllocStringLen(sz, nSize);
}
/*explicit*/ CComBSTR(LPCOLESTR pSrc)
{
m_str = ::SysAllocString(pSrc);
}
/*explicit*/ CComBSTR(const CComBSTR& src)
{
m_str = src.Copy();
}
CComBSTR& operator=(const CComBSTR& src);
CComBSTR& operator=(LPCOLESTR pSrc);
~CComBSTR()
{
::SysFreeString(m_str);
}
unsigned int Length() const
{
return SysStringLen(m_str);
}
operator BSTR() const
{
return m_str;
}
BSTR* operator&()
{
return &m_str;
}
BSTR Copy() const
{
return ::SysAllocStringLen(m_str, ::SysStringLen(m_str));
}
void Attach(BSTR src)
{
_ASSERTE(m_str == NULL);
m_str = src;
}
BSTR Detach()
{
BSTR s = m_str;
m_str = NULL;
return s;
}
void Empty()
{
::SysFreeString(m_str);
m_str = NULL;
}
#if _MSC_VER>1020
bool operator!()
{
return (m_str == NULL);
}
#else
BOOL operator!()
{
return (m_str == NULL) ? TRUE : FALSE;
}
#endif
void Append(const CComBSTR& bstrSrc)
{
Append(bstrSrc.m_str, SysStringLen(bstrSrc.m_str));
}
void Append(LPCOLESTR lpsz)
{
Append(lpsz, ocslen(lpsz));
}
// a BSTR is just a LPCOLESTR so we need a special version to signify
// that we are appending a BSTR
void AppendBSTR(BSTR p)
{
Append(p, SysStringLen(p));
}
void Append(LPCOLESTR lpsz, int nLen);

CComBSTR& operator+=(const CComBSTR& bstrSrc)
{
AppendBSTR(bstrSrc.m_str);
return *this;
}
#ifndef OLE2ANSI
/*explicit*/ CComBSTR(LPCSTR pSrc);
/*explicit*/ CComBSTR(int nSize, LPCSTR sz = NULL);
CComBSTR& operator=(LPCSTR pSrc);
void Append(LPCSTR);
#endif
HRESULT WriteToStream(IStream* pStream);
HRESULT ReadFromStream(IStream* pStream);
};

/////////////////////////////////////////////////////////////////////////////
// CComVariant

class CComVariant : public tagVARIANT
{
// Constructors
public:
CComVariant()
{
::VariantInit(this);
}
~CComVariant()
{
Clear();
}

CComVariant(const VARIANT& varSrc)
{
::VariantInit(this);
InternalCopy(&varSrc);
}

CComVariant(const CComVariant& varSrc)
{
::VariantInit(this);
InternalCopy(&varSrc);
}

CComVariant(BSTR bstrSrc)
{
::VariantInit(this);
*this = bstrSrc;
}
CComVariant(LPCOLESTR lpszSrc)
{
::VariantInit(this);
*this = lpszSrc;
}

#ifndef OLE2ANSI
CComVariant(LPCSTR lpszSrc)
{
::VariantInit(this);
*this = lpszSrc;}
#endif

#if _MSC_VER>1020
CComVariant(bool bSrc)
{
::VariantInit(this);
vt = VT_BOOL;
#pragma warning(disable: 4310) // cast truncates constant value
boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE;
#pragma warning(default: 4310) // cast truncates constant value
}
#endif

CComVariant(int nSrc)
{
::VariantInit(this);
vt = VT_I4;
lVal = nSrc;
}
CComVariant(BYTE nSrc)
{
::VariantInit(this);
vt = VT_UI1;
bVal = nSrc;
}
CComVariant(short nSrc)
{
::VariantInit(this);
vt = VT_I2;
iVal = nSrc;
}
CComVariant(long nSrc, VARTYPE vtSrc = VT_I4)
{
_ASSERTE(vtSrc == VT_I4 || vtSrc == VT_ERROR);
::VariantInit(this);
vt = vtSrc;
lVal = nSrc;
}
CComVariant(float fltSrc)
{
::VariantInit(this);
vt = VT_R4;
fltVal = fltSrc;
}
CComVariant(double dblSrc)
{
::VariantInit(this);
vt = VT_R8;
dblVal = dblSrc;
}
CComVariant(CY cySrc)
{
::VariantInit(this);
vt = VT_CY;
cyVal.Hi = cySrc.Hi;
cyVal.Lo = cySrc.Lo;
}
CComVariant(IDispatch* pSrc)
{
::VariantInit(this);
vt = VT_DISPATCH;
pdispVal = pSrc;
// Need to AddRef as VariantClear will Release
if (pdispVal != NULL)
pdispVal->AddRef();
}
CComVariant(IUnknown* pSrc)
{
::VariantInit(this);
vt = VT_UNKNOWN;
punkVal = pSrc;
// Need to AddRef as VariantClear will Release
if (punkVal != NULL)
punkVal->AddRef();
}

// Assignment Operators
public:
CComVariant& operator=(const CComVariant& varSrc)
{
InternalCopy(&varSrc);
return *this;
}
CComVariant& operator=(const VARIANT& varSrc)
{
InternalCopy(&varSrc);
return *this;
}

CComVariant& operator=(BSTR bstrSrc);
CComVariant& operator=(LPCOLESTR lpszSrc);

#ifndef OLE2ANSI
CComVariant& operator=(LPCSTR lpszSrc);
#endif

#if _MSC_VER>1020
CComVariant& operator=(bool bSrc);
#endif
CComVariant& operator=(int nSrc);
CComVariant& operator=(BYTE nSrc);
CComVariant& operator=(short nSrc);
CComVariant& operator=(long nSrc);
CComVariant& operator=(float fltSrc);
CComVariant& operator=(double dblSrc);
CComVariant& operator=(CY cySrc);

CComVariant& operator=(IDispatch* pSrc);
CComVariant& operator=(IUnknown* pSrc);

// Comparison Operators
public:
#if _MSC_VER>1020
bool operator==(const VARIANT& varSrc);
bool operator!=(const VARIANT& varSrc) {return !operator==(varSrc);}
#else
BOOL operator==(const VARIANT& varSrc);
BOOL operator!=(const VARIANT& varSrc) {return !operator==(varSrc);}
#endif

// Operations
public:
HRESULT Clear() { return ::VariantClear(this); }
HRESULT Copy(const VARIANT* pSrc) { return ::VariantCopy(this, const_cast<VARIANT*>(pSrc)); }
HRESULT Attach(VARIANT* pSrc);
HRESULT Detach(VARIANT* pDest);
HRESULT ChangeType(VARTYPE vtNew, const VARIANT* pSrc = NULL);
HRESULT WriteToStream(IStream* pStream);
HRESULT ReadFromStream(IStream* pStream);

// Implementation
public:
HRESULT InternalClear();
void InternalCopy(const VARIANT* pSrc);
};
/////////////////////////////////////////////////////////////////////////////
// GUID comparison

inline BOOL InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2)
{
   return (
  ((PLONG) &rguid1)[0] == ((PLONG) &rguid2)[0] &&
  ((PLONG) &rguid1)[1] == ((PLONG) &rguid2)[1] &&
  ((PLONG) &rguid1)[2] == ((PLONG) &rguid2)[2] &&
  ((PLONG) &rguid1)[3] == ((PLONG) &rguid2)[3]);
}

inline BOOL InlineIsEqualUnknown(REFGUID rguid1)
{
   return (
  ((PLONG) &rguid1)[0] == 0 &&
  ((PLONG) &rguid1)[1] == 0 &&
#ifdef _MAC
  ((PLONG) &rguid1)[2] == 0xC0000000 &&
  ((PLONG) &rguid1)[3] == 0x00000046);
#else
  ((PLONG) &rguid1)[2] == 0x000000C0 &&
  ((PLONG) &rguid1)[3] == 0x46000000);
#endif
}

/////////////////////////////////////////////////////////////////////////////
// Threading Model Support

class CComCriticalSection
{
public:
void Lock() {EnterCriticalSection(&m_sec);}
void Unlock() {LeaveCriticalSection(&m_sec);}
void Init() {InitializeCriticalSection(&m_sec);}
void Term() {DeleteCriticalSection(&m_sec);}
CRITICAL_SECTION m_sec;
};

class CComAutoCriticalSection
{
public:
void Lock() {EnterCriticalSection(&m_sec);}
void Unlock() {LeaveCriticalSection(&m_sec);}
CComAutoCriticalSection() {InitializeCriticalSection(&m_sec);}
~CComAutoCriticalSection() {DeleteCriticalSection(&m_sec);}
CRITICAL_SECTION m_sec;
};

class CComFakeCriticalSection
{
public:
void Lock() {}
void Unlock() {}
void Init() {}
void Term() {}
};

class CComMultiThreadModelNoCS
{
public:
static ULONG WINAPI Increment(LPLONG p) {return InterlockedIncrement(p);}
static ULONG WINAPI Decrement(LPLONG p) {return InterlockedDecrement(p);}
typedef CComFakeCriticalSection AutoCriticalSection;
typedef CComFakeCriticalSection CriticalSection;
typedef CComMultiThreadModelNoCS ThreadModelNoCS;
};

class CComMultiThreadModel
{
public:
static ULONG WINAPI Increment(LPLONG p) {return InterlockedIncrement(p);}
static ULONG WINAPI Decrement(LPLONG p) {return InterlockedDecrement(p);}
typedef CComAutoCriticalSection AutoCriticalSection;
typedef CComCriticalSection CriticalSection;
typedef CComMultiThreadModelNoCS ThreadModelNoCS;
};

class CComSingleThreadModel
{
public:
static ULONG WINAPI Increment(LPLONG p) {return ++(*p);}
static ULONG WINAPI Decrement(LPLONG p) {return --(*p);}
typedef CComFakeCriticalSection AutoCriticalSection;
typedef CComFakeCriticalSection CriticalSection;
typedef CComSingleThreadModel ThreadModelNoCS;
};

#ifndef _ATL_SINGLE_THREADED
#ifndef _ATL_APARTMENT_THREADED
#ifndef _ATL_FREE_THREADED
#define _ATL_FREE_THREADED
#endif
#endif
#endif

#if defined(_ATL_SINGLE_THREADED)
typedef CComSingleThreadModel CComObjectThreadModel;
typedef CComSingleThreadModel CComGlobalsThreadModel;
#elif defined(_ATL_APARTMENT_THREADED)
typedef CComSingleThreadModel CComObjectThreadModel;
typedef CComMultiThreadModel CComGlobalsThreadModel;
#else
typedef CComMultiThreadModel CComObjectThreadModel;
typedef CComMultiThreadModel CComGlobalsThreadModel;
#endif

/////////////////////////////////////////////////////////////////////////////
// CComModule

#define THREADFLAGS_APARTMENT 0x1
#define THREADFLAGS_BOTH 0x2
#define AUTPRXFLAG 0x4

struct _AtlCreateWndData
{
void* m_pThis;
DWORD m_dwThreadID;
_AtlCreateWndData* m_pNext;
};

class CComModule : public _ATL_MODULE
{
// Operations
public:
_AtlCreateWndData* m_pCreateWndList;

void AddCreateWndData(_AtlCreateWndData* pData, void* pObject);
void* ExtractCreateWndData();

void Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h)
{
cbSize = sizeof(_ATL_MODULE);
m_pCreateWndList = NULL;
AtlModuleInit(this, p, h);
}
void Term()
{
AtlModuleTerm(this);
}

LONG Lock() {return CComGlobalsThreadModel::Increment(&m_nLockCnt);}
LONG Unlock() {return CComGlobalsThreadModel::Decrement(&m_nLockCnt);}
LONG GetLockCount() {return m_nLockCnt;}

HINSTANCE GetModuleInstance() {return m_hInst;}
HINSTANCE GetResourceInstance() {return m_hInstResource;}
HINSTANCE GetTypeLibInstance() {return m_hInstTypeLib;}

// Registry support (helpers)
HRESULT RegisterTypeLib()
{
return AtlModuleRegisterTypeLib(this, NULL);
}
HRESULT RegisterTypeLib(LPCTSTR lpszIndex)
{
USES_CONVERSION;
return AtlModuleRegisterTypeLib(this, T2COLE(lpszIndex));
}
HRESULT RegisterServer(BOOL bRegTypeLib = FALSE, const CLSID* pCLSID = NULL)
{
return AtlModuleRegisterServer(this, bRegTypeLib, pCLSID);
}

HRESULT UnregisterServer(const CLSID* pCLSID = NULL)
{
return AtlModuleUnregisterServer(this, pCLSID);
}

// Resource-based Registration
HRESULT WINAPI UpdateRegistryFromResourceD(LPCTSTR lpszRes, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries = NULL)
{
USES_CONVERSION;
return AtlModuleUpdateRegistryFromResourceD(this, T2COLE(lpszRes), bRegister,
pMapEntries);
}
HRESULT WINAPI UpdateRegistryFromResourceD(UINT nResID, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries = NULL)
{
return AtlModuleUpdateRegistryFromResourceD(this,
(LPCOLESTR)MAKEINTRESOURCE(nResID), bRegister, pMapEntries);
}

#ifdef _ATL_STATIC_REGISTRY
// Statically linking to Registry Ponent
HRESULT WINAPI UpdateRegistryFromResourceS(LPCTSTR lpszRes, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries = NULL);
HRESULT WINAPI UpdateRegistryFromResourceS(UINT nResID, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries = NULL);
#endif //_ATL_STATIC_REGISTRY

// Standard Registration
HRESULT WINAPI UpdateRegistryClass(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID, UINT nDescID, DWORD dwFlags, BOOL bRegister);
HRESULT WINAPI RegisterClassHelper(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID, UINT nDescID, DWORD dwFlags);
HRESULT WINAPI UnregisterClassHelper(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID);

// Register/Revoke All Class Factories with the OS (EXE only)
HRESULT RegisterClassObjects(DWORD dwClsContext, DWORD dwFlags)
{
return AtlModuleRegisterClassObjects(this, dwClsContext, dwFlags);
}
HRESULT RevokeClassObjects()
{
return AtlModuleRevokeClassObjects(this);
}

// Obtain a Class Factory (DLL only)
HRESULT GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return AtlModuleGetClassObject(this, rclsid, riid, ppv);
}

// Only used in CComAutoThreadModule
HRESULT CreateInstance(void* /*pfnCreateInstance*/, REFIID /*riid*/, void** /*ppvObj*/)
{
return S_OK;
}
};

/////////////////////////////////////////////////////////////////////////////////////////////
// Thread Pooling classes

class _AtlAptCreateObjData
{
public:
_ATL_CREATORFUNC* pfnCreateInstance;
const IID* piid;
HANDLE hEvent;
LPSTREAM pStream;
HRESULT hRes;
};

class CComApartment
{
public:
static UINT ATL_CREATE_OBJECT;
static DWORD WINAPI _Apartment(void* pv)
{
return ((CComApartment*)pv)->Apartment();
}
DWORD Apartment()
{
CoInitialize(NULL);
MSG msg;
while(GetMessage(&msg, 0, 0, 0))
{
if (msg.message == ATL_CREATE_OBJECT)
{
_AtlAptCreateObjData* pdata = (_AtlAptCreateObjData*)msg.lParam;
IUnknown* pUnk = NULL;
pdata->hRes = pdata->pfnCreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
if (SUCCEEDED(pdata->hRes))
pdata->hRes = CoMarshalInterThreadInterfaceInStream(*pdata->piid, pUnk, &pdata->pStream);
if (SUCCEEDED(pdata->hRes))
{
pUnk->Release();
ATLTRACE(_T("Object created on thread = %d\n"), GetCurrentThreadId());
}
SetEvent(pdata->hEvent);
}
DispatchMessage(&msg);
}
CoUninitialize();
return 0;
}
LONG Lock() {return CComGlobalsThreadModel::Increment(&m_nLockCnt);}
LONG Unlock(){return CComGlobalsThreadModel::Decrement(&m_nLockCnt);
}
LONG GetLockCount() {return m_nLockCnt;}

DWORD m_dwThreadID;
HANDLE m_hThread;
LONG m_nLockCnt;
};

class CComSimpleThreadAllocator
{
public:
CComSimpleThreadAllocator()
{
m_nThread = 0;
}
int GetThread(CComApartment* /*pApt*/, int nThreads)
{
if (++m_nThread == nThreads)
m_nThread = 0;
return m_nThread;
}
int m_nThread;
};

#if _MSC_VER>1020
template <class ThreadAllocator = CComSimpleThreadAllocator>
#else
template <class ThreadAllocator>
#endif
class CComAutoThreadModule : public CComModule
{
public:
void Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, int nThreads = GetDefaultThreads());
~CComAutoThreadModule();
HRESULT CreateInstance(void* pfnCreateInstance, REFIID riid, void** ppvObj);
LONG Lock();
LONG Unlock();
DWORD dwThreadID;
int m_nThreads;
CComApartment* m_pApartments;
ThreadAllocator m_Allocator;
static int GetDefaultThreads()
{
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwNumberOfProcessors * 4;
}
};

#ifdef _ATL_STATIC_REGISTRY
#define UpdateRegistryFromResource UpdateRegistryFromResourceS
#else
#define UpdateRegistryFromResource UpdateRegistryFromResourceD
#endif

/////////////////////////////////////////////////////////////////////////////
// CRegKey

class CRegKey
{
public:
CRegKey();
~CRegKey();

// Attributes
public:
operator HKEY() const;
HKEY m_hKey;

// Operations
public:
LONG SetValue(DWORD dwValue, LPCTSTR lpszValueName);
LONG QueryValue(DWORD& dwValue, LPCTSTR lpszValueName);
LONG QueryValue(LPTSTR szValue, LPCTSTR lpszValueName, DWORD* pdwCount);
LONG SetValue(LPCTSTR lpszValue, LPCTSTR lpszValueName = NULL);

LONG SetKeyValue(LPCTSTR lpszKeyName, LPCTSTR lpszValue, LPCTSTR lpszValueName = NULL);
static LONG WINAPI SetValue(HKEY hKeyParent, LPCTSTR lpszKeyName,
LPCTSTR lpszValue, LPCTSTR lpszValueName = NULL);

LONG Create(HKEY hKeyParent, LPCTSTR lpszKeyName,
LPTSTR lpszClass = REG_NONE, DWORD dwOptions = REG_OPTION_NON_VOLATILE,
REGSAM samDesired = KEY_ALL_ACCESS,
LPSECURITY_ATTRIBUTES lpSecAttr = NULL,
LPDWORD lpdwDisposition = NULL);
LONG Open(HKEY hKeyParent, LPCTSTR lpszKeyName,
REGSAM samDesired = KEY_ALL_ACCESS);
LONG Close();
HKEY Detach();
void Attach(HKEY hKey);
LONG DeleteSubKey(LPCTSTR lpszSubKey);
LONG RecurseDeleteKey(LPCTSTR lpszKey);
LONG DeleteValue(LPCTSTR lpszValue);
};

inline CRegKey::CRegKey()
{m_hKey = NULL;}

inline CRegKey::~CRegKey()
{Close();}

inline CRegKey::operator HKEY() const
{return m_hKey;}

inline HKEY CRegKey::Detach()
{
HKEY hKey = m_hKey;
m_hKey = NULL;
return hKey;
}

inline void CRegKey::Attach(HKEY hKey)
{
_ASSERTE(m_hKey == NULL);
m_hKey = hKey;
}

inline LONG CRegKey::DeleteSubKey(LPCTSTR lpszSubKey)
{
_ASSERTE(m_hKey != NULL);
return RegDeleteKey(m_hKey, lpszSubKey);
}

inline LONG CRegKey::DeleteValue(LPCTSTR lpszValue)

{
_ASSERTE(m_hKey != NULL);
return RegDeleteValue(m_hKey, (LPTSTR)lpszValue);
}

#pragma pack(pop)

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
using namespace ATL;
#endif

#endif // __ATLBASE_H__

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


ATLCOM.H

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLCOM_H__
#define __ATLCOM_H__

#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLBASE_H__
#error atlcom.h requires atlbase.h to be included first
#endif

#pragma pack(push, _ATL_PACKING)

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

#define CComConnectionPointContainerImpl IConnectionPointContainerImpl
#define CComISupportErrorInfoImpl ISupportErrorInfoImpl
#define CComProvideClassInfo2Impl IProvideClassInfoImpl
#define CComDualImpl IDispatchImpl

#ifdef _ATL_DEBUG_QI
HRESULT WINAPI AtlDumpIID(REFIID iid, LPCTSTR pszClassName, HRESULT hr);
#define _ATLDUMPIID(iid, name, hr) AtlDumpIID(iid, name, hr)
#else
#define _ATLDUMPIID(iid, name, hr) hr
#endif

#ifdef _ATL_DEBUG_REFCOUNT
//////////////////////////////////////////////////////////////////////////////
// CComDebugRefCount for interface level ref counting
class CComDebugRefCount
{
public:
CComDebugRefCount()
{
m_nRef = 0;
}
~CComDebugRefCount()
{
_ASSERTE(m_nRef == 0);
}
long m_nRef;
};
#define _ATL_DEBUG_ADDREF_RELEASE_IMPL(className) \
public:\
CComDebugRefCount _ref;\
virtual ULONG STDMETHODCALLTYPE _DebugAddRef(void) \
{return ((T*)this)->DebugAddRef(_ref.m_nRef, _T(#className));} \
virtual ULONG STDMETHODCALLTYPE _DebugRelease(void) \
{return ((T*)this)->DebugRelease(_ref.m_nRef, _T(#className));}
#else
#define _ATL_DEBUG_ADDREF_RELEASE_IMPL(className)\
virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;\
virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
#endif // _ATL_DEBUG_REFCOUNT

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc,
const IID& iid = GUID_NULL, HRESULT hRes = 0);

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc,
DWORD dwHelpID, LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL,
HRESULT hRes = 0);

HRESULT WINAPI AtlReportError(const CLSID& clsid, UINT nID,
const IID& iid = GUID_NULL, HRESULT hRes = 0,
HINSTANCE hInst = _Module.GetResourceInstance());

HRESULT WINAPI AtlReportError(const CLSID& clsid, UINT nID,
DWORD dwHelpID, LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL,
HRESULT hRes = 0, HINSTANCE hInst = _Module.GetResourceInstance());

#ifndef OLE2ANSI
HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCSTR lpszDesc,
const IID& iid = GUID_NULL, HRESULT hRes = 0);

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCSTR lpszDesc,
DWORD dwHelpID, LPCSTR lpszHelpFile, const IID& iid = GUID_NULL,
HRESULT hRes = 0);
#endif

#ifndef _ATL_NO_SECURITY

/////////////////////////////////////////////////////////////////////////////
// CSecurityDescriptor

class CSecurityDescriptor
{
public:
CSecurityDescriptor();
~CSecurityDescriptor();

public:
HRESULT Attach(PSECURITY_DESCRIPTOR pSelfRelativeSD);
HRESULT AttachObject(HANDLE hObject);
HRESULT Initialize();
HRESULT InitializeFromProcessToken(BOOL bDefaulted = FALSE);
HRESULT InitializeFromThreadToken(BOOL bDefaulted = FALSE, BOOL bRevertToProcessToken = TRUE);
HRESULT SetOwner(PSID pOwnerSid, BOOL bDefaulted = FALSE);
HRESULT SetGroup(PSID pGroupSid, BOOL bDefaulted = FALSE);
HRESULT Allow(LPCTSTR pszPrincipal, DWORD dwAccessMask);
HRESULT Deny(LPCTSTR pszPrincipal, DWORD dwAccessMask);
HRESULT Revoke(LPCTSTR pszPrincipal);

// utility functions
// Any PSID you get from these functions should be free()ed
static HRESULT SetPrivilege(LPCTSTR Privilege, BOOL bEnable = TRUE, HANDLE hToken = NULL);
static HRESULT GetTokenSids(HANDLE hToken, PSID* ppUserSid, PSID* ppGroupSid);
static HRESULT GetProcessSids(PSID* ppUserSid, PSID* ppGroupSid = NULL);
static HRESULT GetThreadSids(PSID* ppUserSid, PSID* ppGroupSid = NULL, BOOL bOpenAsSelf = FALSE);
static HRESULT CopyACL(PACL pDest, PACL pSrc);
static HRESULT GetCurrentUserSID(PSID *ppSid);
static HRESULT GetPrincipalSID(LPCTSTR pszPrincipal, PSID *ppSid);
static HRESULT AddAccessAllowedACEToACL(PACL *Acl, LPCTSTR pszPrincipal, DWORD dwAccessMask);
static HRESULT AddAccessDeniedACEToACL(PACL *Acl, LPCTSTR pszPrincipal, DWORD dwAccessMask);
static HRESULT RemovePrincipalFromACL(PACL Acl, LPCTSTR pszPrincipal);

operator PSECURITY_DESCRIPTOR()
{
return m_pSD;
}

public:
PSECURITY_DESCRIPTOR m_pSD;
PSID m_pOwner;
PSID m_pGroup;
PACL m_pDACL;
PACL m_pSACL;
};

#endif // _ATL_NO_SECURITY

/////////////////////////////////////////////////////////////////////////////
// COM Objects

#define DECLARE_PROTECT_FINAL_CONSTRUCT()\
void InternalFinalConstructAddRef() {InternalAddRef();}\
void InternalFinalConstructRelease() {InternalRelease();}

template <class T1>
class CComCreator
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
{
_ASSERTE(*ppv == NULL);
HRESULT hRes = E_OUTOFMEMORY;
T1* p = NULL;
ATLTRY(p = new T1(pv))
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes == S_OK)
hRes = p->QueryInterface(riid, ppv);
if (hRes != S_OK)
delete p;
}
return hRes;
}
};

template <class T1>
class CComInternalCreator
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
{
_ASSERTE(*ppv == NULL);
HRESULT hRes = E_OUTOFMEMORY;
T1* p = NULL;
ATLTRY(p = new T1(pv))
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes == S_OK)
hRes = p->_InternalQueryInterface(riid, ppv);
if (hRes != S_OK)
delete p;
}
return hRes;
}
};

template <HRESULT hr>
class CComFailCreator
{
public:
static HRESULT WINAPI CreateInstance(void*, REFIID, LPVOID*)
{
return hr;
}
};

template <class T1, class T2>
class CComCreator2
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
{
_ASSERTE(*ppv == NULL);
HRESULT hRes = E_OUTOFMEMORY;
if (pv == NULL)
hRes = T1::CreateInstance(NULL, riid, ppv);
else
hRes = T2::CreateInstance(pv, riid, ppv);
return hRes;
}
};

#define DECLARE_NOT_AGGREGATABLE(x) public:\
typedef CComCreator2< CComCreator< CComObject< x > >, CComFailCreator<CLASS_E_NOAGGREGATION> > _CreatorClass;
#define DECLARE_AGGREGATABLE(x) public:\
typedef CComCreator2< CComCreator< CComObject< x > >, CComCreator< CComAggObject< x > > > _CreatorClass;
#define DECLARE_ONLY_AGGREGATABLE(x) public:\
typedef CComCreator2< CComFailCreator<E_FAIL>, CComCreator< CComAggObject< x > > > _CreatorClass;
#define DECLARE_POLY_AGGREGATABLE(x) public:\
typedef CComCreator< CComPolyObject< x > > _CreatorClass;

struct _ATL_CREATORDATA
{
_ATL_CREATORFUNC* pFunc;
};

template <class Creator>
class _CComCreatorData
{
public:
static _ATL_CREATORDATA data;
};

template <class Creator>
_ATL_CREATORDATA _CComCreatorData<Creator>::data = {Creator::CreateInstance};

struct _ATL_CACHEDATA
{
DWORD dwOffsetVar;
_ATL_CREATORFUNC* pFunc;
};

template <class Creator, DWORD dwVar>
class _CComCacheData
{
public:
static _ATL_CACHEDATA data;
};

template <class Creator, DWORD dwVar>
_ATL_CACHEDATA _CComCacheData<Creator, dwVar>::data = {dwVar, Creator::CreateInstance};

struct _ATL_CHAINDATA
{
DWORD dwOffset;
const _ATL_INTMAP_ENTRY* (WINAPI *pFunc)();
};

template <class base, class derived>
class _CComChainData
{
public:
static _ATL_CHAINDATA data;
};

template <class base, class derived>
_ATL_CHAINDATA _CComChainData<base, derived>::data =
{offsetofclass(base, derived), base::_GetEntries};

template <class T, const CLSID* pclsid>
class CComAggregateCreator
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID/*riid*/, LPVOID* ppv)
{
_ASSERTE(*ppv == NULL);
_ASSERTE(pv != NULL);
T* p = (T*) pv;
// Add the following line to your object if you get a message about
// GetControllingUnknown() being undefined
// DECLARE_GET_CONTROLLING_UNKNOWN()
return CoCreateInstance(*pclsid, p->GetControllingUnknown(), CLSCTX_INPROC, IID_IUnknown, ppv);
}
};

#ifdef _ATL_DEBUG_QI
#define DEBUG_QI_ENTRY(x) \
{NULL, \
(DWORD)_T(#x), \
(_ATL_CREATORARGFUNC*)0},
#else
#define DEBUG_QI_ENTRY(x)
#endif //_ATL_DEBUG_QI

//If you get a message that FinalConstruct is ambiguous then you need to
// override it in your class and call each base class' version of this
#define BEGIN_COM_MAP(x) public: \
typedef x _ComMapClass; \
static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject, DWORD dw)\
{\
_ComMapClass* p = (_ComMapClass*)pv;\
p->Lock();\
HRESULT hRes = CComObjectRootBase::_Cache(pv, iid, ppvObject, dw);\
p->Unlock();\
return hRes;\
}\
IUnknown* GetUnknown() \
{ _ASSERTE(_GetEntries()[0].pFunc == _ATL_SIMPLEMAPENTRY); return (IUnknown*)((int)this+_GetEntries()->dw); } \
HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject) \
{ return InternalQueryInterface(this, _GetEntries(), iid, ppvObject); } \
const static _ATL_INTMAP_ENTRY* WINAPI _GetEntries() { \
static const _ATL_INTMAP_ENTRY _entries[] = { DEBUG_QI_ENTRY(x)

#define DECLARE_GET_CONTROLLING_UNKNOWN() public:\
virtual IUnknown* GetControllingUnknown() {return GetUnknown();}

#define COM_INTERFACE_ENTRY_BREAK(x)\
{&IID_##x, \
NULL, \
_Break},

#define COM_INTERFACE_ENTRY_NOINTERFACE(x)\
{&IID_##x, \
NULL, \
_NoInterface},

#define COM_INTERFACE_ENTRY(x)\
{&IID_##x, \
offsetofclass(x, _ComMapClass), \
_ATL_SIMPLEMAPENTRY},

#define COM_INTERFACE_ENTRY_IID(iid, x)\
{&iid,\
offsetofclass(x, _ComMapClass),\
_ATL_SIMPLEMAPENTRY},

#define COM_INTERFACE_ENTRY_IMPL(x)\
COM_INTERFACE_ENTRY_IID(IID_##x, x##Impl<_ComMapClass>)

#define COM_INTERFACE_ENTRY_IMPL_IID(iid, x)\
COM_INTERFACE_ENTRY_IID(iid, x##Impl<_ComMapClass>)

#define COM_INTERFACE_ENTRY2(x, x2)\
{&IID_##x,\
(DWORD)((x*)(x2*)((_ComMapClass*)8))-8,\
_ATL_SIMPLEMAPENTRY},

#define COM_INTERFACE_ENTRY2_IID(iid, x, x2)\
{&iid,\
(DWORD)((x*)(x2*)((_ComMapClass*)8))-8,\
_ATL_SIMPLEMAPENTRY},

#define COM_INTERFACE_ENTRY_FUNC(iid, dw, func)\
{&iid, \
dw, \
func},

#define COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func)\
{NULL, \
dw, \
func},

#define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x)\
{&iid,\
(DWORD)&_CComCreatorData<\
CComInternalCreator< CComTearOffObject< x > >\
>::data,\
_Creator},

#define COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, x, punk)\
{&iid,\
(DWORD)&_CComCacheData<\
CComCreator< CComCachedTearOffObject< x > >,\
(DWORD)offsetof(_ComMapClass, punk)\
>::data,\
_Cache},

#define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)\
{&iid,\
(DWORD)offsetof(_ComMapClass, punk),\
_Delegate},

#define COM_INTERFACE_ENTRY_AGGREGATE_BLIND(punk)\
{NULL,\
(DWORD)offsetof(_ComMapClass, punk),\
_Delegate},

#define COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid)\
{&iid,\
(DWORD)&_CComCacheData<\
CComAggregateCreator<_ComMapClass, &clsid>,\
(DWORD)offsetof(_ComMapClass, punk)\
>::data,\
_Cache},

#define COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(punk, clsid)\
{NULL,\
(DWORD)&_CComCacheData<\
CComAggregateCreator<_ComMapClass, &clsid>,\
(DWORD)offsetof(_ComMapClass, punk)\
>::data,\
_Cache},

#define COM_INTERFACE_ENTRY_CHAIN(classname)\
{NULL,\
(DWORD)&_CComChainData<classname, _ComMapClass>::data,\
_Chain},

#ifdef _ATL_DEBUG_QI
#define END_COM_MAP()   {NULL, 0, 0}};\
return &_entries[1];}
#else
#define END_COM_MAP()   {NULL, 0, 0}};\
return _entries;}
#endif // _ATL_DEBUG_QI

#define BEGIN_OBJECT_MAP(x) static _ATL_OBJMAP_ENTRY x[] = {
#define END_OBJECT_MAP()   {NULL, NULL, NULL, NULL}};
#define OBJECT_ENTRY(clsid, class) {&clsid, &class::UpdateRegistry, &class::_ClassFactoryCreatorClass::CreateInstance, &class::_CreatorClass::CreateInstance, NULL, 0, &class::GetObjectDescription },

#ifdef _ATL_DEBUG_QI
extern HRESULT WINAPI AtlDumpIID(REFIID iid, LPCTSTR pszClassName, HRESULT hr);
#endif // _ATL_DEBUG_QI

// the functions in this class don't need to be virtual because
// they are called from CComObject
class CComObjectRootBase
{
public:
CComObjectRootBase()
{
m_dwRef = 0L;
}
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease() {}

static HRESULT WINAPI InternalQueryInterface(void* pThis,
const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
{
_ASSERTE(pThis != NULL);
// First entry in the com map should be a simple map entry
_ASSERTE(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
#ifdef _ATL_DEBUG_QI
LPCTSTR pszClassName = (LPCTSTR) pEntries[-1].dw;
#endif // _ATL_DEBUG_QI
HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);
return _ATLDUMPIID(iid, pszClassName, hRes);
}

//Outer funcs
ULONG OuterAddRef()
{
return m_pOuterUnknown->AddRef();
}
ULONG OuterRelease()
{
return m_pOuterUnknown->Release();
}
HRESULT OuterQueryInterface(REFIID iid, void ** ppvObject)
{
return m_pOuterUnknown->QueryInterface(iid, ppvObject);
}

void SetVoid(void*) {}
void InternalFinalConstructAddRef() {}
void InternalFinalConstructRelease()
{
_ASSERTE(m_dwRef == 0);
}
// If this assert occurs, your object has probably been deleted
// Try using DECLARE_PROTECT_FINAL_CONSTRUCT()

static HRESULT WINAPI _Break(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _NoInterface(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _Creator(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _Delegate(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _Chain(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject, DWORD dw);

union
{
long m_dwRef;
IUnknown* m_pOuterUnknown;
};
};

template <class ThreadModel>
class CComObjectRootEx : public CComObjectRootBase
{
public:
typedef ThreadModel _ThreadModel;
typedef _ThreadModel::AutoCriticalSection _CritSec;

ULONG InternalAddRef()
{
_ASSERTE(m_dwRef != -1L);
return _ThreadModel::Increment(&m_dwRef);
}
ULONG InternalRelease()
{
return _ThreadModel::Decrement(&m_dwRef);
}

#ifdef _ATL_DEBUG_REFCOUNT
ULONG DebugAddRef(long& dw, LPCTSTR lpszClassName)
{
_ThreadModel::Increment(&dw);
ATLTRACE(_T("%s %d>\n"), lpszClassName, dw);
return AddRef();
}
ULONG DebugRelease(long& dw, LPCTSTR lpszClassName)
{
_ThreadModel::Decrement(&dw);
ATLTRACE(_T("%s %d<\n"), lpszClassName, dw);
return Release();
}
virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
#endif // _ATL_DEBUG_REFCOUNT

void Lock() {m_critsec.Lock();}
void Unlock() {m_critsec.Unlock();}
private:
_CritSec m_critsec;
};

#if _MSC_VER>1020
template <>
#endif
class CComObjectRootEx<CComSingleThreadModel> : public CComObjectRootBase
{
public:
typedef CComSingleThreadModel _ThreadModel;
typedef _ThreadModel::AutoCriticalSection _CritSec;

ULONG InternalAddRef()
{
_ASSERTE(m_dwRef != -1L);
return _ThreadModel::Increment(&m_dwRef);
}
ULONG InternalRelease()
{
return _ThreadModel::Decrement(&m_dwRef);
}

#ifdef _ATL_DEBUG_REFCOUNT
ULONG DebugAddRef(long& dw, LPCTSTR lpszClassName)
{
_ThreadModel::Increment(&dw);
ATLTRACE(_T("%s %d>\n"), lpszClassName, dw);
return AddRef();
}
ULONG DebugRelease(long& dw, LPCTSTR lpszClassName)
{
_ThreadModel::Decrement(&dw);
ATLTRACE(_T("%s %d<\n"), lpszClassName, dw);
return Release();
}
virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
#endif // _ATL_DEBUG_REFCOUNT

void Lock() {}
void Unlock() {}
};

typedef CComObjectRootEx<CComObjectThreadModel> CComObjectRoot;

#if defined(_WINDLL) | defined(_USRDLL)
#define DECLARE_CLASSFACTORY_EX(cf) typedef CComCreator< CComObjectCached< cf > > _ClassFactoryCreatorClass;
#else
// don't let class factory refcount influence lock count
#define DECLARE_CLASSFACTORY_EX(cf) typedef CComCreator< CComObjectNoLock< cf > > _ClassFactoryCreatorClass;
#endif
#define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(CComClassFactory)
#define DECLARE_CLASSFACTORY2(lic) DECLARE_CLASSFACTORY_EX(CComClassFactory2<lic>)
#define DECLARE_CLASSFACTORY_AUTO_THREAD() DECLARE_CLASSFACTORY_EX(CComClassFactoryAutoThread)
#define DECLARE_CLASSFACTORY_SINGLETON(obj) DECLARE_CLASSFACTORY_EX(CComClassFactorySingleton<obj>)

#define DECLARE_OBJECT_DESCRIPTION(x)\
static LPCTSTR WINAPI GetObjectDescription()\
{\
return _T(x);\
}

#define DECLARE_NO_REGISTRY()\
static HRESULT WINAPI UpdateRegistry(BOOL /*bRegister*/)\
{return S_OK;}

#define DECLARE_REGISTRY(class, pid, vpid, nid, flags)\
static HRESULT WINAPI UpdateRegistry(BOOL bRegister)\
{\
return _Module.UpdateRegistryClass(GetObjectCLSID(), pid, vpid, nid,\
flags, bRegister);\
}

#define DECLARE_REGISTRY_RESOURCE(x)\
static HRESULT WINAPI UpdateRegistry(BOOL bRegister)\
{\
return _Module.UpdateRegistryFromResource(_T(#x), bRegister);\
}

#define DECLARE_REGISTRY_RESOURCEID(x)\
static HRESULT WINAPI UpdateRegistry(BOOL bRegister)\
{\
return _Module.UpdateRegistryFromResource(x, bRegister);\
}

//DECLARE_STATIC_* provided for backward compatibility
#ifdef _ATL_STATIC_REGISTRY
#define DECLARE_STATIC_REGISTRY_RESOURCE(x) DECLARE_REGISTRY_RESOURCE(x)
#define DECLARE_STATIC_REGISTRY_RESOURCEID(x) DECLARE_REGISTRY_RESOURCEID(x)
#endif //_ATL_STATIC_REGISTRY

template<class Base> class CComObject; // fwd decl

template <class Owner, class ThreadModel = CComObjectThreadModel>
class CComTearOffObjectBase : public CComObjectRootEx<ThreadModel>
{
public:
typedef Owner _OwnerClass;
CComObject<Owner>* m_pOwner;
CComTearOffObjectBase() {m_pOwner = NULL;}
};

//Base is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
template <class Base>
class CComObject : public Base
{
public:
typedef Base _BaseClass;
CComObject(void* = NULL)
{
_Module.Lock();
}
// Set refcount to 1 to protect destruction
~CComObject()
{
m_dwRef = 1L;
FinalRelease();
_Module.Unlock();
}
//If InternalAddRef or InteralRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
static HRESULT WINAPI CreateInstance(CComObject<Base>** pp);
};

template <class Base>
HRESULT WINAPI CComObject<Base>::CreateInstance(CComObject<Base>** pp)
{
_ASSERTE(pp != NULL);
HRESULT hRes = E_OUTOFMEMORY;
CComObject<Base>* p = NULL;
ATLTRY(p = new CComObject<Base>())
if (p != NULL)
{
p->SetVoid(NULL);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes != S_OK)
{
delete p;
p = NULL;
}
}
*pp = p;
return hRes;
}

//Base is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
// CComObjectCached is used primarily for class factories in DLL's
// but it is useful anytime you want to cache an object
template <class Base>
class CComObjectCached : public Base
{
public:
typedef Base _BaseClass;
CComObjectCached(void* = NULL){}
// Set refcount to 1 to protect destruction
~CComObjectCached(){m_dwRef = 1L; FinalRelease();}
//If InternalAddRef or InteralRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)()
{
m_csCached.Lock();
ULONG l = InternalAddRef();
if (m_dwRef == 2)
_Module.Lock();
m_csCached.Unlock();
return l;
}
STDMETHOD_(ULONG, Release)()
{
m_csCached.Lock();
InternalRelease();
ULONG l = m_dwRef;
m_csCached.Unlock();
if (l == 0)
delete this;
else if (l == 1)
_Module.Unlock();
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
CComGlobalsThreadModel::AutoCriticalSection m_csCached;
};

//Base is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
template <class Base>
class CComObjectNoLock : public Base
{
public:
typedef Base _BaseClass;
CComObjectNoLock(void* = NULL){}
// Set refcount to 1 to protect destruction
~CComObjectNoLock() {m_dwRef = 1L; FinalRelease();}

//If InternalAddRef or InteralRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
};

// It is possible for Base not to derive from CComObjectRoot
// However, you will need to provide FinalConstruct and InternalQueryInterface
template <class Base>
class CComObjectGlobal : public Base
{
public:
typedef Base _BaseClass;
CComObjectGlobal(void* = NULL){m_hResFinalConstruct = FinalConstruct();}
~CComObjectGlobal() {FinalRelease();}

STDMETHOD_(ULONG, AddRef)() {return _Module.Lock();}
STDMETHOD_(ULONG, Release)(){return _Module.Unlock();}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
HRESULT m_hResFinalConstruct;
};

// It is possible for Base not to derive from CComObjectRoot
// However, you will need to provide FinalConstruct and InternalQueryInterface
template <class Base>
class CComObjectStack : public Base
{
public:
typedef Base _BaseClass;
CComObjectStack(void* = NULL){m_hResFinalConstruct = FinalConstruct();}
~CComObjectStack() {FinalRelease();}

STDMETHOD_(ULONG, AddRef)() {_ASSERTE(FALSE);return 0;}
STDMETHOD_(ULONG, Release)(){_ASSERTE(FALSE);return 0;}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{_ASSERTE(FALSE);return E_NOINTERFACE;}
HRESULT m_hResFinalConstruct;
};

template <class Base> //Base must be derived from CComObjectRoot
class CComContainedObject : public Base
{
public:
typedef Base _BaseClass;
CComContainedObject(void* pv) {m_pOuterUnknown = (IUnknown*)pv;}

STDMETHOD_(ULONG, AddRef)() {return OuterAddRef();}
STDMETHOD_(ULONG, Release)() {return OuterRelease();}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return OuterQueryInterface(iid, ppvObject);}
//GetControllingUnknown may be virtual if the Base class has declared
//DECLARE_GET_CONTROLLING_UNKNOWN()
IUnknown* GetControllingUnknown() {return m_pOuterUnknown;}
};

//contained is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
template <class contained>
class CComAggObject :
public IUnknown,
public CComObjectRootEx< contained::_ThreadModel::ThreadModelNoCS >
{
public:
typedef contained _BaseClass;
CComAggObject(void* pv) : m_contained(pv)
{
_Module.Lock();
}
//If you get a message that this call is ambiguous then you need to
// override it in your class and call each base class' version of this
HRESULT FinalConstruct()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalConstruct();
return m_contained.FinalConstruct();
}
void FinalRelease()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalRelease();
m_contained.FinalRelease();
}
// Set refcount to 1 to protect destruction
~CComAggObject()
{
m_dwRef = 1L;
FinalRelease();
_Module.Unlock();
}

STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
HRESULT hRes = S_OK;
if (InlineIsEqualUnknown(iid))
{
*ppvObject = (void*)(IUnknown*)this;
AddRef();
}
else
hRes = m_contained._InternalQueryInterface(iid, ppvObject);
return hRes;
}
CComContainedObject<contained> m_contained;
};

///////////////////////////////////////////////////////////////////////////////
// CComPolyObject can be either aggregated or not aggregated

template <class contained>
class CComPolyObject :
public IUnknown,
public CComObjectRootEx< contained::_ThreadModel::ThreadModelNoCS >
{
public:
typedef contained _BaseClass;
CComPolyObject(void* pv) : m_contained(pv ? pv : this)
{
_Module.Lock();
}
//If you get a message that this call is ambiguous then you need to
// override it in your class and call each base class' version of this
HRESULT FinalConstruct()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalConstruct();
return m_contained.FinalConstruct();
}
void FinalRelease()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalRelease();
m_contained.FinalRelease();
}
// Set refcount to 1 to protect destruction
~CComPolyObject()
{
m_dwRef = 1L;
FinalRelease();
_Module.Unlock();
}

STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
HRESULT hRes = S_OK;
if (InlineIsEqualUnknown(iid))
{
*ppvObject = (void*)(IUnknown*)this;
AddRef();
}
else
hRes = m_contained._InternalQueryInterface(iid, ppvObject);
return hRes;
}
CComContainedObject<contained> m_contained;
};

template <class Base>
class CComTearOffObject : public Base
{
public:
CComTearOffObject(void* pv)
{
_ASSERTE(m_pOwner == NULL);
m_pOwner = reinterpret_cast<CComObject<Base::_OwnerClass>*>(pv);
m_pOwner->AddRef();
}
// Set refcount to 1 to protect destruction
~CComTearOffObject()
{
m_dwRef = 1L;
FinalRelease();
m_pOwner->Release();
}

STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
return m_pOwner->QueryInterface(iid, ppvObject);
}
};

template <class contained>
class CComCachedTearOffObject :
public IUnknown,
public CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>
{
public:
typedef contained _BaseClass;
CComCachedTearOffObject(void* pv) :
m_contained(((contained::_OwnerClass*)pv)->GetControllingUnknown())
{
_ASSERTE(m_contained.m_pOwner == NULL);
m_contained.m_pOwner = reinterpret_cast<CComObject<contained::_OwnerClass>*>(pv);
}
//If you get a message that this call is ambiguous then you need to
// override it in your class and call each base class' version of this
HRESULT FinalConstruct()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalConstruct();
return m_contained.FinalConstruct();
}
void FinalRelease()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalRelease();
m_contained.FinalRelease();
}
// Set refcount to 1 to protect destruction
~CComCachedTearOffObject(){m_dwRef = 1L; FinalRelease();}

STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();

if (l == 0)
delete this;
return l;
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
HRESULT hRes = S_OK;
if (InlineIsEqualUnknown(iid))
{
*ppvObject = (void*)(IUnknown*)this;
AddRef();
}
else
hRes = m_contained._InternalQueryInterface(iid, ppvObject);
return hRes;
}
CComContainedObject<contained> m_contained;
};

class CComClassFactory :
public IClassFactory,
public CComObjectRootEx<CComGlobalsThreadModel>
{
public:
BEGIN_COM_MAP(CComClassFactory)
COM_INTERFACE_ENTRY(IClassFactory)
END_COM_MAP()

// IClassFactory
STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj);
STDMETHOD(LockServer)(BOOL fLock);
// helper
void SetVoid(void* pv)
{
m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv;
}
_ATL_CREATORFUNC* m_pfnCreateInstance;
};

class CComClassFactory2Base :
public IClassFactory2,
public CComObjectRootEx<CComGlobalsThreadModel>
{
public:
BEGIN_COM_MAP(CComClassFactory2Base)
COM_INTERFACE_ENTRY(IClassFactory)
COM_INTERFACE_ENTRY(IClassFactory2)
END_COM_MAP()
// IClassFactory
STDMETHOD(LockServer)(BOOL fLock);
// helper
void SetVoid(void* pv)
{
m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv;
}
_ATL_CREATORFUNC* m_pfnCreateInstance;
};

template <class license>
class CComClassFactory2 : public CComClassFactory2Base, license
{
public:
typedef license _LicenseClass;
typedef CComClassFactory2<license> _ComMapClass;
// IClassFactory
STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter,
REFIID riid, void** ppvObj)
{
_ASSERTE(m_pfnCreateInstance != NULL);
if (ppvObj == NULL)
return E_POINTER;
*ppvObj = NULL;
if (!IsLicenseValid())
return CLASS_E_NOTLICENSED;

if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
return CLASS_E_NOAGGREGATION;
else
return m_pfnCreateInstance(pUnkOuter, riid, ppvObj);
}
// IClassFactory2
STDMETHOD(CreateInstanceLic)(IUnknown* pUnkOuter, IUnknown* pUnkReserved,
REFIID riid, BSTR bstrKey, void** ppvObject)
{
_ASSERTE(m_pfnCreateInstance != NULL);
if (ppvObject == NULL)
return E_POINTER;
*ppvObject = NULL;
if ( ((bstrKey != NULL) && !VerifyLicenseKey(bstrKey)) ||
 ((bstrKey == NULL) && !IsLicenseValid()) )
return CLASS_E_NOTLICENSED;
return m_pfnCreateInstance(pUnkOuter, riid, ppvObject);
}
STDMETHOD(RequestLicKey)(DWORD dwReserved, BSTR* pbstrKey)
{
if (pbstrKey == NULL)
return E_POINTER;
*pbstrKey = NULL;

if (!IsLicenseValid())
return CLASS_E_NOTLICENSED;
return GetLicenseKey(dwReserved,pbstrKey) ? S_OK : E_FAIL;
}
STDMETHOD(GetLicInfo)(LICINFO* pLicInfo)
{
if (pLicInfo == NULL)
return E_POINTER;
pLicInfo->cbLicInfo = sizeof(LICINFO);
pLicInfo->fLicVerified = IsLicenseValid();
BSTR bstr = NULL;
pLicInfo->fRuntimeKeyAvail = GetLicenseKey(0,&bstr);
::SysFreeString(bstr);
return S_OK;
}
};

/////////////////////////////////////////////////////////////////////////////////////////////
// Thread Pooling class factory

class CComClassFactoryAutoThread :
public IClassFactory,
public CComObjectRootEx<CComGlobalsThreadModel>
{
public:
BEGIN_COM_MAP(CComClassFactoryAutoThread)
COM_INTERFACE_ENTRY(IClassFactory)
END_COM_MAP()

// helper
void SetVoid(void* pv)
{
m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv;
}
STDMETHODIMP CComClassFactoryAutoThread::CreateInstance(LPUNKNOWN pUnkOuter,
REFIID riid, void** ppvObj)
{
_ASSERTE(m_pfnCreateInstance != NULL);
HRESULT hRes = E_POINTER;
if (ppvObj != NULL)
{
*ppvObj = NULL;
// cannot aggregate across apartments
_ASSERTE(pUnkOuter == NULL);
if (pUnkOuter != NULL)
hRes = CLASS_E_NOAGGREGATION;
else
hRes = _Module.CreateInstance(m_pfnCreateInstance, riid, ppvObj);
}
return hRes;
}
STDMETHODIMP CComClassFactoryAutoThread::LockServer(BOOL fLock)
{
if (fLock)
_Module.Lock();
else
_Module.Unlock();
return S_OK;
}
_ATL_CREATORFUNC* m_pfnCreateInstance;
};

/////////////////////////////////////////////////////////////////////////////////////////////
// Singleton Class Factory
template <class T>
class CComClassFactorySingleton : public CComClassFactory
{
public:
// IClassFactory
STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
HRESULT hRes = E_POINTER;
if (ppvObj != NULL)
{
// can't ask for anything other than IUnknown when aggregating
_ASSERTE((pUnkOuter == NULL) || InlineIsEqualUnknown(riid));
if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
hRes = CLASS_E_NOAGGREGATION;
else
hRes = m_Obj.QueryInterface(riid, ppvObj);
}
return hRes;
}
CComObjectGlobal<T> m_Obj;
};

template <class T, const CLSID* pclsid>
class CComCoClass
{
public:
DECLARE_CLASSFACTORY()
DECLARE_AGGREGATABLE(T)
typedef T _CoClass;
static const CLSID& WINAPI GetObjectCLSID() {return *pclsid;}
static LPCTSTR WINAPI GetObjectDescription() {return NULL;}
static HRESULT WINAPI Error(LPCOLESTR lpszDesc,
const IID& iid = GUID_NULL, HRESULT hRes = 0)
{
return AtlReportError(GetObjectCLSID(), lpszDesc, iid, hRes);
}
static HRESULT WINAPI Error(LPCOLESTR lpszDesc, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
{
return AtlReportError(GetObjectCLSID(), lpszDesc, dwHelpID, lpszHelpFile,
iid, hRes);
}
static HRESULT WINAPI Error(UINT nID, const IID& iid = GUID_NULL,
HRESULT hRes = 0, HINSTANCE hInst = _Module.GetResourceInstance())
{
return AtlReportError(GetObjectCLSID(), nID, iid, hRes, hInst);
}
static HRESULT WINAPI Error(UINT nID, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL,
HRESULT hRes = 0, HINSTANCE hInst = _Module.GetResourceInstance())
{
return AtlReportError(GetObjectCLSID(), nID, dwHelpID, lpszHelpFile,
iid, hRes, hInst);
}
#ifndef OLE2ANSI
static HRESULT WINAPI Error(LPCSTR lpszDesc,
const IID& iid = GUID_NULL, HRESULT hRes = 0)
{
return AtlReportError(GetObjectCLSID(), lpszDesc, iid, hRes);
}
static HRESULT WINAPI Error(LPCSTR lpszDesc, DWORD dwHelpID,
LPCSTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
{
return AtlReportError(GetObjectCLSID(), lpszDesc, dwHelpID,
lpszHelpFile, iid, hRes);
}
#endif
};

// ATL doesn't support multiple LCID's at the same time
// Whatever LCID is queried for first is the one that is used.
class CComTypeInfoHolder
{
// Should be 'protected' but can cause compiler to generate fat code.
public:
const GUID* m_pguid;
const GUID* m_plibid;
WORD m_wMajor;
WORD m_wMinor;

ITypeInfo* m_pInfo;
long m_dwRef;

public:
HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo);

void AddRef();
void Release();
HRESULT GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo);
HRESULT GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
LCID lcid, DISPID* rgdispid);
HRESULT Invoke(IDispatch* p, DISPID dispidMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
EXCEPINFO* pexcepinfo, UINT* puArgErr);
};

//////////////////////////////////////////////////////////////////////////////
// IObjectWithSite
//
template <class T>
class ATL_NO_VTABLE IObjectWithSiteImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IObjectWithSiteImpl)

// IObjectWithSite
//
STDMETHOD(SetSite)(IUnknown *pUnkSite)
{
ATLTRACE(_T("IObjectWithSiteImpl::SetSite\n"));
T* pT = static_cast<T*>(this);
pT->m_spUnkSite = pUnkSite;
return S_OK;
}
STDMETHOD(GetSite)(REFIID riid, void **ppvSite)
{
ATLTRACE(_T("IObjectWithSiteImpl::GetSite\n"));
T* pT = static_cast<T*>(this);
_ASSERTE(ppvSite);
HRESULT hRes = E_POINTER;
if (ppvSite != NULL)
{
if (pT->m_spUnkSite)
hRes = pT->m_spUnkSite->QueryInterface(riid, ppvSite);
else
{
*ppvSite = NULL;
hRes = E_FAIL;
}
}
return hRes;
}

CComPtr<IUnknown> m_spUnkSite;
};

/////////////////////////////////////////////////////////////////////////////
// IDispatchImpl

template <class T, const IID* piid, const GUID* plibid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE IDispatchImpl : public T
{
public:
typedef tihclass _tihclass;
IDispatchImpl() {_tih.AddRef();}
~IDispatchImpl() {_tih.Release();}

STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
{*pctinfo = 1; return S_OK;}

STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
{return _tih.GetTypeInfo(itinfo, lcid, pptinfo);}

STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
LCID lcid, DISPID* rgdispid)
{return _tih.GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);}

STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
EXCEPINFO* pexcepinfo, UINT* puArgErr)
{return _tih.Invoke((IDispatch*)this, dispidMember, riid, lcid,
wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);}
protected:
static _tihclass _tih;
static HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo)
{return _tih.GetTI(lcid, ppInfo);}
};

template <class T, const IID* piid, const GUID* plibid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
IDispatchImpl<T, piid, plibid, wMajor, wMinor, tihclass>::_tihclass
IDispatchImpl<T, piid, plibid, wMajor, wMinor, tihclass>::_tih =
{piid, plibid, wMajor, wMinor, NULL, 0};

/////////////////////////////////////////////////////////////////////////////
// IProvideClassInfoImpl

template <const CLSID* pcoclsid, const IID* psrcid, const GUID* plibid,
WORD wMajor = 1, WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE IProvideClassInfo2Impl : public IProvideClassInfo2
{
public:
typedef tihclass _tihclass;
IProvideClassInfo2Impl() {_tih.AddRef();}
~IProvideClassInfo2Impl() {_tih.Release();}

STDMETHOD(GetClassInfo)(ITypeInfo** pptinfo)
{
return _tih.GetTypeInfo(0, LANG_NEUTRAL, pptinfo);
}
STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID* pGUID)
{
if (pGUID == NULL)
return E_POINTER;

if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID && psrcid)
{
*pGUID = *psrcid;
return S_OK;
}
*pGUID = GUID_NULL;
return E_FAIL;
}

protected:
static _tihclass _tih;
};

template <const CLSID* pcoclsid, const IID* psrcid, const GUID* plibid,
WORD wMajor = 1, WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
IProvideClassInfo2Impl<pcoclsid, psrcid, plibid, wMajor, wMinor, tihclass>::_tihclass
IProvideClassInfo2Impl<pcoclsid, psrcid, plibid, wMajor, wMinor, tihclass>::_tih =
{pcoclsid,plibid, wMajor, wMinor, NULL, 0};

/////////////////////////////////////////////////////////////////////////////
// ISupportErrorInfoImpl

template <const IID* piid>
class ATL_NO_VTABLE ISupportErrorInfoImpl : public ISupportErrorInfo
{
public:
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid)\
{return (InlineIsEqualGUID(riid,*piid)) ? S_OK : S_FALSE;}
};

/////////////////////////////////////////////////////////////////////////////
// CComEnumImpl

// These _CopyXXX classes are used with enumerators in order to control
// how enumerated items are initialized, copied, and deleted

// Default is shallow copy with no special init or cleanup
template <class T>
class _Copy
{
public:
static void copy(T* p1, T* p2) {memcpy(p1, p2, sizeof(T));}
static void init(T*) {}
static void destroy(T*) {}
};

#if _MSC_VER>1020
template<>
#endif
class _Copy<VARIANT>
{
public:
static void copy(VARIANT* p1, VARIANT* p2) {VariantCopy(p1, p2);}
static void init(VARIANT* p) {VariantInit(p);}
static void destroy(VARIANT* p) {VariantClear(p);}
};

#if _MSC_VER>1020
template<>
#endif
class _Copy<LPOLESTR>
{
public:
static void copy(LPOLESTR* p1, LPOLESTR* p2)
{
(*p1) = (LPOLESTR)CoTaskMemAlloc(sizeof(OLECHAR)*(ocslen(*p2)+1));
ocscpy(*p1,*p2);
}
static void init(LPOLESTR* p) {*p = NULL;}
static void destroy(LPOLESTR* p) { CoTaskMemFree(*p);}
};

#if _MSC_VER>1020
template<>
#endif
class _Copy<OLEVERB>
{
public:
static void copy(OLEVERB* p1, OLEVERB* p2)
{
*p1 = *p2;
if (p1->lpszVerbName == NULL)
return;
p1->lpszVerbName = (LPOLESTR)CoTaskMemAlloc(sizeof(OLECHAR)*(ocslen(p2->lpszVerbName)+1));
ocscpy(p1->lpszVerbName,p2->lpszVerbName);
}
static void init(OLEVERB* p) { p->lpszVerbName = NULL;}
static void destroy(OLEVERB* p) { if (p->lpszVerbName) CoTaskMemFree(p->lpszVerbName);}
};

#if _MSC_VER>1020
template<>
#endif
class _Copy<CONNECTDATA>
{
public:
static void copy(CONNECTDATA* p1, CONNECTDATA* p2)
{
*p1 = *p2;
if (p1->pUnk)
p1->pUnk->AddRef();
}
static void init(CONNECTDATA* ) {}
static void destroy(CONNECTDATA* p) {if (p->pUnk) p->pUnk->Release();}
};

template <class T>
class _CopyInterface
{
public:
static void copy(T** p1, T** p2)
{*p1 = *p2;if (*p1) (*p1)->AddRef();}
static void init(T** ) {}
static void destroy(T** p) {if (*p) (*p)->Release();}
};

template<class T>
class ATL_NO_VTABLE CComIEnum : public IUnknown
{
public:
STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched) = 0;
STDMETHOD(Skip)(ULONG celt) = 0;
STDMETHOD(Reset)(void) = 0;
STDMETHOD(Clone)(CComIEnum<T>** ppEnum) = 0;
};

enum CComEnumFlags
{
//see FlagBits in CComEnumImpl
AtlFlagNoCopy = 0,
AtlFlagTakeOwnership = 2,
AtlFlagCopy = 3 // copy implies ownership
};

template <class Base, const IID* piid, class T, class Copy>
class ATL_NO_VTABLE CComEnumImpl : public Base
{
public:
CComEnumImpl() {m_begin = m_end = m_iter = NULL; m_dwFlags = 0; m_pUnk = NULL;}
~CComEnumImpl();
STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched);
STDMETHOD(Skip)(ULONG celt);
STDMETHOD(Reset)(void){m_iter = m_begin;return S_OK;}
STDMETHOD(Clone)(Base** ppEnum);
HRESULT Init(T* begin, T* end, IUnknown* pUnk,
CComEnumFlags flags = AtlFlagNoCopy);
IUnknown* m_pUnk;
T* m_begin;
T* m_end;
T* m_iter;
DWORD m_dwFlags;
protected:
enum FlagBits
{
BitCopy=1,
BitOwn=2
};
};

template <class Base, const IID* piid, class T, class Copy>
CComEnumImpl<Base, piid, T, Copy>::~CComEnumImpl()
{
if (m_dwFlags & BitOwn)
{
for (T* p = m_begin; p != m_end; p++)
Copy::destroy(p);
delete [] m_begin;
}
if (m_pUnk)
m_pUnk->Release();
}

template <class Base, const IID* piid, class T, class Copy>
STDMETHODIMP CComEnumImpl<Base, piid, T, Copy>::Next(ULONG celt, T* rgelt,
ULONG* pceltFetched)
{
if (rgelt == NULL || (celt != 1 && pceltFetched == NULL))
return E_POINTER;
if (m_begin == NULL || m_end == NULL || m_iter == NULL)
return E_FAIL;
ULONG nRem = (ULONG)(m_end - m_iter);
HRESULT hRes = S_OK;
if (nRem < celt)
hRes = S_FALSE;
ULONG nMin = min(celt, nRem);
if (pceltFetched != NULL)
*pceltFetched = nMin;
while(nMin--)
Copy::copy(rgelt++, m_iter++);
return hRes;
}

template <class Base, const IID* piid, class T, class Copy>
STDMETHODIMP CComEnumImpl<Base, piid, T, Copy>::Skip(ULONG celt)
{
m_iter += celt;
if (m_iter < m_end)
return S_OK;
m_iter = m_end;
return S_FALSE;
}

template <class Base, const IID* piid, class T, class Copy>
STDMETHODIMP CComEnumImpl<Base, piid, T, Copy>::Clone(Base** ppEnum)
{
typedef CComObject<CComEnum<Base, piid, T, Copy> > _class;
HRESULT hRes = E_POINTER;
if (ppEnum != NULL)
{
_class* p = NULL;
ATLTRY(p = new _class)
if (p == NULL)
{
*ppEnum = NULL;
hRes = E_OUTOFMEMORY;
}
else
{
// If the data is a copy then we need to keep "this" object around
hRes = p->Init(m_begin, m_end, (m_dwFlags & BitCopy) ? this : m_pUnk);
if (FAILED(hRes))
delete p;
else
{
p->m_iter = m_iter;
hRes = p->_InternalQueryInterface(*piid, (void**)ppEnum);
if (FAILED(hRes))
delete p;
}
}
}
return hRes;
}

template <class Base, const IID* piid, class T, class Copy>
HRESULT CComEnumImpl<Base, piid, T, Copy>::Init(T* begin, T* end, IUnknown* pUnk,
CComEnumFlags flags)
{
if (flags == AtlFlagCopy)
{
_ASSERTE(m_begin == NULL); //Init called twice?
ATLTRY(m_begin = new T[end-begin])
m_iter = m_begin;
if (m_begin == NULL)
return E_OUTOFMEMORY;
for (T* i=begin; i != end; i++)
{
Copy::init(m_iter);
Copy::copy(m_iter++, i);
}
m_end = m_begin + (end-begin);
}
else
{
m_begin = begin;
m_end = end;
}
m_pUnk = pUnk;
if (m_pUnk)
m_pUnk->AddRef();
m_iter = m_begin;
m_dwFlags = flags;
return S_OK;
}

template <class Base, const IID* piid, class T, class Copy, class ThreadModel = CComObjectThreadModel>
class ATL_NO_VTABLE CComEnum :
public CComEnumImpl<Base, piid, T, Copy>,
public CComObjectRootEx< ThreadModel >
{
public:
typedef CComEnum<Base, piid, T, Copy > _CComEnum;
typedef CComEnumImpl<Base, piid, T, Copy > _CComEnumBase;
BEGIN_COM_MAP(_CComEnum)
COM_INTERFACE_ENTRY_IID(*piid, _CComEnumBase)
END_COM_MAP()
};

#ifndef _ATL_NO_CONNECTION_POINTS
/////////////////////////////////////////////////////////////////////////////
// Connection Points

struct _ATL_CONNMAP_ENTRY
{
DWORD dwOffset;
};

// We want the offset of the connection point relative to the connection
// point container base class
#define BEGIN_CONNECTION_POINT_MAP(x)\
typedef x _atl_conn_classtype;\
static const _ATL_CONNMAP_ENTRY* GetConnMap(int* pnEntries) {\
static const _ATL_CONNMAP_ENTRY _entries[] = {
// CONNECTION_POINT_ENTRY computes the offset of the connection point to the
// IConnectionPointContainer interface
#define CONNECTION_POINT_ENTRY(iid){offsetofclass(_ICPLocator<&iid>, _atl_conn_classtype)-\
offsetofclass(IConnectionPointContainerImpl<_atl_conn_classtype>, _atl_conn_classtype)},
#define END_CONNECTION_POINT_MAP() {(DWORD)-1} }; \
if (pnEntries) *pnEntries = sizeof(_entries)/sizeof(_ATL_CONNMAP_ENTRY) - 1; \
return _entries;}

#ifndef _DEFAULT_VECTORLENGTH
#define _DEFAULT_VECTORLENGTH 4
#endif

template <unsigned int nMaxSize>
class CComUnkArray
{
public:
CComUnkArray()
{
memset(m_arr, 0, sizeof(IUnknown*)*nMaxSize);
}
DWORD Add(IUnknown* pUnk);
BOOL Remove(DWORD dwCookie);
static DWORD WINAPI GetCookie(IUnknown** pp)
{
return (DWORD)pp;
}
static IUnknown* WINAPI GetUnknown(DWORD dwCookie)
{
return dwCookie ? *(IUnknown**)dwCookie : 0;
}
IUnknown** begin()
{
return &m_arr[0];
}
IUnknown** end()
{
return &m_arr[nMaxSize];
}
protected:
IUnknown* m_arr[nMaxSize];
};

template <unsigned int nMaxSize>
inline DWORD CComUnkArray<nMaxSize>::Add(IUnknown* pUnk)
{
for (IUnknown** pp = begin();pp<end();pp++)
{
if (*pp == NULL)
{
*pp = pUnk;
return (DWORD)pp; // return cookie
}
}
// If this fires then you need a larger array
_ASSERTE(0);
return 0;
}

template <unsigned int nMaxSize>
inline BOOL CComUnkArray<nMaxSize>::Remove(DWORD dwCookie)
{
IUnknown** pp = (IUnknown**)dwCookie;
BOOL b = ((pp >= begin()) && (pp < end()));
if (b)
*pp = NULL;
return b;
}

#if _MSC_VER>1020
template<>
#endif
class CComUnkArray<1>
{
public:
CComUnkArray()
{
m_arr[0] = NULL;
}
DWORD Add(IUnknown* pUnk)
{
if (m_arr[0] != NULL)
{
// If this fires then you need a larger array
_ASSERTE(0);
return 0;
}
m_arr[0] = pUnk;
return (DWORD)&m_arr[0];
}
BOOL Remove(DWORD dwCookie)
{
if (dwCookie != (DWORD)&m_arr[0])
return FALSE;
m_arr[0] = NULL;
return TRUE;
}
static DWORD WINAPI GetCookie(IUnknown** pp)
{
return (DWORD)pp;
}
static IUnknown* WINAPI GetUnknown(DWORD dwCookie)
{
return dwCookie ? *(IUnknown**)dwCookie : 0;
}
IUnknown** begin()
{
return &m_arr[0];
}
IUnknown** end()
{
return (&m_arr[0])+1;
}
protected:
IUnknown* m_arr[1];
};

class CComDynamicUnkArray
{
public:
CComDynamicUnkArray()
{
m_nSize = 0;
m_ppUnk = NULL;
}

~CComDynamicUnkArray()
{
if (m_nSize > 1)
free(m_ppUnk);
}
DWORD Add(IUnknown* pUnk);
BOOL Remove(DWORD dwCookie);
static DWORD WINAPI GetCookie(IUnknown** pp)
{
return (DWORD)*pp;
}
static IUnknown* WINAPI GetUnknown(DWORD dwCookie)
{
return (IUnknown*)dwCookie;
}
IUnknown** begin()
{
return (m_nSize < 2) ? &m_pUnk : m_ppUnk;
}
IUnknown** end()
{
return (m_nSize < 2) ? (&m_pUnk)+m_nSize : &m_ppUnk[m_nSize];
}
protected:
union
{
IUnknown** m_ppUnk;
IUnknown* m_pUnk;
};
int m_nSize;
};

template <const IID* piid>
class ATL_NO_VTABLE _ICPLocator
{
//this method needs a different name than QueryInterface
STDMETHOD(_LocCPQueryInterface)(REFIID riid, void ** ppvObject) = 0;
};

template <class T, const IID* piid, class CDV = CComDynamicUnkArray >
class ATL_NO_VTABLE IConnectionPointImpl : public _ICPLocator<piid>
{
typedef CComEnum<IEnumConnections, &IID_IEnumConnections, CONNECTDATA,
_Copy<CONNECTDATA> > CComEnumConnections;
typedef CDV _CDV;
public:
~IConnectionPointImpl();
STDMETHOD(_LocCPQueryInterface)(REFIID riid, void ** ppvObject)
{
if (InlineIsEqualGUID(riid, IID_IConnectionPoint) || InlineIsEqualGUID(riid, IID_IUnknown))
{
*ppvObject = this;
#ifdef _ATL_DEBUG_REFCOUNT
_DebugAddRef();
#else
AddRef();
#endif
return S_OK;
}
else
return E_NOINTERFACE;
}
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IConnectionPointImpl)

STDMETHOD(GetConnectionInterface)(IID* piid2)
{
if (piid2 == NULL)
return E_POINTER;
*piid2 = *piid;
return S_OK;
}
STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer** ppCPC)
{
if (ppCPC == NULL)
return E_POINTER;
*ppCPC = reinterpret_cast<IConnectionPointContainer*>(
(IConnectionPointContainerImpl<T>*)(T*)this);
return S_OK;
}
STDMETHOD(Advise)(IUnknown* pUnkSink, DWORD* pdwCookie);
STDMETHOD(Unadvise)(DWORD dwCookie);
STDMETHOD(EnumConnections)(IEnumConnections** ppEnum);
CDV m_vec;
};

template <class T, const IID* piid, class CDV>
IConnectionPointImpl<T, piid, CDV>::~IConnectionPointImpl()
{
IUnknown** pp = m_vec.begin();
while (pp < m_vec.end())
{
if (*pp != NULL)
(*pp)->Release();
pp++;
}
}

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImpl<T, piid, CDV>::Advise(IUnknown* pUnkSink,
DWORD* pdwCookie)
{
T* pT = (T*)this;
IUnknown* p;
HRESULT hRes = S_OK;
if (pUnkSink == NULL || pdwCookie == NULL)
return E_POINTER;
IID iid;
GetConnectionInterface(&iid);
hRes = pUnkSink->QueryInterface(iid, (void**)&p);
if (SUCCEEDED(hRes))
{
pT->Lock();
*pdwCookie = m_vec.Add(p);
hRes = (*pdwCookie != NULL) ? S_OK : CONNECT_E_ADVISELIMIT;
pT->Unlock();
if (hRes != S_OK)
{
*pdwCookie = 0;
p->Release();
}
}
else if (hRes == E_NOINTERFACE)
hRes = CONNECT_E_CANNOTCONNECT;
return hRes;
}

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImpl<T, piid, CDV>::Unadvise(DWORD dwCookie)
{
T* pT = (T*)this;
pT->Lock();
IUnknown* p = _CDV::GetUnknown(dwCookie);
HRESULT hRes = m_vec.Remove(dwCookie) ? S_OK : CONNECT_E_NOCONNECTION;
pT->Unlock();
if (hRes == S_OK && p != NULL)
p->Release();
return hRes;
}

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImpl<T, piid, CDV>::EnumConnections(
IEnumConnections** ppEnum)
{
if (ppEnum == NULL)
return E_POINTER;
*ppEnum = NULL;
CComObject<CComEnumConnections>* pEnum = NULL;
ATLTRY(pEnum = new CComObject<CComEnumConnections>)
if (pEnum == NULL)
return E_OUTOFMEMORY;
T* pT = (T*)this;
pT->Lock();
CONNECTDATA* pcd = NULL;
ATLTRY(pcd = new CONNECTDATA[m_vec.end()-m_vec.begin()])
if (pcd == NULL)
{
delete pEnum;
pT->Unlock();
return E_OUTOFMEMORY;
}
CONNECTDATA* pend = pcd;
// Copy the valid CONNECTDATA's
for (IUnknown** pp = m_vec.begin();pp<m_vec.end();pp++)
{
if (*pp != NULL)
{
(*pp)->AddRef();
pend->pUnk = *pp;
pend->dwCookie = _CDV::GetCookie(pp);
pend++;
}
}
// don't copy the data, but transfer ownership to it
pEnum->Init(pcd, pend, NULL, AtlFlagTakeOwnership);
pT->Unlock();
HRESULT hRes = pEnum->_InternalQueryInterface(IID_IEnumConnections, (void**)ppEnum);
if (FAILED(hRes))
delete pEnum;
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// IConnectionPointContainerImpl

template <class T>
class ATL_NO_VTABLE IConnectionPointContainerImpl
{
typedef CComEnum<IEnumConnectionPoints,
&IID_IEnumConnectionPoints, IConnectionPoint*,
_CopyInterface<IConnectionPoint> >
CComEnumConnectionPoints;
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IConnectionPointContainerImpl)

STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints** ppEnum)
{
if (ppEnum == NULL)
return E_POINTER;
*ppEnum = NULL;
CComEnumConnectionPoints* pEnum = NULL;
ATLTRY(pEnum = new CComObject<CComEnumConnectionPoints>)
if (pEnum == NULL)
return E_OUTOFMEMORY;

int nCPCount;
const _ATL_CONNMAP_ENTRY* pEntry = T::GetConnMap(&nCPCount);

// allocate an initialize a vector of connection point object pointers
IConnectionPoint** ppCP = (IConnectionPoint**)alloca(sizeof(IConnectionPoint*)*nCPCount);

int i = 0;
while (pEntry->dwOffset != (DWORD)-1)
{
ppCP[i++] = (IConnectionPoint*)((int)this+pEntry->dwOffset);
pEntry++;
}

// copy the pointers: they will AddRef this object
HRESULT hRes = pEnum->Init((IConnectionPoint**)&ppCP[0],
(IConnectionPoint**)&ppCP[nCPCount],
reinterpret_cast<IConnectionPointContainer*>(this), AtlFlagCopy);
if (FAILED(hRes))
{
delete pEnum;
return hRes;
}
hRes = pEnum->QueryInterface(IID_IEnumConnectionPoints, (void**)ppEnum);
if (FAILED(hRes))
delete pEnum;
return hRes;
}
STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint** ppCP)
{
if (ppCP == NULL)
return E_POINTER;
*ppCP = NULL;
HRESULT hRes = CONNECT_E_NOCONNECTION;
const _ATL_CONNMAP_ENTRY* pEntry = T::GetConnMap(NULL);
IID iid;
while (pEntry->dwOffset != (DWORD)-1)
{
IConnectionPoint* pCP =
(IConnectionPoint*)((int)this+pEntry->dwOffset);
if (SUCCEEDED(pCP->GetConnectionInterface(&iid)) &&
InlineIsEqualGUID(riid, iid))
{
*ppCP = pCP;
pCP->AddRef();
hRes = S_OK;
break;
}
pEntry++;
}
return hRes;
}
};

#endif //!_ATL_NO_CONNECTION_POINTS

#pragma pack(pop)

/////////////////////////////////////////////////////////////////////////////
// CComAutoThreadModule

template <class ThreadAllocator>
inline void CComAutoThreadModule<ThreadAllocator>::Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, int nThreads)
{
m_nThreads = nThreads;
m_pApartments = new CComApartment[m_nThreads];
for (int i = 0; i < nThreads; i++)
m_pApartments[i].m_hThread = CreateThread(NULL, 0, CComApartment::_Apartment, (void*)&m_pApartments[i], 0, &m_pApartments[i].m_dwThreadID);
CComApartment::ATL_CREATE_OBJECT = RegisterWindowMessage(_T("ATL_CREATE_OBJECT"));
CComModule::Init(p, h);
}

template <class ThreadAllocator>
inline LONG CComAutoThreadModule<ThreadAllocator>::Lock()
{
LONG l = CComModule::Lock();
DWORD dwThreadID = GetCurrentThreadId();
for (int i=0; i < m_nThreads; i++)
{
if (m_pApartments[i].m_dwThreadID == dwThreadID)
{
m_pApartments[i].Lock();
break;
}
}
return l;
}

template <class ThreadAllocator>
inline LONG CComAutoThreadModule<ThreadAllocator>::Unlock()
{
LONG l = CComModule::Unlock();
DWORD dwThreadID = GetCurrentThreadId();
for (int i=0; i < m_nThreads; i++)
{
if (m_pApartments[i].m_dwThreadID == dwThreadID)
{
m_pApartments[i].Unlock();
break;
}
}
return l;
}

template <class ThreadAllocator>
HRESULT CComAutoThreadModule<ThreadAllocator>::CreateInstance(void* pfnCreateInstance, REFIID riid, void** ppvObj)

{
_ATL_CREATORFUNC* pFunc = (_ATL_CREATORFUNC*) pfnCreateInstance;
_AtlAptCreateObjData data;
data.pfnCreateInstance = pFunc;
data.piid = &riid;
data.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
data.hRes = S_OK;
int nThread = m_Allocator.GetThread(m_pApartments, m_nThreads);
::PostThreadMessage(m_pApartments[nThread].m_dwThreadID, CComApartment::ATL_CREATE_OBJECT, 0, (LPARAM)&data);
AtlWaitWithMessageLoop(data.hEvent);
CloseHandle(data.hEvent);
if (SUCCEEDED(data.hRes))
data.hRes = CoGetInterfaceAndReleaseStream(data.pStream, riid, ppvObj);
return data.hRes;
}

template <class ThreadAllocator>
CComAutoThreadModule<ThreadAllocator>::~CComAutoThreadModule()
{
for (int i=0; i < m_nThreads; i++)
{
::PostThreadMessage(m_pApartments[i].m_dwThreadID, WM_QUIT, 0, 0);
::WaitForSingleObject(m_pApartments[i].m_hThread, INFINITE);
}
delete[] m_pApartments;
}

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

#endif // __ATLCOM_H__

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


ATLCONV.CPP

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLCONV_H__
#error atlconv.cpp requires atlconv.h to be included first
#endif

#ifndef _ATL_NO_CONVERSIONS
/////////////////////////////////////////////////////////////////////////////
// Global UNICODE<>ANSI translation helpers
LPWSTR WINAPI AtlA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars)
{
_ASSERTE(lpa != NULL);
_ASSERTE(lpw != NULL);
// verify that no illegal character present
// since lpw was allocated based on the size of lpa
// don't worry about the number of chars
lpw[0] = '\0';
MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars);
return lpw;
}

LPSTR WINAPI AtlW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars)
{
_ASSERTE(lpw != NULL);
_ASSERTE(lpa != NULL);
// verify that no illegal character present
// since lpa was allocated based on the size of lpw
// don't worry about the number of chars
lpa[0] = '\0';
WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL);
return lpa;
}

#ifdef _WINGDI_

// don't include this code when building DLL
LPDEVMODEW AtlDevModeA2W(LPDEVMODEW lpDevModeW, LPDEVMODEA lpDevModeA)
{
if (lpDevModeA == NULL)
return NULL;
_ASSERTE(lpDevModeW != NULL);
AtlA2WHelper(lpDevModeW->dmDeviceName, (LPCSTR)lpDevModeA->dmDeviceName, 32*sizeof(WCHAR));
memcpy(&lpDevModeW->dmSpecVersion, &lpDevModeA->dmSpecVersion,
offsetof(DEVMODEW, dmFormName) - offsetof(DEVMODEW, dmSpecVersion));
AtlA2WHelper(lpDevModeW->dmFormName, (LPCSTR)lpDevModeA->dmFormName, 32*sizeof(WCHAR));
memcpy(&lpDevModeW->dmLogPixels, &lpDevModeA->dmLogPixels,
sizeof(DEVMODEW) - offsetof(DEVMODEW, dmLogPixels));
if (lpDevModeA->dmDriverExtra != 0)
memcpy(lpDevModeW+1, lpDevModeA+1, lpDevModeA->dmDriverExtra);
lpDevModeW->dmSize = sizeof(DEVMODEW);
return lpDevModeW;
}

LPTEXTMETRICW AtlTextMetricA2W(LPTEXTMETRICW lptmW, LPTEXTMETRICA lptmA)
{
if (lptmA == NULL)
return NULL;
_ASSERTE(lptmW != NULL);
memcpy(lptmW, lptmA, sizeof(LONG) * 11);
memcpy(&lptmW->tmItalic, &lptmA->tmItalic, sizeof(BYTE) * 5);
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&lptmA->tmFirstChar, 1, &lptmW->tmFirstChar, 1);
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&lptmA->tmLastChar, 1, &lptmW->tmLastChar, 1);
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&lptmA->tmDefaultChar, 1, &lptmW->tmDefaultChar, 1);
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&lptmA->tmBreakChar, 1, &lptmW->tmBreakChar, 1);
return lptmW;
}

LPTEXTMETRICA AtlTextMetricW2A(LPTEXTMETRICA lptmA, LPTEXTMETRICW lptmW)
{
if (lptmW == NULL)
return NULL;
_ASSERTE(lptmA != NULL);
memcpy(lptmA, lptmW, sizeof(LONG) * 11);
memcpy(&lptmA->tmItalic, &lptmW->tmItalic, sizeof(BYTE) * 5);
WideCharToMultiByte(CP_ACP, 0, &lptmW->tmFirstChar, 1, (LPSTR)&lptmA->tmFirstChar, 1, NULL, NULL);
WideCharToMultiByte(CP_ACP, 0, &lptmW->tmLastChar, 1, (LPSTR)&lptmA->tmLastChar, 1, NULL, NULL);
WideCharToMultiByte(CP_ACP, 0, &lptmW->tmDefaultChar, 1, (LPSTR)&lptmA->tmDefaultChar, 1, NULL, NULL);
WideCharToMultiByte(CP_ACP, 0, &lptmW->tmBreakChar, 1, (LPSTR)&lptmA->tmBreakChar, 1, NULL, NULL);
return lptmA;
}

LPDEVMODEA AtlDevModeW2A(LPDEVMODEA lpDevModeA, LPDEVMODEW lpDevModeW)
{
if (lpDevModeW == NULL)
return NULL;
_ASSERTE(lpDevModeA != NULL);
AtlW2AHelper((LPSTR)lpDevModeA->dmDeviceName, lpDevModeW->dmDeviceName, 32*sizeof(char));
memcpy(&lpDevModeA->dmSpecVersion, &lpDevModeW->dmSpecVersion,
offsetof(DEVMODEA, dmFormName) - offsetof(DEVMODEA, dmSpecVersion));
AtlW2AHelper((LPSTR)lpDevModeA->dmFormName, lpDevModeW->dmFormName, 32*sizeof(char));
memcpy(&lpDevModeA->dmLogPixels, &lpDevModeW->dmLogPixels,
sizeof(DEVMODEA) - offsetof(DEVMODEA, dmLogPixels));
if (lpDevModeW->dmDriverExtra != 0)
memcpy(lpDevModeA+1, lpDevModeW+1, lpDevModeW->dmDriverExtra);
lpDevModeA->dmSize = sizeof(DEVMODEA);
return lpDevModeA;
}

#endif //_WINGDI_
#endif //!_ATL_NO_CONVERSIONS


ATLCONV.H

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLCONV_H__
#define __ATLCONV_H__

#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef _INC_MALLOC
#include <malloc.h>
#endif // _INC_MALLOC

#pragma pack(push,8)

// Make sure MFC's afxconv.h hasn't already been loaded to do this
#ifndef USES_CONVERSION

#ifndef _DEBUG
#define USES_CONVERSION int _convert; _convert
#else
#define USES_CONVERSION int _convert = 0
#endif

/////////////////////////////////////////////////////////////////////////////
// Global UNICODE<>ANSI translation helpers
LPWSTR WINAPI AtlA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars);
LPSTR WINAPI AtlW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars);

#ifndef ATLA2WHELPER
#define ATLA2WHELPER AtlA2WHelper
#define ATLW2AHELPER AtlW2AHelper
#endif

#define A2W(lpa) (\
((LPCSTR)lpa == NULL) ? NULL : (\
_convert = (lstrlenA(lpa)+1),\
ATLA2WHELPER((LPWSTR) alloca(_convert*2), lpa, _convert)))

#define W2A(lpw) (\
((LPCWSTR)lpw == NULL) ? NULL : (\
_convert = (lstrlenW(lpw)+1)*2,\
ATLW2AHELPER((LPSTR) alloca(_convert), lpw, _convert)))

#define A2CW(lpa) ((LPCWSTR)A2W(lpa))
#define W2CA(lpw) ((LPCSTR)W2A(lpw))

#if defined(_UNICODE)
// in these cases the default (TCHAR) is the same as OLECHAR
inline size_t ocslen(LPCOLESTR x) { return lstrlenW(x); }
inline OLECHAR* ocscpy(LPOLESTR dest, LPCOLESTR src) { return lstrcpyW(dest, src); }
inline LPCOLESTR T2COLE(LPCTSTR lp) { return lp; }
inline LPCTSTR OLE2CT(LPCOLESTR lp) { return lp; }
inline LPOLESTR T2OLE(LPTSTR lp) { return lp; }
inline LPTSTR OLE2T(LPOLESTR lp) { return lp; }
#elif defined(OLE2ANSI)
// in these cases the default (TCHAR) is the same as OLECHAR
inline size_t ocslen(LPCOLESTR x) { return lstrlen(x); }
inline OLECHAR* ocscpy(LPOLESTR dest, LPCOLESTR src) { return lstrcpy(dest, src); }
inline LPCOLESTR T2COLE(LPCTSTR lp) { return lp; }
inline LPCTSTR OLE2CT(LPCOLESTR lp) { return lp; }
inline LPOLESTR T2OLE(LPTSTR lp) { return lp; }
inline LPTSTR OLE2T(LPOLESTR lp) { return lp; }
#else
inline size_t ocslen(LPCOLESTR x) { return lstrlenW(x); }
//lstrcpyW doesn't work on Win95, so we do this
inline OLECHAR* ocscpy(LPOLESTR dest, LPCOLESTR src)
{return (LPOLESTR) memcpy(dest, src, (lstrlenW(src)+1)*sizeof(WCHAR));}
//CharNextW doesn't work on Win95 so we use this
#define T2COLE(lpa) A2CW(lpa)
#define T2OLE(lpa) A2W(lpa)
#define OLE2CT(lpo) W2CA(lpo)
#define OLE2T(lpo) W2A(lpo)
#endif

#ifdef OLE2ANSI
inline LPOLESTR A2OLE(LPSTR lp) { return lp;}
inline LPSTR OLE2A(LPOLESTR lp) { return lp;}
#define W2OLE W2A
#define OLE2W A2W
inline LPCOLESTR A2COLE(LPCSTR lp) { return lp;}
inline LPCSTR OLE2CA(LPCOLESTR lp) { return lp;}
#define W2COLE W2CA
#define OLE2CW A2CW
#else
inline LPOLESTR W2OLE(LPWSTR lp) { return lp; }
inline LPWSTR OLE2W(LPOLESTR lp) { return lp; }
#define A2OLE A2W
#define OLE2A W2A
inline LPCOLESTR W2COLE(LPCWSTR lp) { return lp; }
inline LPCWSTR OLE2CW(LPCOLESTR lp) { return lp; }
#define A2COLE A2CW
#define OLE2CA W2CA
#endif

#ifdef _UNICODE
#define T2A W2A
#define A2T A2W
inline LPWSTR T2W(LPTSTR lp) { return lp; }
inline LPTSTR W2T(LPWSTR lp) { return lp; }
#define T2CA W2CA
#define A2CT A2CW
inline LPCWSTR T2CW(LPCTSTR lp) { return lp; }
inline LPCTSTR W2CT(LPCWSTR lp) { return lp; }
#else
#define T2W A2W
#define W2T W2A
inline LPSTR T2A(LPTSTR lp) { return lp; }
inline LPTSTR A2T(LPSTR lp) { return lp; }
#define T2CW A2CW
#define W2CT W2CA
inline LPCSTR T2CA(LPCTSTR lp) { return lp; }
inline LPCTSTR A2CT(LPCSTR lp) { return lp; }
#endif

inline BSTR OLE2BSTR(LPCOLESTR lp) {return ::SysAllocString(lp);}
#if defined(_UNICODE)
// in these cases the default (TCHAR) is the same as OLECHAR
inline BSTR T2BSTR(LPCTSTR lp) {return ::SysAllocString(lp);}
inline BSTR A2BSTR(LPCSTR lp) {USES_CONVERSION; return ::SysAllocString(A2COLE(lp));}
inline BSTR W2BSTR(LPCWSTR lp) {return ::SysAllocString(lp);}
#elif defined(OLE2ANSI)
// in these cases the default (TCHAR) is the same as OLECHAR
inline BSTR T2BSTR(LPCTSTR lp) {return ::SysAllocString(lp);}
inline BSTR A2BSTR(LPCSTR lp) {return ::SysAllocString(lp);}
inline BSTR W2BSTR(LPCWSTR lp) {USES_CONVERSION; return ::SysAllocString(W2COLE(lp));}
#else
inline BSTR T2BSTR(LPCTSTR lp) {USES_CONVERSION; return ::SysAllocString(T2COLE(lp));}
inline BSTR A2BSTR(LPCSTR lp) {USES_CONVERSION; return ::SysAllocString(A2COLE(lp));}
inline BSTR W2BSTR(LPCWSTR lp) {return ::SysAllocString(lp);}
#endif

#ifdef _WINGDI_
/////////////////////////////////////////////////////////////////////////////
// Global UNICODE<>ANSI translation helpers
LPDEVMODEW AtlDevModeA2W(LPDEVMODEW lpDevModeW, LPDEVMODEA lpDevModeA);
LPDEVMODEA AtlDevModeW2A(LPDEVMODEA lpDevModeA, LPDEVMODEW lpDevModeW);
LPTEXTMETRICW AtlTextMetricA2W(LPTEXTMETRICW lptmW, LPTEXTMETRICA pltmA);
LPTEXTMETRICA AtlTextMetricW2A(LPTEXTMETRICA lptmA, LPTEXTMETRICW pltmW);

#ifndef ATLDEVMODEA2W
#define ATLDEVMODEA2W AtlDevModeA2W
#define ATLDEVMODEW2A AtlDevModeW2A
#define ATLTEXTMETRICA2W AtlTextMetricA2W
#define ATLTEXTMETRICW2A AtlTextMetricW2A
#endif

#define DEVMODEW2A(lpw)\
((lpw == NULL) ? NULL : ATLDEVMODEW2A((LPDEVMODEA)alloca(sizeof(DEVMODEA)+lpw->dmDriverExtra), lpw))
#define DEVMODEA2W(lpa)\
((lpa == NULL) ? NULL : ATLDEVMODEA2W((LPDEVMODEW)alloca(sizeof(DEVMODEW)+lpa->dmDriverExtra), lpa))
#define TEXTMETRICW2A(lptmw)\
((lptmw == NULL) ? NULL : ATLTEXTMETRICW2A((LPTEXTMETRICA)alloca(sizeof(TEXTMETRICA)), lptmw))
#define TEXTMETRICA2W(lptma)\
((lptma == NULL) ? NULL : ATLTEXTMETRICA2W((LPTEXTMETRICW)alloca(sizeof(TEXTMETRICW)), lptma))

#ifdef OLE2ANSI
#define DEVMODEOLE DEVMODEA
#define LPDEVMODEOLE LPDEVMODEA
#define TEXTMETRICOLE TEXTMETRICA
#define LPTEXTMETRICOLE LPTEXTMETRICA
#else
#define DEVMODEOLE DEVMODEW
#define LPDEVMODEOLE LPDEVMODEW
#define TEXTMETRICOLE TEXTMETRICW
#define LPTEXTMETRICOLE LPTEXTMETRICW
#endif

#if defined(_UNICODE)
// in these cases the default (TCHAR) is the same as OLECHAR
inline LPDEVMODEW DEVMODEOLE2T(LPDEVMODEOLE lp) { return lp; }
inline LPDEVMODEOLE DEVMODET2OLE(LPDEVMODEW lp) { return lp; }
inline LPTEXTMETRICW TEXTMETRICOLE2T(LPTEXTMETRICOLE lp) { return lp; }
inline LPTEXTMETRICOLE TEXTMETRICT2OLE(LPTEXTMETRICW lp) { return lp; }
#elif defined(OLE2ANSI)
// in these cases the default (TCHAR) is the same as OLECHAR
inline LPDEVMODE DEVMODEOLE2T(LPDEVMODEOLE lp) { return lp; }
inline LPDEVMODEOLE DEVMODET2OLE(LPDEVMODE lp) { return lp; }
inline LPTEXTMETRIC TEXTMETRICOLE2T(LPTEXTMETRICOLE lp) { return lp; }
inline LPTEXTMETRICOLE TEXTMETRICT2OLE(LPTEXTMETRIC lp) { return lp; }
#else
#define DEVMODEOLE2T(lpo) DEVMODEW2A(lpo)
#define DEVMODET2OLE(lpa) DEVMODEA2W(lpa)
#define TEXTMETRICOLE2T(lptmw) TEXTMETRICW2A(lptmw)
#define TEXTMETRICT2OLE(lptma) TEXTMETRICA2W(lptma)
#endif

#endif //_WINGDI_

#else //!USES_CONVERSION

// if USES_CONVERSION already defined (i.e. MFC_VER < 4.21 )
// flip this switch to avoid atlconv.cpp
#define _ATL_NO_CONVERSIONS

#endif //!USES_CONVERSION

// Define these even if MFC already included
#if defined(_UNICODE)
// in these cases the default (TCHAR) is the same as OLECHAR
inline LPOLESTR CharNextO(LPCOLESTR lp) {return CharNextW(lp);}
#elif defined(OLE2ANSI)
// in these cases the default (TCHAR) is the same as OLECHAR
inline LPOLESTR CharNextO(LPCOLESTR lp) {return CharNext(lp);}
#else
inline LPOLESTR CharNextO(LPCOLESTR lp) {return (LPOLESTR)(lp+1);}
#endif

#pragma pack(pop)

#endif // __ATLCONV_H__

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


ATLCTL.CPP

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

/////////////////////////////////////////////////////////////////////////////
// CComDispatchDriver support

HRESULT CComDispatchDriver::GetProperty(IDispatch* pDisp, DISPID dwDispID,
VARIANT* pVar)
{
ATLTRACE(_T("CPropertyHelper::GetProperty\n"));
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
return pDisp->Invoke(dwDispID, IID_NULL,
LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
&dispparamsNoArgs, pVar, NULL, NULL);
}

HRESULT CComDispatchDriver::PutProperty(IDispatch* pDisp, DISPID dwDispID,
VARIANT* pVar)
{
ATLTRACE(_T("CPropertyHelper::PutProperty\n"));
DISPPARAMS dispparams = {NULL, NULL, 1, 1};
dispparams.rgvarg = pVar;
DISPID dispidPut = DISPID_PROPERTYPUT;
dispparams.rgdispidNamedArgs = &dispidPut;

if (pVar->vt == VT_UNKNOWN || pVar->vt == VT_DISPATCH ||
(pVar->vt & VT_ARRAY) || (pVar->vt & VT_BYREF))
{
HRESULT hr = pDisp->Invoke(dwDispID, IID_NULL,
LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF,
&dispparams, NULL, NULL, NULL);
if (SUCCEEDED(hr))
return hr;
}

return pDisp->Invoke(dwDispID, IID_NULL,
LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT,
&dispparams, NULL, NULL, NULL);
}

//////////////////////////////////////////////////////////////////////////////
// CFirePropNotifyEvent

HRESULT CFirePropNotifyEvent::FireOnRequestEdit(IUnknown* pUnk, DISPID dispID)
{
CComQIPtr<IConnectionPointContainer, &IID_IConnectionPointContainer> pCPC(pUnk);
if (!pCPC)
return S_OK;
CComPtr<IConnectionPoint> pCP;
pCPC->FindConnectionPoint(IID_IPropertyNotifySink, &pCP);
if (!pCP)
return S_OK;
CComPtr<IEnumConnections> pEnum;

if (FAILED(pCP->EnumConnections(&pEnum)))
return S_OK;
CONNECTDATA cd;
while (pEnum->Next(1, &cd, NULL) == S_OK)
{
if (cd.pUnk)
{
HRESULT hr = S_OK;
CComQIPtr<IPropertyNotifySink, &IID_IPropertyNotifySink> pSink(cd.pUnk);
if (pSink)
hr = pSink->OnRequestEdit(dispID);
cd.pUnk->Release();
if (hr == S_FALSE)
return S_FALSE;
}
}
return S_OK;
}

HRESULT CFirePropNotifyEvent::FireOnChanged(IUnknown* pUnk, DISPID dispID)
{
CComQIPtr<IConnectionPointContainer, &IID_IConnectionPointContainer> pCPC(pUnk);
if (!pCPC)
return S_OK;
CComPtr<IConnectionPoint> pCP;
pCPC->FindConnectionPoint(IID_IPropertyNotifySink, &pCP);
if (!pCP)
return S_OK;
CComPtr<IEnumConnections> pEnum;

if (FAILED(pCP->EnumConnections(&pEnum)))
return S_OK;
CONNECTDATA cd;
while (pEnum->Next(1, &cd, NULL) == S_OK)
{
if (cd.pUnk)
{
CComQIPtr<IPropertyNotifySink, &IID_IPropertyNotifySink> pSink(cd.pUnk);
if (pSink)
pSink->OnChanged(dispID);
cd.pUnk->Release();
}
}
return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Control support

HRESULT CComControlBase::IQuickActivate_QuickActivate(QACONTAINER *pQACont,
QACONTROL *pQACtrl)
{
_ASSERTE(pQACont != NULL);
_ASSERTE(pQACtrl != NULL);
_ASSERTE(pQACont->cbSize >= sizeof(QACONTAINER));
_ASSERTE(pQACtrl->cbSize >= sizeof(QACONTROL));

if (!pQACont || !pQACtrl)
return E_POINTER;

HRESULT hRes;
memset(pQACtrl, 0, sizeof(QACONTROL));
pQACtrl->cbSize = sizeof(QACONTROL);

// get all interfaces we are going to need
CComPtr<IOleObject> pOO;
ControlQueryInterface(IID_IOleObject, (void**)&pOO);
CComPtr<IViewObjectEx> pVOEX;
ControlQueryInterface(IID_IViewObjectEx, (void**)&pVOEX);
CComPtr<IPointerInactive> pPI;
ControlQueryInterface(IID_IPointerInactive, (void**)&pPI);
CComPtr<IProvideClassInfo2> pPCI;
ControlQueryInterface(IID_IProvideClassInfo2, (void**)&pPCI);

if (pOO == NULL || pVOEX == NULL)
return E_FAIL;

pOO->SetClientSite(pQACont->pClientSite);

if (pQACont->pAdviseSink != NULL)
{
ATLTRACE(_T("Setting up IOleObject Advise\n"));
pVOEX->SetAdvise(DVASPECT_CONTENT, 0, pQACont->pAdviseSink);
}

CComPtr<IConnectionPointContainer> pCPC;
ControlQueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);

if (pQACont->pPropertyNotifySink)
{
ATLTRACE(_T("Setting up PropNotify CP\n"));
CComPtr<IConnectionPoint> pCP;
if (pCPC != NULL)
{
hRes = pCPC->FindConnectionPoint(IID_IPropertyNotifySink, &pCP);
if (SUCCEEDED(hRes))
pCP->Advise(pQACont->pPropertyNotifySink, &pQACtrl->dwPropNotifyCookie);
}
}

if (pPCI)
{
GUID iidDefaultSrc;
if (SUCCEEDED(pPCI->GetGUID(GUIDKIND_DEFAULT_SOURCE_DISP_IID,
&iidDefaultSrc)))
{
if (pQACont->pUnkEventSink)
{
ATLTRACE(_T("Setting up Default Out Going Interface\n"));
CComPtr<IConnectionPoint> pCP;
if (pCPC != NULL)
{
hRes = pCPC->FindConnectionPoint(iidDefaultSrc, &pCP);
if (SUCCEEDED(hRes))
pCP->Advise(pQACont->pUnkEventSink, &pQACtrl->dwEventCookie);
}
}
}
}
// give information to container
if (pOO != NULL)
pOO->GetMiscStatus(DVASPECT_CONTENT, &pQACtrl->dwMiscStatus);

if (pVOEX != NULL)
pVOEX->GetViewStatus(&pQACtrl->dwViewStatus);

if (pPI != NULL)
pPI->GetActivationPolicy(&pQACtrl->dwPointerActivationPolicy);
return S_OK;
}

HRESULT CComControlBase::IPersistPropertyBag_Load(LPPROPERTYBAG pPropBag,
LPERRORLOG pErrorLog, ATL_PROPMAP_ENTRY* pMap)
{
USES_CONVERSION;
CComPtr<IDispatch> pDispatch;
const IID* piidOld = NULL;
for(int i = 0; pMap[i].pclsidPropPage != NULL; i++)
{
if (pMap[i].szDesc == NULL)
continue;
CComVariant var;

if(pMap[i].piidDispatch != piidOld)
{
pDispatch.Release();
if(FAILED(ControlQueryInterface(*pMap[i].piidDispatch, (void**)&pDispatch)))
{
ATLTRACE(_T("Failed to get a dispatch pointer for property #%i\n"), i);
return E_FAIL;
}
piidOld = pMap[i].piidDispatch;
}

if (FAILED(CComDispatchDriver::GetProperty(pDispatch, pMap[i].dispid, &var)))
{
ATLTRACE(_T("Invoked failed on DISPID %x\n"), pMap[i].dispid);
return E_FAIL;
}

HRESULT hr = pPropBag->Read(pMap[i].szDesc, &var, pErrorLog);
if (FAILED(hr))
{
if (hr == E_INVALIDARG)
{
ATLTRACE(_T("Property %s not in Bag\n"), OLE2CT(pMap[i].szDesc));
}
else
{
// Many containers return different ERROR values for Member not found
ATLTRACE(_T("Error attempting to read Property %s from PropertyBag \n"), OLE2CT(pMap[i].szDesc));
}
continue;
}

if (FAILED(CComDispatchDriver::PutProperty(pDispatch, pMap[i].dispid, &var)))
{
ATLTRACE(_T("Invoked failed on DISPID %x\n"), pMap[i].dispid);
return E_FAIL;
}
}
return S_OK;

}

HRESULT CComControlBase::IPersistPropertyBag_Save(LPPROPERTYBAG pPropBag,
BOOL fClearDirty, BOOL /*fSaveAllProperties*/, ATL_PROPMAP_ENTRY* pMap)
{
if (pPropBag == NULL)
{
ATLTRACE(_T("PropBag pointer passed in was invalid\n"));
return E_POINTER;
}

CComPtr<IDispatch> pDispatch;
const IID* piidOld = NULL;
for(int i = 0; pMap[i].pclsidPropPage != NULL; i++)
{
if (pMap[i].szDesc == NULL)
continue;
CComVariant var;

if(pMap[i].piidDispatch != piidOld)
{
pDispatch.Release();
if(FAILED(ControlQueryInterface(*pMap[i].piidDispatch, (void**)&pDispatch)))
{
ATLTRACE(_T("Failed to get a dispatch pointer for property #%i\n"), i);
return E_FAIL;
}
piidOld = pMap[i].piidDispatch;
}

if (FAILED(CComDispatchDriver::GetProperty(pDispatch, pMap[i].dispid, &var)))
{
ATLTRACE(_T("Invoked failed on DISPID %x\n"), pMap[i].dispid);
return E_FAIL;
}

if (var.vt == VT_UNKNOWN || var.vt == VT_DISPATCH)
{
if (var.punkVal == NULL)
{
ATLTRACE(_T("Warning skipping empty IUnknown in Save\n"));
continue;
}
}

HRESULT hr = pPropBag->Write(pMap[i].szDesc, &var);
if (FAILED(hr))
return hr;
}
m_bRequiresSave = FALSE;
return S_OK;
}

HRESULT CComControlBase::ISpecifyPropertyPages_GetPages(CAUUID* pPages,
ATL_PROPMAP_ENTRY* pMap)
{
_ASSERTE(pMap != NULL);
int nCnt = 0;
// Get count of unique pages
for(int i = 0; pMap[i].pclsidPropPage != NULL; i++)
{
if (!InlineIsEqualGUID(*pMap[i].pclsidPropPage, CLSID_NULL))
nCnt++;
}
pPages->pElems = NULL;
pPages->pElems = (GUID*) CoTaskMemAlloc(sizeof(CLSID)*nCnt);
if (pPages->pElems == NULL)
return E_OUTOFMEMORY;
nCnt = 0;
for(i = 0; pMap[i].pclsidPropPage != NULL; i++)
{
if (!InlineIsEqualGUID(*pMap[i].pclsidPropPage, CLSID_NULL))
{
BOOL bMatch = FALSE;
for (int j=0;j<nCnt;j++)
{
if (InlineIsEqualGUID(*(pMap[i].pclsidPropPage), pPages->pElems[j]))
{
bMatch = TRUE;
break;
}
}
if (!bMatch)
pPages->pElems[nCnt++] = *pMap[i].pclsidPropPage;
}
}
pPages->cElems = nCnt;
return S_OK;
}

BOOL CComControlBase::SetControlFocus(BOOL bGrab)
{
if (m_bWndLess)
{
if (!m_bUIActive && bGrab)
if (FAILED(InPlaceActivate(OLEIVERB_UIACTIVATE)))
return FALSE;

return (m_spInPlaceSite->SetFocus(bGrab) == S_OK);
}
else
{
// we've got a window.
//
if (m_bInPlaceActive)
{
HWND hwnd = (bGrab) ? m_hWndCD : ::GetParent(m_hWndCD);
if (!m_bUIActive && bGrab)
return SUCCEEDED(InPlaceActivate(OLEIVERB_UIACTIVATE));
else
return (::SetFocus(hwnd) != NULL);
}
}
return FALSE;
}

HRESULT CComControlBase::DoVerbProperties(LPCRECT /* prcPosRect */, HWND hwndParent)
{
HRESULT hr = S_OK;
CComQIPtr <ISpecifyPropertyPages, &IID_ISpecifyPropertyPages> spPages;
CComQIPtr <IOleObject, &IID_IOleObject> spObj;
CComQIPtr <IOleControlSite, &IID_IOleControlSite> spSite(m_spClientSite);

if (spSite)
{
hr = spSite->ShowPropertyFrame();
if (SUCCEEDED(hr))
return hr;
}

CComPtr<IUnknown> pUnk;
ControlQueryInterface(IID_IUnknown, (void**)&pUnk);
_ASSERTE(pUnk != NULL);
CAUUID pages;
spPages = pUnk;
if (spPages)
{
spPages->GetPages(&pages);
spObj = pUnk;
if (spObj)
{
LPOLESTR szTitle = NULL;

spObj->GetUserType(USERCLASSTYPE_SHORT, &szTitle);

hr = OleCreatePropertyFrame(hwndParent, m_rcPos.top, m_rcPos.left, szTitle,
1, &pUnk.p, pages.cElems, pages.pElems, LOCALE_USER_DEFAULT, 0, 0);

CoTaskMemFree(szTitle);
}
else
{
hr = OLEOBJ_S_CANNOT_DOVERB_NOW;
}
}
else
{
hr = OLEOBJ_S_CANNOT_DOVERB_NOW;
}

return hr;
}

HRESULT CComControlBase::InPlaceActivate(LONG iVerb, const RECT* prcPosRect)
{
HRESULT hr;

if (m_spClientSite == NULL)
return S_OK;

CComPtr<IOleInPlaceObject> pIPO;
ControlQueryInterface(IID_IOleInPlaceObject, (void**)&pIPO);
_ASSERTE(pIPO != NULL);
if (prcPosRect != NULL)
pIPO->SetObjectRects(prcPosRect, prcPosRect);

if (!m_bNegotiatedWnd)
{
if (!m_bWindowOnly)
// Try for windowless site
hr = m_spClientSite->QueryInterface(IID_IOleInPlaceSiteWindowless, (void **)&m_spInPlaceSite);

if (m_spInPlaceSite)
{
m_bInPlaceSiteEx = TRUE;
m_bWndLess = SUCCEEDED(m_spInPlaceSite->CanWindowlessActivate());
m_bWasOnceWindowless = TRUE;
}
else
{
m_spClientSite->QueryInterface(IID_IOleInPlaceSiteEx, (void **)&m_spInPlaceSite);
if (m_spInPlaceSite)
m_bInPlaceSiteEx = TRUE;
else
hr = m_spClientSite->QueryInterface(IID_IOleInPlaceSite, (void **)&m_spInPlaceSite);
}
}

_ASSERTE(m_spInPlaceSite);
if (!m_spInPlaceSite)
return E_FAIL;

m_bNegotiatedWnd = TRUE;

if (!m_bInPlaceActive)
{

BOOL bNoRedraw = FALSE;
if (m_bWndLess)
m_spInPlaceSite->OnInPlaceActivateEx(&bNoRedraw, ACTIVATE_WINDOWLESS);
else
{
if (m_bInPlaceSiteEx)
m_spInPlaceSite->OnInPlaceActivateEx(&bNoRedraw, 0);
else
{
HRESULT hr = m_spInPlaceSite->CanInPlaceActivate();
if (FAILED(hr))
return hr;
m_spInPlaceSite->OnInPlaceActivate();
}
}
}

m_bInPlaceActive = TRUE;

// get location in the parent window,
// as well as some information about the parent
//
OLEINPLACEFRAMEINFO frameInfo;
RECT rcPos, rcClip;
CComPtr<IOleInPlaceFrame> spInPlaceFrame;
CComPtr<IOleInPlaceUIWindow> spInPlaceUIWindow;
frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO);
HWND hwndParent;
if (m_spInPlaceSite->GetWindow(&hwndParent) == S_OK)
{
m_spInPlaceSite->GetWindowContext(&spInPlaceFrame,
&spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);

if (!m_bWndLess)
{
if (m_hWndCD)
{
ShowWindow(m_hWndCD, SW_SHOW);
SetFocus(m_hWndCD);
}
else
{
HWND h = CreateControlWindow(hwndParent, rcPos);
_ASSERTE(h == m_hWndCD);
}
}

pIPO->SetObjectRects(&rcPos, &rcClip);
}

CComPtr<IOleInPlaceActiveObject> spActiveObject;
ControlQueryInterface(IID_IOleInPlaceActiveObject, (void**)&spActiveObject);

// Gone active by now, take care of UIACTIVATE
if (DoesVerbUIActivate(iVerb))
{
if (!m_bUIActive)
{
m_bUIActive = TRUE;
hr = m_spInPlaceSite->OnUIActivate();
if (FAILED(hr))
return hr;

SetControlFocus(TRUE);
// set ourselves up in the host.
//
if (spActiveObject)
{
if (spInPlaceFrame)
spInPlaceFrame->SetActiveObject(spActiveObject, NULL);
if (spInPlaceUIWindow)
spInPlaceUIWindow->SetActiveObject(spActiveObject, NULL);
}

if (spInPlaceFrame)
spInPlaceFrame->SetBorderSpace(NULL);
if (spInPlaceUIWindow)
spInPlaceUIWindow->SetBorderSpace(NULL);
}
}

m_spClientSite->ShowObject();

return S_OK;
}

HRESULT CComControlBase::IPersistStreamInit_Load(LPSTREAM pStm, ATL_PROPMAP_ENTRY* pMap)
{
_ASSERTE(pMap != NULL);
HRESULT hr = S_OK;
DWORD dwVer;
hr = pStm->Read(&dwVer, sizeof(DWORD), NULL);
if (SUCCEEDED(hr) && dwVer <= _ATL_VER)
hr = pStm->Read(&m_sizeExtent, sizeof(m_sizeExtent), NULL);
if (FAILED(hr))
return hr;

CComPtr<IDispatch> pDispatch;
const IID* piidOld = NULL;
for(int i = 0; pMap[i].pclsidPropPage != NULL; i++)
{
if (pMap[i].szDesc == NULL)
continue;
CComVariant var;

HRESULT hr = var.ReadFromStream(pStm);
if (FAILED(hr))
break;

if(pMap[i].piidDispatch != piidOld)
{
if(FAILED(ControlQueryInterface(*pMap[i].piidDispatch, (void**)&pDispatch)))
{
ATLTRACE(_T("Failed to get a dispatch pointer for property #%i\n"), i);
hr = E_FAIL;
break;
}
piidOld = pMap[i].piidDispatch;
}

if (FAILED(CComDispatchDriver::PutProperty(pDispatch, pMap[i].dispid, &var)))
{
ATLTRACE(_T("Invoked failed on DISPID %x\n"), pMap[i].dispid);
hr = E_FAIL;
break;
}
}
return hr;
}

HRESULT CComControlBase::IPersistStreamInit_Save(LPSTREAM pStm, BOOL /* fClearDirty */, ATL_PROPMAP_ENTRY* pMap)
{
_ASSERTE(pMap != NULL);
DWORD dw = _ATL_VER;
HRESULT hr = pStm->Write(&dw, sizeof(DWORD), NULL);
if (FAILED(hr))
return hr;
hr = pStm->Write(&m_sizeExtent, sizeof(m_sizeExtent), NULL);
if (FAILED(hr))
return hr;

CComPtr<IDispatch> pDispatch;
const IID* piidOld = NULL;
for(int i = 0; pMap[i].pclsidPropPage != NULL; i++)
{
if (pMap[i].szDesc == NULL)
continue;
CComVariant var;

if(pMap[i].piidDispatch != piidOld)
{
if(FAILED(ControlQueryInterface(*pMap[i].piidDispatch, (void**)&pDispatch)))
{
ATLTRACE(_T("Failed to get a dispatch pointer for property #%i\n"), i);
hr = E_FAIL;
break;
}
piidOld = pMap[i].piidDispatch;
}

if (FAILED(CComDispatchDriver::GetProperty(pDispatch, pMap[i].dispid, &var)))
{
ATLTRACE(_T("Invoked failed on DISPID %x\n"), pMap[i].dispid);
hr = E_FAIL;
break;
}

HRESULT hr = var.WriteToStream(pStm);
if (FAILED(hr))
break;
}
if (SUCCEEDED(hr))
m_bRequiresSave = FALSE;

return hr;
}

HRESULT CComControlBase::SendOnDataChange(DWORD advf)
{
HRESULT hRes = S_OK;
if (m_spDataAdviseHolder)
{
CComPtr<IDataObject> pdo;
if (SUCCEEDED(ControlQueryInterface(IID_IDataObject, (void**)&pdo)))
hRes = m_spDataAdviseHolder->SendOnDataChange(pdo, 0, advf);
}
return hRes;
}

HRESULT CComControlBase::IOleObject_SetClientSite(IOleClientSite *pClientSite)
{
_ASSERTE(pClientSite == NULL || m_spClientSite == NULL);
m_spClientSite = pClientSite;
m_spAmbientDispatch.Release();
if (m_spClientSite != NULL)
{
m_spClientSite->QueryInterface(IID_IDispatch,
(void**) &m_spAmbientDispatch.p);
}
return S_OK;
}

HRESULT CComControlBase::IOleObject_GetClientSite(IOleClientSite **ppClientSite)
{
_ASSERTE(ppClientSite);
HRESULT hRes = E_POINTER;
if (ppClientSite != NULL)
{
*ppClientSite = m_spClientSite;
m_spClientSite->AddRef();
hRes = S_OK;
}
return hRes;
}

HRESULT CComControlBase::IOleObject_Advise(IAdviseSink *pAdvSink,
DWORD *pdwConnection)
{
HRESULT hr = S_OK;
if (m_spOleAdviseHolder == NULL)
hr = CreateOleAdviseHolder(&m_spOleAdviseHolder);
if (SUCCEEDED(hr))
hr = m_spOleAdviseHolder->Advise(pAdvSink, pdwConnection);
return hr;
}

HRESULT CComControlBase::IOleObject_Close(DWORD dwSaveOption)
{
CComPtr<IOleInPlaceObject> pIPO;
ControlQueryInterface(IID_IOleInPlaceObject, (void**)&pIPO);
_ASSERTE(pIPO != NULL);
if (m_hWndCD)
{
if (m_spClientSite)
m_spClientSite->OnShowWindow(FALSE);
}

if (m_bInPlaceActive)
{
HRESULT hr = pIPO->InPlaceDeactivate();
if (FAILED(hr))
return hr;
_ASSERTE(!m_bInPlaceActive);
}
if (m_hWndCD)
{
ATLTRACE(_T("Destroying Window\n"));
if (::IsWindow(m_hWndCD))
DestroyWindow(m_hWndCD);
m_hWndCD = NULL;
}

// handle the save flag.
//
if ((dwSaveOption == OLECLOSE_SAVEIFDIRTY ||
dwSaveOption == OLECLOSE_PROMPTSAVE) && m_bRequiresSave)
{
if (m_spClientSite)
m_spClientSite->SaveObject();
SendOnSave();
}

m_spInPlaceSite.Release();
m_bNegotiatedWnd = FALSE;
m_bWndLess = FALSE;
m_bInPlaceSiteEx = FALSE;
m_spAdviseSink.Release();
return S_OK;
}

HRESULT CComControlBase::IOleInPlaceObject_InPlaceDeactivate(void)
{
CComPtr<IOleInPlaceObject> pIPO;
ControlQueryInterface(IID_IOleInPlaceObject, (void**)&pIPO);
_ASSERTE(pIPO != NULL);

if (!m_bInPlaceActive)
return S_OK;
pIPO->UIDeactivate();

m_bInPlaceActive = FALSE;

// if we have a window, tell it to go away.
//
if (m_hWndCD)
{
ATLTRACE(_T("Destroying Window\n"));
if (::IsWindow(m_hWndCD))
DestroyWindow(m_hWndCD);
m_hWndCD = NULL;
}

if (m_spInPlaceSite)
m_spInPlaceSite->OnInPlaceDeactivate();

return S_OK;
}

HRESULT CComControlBase::IOleInPlaceObject_UIDeactivate(void)
{
// if we're not UIActive, not much to do.
//
if (!m_bUIActive)
return S_OK;

m_bUIActive = FALSE;

// notify frame windows, if appropriate, that we're no longer ui-active.
//
CComPtr<IOleInPlaceFrame> spInPlaceFrame;
CComPtr<IOleInPlaceUIWindow> spInPlaceUIWindow;
OLEINPLACEFRAMEINFO frameInfo;
frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO);
RECT rcPos, rcClip;

HWND hwndParent;
// This call to GetWindow is a fix for Delphi
if (m_spInPlaceSite->GetWindow(&hwndParent) == S_OK)
{
m_spInPlaceSite->GetWindowContext(&spInPlaceFrame,
&spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);
if (spInPlaceUIWindow)
spInPlaceUIWindow->SetActiveObject(NULL, NULL);
if (spInPlaceFrame)
spInPlaceFrame->SetActiveObject(NULL, NULL);
}
// we don't need to explicitly release the focus here since somebody
// else grabbing the focus is what is likely to cause us to get lose it
//
m_spInPlaceSite->OnUIDeactivate(FALSE);

return S_OK;
}

HRESULT CComControlBase::IOleInPlaceObject_SetObjectRects(LPCRECT prcPos,LPCRECT prcClip)
{
if (prcPos == NULL || prcClip == NULL)
return E_POINTER;

m_rcPos = *prcPos;
if (m_hWndCD)
{
// the container wants us to clip, so figure out if we really
// need to
//
RECT rcIXect;
BOOL b = IntersectRect(&rcIXect, prcPos, prcClip);
HRGN tempRgn = NULL;
if (b && !EqualRect(&rcIXect, prcPos))
{
OffsetRect(&rcIXect, -(prcPos->left), -(prcPos->top));
tempRgn = CreateRectRgnIndirect(&rcIXect);
}

SetWindowRgn(m_hWndCD, tempRgn, TRUE);

// set our control's location, but don't change it's size at all
// [people for whom zooming is important should set that up here]
//
SIZEL size = {prcPos->right - prcPos->left, prcPos->bottom - prcPos->top};
SetWindowPos(m_hWndCD, NULL, prcPos->left,
 prcPos->top, size.cx, size.cy, SWP_NOZORDER | SWP_NOACTIVATE);
}

return S_OK;
}

HRESULT CComControlBase::IOleObject_SetExtent(DWORD dwDrawAspect, SIZEL *psizel)
{
if (dwDrawAspect != DVASPECT_CONTENT)
return DV_E_DVASPECT;
if (psizel == NULL)
return E_POINTER;

BOOL bSizeMatchesNatural =
memcmp(psizel, &m_sizeNatural, sizeof(SIZE)) == 0;

if (m_bAutoSize) //object can't do any other size
return (bSizeMatchesNatural) ? S_OK : E_FAIL;

BOOL bResized = FALSE;
if (memcmp(psizel, &m_sizeExtent, sizeof(SIZE)) != 0)
{
m_sizeExtent = *psizel;
bResized = TRUE;
}
if (m_bResizeNatural && !bSizeMatchesNatural)
{
m_sizeNatural = *psizel;
bResized = TRUE;
}

if (m_bRecomposeOnResize && bResized)
{
SendOnDataChange();
FireViewChange();
}
return S_OK;
}

HRESULT CComControlBase::IViewObject_Draw(DWORD dwDrawAspect, LONG lindex,
void *pvAspect, DVTARGETDEVICE *ptd, HDC hicTargetDev, HDC hdcDraw,
LPCRECTL prcBounds, LPCRECTL prcWBounds)
{
ATLTRACE(_T("Draw dwDrawAspect=%x lindex=%d ptd=%x hic=%x hdc=%x\n"),
dwDrawAspect, lindex, ptd, hicTargetDev, hdcDraw);
#ifdef _DEBUG
if (prcBounds == NULL)
ATLTRACE(_T("\tprcBounds=NULL\n"));
else
ATLTRACE(_T("\tprcBounds=%d,%d,%d,%d\n"), prcBounds->left,
prcBounds->top, prcBounds->right, prcBounds->bottom);
if (prcWBounds == NULL)
ATLTRACE(_T("\tprcWBounds=NULL\n"));
else
ATLTRACE(_T("\tprcWBounds=%d,%d,%d,%d\n"), prcWBounds->left,
prcWBounds->top, prcWBounds->right, prcWBounds->bottom);
#endif
_ASSERTE(prcBounds != NULL | m_bWndLess);

if (prcBounds == NULL)
{
if (!m_bWndLess)
return E_INVALIDARG;
prcBounds = (RECTL*)&m_rcPos;
}

// support the aspects required for multi-pass drawing
switch (dwDrawAspect)
{
case DVASPECT_CONTENT:
case DVASPECT_OPAQUE:
case DVASPECT_TRANSPARENT:
break;
default:
_ASSERTE(FALSE);
return DV_E_DVASPECT;
break;
}

// make sure nobody forgets to do this
if (ptd == NULL)
hicTargetDev = NULL;

BOOL bOptimize = FALSE;
if (pvAspect && ((DVASPECTINFO *)pvAspect)->cb >= sizeof(DVASPECTINFO))
bOptimize = (((DVASPECTINFO *)pvAspect)->dwFlags & DVASPECTINFOFLAG_CANOPTIMIZE);

ATL_DRAWINFO di;
memset(&di, 0, sizeof(di));
di.cbSize = sizeof(di);
di.dwDrawAspect = dwDrawAspect;
di.lindex = lindex;
di.ptd = ptd;
di.hicTargetDev = hicTargetDev;
di.hdcDraw = hdcDraw;
di.prcBounds = prcBounds;
di.prcWBounds = prcWBounds;
di.bOptimize = bOptimize;
return OnDrawAdvanced(di);
}

HRESULT CComControlBase::IDataObject_GetData(FORMATETC *pformatetcIn,
STGMEDIUM *pmedium)
{
if (pmedium == NULL)
return E_POINTER;
memset(pmedium, 0, sizeof(STGMEDIUM));
ATLTRACE(_T("Format = %x\n"), pformatetcIn->cfFormat);
ATLTRACE(_T("TYMED = %x\n"), pformatetcIn->tymed);

if ((pformatetcIn->tymed & TYMED_MFPICT) == 0)
return DATA_E_FORMATETC;

SIZEL sizeMetric, size;
if (m_bDrawFromNatural)
sizeMetric = m_sizeNatural;
else
sizeMetric = m_sizeExtent;
if (!m_bDrawGetDataInHimetric)
AtlHiMetricToPixel(&sizeMetric, &size);
else
size = sizeMetric;
RECTL rectl = {0 ,0, size.cx, size.cy};

ATL_DRAWINFO di;
memset(&di, 0, sizeof(di));
di.cbSize = sizeof(di);
di.dwDrawAspect = DVASPECT_CONTENT;
di.lindex = -1;
di.ptd = NULL;
di.hicTargetDev = NULL;
di.prcBounds = &rectl;
di.prcWBounds = &rectl;
di.bOptimize = TRUE; //we do a SaveDC/RestoreDC
di.bRectInHimetric = m_bDrawGetDataInHimetric;
// create appropriate memory metafile DC
di.hdcDraw = CreateMetaFile(NULL);

// create attribute DC according to pformatetcIn->ptd

SaveDC(di.hdcDraw);
SetWindowOrgEx(di.hdcDraw, 0, 0, NULL);
SetWindowExtEx(di.hdcDraw, rectl.right, rectl.bottom, NULL);
OnDrawAdvanced(di);
RestoreDC(di.hdcDraw, -1);

HMETAFILE hMF = CloseMetaFile(di.hdcDraw);
if (hMF == NULL)
return E_UNEXPECTED;

HGLOBAL hMem=GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, sizeof(METAFILEPICT));

if (NULL==hMem)
{
DeleteMetaFile(hMF);
return ResultFromScode(STG_E_MEDIUMFULL);
}

LPMETAFILEPICT pMF=(LPMETAFILEPICT)GlobalLock(hMem);
pMF->hMF=hMF;
pMF->mm=MM_ANISOTROPIC;
pMF->xExt=sizeMetric.cx;
pMF->yExt=sizeMetric.cy;
GlobalUnlock(hMem);

pmedium->tymed = TYMED_MFPICT;
pmedium->hGlobal = hMem;
pmedium->pUnkForRelease = NULL;

return S_OK;

}

HRESULT CComControlBase::FireViewChange()
{
if (m_bInPlaceActive)
{
// Active
if (m_hWndCD != NULL)
return ::InvalidateRect(m_hWndCD, NULL, TRUE); // Window based
if (m_spInPlaceSite != NULL)
return m_spInPlaceSite->InvalidateRect(NULL, TRUE); // Windowless
}
// Inactive
SendOnViewChange(DVASPECT_CONTENT);
return S_OK;
}

void CComControlBase::GetZoomInfo(ATL_DRAWINFO& di)
{
const RECTL& rcPos = *di.prcBounds;
SIZEL sizeDen;
if (m_bDrawFromNatural)
sizeDen = m_sizeNatural;
else
sizeDen = m_sizeExtent;
if (!di.bRectInHimetric)
AtlHiMetricToPixel(&sizeDen, &sizeDen);
SIZEL sizeNum = {rcPos.right-rcPos.left, rcPos.bottom-rcPos.top};
di.ZoomNum.cx = sizeNum.cx;
di.ZoomNum.cy = sizeNum.cy;
di.ZoomDen.cx = sizeDen.cx;
di.ZoomDen.cy = sizeDen.cy;
if (sizeDen.cx == 0 || sizeDen.cy == 0 ||
sizeNum.cx == 0 || sizeNum.cy == 0)
{
di.ZoomNum.cx = di.ZoomNum.cy = di.ZoomDen.cx = di.ZoomDen.cy = 1;
di.bZoomed = FALSE;
}
else if (sizeNum.cx != sizeDen.cx || sizeNum.cy != sizeDen.cy)
di.bZoomed = TRUE;
else
di.bZoomed = FALSE;
}

HRESULT CComControlBase::OnDrawAdvanced(ATL_DRAWINFO& di)
{
BOOL bDeleteDC = FALSE;
if (di.hicTargetDev == NULL)
{
di.hicTargetDev = AtlCreateTargetDC(di.hdcDraw, di.ptd);
bDeleteDC = (di.hicTargetDev != di.hdcDraw);
}
RECTL rectBoundsDP = *di.prcBounds;
BOOL bMetafile = GetDeviceCaps(di.hdcDraw, TECHNOLOGY) == DT_METAFILE;
if (!bMetafile)
{
::LPtoDP(di.hicTargetDev, (LPPOINT)&rectBoundsDP, 2);
SaveDC(di.hdcDraw);
SetMapMode(di.hdcDraw, MM_TEXT);
SetWindowOrgEx(di.hdcDraw, 0, 0, NULL);
SetViewportOrgEx(di.hdcDraw, 0, 0, NULL);
di.bOptimize = TRUE; //since we save the DC we can do this
}
di.prcBounds = &rectBoundsDP;
GetZoomInfo(di);

HRESULT hRes = OnDraw(di);
if (bDeleteDC)
::DeleteDC(di.hicTargetDev);
if (!bMetafile)
RestoreDC(di.hdcDraw, -1);
return hRes;
}

LRESULT CComControlBase::OnPaint(UINT /* nMsg */, WPARAM /* wParam */,
LPARAM /* lParam */, BOOL& /* lResult */)
{
RECT rc;
PAINTSTRUCT ps;

HDC hdc = ::BeginPaint(m_hWndCD, &ps);
if (hdc == NULL)
return 0;
::GetClientRect(m_hWndCD, &rc);

ATL_DRAWINFO di;
memset(&di, 0, sizeof(di));
di.cbSize = sizeof(di);
di.dwDrawAspect = DVASPECT_CONTENT;
di.lindex = -1;
di.hdcDraw = hdc;
di.prcBounds = (LPCRECTL)&rc;

OnDrawAdvanced(di);
::EndPaint(m_hWndCD, &ps);
return 0;
}

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

///////////////////////////////////////////////////////////////////////////////
//All Global stuff goes below this line
///////////////////////////////////////////////////////////////////////////////

#ifndef _ATL_DLL
ATLAPI_(HDC) AtlCreateTargetDC(HDC hdc, DVTARGETDEVICE* ptd)
{
USES_CONVERSION;

// cases  hdc, ptd, hdc is metafile, hic
//  NULL,    NULL,  n/a,    Display
//  NULL,   !NULL,  n/a,    ptd
//  !NULL,   NULL,  FALSE,  hdc
//  !NULL,   NULL,  TRUE,   display
//  !NULL,  !NULL,  FALSE,  ptd
//  !NULL,  !NULL,  TRUE,   ptd

if (ptd != NULL)
{
LPDEVMODEOLE lpDevMode;
LPOLESTR lpszDriverName;
LPOLESTR lpszDeviceName;
LPOLESTR lpszPortName;

if (ptd->tdExtDevmodeOffset == 0)
lpDevMode = NULL;
else
lpDevMode  = (LPDEVMODEOLE) ((LPSTR)ptd + ptd->tdExtDevmodeOffset);

lpszDriverName = (LPOLESTR)((BYTE*)ptd + ptd->tdDriverNameOffset);
lpszDeviceName = (LPOLESTR)((BYTE*)ptd + ptd->tdDeviceNameOffset);
lpszPortName   = (LPOLESTR)((BYTE*)ptd + ptd->tdPortNameOffset);

return ::CreateDC(OLE2CT(lpszDriverName), OLE2CT(lpszDeviceName),
OLE2CT(lpszPortName), DEVMODEOLE2T(lpDevMode));
}
else if (hdc == NULL || GetDeviceCaps(hdc, TECHNOLOGY) == DT_METAFILE)
return ::CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
else
return hdc;
}

#define HIMETRIC_PER_INCH   2540
#define MAP_PIX_TO_LOGHIM(x,ppli)   ( (HIMETRIC_PER_INCH*(x) + ((ppli)>>1)) / (ppli) )
#define MAP_LOGHIM_TO_PIX(x,ppli)   ( ((ppli)*(x) + HIMETRIC_PER_INCH/2) / HIMETRIC_PER_INCH )

ATLAPI_(void) AtlHiMetricToPixel(const SIZEL * lpSizeInHiMetric, LPSIZEL lpSizeInPix)
{
int nPixelsPerInchX;    // Pixels per logical inch along width
int nPixelsPerInchY;    // Pixels per logical inch along height

HDC hDCScreen = GetDC(NULL);
_ASSERTE(hDCScreen != NULL);
nPixelsPerInchX = GetDeviceCaps(hDCScreen, LOGPIXELSX);
nPixelsPerInchY = GetDeviceCaps(hDCScreen, LOGPIXELSY);
ReleaseDC(NULL, hDCScreen);

lpSizeInPix->cx = MAP_LOGHIM_TO_PIX(lpSizeInHiMetric->cx, nPixelsPerInchX);
lpSizeInPix->cy = MAP_LOGHIM_TO_PIX(lpSizeInHiMetric->cy, nPixelsPerInchY);
}

ATLAPI_(void) AtlPixelToHiMetric(const SIZEL * lpSizeInPix, LPSIZEL lpSizeInHiMetric)
{
int nPixelsPerInchX;    // Pixels per logical inch along width
int nPixelsPerInchY;    // Pixels per logical inch along height

HDC hDCScreen = GetDC(NULL);
_ASSERTE(hDCScreen != NULL);
nPixelsPerInchX = GetDeviceCaps(hDCScreen, LOGPIXELSX);
nPixelsPerInchY = GetDeviceCaps(hDCScreen, LOGPIXELSY);
ReleaseDC(NULL, hDCScreen);

lpSizeInHiMetric->cx = MAP_PIX_TO_LOGHIM(lpSizeInPix->cx, nPixelsPerInchX);
lpSizeInHiMetric->cy = MAP_PIX_TO_LOGHIM(lpSizeInPix->cy, nPixelsPerInchY);
}
#endif


ATLCTL.H

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLCTL_H__
#define __ATLCTL_H__

#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#include <atlwin.h>
#include <objsafe.h>
#include <urlmon.h>

#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "urlmon.lib")

ATLAPI_(void) AtlHiMetricToPixel(const SIZEL * lpSizeInHiMetric, LPSIZEL lpSizeInPix);
ATLAPI_(void) AtlPixelToHiMetric(const SIZEL * lpSizeInPix, LPSIZEL lpSizeInHiMetric);
ATLAPI_(HDC) AtlCreateTargetDC(HDC hdc, DVTARGETDEVICE* ptd);

// Include GUIDs for the new stock property dialogs contained in the dll MSStkProp.DLL
#include "msstkppg.h"
#define CLSID_MSStockFont CLSID_StockFontPage
#define CLSID_MSStockColor CLSID_StockColorPage
#define CLSID_MSStockPicture CLSID_StockPicturePage

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

#pragma pack(push, _ATL_PACKING)

// Forward declarations
//
class ATL_NO_VTABLE CComControlBase;
template <class T> class CComControl;
class CComDispatchDriver;

struct ATL_PROPMAP_ENTRY
{
LPCOLESTR szDesc;
DISPID dispid;
const CLSID* pclsidPropPage;
const IID* piidDispatch;

};

struct ATL_DRAWINFO
{
UINT cbSize;
DWORD dwDrawAspect;
LONG lindex;
DVTARGETDEVICE* ptd;
HDC hicTargetDev;
HDC hdcDraw;
LPCRECTL prcBounds; //Rectangle in which to draw
LPCRECTL prcWBounds; //WindowOrg and Ext if metafile
BOOL bOptimize;
BOOL bZoomed;
BOOL bRectInHimetric;
SIZEL ZoomNum;      //ZoomX = ZoomNum.cx/ZoomNum.cy
SIZEL ZoomDen;
};

//////////////////////////////////////////////////////////////////////////////
// CComDispatchDriver / Specialization of CComQIPtr<IDispatch, IID_IDispatch>
class CComDispatchDriver
{
public:
CComDispatchDriver()
{
p = NULL;
}
CComDispatchDriver(IDispatch* lp)
{
if ((p = lp) != NULL)
p->AddRef();
}
CComDispatchDriver(IUnknown* lp)
{
p=NULL;
if (lp != NULL)
lp->QueryInterface(IID_IDispatch, (void **)&p);
}
~CComDispatchDriver() { if (p) p->Release(); }
void Release() {if (p) p->Release(); p=NULL;}
operator IDispatch*() {return p;}
IDispatch& operator*() {_ASSERTE(p!=NULL); return *p; }
IDispatch** operator&() {_ASSERTE(p==NULL); return &p; }
IDispatch* operator->() {_ASSERTE(p!=NULL); return p; }
IDispatch* operator=(IDispatch* lp){return (IDispatch*)AtlComPtrAssign((IUnknown**)&p, lp);}
IDispatch* operator=(IUnknown* lp)
{
return (IDispatch*)AtlComQIPtrAssign((IUnknown**)&p, lp, IID_IDispatch);
}
BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}

HRESULT GetProperty(DISPID dwDispID, VARIANT* pVar)
{
_ASSERTE(p);
return GetProperty(p, dwDispID, pVar);
}
HRESULT PutProperty(DISPID dwDispID, VARIANT* pVar)
{
_ASSERTE(p);
return PutProperty(p, dwDispID, pVar);
}

static HRESULT GetProperty(IDispatch* pDisp, DISPID dwDispID, VARIANT* pVar);
static HRESULT PutProperty(IDispatch* pDisp, DISPID dwDispID, VARIANT* pVar);
IDispatch* p;
};

//////////////////////////////////////////////////////////////////////////////
// CFirePropNotifyEvent
class CFirePropNotifyEvent
{
public:
static HRESULT FireOnRequestEdit(IUnknown* pUnk, DISPID dispID);
static HRESULT FireOnChanged(IUnknown* pUnk, DISPID dispID);
};

//////////////////////////////////////////////////////////////////////////////
// CFakeFirePropNotifyEvent
class CFakeFirePropNotifyEvent
{
public:
static HRESULT FireOnRequestEdit(IUnknown* /*pUnk*/, DISPID /*dispID*/)
{
return S_OK;
}
static HRESULT FireOnChanged(IUnknown* /*pUnk*/, DISPID /*dispID*/)
{
return S_OK;
}
};

typedef CFakeFirePropNotifyEvent _ATL_PROP_NOTIFY_EVENT_CLASS;

//////////////////////////////////////////////////////////////////////////////
// CComControl
class ATL_NO_VTABLE CComControlBase
{
public:
CComControlBase(HWND& h) : m_hWndCD(h)
{
memset(this, 0, sizeof(CComControlBase));
m_phWndCD = &h;
m_sizeExtent.cx = 2*2540;
m_sizeExtent.cy = 2*2540;
m_sizeNatural = m_sizeExtent;
}
~CComControlBase()
{
if (m_hWndCD != NULL)
::DestroyWindow(m_hWndCD);
ATLTRACE(_T("Control Destroyed\n"));
}

// methods
public:
// Control helper functions can go here
// non-virtuals only please
void SetDirty(BOOL bDirty)
{
m_bRequiresSave = bDirty;
}
BOOL GetDirty()
{
return m_bRequiresSave ? TRUE : FALSE;
}
void GetZoomInfo(ATL_DRAWINFO& di);
HRESULT SendOnRename(IMoniker *pmk)
{
HRESULT hRes = S_OK;
if (m_spOleAdviseHolder)
hRes = m_spOleAdviseHolder->SendOnRename(pmk);
return hRes;
}
HRESULT SendOnSave()
{
HRESULT hRes = S_OK;
if (m_spOleAdviseHolder)
hRes = m_spOleAdviseHolder->SendOnSave();
return hRes;
}
HRESULT SendOnClose()
{
HRESULT hRes = S_OK;
if (m_spOleAdviseHolder)
hRes = m_spOleAdviseHolder->SendOnClose();
return hRes;
}
HRESULT SendOnDataChange(DWORD advf = 0);
HRESULT SendOnViewChange(DWORD dwAspect, LONG lindex = -1)
{
if (m_spAdviseSink)
m_spAdviseSink->OnViewChange(dwAspect, lindex);
return S_OK;
}
LRESULT OnSetFocus(UINT /* nMsg */, WPARAM /* wParam */, LPARAM /* lParam */, BOOL& /* bHandled */)
{
CComQIPtr <IOleControlSite, &IID_IOleControlSite> spSite(m_spClientSite);
if (m_bInPlaceActive && spSite)
spSite->OnFocus(TRUE);
return 0;
}

LRESULT OnKillFocus(UINT /* nMsg */, WPARAM /* wParam */, LPARAM /* lParam */, BOOL& /* bHandled */)
{
CComQIPtr <IOleControlSite, &IID_IOleControlSite> spSite(m_spClientSite);
if (m_bInPlaceActive && spSite)
spSite->OnFocus(FALSE);
return 0;
}
LRESULT OnGetDlgCode(UINT /* nMsg */, WPARAM /* wParam */, LPARAM /* lParam */, BOOL& /* bHandled */)
{
return 0;
}

HRESULT GetAmbientProperty(DISPID dispid, VARIANT& var)
{
HRESULT hRes = E_FAIL;
if (m_spAmbientDispatch.p != NULL)
hRes = m_spAmbientDispatch.GetProperty(dispid, &var);
return hRes;
}
HRESULT GetAmbientAppearance(short& nAppearance)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_APPEARANCE, var);
_ASSERTE(var.vt == VT_I2 || FAILED(hRes));
nAppearance = var.iVal;
return hRes;
}
HRESULT GetAmbientBackColor(OLE_COLOR& BackColor)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_BACKCOLOR, var);
_ASSERTE(var.vt == VT_I4 || FAILED(hRes));
BackColor = var.lVal;
return hRes;
}
HRESULT GetAmbientDisplayName(BSTR& bstrDiaplayName)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_DISPLAYNAME, var);
_ASSERTE(var.vt == VT_BSTR || FAILED(hRes));
bstrDiaplayName = var.bstrVal;
return hRes;
}
HRESULT GetAmbientFont(IFont** ppFont)
{
// caller MUST Release the font!
if (ppFont == NULL)
return E_POINTER;
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_FONT, var);
_ASSERTE((var.vt == VT_UNKNOWN || var.vt == VT_DISPATCH) || FAILED(hRes));
if (SUCCEEDED(hRes) && var.pdispVal)
{
if (var.vt == VT_UNKNOWN || var.vt == VT_DISPATCH)
hRes = var.pdispVal->QueryInterface(IID_IFont, (void**)ppFont);
else
hRes = DISP_E_BADVARTYPE;
}
return hRes;
}
HRESULT GetAmbientForeColor(OLE_COLOR& ForeColor)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_FORECOLOR, var);
_ASSERTE(var.vt == VT_I4 || FAILED(hRes));
ForeColor = var.lVal;
return hRes;
}
HRESULT GetAmbientLocaleID(LCID& lcid)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_LOCALEID, var);
_ASSERTE(var.vt == VT_I4 || FAILED(hRes));
lcid = var.lVal;
return hRes;
}
HRESULT GetAmbientScaleUnits(BSTR& bstrScaleUnits)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_SCALEUNITS, var);
_ASSERTE(var.vt == VT_BSTR || FAILED(hRes));
bstrScaleUnits = var.bstrVal;
return hRes;
}
HRESULT GetAmbientTextAlign(short& nTextAlign)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_TEXTALIGN, var);
_ASSERTE(var.vt == VT_I2 || FAILED(hRes));
nTextAlign = var.iVal;
return hRes;
}
HRESULT GetAmbientUserMode(BOOL& bUserMode)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_USERMODE, var);
_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
bUserMode = var.boolVal;
return hRes;
}
HRESULT GetAmbientUIDead(BOOL& bUIDead)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_UIDEAD, var);
_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
bUIDead = var.boolVal;
return hRes;
}
HRESULT GetAmbientShowGrabHandles(BOOL& bShowGrabHandles)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_SHOWGRABHANDLES, var);
_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
bShowGrabHandles = var.boolVal;
return hRes;
}
HRESULT GetAmbientShowHatching(BOOL& bShowHatching)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_SHOWHATCHING, var);
_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
bShowHatching = var.boolVal;
return hRes;
}
HRESULT GetAmbientMessageReflect(BOOL& bMessageReflect)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_MESSAGEREFLECT, var);
_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
bMessageReflect = var.boolVal;
return hRes;
}
HRESULT GetAmbientAutoClip(BOOL& bAutoClip)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_AUTOCLIP, var);
_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
bAutoClip = var.boolVal;
return hRes;
}
HRESULT GetAmbientDisplayAsDefault(BOOL& bDisplaysDefault)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_DISPLAYASDEFAULT, var);
_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
bDisplaysDefault = var.boolVal;
return hRes;
}
HRESULT GetAmbientSupportsMnemonics(BOOL& bSupportMnemonics)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_SUPPORTSMNEMONICS, var);
_ASSERTE(var.vt == VT_BOOL || FAILED(hRes));
bSupportMnemonics = var.boolVal;
return hRes;
}
HRESULT GetAmbientPalette(HPALETTE& hPalette)
{
CComVariant var;
HRESULT hRes = GetAmbientProperty(DISPID_AMBIENT_PALETTE, var);
_ASSERTE(var.vt == VT_I4 || FAILED(hRes));
hPalette = reinterpret_cast<HPALETTE>(var.lVal);
return hRes;
}

BOOL DoesVerbUIActivate(LONG iVerb)
{
BOOL b = FALSE;
switch (iVerb)
{
case OLEIVERB_UIACTIVATE:
case OLEIVERB_PRIMARY:
b = TRUE;
break;
}
// if no ambient dispatch then in old style OLE container
if (DoesVerbActivate(iVerb) && m_spAmbientDispatch.p == NULL)
b = TRUE;
return b;
}

BOOL DoesVerbActivate(LONG iVerb)
{
BOOL b = FALSE;
switch (iVerb)
{
case OLEIVERB_UIACTIVATE:
case OLEIVERB_PRIMARY:
case OLEIVERB_SHOW:
case OLEIVERB_INPLACEACTIVATE:
b = TRUE;
break;
}
return b;
}

BOOL SetControlFocus(BOOL bGrab);
HRESULT IQuickActivate_QuickActivate(QACONTAINER *pQACont,
QACONTROL *pQACtrl);
HRESULT IPersistPropertyBag_Load(LPPROPERTYBAG pPropBag,
LPERRORLOG pErrorLog, ATL_PROPMAP_ENTRY* pMap);
HRESULT IPersistPropertyBag_Save(LPPROPERTYBAG pPropBag,
BOOL fClearDirty, BOOL fSaveAllProperties, ATL_PROPMAP_ENTRY* pMap);
HRESULT ISpecifyPropertyPages_GetPages(CAUUID* pPages,
ATL_PROPMAP_ENTRY* pMap);
HRESULT DoVerbProperties(LPCRECT /* prcPosRect */, HWND hwndParent);
HRESULT InPlaceActivate(LONG iVerb, const RECT* prcPosRect = NULL);
HRESULT IPersistStreamInit_Load(LPSTREAM pStm, ATL_PROPMAP_ENTRY* pMap);
HRESULT IPersistStreamInit_Save(LPSTREAM pStm, BOOL /* fClearDirty */,
ATL_PROPMAP_ENTRY* pMap);

HRESULT IOleObject_SetClientSite(IOleClientSite *pClientSite);
HRESULT IOleObject_GetClientSite(IOleClientSite **ppClientSite);
HRESULT IOleObject_Advise(IAdviseSink *pAdvSink, DWORD *pdwConnection);
HRESULT IOleObject_Close(DWORD dwSaveOption);
HRESULT IOleObject_SetExtent(DWORD dwDrawAspect, SIZEL *psizel);
HRESULT IOleInPlaceObject_InPlaceDeactivate(void);
HRESULT IOleInPlaceObject_UIDeactivate(void);
HRESULT IOleInPlaceObject_SetObjectRects(LPCRECT prcPos,LPCRECT prcClip);
HRESULT IViewObject_Draw(DWORD dwDrawAspect, LONG lindex, void *pvAspect,
DVTARGETDEVICE *ptd, HDC hicTargetDev, HDC hdcDraw,
LPCRECTL prcBounds, LPCRECTL prcWBounds);
HRESULT IDataObject_GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);

HRESULT FireViewChange();
LRESULT OnPaint(UINT /* nMsg */, WPARAM /* wParam */, LPARAM /* lParam */,
BOOL& /* lResult */);

virtual HWND CreateControlWindow(HWND hWndParent, RECT& rcPos) = 0;
virtual HRESULT ControlQueryInterface(const IID& iid, void** ppv) = 0;
virtual HRESULT OnDrawAdvanced(ATL_DRAWINFO& di);
virtual HRESULT OnDraw(ATL_DRAWINFO& di)
{
return S_OK;
}

// Attributes
public:
CComPtr<IOleInPlaceSiteWindowless> m_spInPlaceSite;
CComPtr<IDataAdviseHolder> m_spDataAdviseHolder;
CComPtr<IOleAdviseHolder> m_spOleAdviseHolder;
CComPtr<IOleClientSite> m_spClientSite;
CComPtr<IAdviseSink> m_spAdviseSink;
CComDispatchDriver m_spAmbientDispatch;

SIZE m_sizeNatural; //unscaled size in himetric
SIZE m_sizeExtent;  //current extents in himetric
RECT m_rcPos; // position in pixels
union
{
HWND& m_hWndCD;
HWND* m_phWndCD;
};
union
{
// m_nFreezeEvents is the only one actually used
int m_nFreezeEvents; // count of freezes versus thaws

// These are here to make stock properties work
IPictureDisp* m_pMouseIcon;
IPictureDisp* m_pPicture;
IFontDisp* m_pFont;
OLE_COLOR m_clrBackColor;
OLE_COLOR m_clrBorderColor;
OLE_COLOR m_clrFillColor;
OLE_COLOR m_clrForeColor;
BSTR m_bstrText;
BSTR m_bstrCaption;
BOOL m_bValid;
BOOL m_bTabStop;
BOOL m_bBorderVisible;
BOOL m_bEnabled;
long m_nBackStyle;
long m_nBorderStyle;
long m_nBorderWidth;
long m_nDrawMode;
long m_nDrawStyle;
long m_nDrawWidth;
long m_nFillStyle;
long m_nAppearance;
long m_nMousePointer;
long m_nReadyState;
};

unsigned m_bNegotiatedWnd:1;
unsigned m_bWndLess:1;
unsigned m_bInPlaceActive:1;
unsigned m_bUIActive:1;
unsigned m_bUsingWindowRgn:1;
unsigned m_bInPlaceSiteEx:1;
unsigned m_bWindowOnly:1;
unsigned m_bRequiresSave:1;
unsigned m_bWasOnceWindowless:1;
unsigned m_bAutoSize:1; //SetExtent fails if size doesn't match existing
unsigned m_bRecomposeOnResize:1; //implies OLEMISC_RECOMPOSEONRESIZE
unsigned m_bResizeNatural:1;  //resize natural extent on SetExtent
unsigned m_bDrawFromNatural:1; //instead of m_sizeExtent
unsigned m_bDrawGetDataInHimetric:1; //instead of pixels
};

template <class T>
class ATL_NO_VTABLE CComControl :  public CComControlBase, public CWindowImpl<T>
{
public:
CComControl() : CComControlBase(m_hWnd) {}
HRESULT FireOnRequestEdit(DISPID dispID)
{
T* pT = static_cast<T*>(this);
return T::__ATL_PROP_NOTIFY_EVENT_CLASS::FireOnRequestEdit(pT->GetUnknown(), dispID);
}
HRESULT FireOnChanged(DISPID dispID)
{
T* pT = static_cast<T*>(this);
return T::__ATL_PROP_NOTIFY_EVENT_CLASS::FireOnChanged(pT->GetUnknown(), dispID);
}
virtual HRESULT ControlQueryInterface(const IID& iid, void** ppv)
{
T* pT = static_cast<T*>(this);
return pT->_InternalQueryInterface(iid, ppv);
}
virtual HWND CreateControlWindow(HWND hWndParent, RECT& rcPos)
{
T* pT = static_cast<T*>(this);
return pT->Create(hWndParent, rcPos);
}
};

// Forward declarations
//
template <class T> class IPersistImpl;
template <class T> class IPersistStreamInitImpl;
template <class T> class IPersistStorageImpl;
template <class T> class IPersistPropertyBagImpl;

template <class T> class IOleControlImpl;
template <class T> class IRunnableObjectImpl;
template <class T> class IQuickActivateImpl;
template <class T> class IOleObjectImpl;
template <class T> class IPropertyPageImpl;
template <class T> class IPropertyPage2Impl;
template <class T> class IPerPropertyBrowsingImpl;
template <class T> class IViewObjectExImpl;
template <class T> class IOleWindowImpl;
template <class T> class ISpecifyPropertyPagesImpl;
template <class T> class IPointerInactiveImpl;
template <class T, class CDV> class IPropertyNotifySinkCP;
template <class T> class IBindStatusCallbackImpl;
template <class T> class CBindStatusCallback;

//////////////////////////////////////////////////////////////////////////////
// IPersistImpl
template <class T>
class ATL_NO_VTABLE IPersistImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistImpl)

// IPersist
STDMETHOD(GetClassID)(CLSID *pClassID)
{
ATLTRACE(_T("IPersistImpl::GetClassID\n"));
T* pT = static_cast<T*>(this);
*pClassID = pT->GetObjectCLSID();
return S_OK;
}
};

#define BEGIN_PROPERTY_MAP(theClass) \
typedef _ATL_PROP_NOTIFY_EVENT_CLASS __ATL_PROP_NOTIFY_EVENT_CLASS; \
static ATL_PROPMAP_ENTRY* GetPropertyMap()\
{\
static ATL_PROPMAP_ENTRY pPropMap[] = \
{

#define PROP_ENTRY(szDesc, dispid, clsid) \
{OLESTR(szDesc), dispid, &clsid, &IID_IDispatch},

#define PROP_ENTRY_EX(szDesc, dispid, clsid, iidDispatch) \
{OLESTR(szDesc), dispid, &clsid, &iidDispatch},

#define PROP_PAGE(clsid) \
{NULL, NULL, &clsid, &IID_NULL},

#define END_PROPERTY_MAP() \
{NULL, 0, NULL, &IID_NULL} \
}; \
return pPropMap; \
}

//////////////////////////////////////////////////////////////////////////////
// IPersistStreamInitImpl
template <class T>
class ATL_NO_VTABLE IPersistStreamInitImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistStreamInitImpl)

// IPersist
STDMETHOD(GetClassID)(CLSID *pClassID)
{
ATLTRACE(_T("IPersistStreamInitImpl::GetClassID\n"));
T* pT = static_cast<T*>(this);
*pClassID = pT->GetObjectCLSID();
return S_OK;
}

// IPersistStream
STDMETHOD(IsDirty)()
{
ATLTRACE(_T("IPersistStreamInitImpl::IsDirty\n"));
T* pT = static_cast<T*>(this);
return (pT->m_bRequiresSave) ? S_OK : S_FALSE;
}
STDMETHOD(Load)(LPSTREAM pStm)
{
ATLTRACE(_T("IPersistStreamInitImpl::Load\n"));
T* pT = static_cast<T*>(this);
return pT->IPersistStreamInit_Load(pStm, T::GetPropertyMap());
}
STDMETHOD(Save)(LPSTREAM pStm, BOOL fClearDirty)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPersistStreamInitImpl::Save\n"));
return pT->IPersistStreamInit_Save(pStm, fClearDirty, T::GetPropertyMap());
}
STDMETHOD(GetSizeMax)(ULARGE_INTEGER FAR* /* pcbSize */)
{
ATLTRACENOTIMPL(_T("IPersistStreamInitImpl::GetSizeMax"));
}

// IPersistStreamInit
STDMETHOD(InitNew)()
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPersistStreamInitImpl::InitNew\n"));
pT->SendOnDataChange();
return S_OK;
}

};

//////////////////////////////////////////////////////////////////////////////
// IPersistStorageImpl
template <class T>
class ATL_NO_VTABLE IPersistStorageImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistStorageImpl)

// IPersist
STDMETHOD(GetClassID)(CLSID *pClassID)
{
ATLTRACE(_T("IPersistStorageImpl::GetClassID\n"));
T* pT = static_cast<T*>(this);
*pClassID = pT->GetObjectCLSID();
return S_OK;
}

// IPersistStorage
STDMETHOD(IsDirty)(void)
{
ATLTRACE(_T("IPersistStorageImpl::IsDirty\n"));
T* pT = static_cast<T*>(this);
CComPtr<IPersistStreamInit> p;
p.p = IPSI_GetIPersistStreamInit();
return (p != NULL) ? p->IsDirty() : E_FAIL;
}
STDMETHOD(InitNew)(IStorage*)
{
ATLTRACE(_T("IPersistStorageImpl::InitNew\n"));
T* pT = static_cast<T*>(this);
CComPtr<IPersistStreamInit> p;
p.p = IPSI_GetIPersistStreamInit();
return (p != NULL) ? p->InitNew() : E_FAIL;
}
STDMETHOD(Load)(IStorage* pStorage)
{
ATLTRACE(_T("IPersistStorageImpl::Load\n"));
T* pT = static_cast<T*>(this);
CComPtr<IPersistStreamInit> p;
p.p = IPSI_GetIPersistStreamInit();
HRESULT hr = E_FAIL;
if (p != NULL)
{
CComPtr<IStream> spStream;
hr = pStorage->OpenStream(OLESTR("Contents"), NULL,
STGM_DIRECT | STGM_SHARE_EXCLUSIVE, 0, &spStream);
if (SUCCEEDED(hr))
hr = p->Load(spStream);
}
return hr;
}
STDMETHOD(Save)(IStorage* pStorage, BOOL fSameAsLoad)
{
ATLTRACE(_T("IPersistStorageImpl::Save\n"));
T* pT = static_cast<T*>(this);
CComPtr<IPersistStreamInit> p;
p.p = IPSI_GetIPersistStreamInit();
HRESULT hr = E_FAIL;
if (p != NULL)
{
CComPtr<IStream> spStream;
static LPCOLESTR vszContents = OLESTR("Contents");
hr = pStorage->CreateStream(vszContents,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
0, 0, &spStream);
if (SUCCEEDED(hr))
hr = p->Save(spStream, fSameAsLoad);
}
return hr;
}
STDMETHOD(SaveCompleted)(IStorage* /* pStorage */)
{
ATLTRACE(_T("IPersistStorageImpl::SaveCompleted\n"));
return S_OK;
}
STDMETHOD(HandsOffStorage)(void)
{
ATLTRACE(_T("IPersistStorageImpl::HandsOffStorage\n"));
return S_OK;
}
private:
IPersistStreamInit* IPSI_GetIPersistStreamInit();
};

template <class T>
IPersistStreamInit* IPersistStorageImpl<T>::IPSI_GetIPersistStreamInit()
{
T* pT = static_cast<T*>(this);
IPersistStreamInit* p;
if (FAILED(pT->GetUnknown()->QueryInterface(IID_IPersistStreamInit, (void**)&p)))
pT->_InternalQueryInterface(IID_IPersistStreamInit, (void**)&p);
return p;
}

//////////////////////////////////////////////////////////////////////////////
// IPersistPropertyBagImpl
template <class T>
class ATL_NO_VTABLE IPersistPropertyBagImpl
{
public:

// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistPropertyBagImpl)

// IPersist
STDMETHOD(GetClassID)(CLSID *pClassID)
{
ATLTRACE(_T("IPersistPropertyBagImpl::GetClassID\n"));
T* pT = static_cast<T*>(this);
*pClassID = pT->GetObjectCLSID();
return S_OK;
}

// IPersistPropertyBag
//
STDMETHOD(InitNew)()
{
ATLTRACE(_T("IPersistPropertyBagImpl::InitNew\n"));
return S_OK;
}
STDMETHOD(Load)(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
{
ATLTRACE(_T("IPersistPropertyBagImpl::Load\n"));
T* pT = static_cast<T*>(this);
ATL_PROPMAP_ENTRY* pMap = T::GetPropertyMap();
_ASSERTE(pMap != NULL);
return pT->IPersistPropertyBag_Load(pPropBag, pErrorLog, pMap);
}
STDMETHOD(Save)(LPPROPERTYBAG pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
{
ATLTRACE(_T("IPersistPropertyBagImpl::Save\n"));
T* pT = static_cast<T*>(this);
ATL_PROPMAP_ENTRY* pMap = T::GetPropertyMap();
_ASSERTE(pMap != NULL);
return pT->IPersistPropertyBag_Save(pPropBag, fClearDirty, fSaveAllProperties, pMap);
}
};

//////////////////////////////////////////////////////////////////////////////
// IOleControlImpl
template <class T>
class ATL_NO_VTABLE IOleControlImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleControlImpl)

// IOleControl methods
//
STDMETHOD(GetControlInfo)(LPCONTROLINFO /* pCI */)
{
ATLTRACENOTIMPL(_T("IOleControlImpl::GetControlInfo"));
}
STDMETHOD(OnMnemonic)(LPMSG /* pMsg */)
{
ATLTRACENOTIMPL(_T("IOleControlImpl::OnMnemonic"));
}
STDMETHOD(OnAmbientPropertyChange)(DISPID dispid)
{
dispid;
ATLTRACE(_T("IOleControlImpl::OnAmbientPropertyChange\n"));
ATLTRACE(_T(" -- DISPID = %d (%d)\n"), dispid);
return S_OK;
}
STDMETHOD(FreezeEvents)(BOOL bFreeze)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleControlImpl::FreezeEvents\n"));
if (bFreeze)
pT->m_nFreezeEvents++;
else
pT->m_nFreezeEvents--;
return S_OK;
}
};

//////////////////////////////////////////////////////////////////////////////
// IQuickActivateImpl
template <class T>
class ATL_NO_VTABLE IQuickActivateImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IQuickActivateImpl)

// IQuickActivate
//
STDMETHOD(QuickActivate)(QACONTAINER *pQACont, QACONTROL *pQACtrl)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IQuickActivateImpl::QuickActivate\n"));
return pT->IQuickActivate_QuickActivate(pQACont, pQACtrl);
}
STDMETHOD(SetContentExtent)(LPSIZEL pSize)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IQuickActivateImpl::SetContentExtent\n"));
return pT->IOleObjectImpl<T>::SetExtent(DVASPECT_CONTENT, pSize);
}
STDMETHOD(GetContentExtent)(LPSIZEL pSize)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IQuickActivateImpl::GetContentExtent\n"));
return pT->IOleObjectImpl<T>::GetExtent(DVASPECT_CONTENT, pSize);
}
};

//////////////////////////////////////////////////////////////////////////////
// IOleObjectImpl
template <class T>
class ATL_NO_VTABLE IOleObjectImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleObjectImpl)

// IOleObject
//
STDMETHOD(SetClientSite)(IOleClientSite *pClientSite)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleObjectImpl::SetClientSite\n"));
return pT->IOleObject_SetClientSite(pClientSite);
}
STDMETHOD(GetClientSite)(IOleClientSite **ppClientSite)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleObjectImpl::GetClientSite\n"));
return pT->IOleObject_GetClientSite(ppClientSite);
}
STDMETHOD(SetHostNames)(LPCOLESTR /* szContainerApp */, LPCOLESTR /* szContainerObj */)
{
ATLTRACE(_T("IOleObjectImpl::SetHostNames\n"));
return S_OK;
}
STDMETHOD(Close)(DWORD dwSaveOption)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleObjectImpl::Close\n"));
return pT->IOleObject_Close(dwSaveOption);
}
STDMETHOD(SetMoniker)(DWORD /* dwWhichMoniker */, IMoniker* /* pmk */)
{
ATLTRACENOTIMPL(_T("IOleObjectImpl::SetMoniker"));
}
STDMETHOD(GetMoniker)(DWORD /* dwAssign */, DWORD /* dwWhichMoniker */, IMoniker** /* ppmk */)
{
ATLTRACENOTIMPL(_T("IOleObjectImpl::GetMoniker"));
}
STDMETHOD(InitFromData)(IDataObject* /* pDataObject */, BOOL /* fCreation */, DWORD /* dwReserved */)
{
ATLTRACENOTIMPL(_T("IOleObjectImpl::InitFromData"));
}
STDMETHOD(GetClipboardData)(DWORD /* dwReserved */, IDataObject** /* ppDataObject */)
{
ATLTRACENOTIMPL(_T("IOleObjectImpl::GetClipboardData"));
}

// Helpers for DoVerb - Over-rideable in user class
HRESULT DoVerbPrimary(LPCRECT prcPosRect, HWND hwndParent)
{
T* pT = static_cast<T*>(this);
BOOL bDesignMode = FALSE;
CComVariant var;
// if container doesn't support this property
// don't allow design mode
HRESULT hRes = pT->GetAmbientProperty(DISPID_AMBIENT_USERMODE, var);
if (SUCCEEDED(hRes) && var.vt == VT_BOOL && !var.boolVal)
bDesignMode = TRUE;
if (bDesignMode)
return pT->DoVerbProperties(prcPosRect, hwndParent);
else
return pT->DoVerbInPlaceActivate(prcPosRect, hwndParent);
}
HRESULT DoVerbShow(LPCRECT prcPosRect, HWND /* hwndParent */)
{
T* pT = static_cast<T*>(this);
return pT->InPlaceActivate(OLEIVERB_SHOW, prcPosRect);
}
HRESULT DoVerbInPlaceActivate(LPCRECT prcPosRect, HWND /* hwndParent */)
{
T* pT = static_cast<T*>(this);
return pT->InPlaceActivate(OLEIVERB_INPLACEACTIVATE, prcPosRect);
}
HRESULT DoVerbUIActivate(LPCRECT prcPosRect, HWND /* hwndParent */)
{
T* pT = static_cast<T*>(this);
return pT->InPlaceActivate(OLEIVERB_UIACTIVATE, prcPosRect);
}
HRESULT DoVerbHide(LPCRECT /* prcPosRect */, HWND /* hwndParent */)
{
T* pT = static_cast<T*>(this);
pT->UIDeactivate();
if (pT->m_hWnd)
pT->ShowWindow(SW_HIDE);
return S_OK;
}
HRESULT DoVerbOpen(LPCRECT /* prcPosRect */, HWND /* hwndParent */)
{
return S_OK;
}
HRESULT DoVerbDiscardUndo(LPCRECT /* prcPosRect */, HWND /* hwndParent */)
{
return S_OK;
}
STDMETHOD(DoVerb)(LONG iVerb, LPMSG /* lpmsg */, IOleClientSite* /* pActiveSite */, LONG /* lindex */,
 HWND hwndParent, LPCRECT lprcPosRect)
{

T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleObjectImpl::DoVerb\n"));
_ASSERTE(pT->m_spClientSite);

HRESULT hr = E_NOTIMPL;
switch (iVerb)
{
case OLEIVERB_PRIMARY:
hr = pT->DoVerbPrimary(lprcPosRect, hwndParent);
break;
case OLEIVERB_SHOW:
hr = pT->DoVerbShow(lprcPosRect, hwndParent);
break;
case OLEIVERB_INPLACEACTIVATE:
hr = pT->DoVerbInPlaceActivate(lprcPosRect, hwndParent);
break;
case OLEIVERB_UIACTIVATE:
hr = pT->DoVerbUIActivate(lprcPosRect, hwndParent);
break;
case OLEIVERB_HIDE:
hr = pT->DoVerbHide(lprcPosRect, hwndParent);
break;
case OLEIVERB_OPEN:
hr = pT->DoVerbOpen(lprcPosRect, hwndParent);
break;
case OLEIVERB_DISCARDUNDOSTATE:
hr = pT->DoVerbDiscardUndo(lprcPosRect, hwndParent);
break;
case OLEIVERB_PROPERTIES:
hr = pT->DoVerbProperties(lprcPosRect, hwndParent);
}
return hr;
}
STDMETHOD(EnumVerbs)(IEnumOLEVERB **ppEnumOleVerb)
{
ATLTRACE(_T("IOleObjectImpl::EnumVerbs\n"));
_ASSERTE(ppEnumOleVerb);
if (!ppEnumOleVerb)
return E_POINTER;
return OleRegEnumVerbs(T::GetObjectCLSID(), ppEnumOleVerb);
}
STDMETHOD(Update)(void)
{
ATLTRACE(_T("IOleObjectImpl::Update\n"));
return S_OK;
}
STDMETHOD(IsUpToDate)(void)
{
ATLTRACE(_T("IOleObjectImpl::IsUpToDate\n"));
return S_OK;
}
STDMETHOD(GetUserClassID)(CLSID *pClsid)
{
ATLTRACE(_T("IOleObjectImpl::GetUserClassID\n"));
_ASSERTE(pClsid);
if (!pClsid)
return E_POINTER;
*pClsid = T::GetObjectCLSID();
return S_OK;
}
STDMETHOD(GetUserType)(DWORD dwFormOfType, LPOLESTR *pszUserType)
{
ATLTRACE(_T("IOleObjectImpl::GetUserType\n"));
return OleRegGetUserType(T::GetObjectCLSID(), dwFormOfType, pszUserType);
}
STDMETHOD(SetExtent)(DWORD dwDrawAspect, SIZEL *psizel)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleObjectImpl::SetExtent\n"));
return pT->IOleObject_SetExtent(dwDrawAspect, psizel);
}
STDMETHOD(GetExtent)(DWORD dwDrawAspect, SIZEL *psizel)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleObjectImpl::GetExtent\n"));
if (dwDrawAspect != DVASPECT_CONTENT)
return E_FAIL;
if (psizel == NULL)
return E_POINTER;
*psizel = pT->m_sizeExtent;
return S_OK;
}
STDMETHOD(Advise)(IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleObjectImpl::Advise\n"));
return pT->IOleObject_Advise(pAdvSink, pdwConnection);
}
STDMETHOD(Unadvise)(DWORD dwConnection)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleObjectImpl::Unadvise\n"));
HRESULT hRes = E_FAIL;
if (pT->m_spOleAdviseHolder != NULL)
hRes = pT->m_spOleAdviseHolder->Unadvise(dwConnection);
return hRes;
}
STDMETHOD(EnumAdvise)(IEnumSTATDATA **ppenumAdvise)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleObjectImpl::EnumAdvise\n"));
HRESULT hRes = E_FAIL;
if (pT->m_spOleAdviseHolder != NULL)
hRes = pT->m_spOleAdviseHolder->EnumAdvise(ppenumAdvise);
return hRes;
}
STDMETHOD(GetMiscStatus)(DWORD dwAspect, DWORD *pdwStatus)
{
ATLTRACE(_T("IOleObjectImpl::GetMiscStatus\n"));
return OleRegGetMiscStatus(T::GetObjectCLSID(), dwAspect, pdwStatus);
}
STDMETHOD(SetColorScheme)(LOGPALETTE* /* pLogpal */)
{
ATLTRACENOTIMPL(_T("IOleObjectImpl::SetColorScheme"));
}
};

//local struct used for implementation
#pragma pack(push, 1)
struct _ATL_DLGTEMPLATEEX
{
WORD dlgVer;
WORD signature;
DWORD helpID;
DWORD exStyle;
DWORD style;
WORD cDlgItems;
short x;
short y;
short cx;
short cy;
};
#pragma pack(pop)

//////////////////////////////////////////////////////////////////////////////
// IPropertyPageImpl
template <class T>
class ATL_NO_VTABLE IPropertyPageImpl
{

public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleControlImpl)

void SetDirty(BOOL bDirty)
{
T* pT = static_cast<T*>(this);
if (!pT->m_bDirty && bDirty)
pT->m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY | PROPPAGESTATUS_VALIDATE);
pT->m_bDirty = bDirty;
}

IPropertyPageImpl()
{
T* pT = static_cast<T*>(this);
pT->m_pPageSite = NULL;
pT->m_size.cx = 0;
pT->m_size.cy = 0;
pT->m_dwTitleID = 0;
pT->m_dwHelpFileID = 0;
pT->m_dwDocStringID = 0;
pT->m_dwHelpContext = 0;
pT->m_ppUnk = NULL;
pT->m_nObjects = 0;
pT->m_bDirty = FALSE;
pT->m_hWnd = NULL;
}

~IPropertyPageImpl()
{
T* pT = static_cast<T*>(this);
if (pT->m_pPageSite != NULL)
pT->m_pPageSite->Release();

for (UINT i = 0; i < m_nObjects; i++)
pT->m_ppUnk[i]->Release();

delete[] pT->m_ppUnk;
}

// IPropertyPage
//
STDMETHOD(SetPageSite)(IPropertyPageSite *pPageSite)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::SetPageSite\n"));

if (!pPageSite && pT->m_pPageSite)
{
pT->m_pPageSite->Release();
return S_OK;
}

if (!pPageSite && !pT->m_pPageSite)
return S_OK;

if (pPageSite && pT->m_pPageSite)
{
ATLTRACE(_T("Error : setting page site again with non NULL value\n"));
return E_UNEXPECTED;
}

pT->m_pPageSite = pPageSite;
pT->m_pPageSite->AddRef();
return S_OK;
}
STDMETHOD(Activate)(HWND hWndParent, LPCRECT pRect, BOOL /* bModal */)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::Activate\n"));

if (pRect == NULL)
{
ATLTRACE(_T("Error : Passed a NULL rect\n"));
return E_POINTER;
}

pT->m_hWnd = pT->Create(hWndParent);
Move(pRect);

m_size.cx = pRect->right - pRect->left;
m_size.cy = pRect->bottom - pRect->top;

return S_OK;

}
STDMETHOD(Deactivate)( void)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::Deactivate\n"));

if (pT->m_hWnd)
{
ATLTRACE(_T("Destroying Dialog\n"));
if (::IsWindow(pT->m_hWnd))
pT->DestroyWindow();
pT->m_hWnd = NULL;
}

return S_OK;

}
STDMETHOD(GetPageInfo)(PROPPAGEINFO *pPageInfo)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::GetPageInfo\n"));

if (pPageInfo == NULL)
{
ATLTRACE(_T("Error : PROPPAGEINFO passed == NULL\n"));
return E_POINTER;
}

HRSRC hRsrc = FindResource(_Module.GetResourceInstance(),
   MAKEINTRESOURCE(T::IDD), RT_DIALOG);
if (hRsrc == NULL)
{
ATLTRACE(_T("Could not find resource template\n"));
return E_UNEXPECTED;
}

HGLOBAL hGlob = LoadResource(_Module.GetResourceInstance(), hRsrc);
DLGTEMPLATE* pTemp = (DLGTEMPLATE*)LockResource(hGlob);
if (pTemp == NULL)
{
ATLTRACE(_T("Could not load resource template\n"));
return E_UNEXPECTED;
}
pT->GetDialogSize(pTemp, &m_size);

pPageInfo->cb = sizeof(PROPPAGEINFO);
pPageInfo->pszTitle = LoadStringHelper(pT->m_dwTitleID);
pPageInfo->size = m_size;
pPageInfo->pszHelpFile = LoadStringHelper(pT->m_dwHelpFileID);
pPageInfo->pszDocString = LoadStringHelper(pT->m_dwDocStringID);
pPageInfo->dwHelpContext = pT->m_dwHelpContext;

return S_OK;
}

STDMETHOD(SetObjects)(ULONG nObjects, IUnknown **ppUnk)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::SetObjects\n"));

if (ppUnk == NULL)
return E_POINTER;

if (pT->m_ppUnk != NULL && pT->m_nObjects > 0)
{
for (UINT iObj = 0; iObj < pT->m_nObjects; iObj++)
pT->m_ppUnk[iObj]->Release();

delete [] pT->m_ppUnk;
}

pT->m_ppUnk = NULL;
ATLTRY(pT->m_ppUnk = new IUnknown*[nObjects]);

if (pT->m_ppUnk == NULL)
return E_OUTOFMEMORY;

for (UINT i = 0; i < nObjects; i++)
{
ppUnk[i]->AddRef();
pT->m_ppUnk[i] = ppUnk[i];
}

pT->m_nObjects = nObjects;

return S_OK;
}
STDMETHOD(Show)(UINT nCmdShow)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::Show\n"));

if (pT->m_hWnd == NULL)
return E_UNEXPECTED;

ShowWindow(pT->m_hWnd, nCmdShow);
return S_OK;
}
STDMETHOD(Move)(LPCRECT pRect)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::Move\n"));

if (pT->m_hWnd == NULL)
return E_UNEXPECTED;

if (pRect == NULL)
return E_POINTER;

MoveWindow(pT->m_hWnd, pRect->left, pRect->top, pRect->right - pRect->left,
 pRect->bottom - pRect->top, TRUE);

return S_OK;

}
STDMETHOD(IsPageDirty)(void)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::IsPageDirty\n"));
return pT->m_bDirty ? S_OK : S_FALSE;
}
STDMETHOD(Apply)(void)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::Apply\n"));
return S_OK;
}
STDMETHOD(Help)(LPCOLESTR pszHelpDir)
{
T* pT = static_cast<T*>(this);
USES_CONVERSION;

ATLTRACE(_T("IPropertyPageImpl::Help\n"));
WinHelp(pT->m_hWnd, OLE2CT(pszHelpDir), HELP_CONTEXTPOPUP, NULL);
return S_OK;
}
STDMETHOD(TranslateAccelerator)(MSG *pMsg)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IPropertyPageImpl::TranslateAccelerator\n"));
if (pMsg == NULL)
return E_POINTER;
LRESULT lRes;
return pT->ProcessWindowMessage(pT->m_hWnd, pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ? S_OK : S_FALSE;
}

IPropertyPageSite* m_pPageSite;
IUnknown** m_ppUnk;
ULONG m_nObjects;
SIZE m_size;
UINT m_dwTitleID;
UINT m_dwHelpFileID;
UINT m_dwDocStringID;
DWORD m_dwHelpContext;
BOOL m_bDirty;

//methods
public:

BEGIN_MSG_MAP(IPropertyPageImpl<T>)
MESSAGE_HANDLER(WM_STYLECHANGING, OnStyleChange)
END_MSG_MAP()

LRESULT OnStyleChange(UINT, WPARAM wParam, LPARAM lParam, BOOL&)
{
if (wParam == GWL_EXSTYLE)
{
LPSTYLESTRUCT lpss = (LPSTYLESTRUCT) lParam;
lpss->styleNew |= WS_EX_CONTROLPARENT;
return 0;
}
return 1;
}

LPOLESTR LoadStringHelper(UINT idRes)
{
USES_CONVERSION;

TCHAR szTemp[_MAX_PATH];
LPOLESTR sz = (LPOLESTR)CoTaskMemAlloc(_MAX_PATH*sizeof(OLECHAR));
if (sz == NULL)
return NULL;
sz[0] = NULL;

if (LoadString(_Module.GetResourceInstance(), idRes, szTemp, _MAX_PATH))
ocscpy(sz, T2OLE(szTemp));
else
{
ATLTRACE(_T("Error : Failed to load string from res\n"));
}

return sz;
}

void GetDialogSize(const DLGTEMPLATE* pTemplate, SIZE* pSize)
{
// If the dialog has a font we use it otherwise we default
// to the system font.
if (HasFont(pTemplate))
{
TCHAR szFace[LF_FACESIZE];
WORD  wFontSize = 0;
GetFont(pTemplate, szFace, &wFontSize);
GetSizeInDialogUnits(pTemplate, pSize);
ConvertDialogUnitsToPixels(szFace, wFontSize, pSize);
}
else
{
GetSizeInDialogUnits(pTemplate, pSize);
LONG nDlgBaseUnits = GetDialogBaseUnits();
pSize->cx = MulDiv(pSize->cx, LOWORD(nDlgBaseUnits), 4);
pSize->cy = MulDiv(pSize->cy, HIWORD(nDlgBaseUnits), 8);
}
}

static void ConvertDialogUnitsToPixels(LPCTSTR pszFontFace, WORD wFontSize, SIZE* pSizePixel)
{
// Attempt to create the font to be used in the dialog box
UINT cxSysChar, cySysChar;
LOGFONT lf;
HDC hDC = ::GetDC(NULL);
int cxDlg = pSizePixel->cx;
int cyDlg = pSizePixel->cy;

ZeroMemory(&lf, sizeof(LOGFONT));
lf.lfHeight = -MulDiv(wFontSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
lf.lfWeight = FW_NORMAL;
lf.lfCharSet = DEFAULT_CHARSET;
lstrcpy(lf.lfFaceName, pszFontFace);

HFONT hNewFont = CreateFontIndirect(&lf);
if (hNewFont != NULL)
{
TEXTMETRIC  tm;
SIZE        size;
HFONT       hFontOld = (HFONT)SelectObject(hDC, hNewFont);
GetTextMetrics(hDC, &tm);
cySysChar = tm.tmHeight + tm.tmExternalLeading;
::GetTextExtentPoint(hDC,
_T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), 52,
&size);
cxSysChar = (size.cx + 26) / 52;
SelectObject(hDC, hFontOld);
DeleteObject(hNewFont);
}
else
{
// Could not create the font so just use the system's values
cxSysChar = LOWORD(GetDialogBaseUnits());
cySysChar = HIWORD(GetDialogBaseUnits());
}
::ReleaseDC(NULL, hDC);

// Translate dialog units to pixels
pSizePixel->cx = MulDiv(cxDlg, cxSysChar, 4);
pSizePixel->cy = MulDiv(cyDlg, cySysChar, 8);
}

static BOOL IsDialogEx(const DLGTEMPLATE* pTemplate)
{
return ((_ATL_DLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF;
}

static BOOL HasFont(const DLGTEMPLATE* pTemplate)
{
return (DS_SETFONT &
(IsDialogEx(pTemplate) ?
((_ATL_DLGTEMPLATEEX*)pTemplate)->style : pTemplate->style));
}

static BYTE* GetFontSizeField(const DLGTEMPLATE* pTemplate)
{
BOOL bDialogEx = IsDialogEx(pTemplate);
WORD* pw;

if (bDialogEx)
pw = (WORD*)((_ATL_DLGTEMPLATEEX*)pTemplate + 1);
else
pw = (WORD*)(pTemplate + 1);

if (*pw == (WORD)-1)        // Skip menu name string or ordinal
pw += 2; // WORDs
else
while(*pw++);

if (*pw == (WORD)-1)        // Skip class name string or ordinal
pw += 2; // WORDs
else
while(*pw++);

while (*pw++);          // Skip caption string

return (BYTE*)pw;
}

static BOOL GetFont(const DLGTEMPLATE* pTemplate, TCHAR* pszFace, WORD* pFontSize)
{
USES_CONVERSION;
if (!HasFont(pTemplate))
return FALSE;

BYTE* pb = GetFontSizeField(pTemplate);
*pFontSize = *(WORD*)pb;
// Skip over font attributes to get to the font name
pb += sizeof(WORD) * (IsDialogEx(pTemplate) ? 3 : 1);

_tcscpy(pszFace, W2T((WCHAR*)pb));
return TRUE;
}

static void GetSizeInDialogUnits(const DLGTEMPLATE* pTemplate, SIZE* pSize)
{
if (IsDialogEx(pTemplate))
{
pSize->cx = ((_ATL_DLGTEMPLATEEX*)pTemplate)->cx;
pSize->cy = ((_ATL_DLGTEMPLATEEX*)pTemplate)->cy;
}
else
{
pSize->cx = pTemplate->cx;
pSize->cy = pTemplate->cy;
}
}
};

//////////////////////////////////////////////////////////////////////////////
// IPropertyPage2Impl
template <class T>
class ATL_NO_VTABLE IPropertyPage2Impl : public IPropertyPageImpl<T>
{
public:

STDMETHOD(EditProperty)(DISPID dispID)
{
ATLTRACENOTIMPL(_T("IPropertyPage2Impl::EditProperty\n"));
}
};

//////////////////////////////////////////////////////////////////////////////
// IPerPropertyBrowsingImpl
template <class T>
class ATL_NO_VTABLE IPerPropertyBrowsingImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPerPropertyBrowsingImpl)

STDMETHOD(GetDisplayString)(DISPID dispID,BSTR *pBstr)
{
ATLTRACE(_T("IPerPropertyBrowsingImpl::GetDisplayString\n"));
T* pT = static_cast<T*>(this);
CComVariant var;
if (FAILED(CComDispatchDriver::GetProperty(pT, dispID, &var)))
{
*pBstr = NULL;
return S_FALSE;
}

BSTR bstrTemp = var.bstrVal;
if (var.vt != VT_BSTR)
{
CComVariant varDest;
if (FAILED(::VariantChangeType(&varDest, &var, VARIANT_NOVALUEPROP, VT_BSTR)))
{
*pBstr = NULL;
return S_FALSE;
}
bstrTemp = varDest.bstrVal;
}
*pBstr = SysAllocString(bstrTemp);
return S_OK;
}

STDMETHOD(MapPropertyToPage)(DISPID dispID, CLSID *pClsid)
{
ATLTRACE(_T("IPerPropertyBrowsingImpl::MapPropertyToPage\n"));
T* pT = static_cast<T*>(this);
ATL_PROPMAP_ENTRY* pMap = T::GetPropertyMap();
_ASSERTE(pMap != NULL);
for(int i = 0; pMap[i].pclsidPropPage != NULL; i++)
{
if (pMap[i].szDesc == NULL)
continue;
if (pMap[i].dispid == dispID)
{
_ASSERTE(pMap[i].pclsidPropPage != NULL);
*pClsid = *(pMap[i].pclsidPropPage);
return S_OK;
}
}
*pClsid = CLSID_NULL;
return E_INVALIDARG;
}
STDMETHOD(GetPredefinedStrings)(DISPID dispID, CALPOLESTR *pCaStringsOut,CADWORD *pCaCookiesOut)
{
dispID;
ATLTRACE(_T("IPerPropertyBrowsingImpl::GetPredefinedStrings\n"));
if (pCaStringsOut == NULL || pCaCookiesOut == NULL)
return E_POINTER;

pCaStringsOut->cElems = 0;
pCaStringsOut->pElems = NULL;
pCaCookiesOut->cElems = 0;
pCaCookiesOut->pElems = NULL;
return S_OK;
}
STDMETHOD(GetPredefinedValue)(DISPID /*dispID*/, DWORD /*dwCookie*/, VARIANT* /*pVarOut*/)
{
ATLTRACENOTIMPL(_T("IPerPropertyBrowsingImpl::GetPredefinedValue"));
}
};

//////////////////////////////////////////////////////////////////////////////
// IViewObjectExImpl
template <class T>
class ATL_NO_VTABLE IViewObjectExImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IViewObjectExImpl)

// IViewObject
//
STDMETHOD(Draw)(DWORD dwDrawAspect, LONG lindex, void *pvAspect,
DVTARGETDEVICE *ptd, HDC hicTargetDev, HDC hdcDraw,
LPCRECTL prcBounds, LPCRECTL prcWBounds,
BOOL (__stdcall * /*pfnContinue*/)(DWORD dwContinue),
DWORD /*dwContinue*/)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IViewObjectExImpl::Draw\n"));
return pT->IViewObject_Draw(dwDrawAspect, lindex, pvAspect, ptd, hicTargetDev, hdcDraw,
prcBounds, prcWBounds);
}

STDMETHOD(GetColorSet)(DWORD /* dwDrawAspect */,LONG /* lindex */, void* /* pvAspect */, DVTARGETDEVICE* /* ptd */, HDC /* hicTargetDev */, LOGPALETTE** /* ppColorSet */)
{
ATLTRACENOTIMPL(_T("IViewObjectExImpl::GetColorSet"));
}
STDMETHOD(Freeze)(DWORD /* dwDrawAspect */, LONG /* lindex */, void* /* pvAspect */,DWORD* /* pdwFreeze */)
{
ATLTRACENOTIMPL(_T("IViewObjectExImpl::Freeze"));
}
STDMETHOD(Unfreeze)(DWORD /* dwFreeze */)
{
ATLTRACENOTIMPL(_T("IViewObjectExImpl::Unfreeze"));
}
STDMETHOD(SetAdvise)(DWORD /* aspects */, DWORD /* advf */, IAdviseSink* pAdvSink)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IViewObjectExImpl::SetAdvise\n"));
pT->m_spAdviseSink = pAdvSink;
return S_OK;
}
STDMETHOD(GetAdvise)(DWORD* /* pAspects */, DWORD* /* pAdvf */, IAdviseSink** ppAdvSink)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IViewObjectExImpl::GetAdvise\n"));
if (ppAdvSink != NULL)
{
*ppAdvSink = pT->m_spAdviseSink;
if (pT->m_spAdviseSink)
pT->m_spAdviseSink->AddRef();
}
return S_OK;
}

// IViewObject2
//
STDMETHOD(GetExtent)(DWORD /* dwDrawAspect */, LONG /* lindex */, DVTARGETDEVICE* /* ptd */, LPSIZEL lpsizel)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IViewObjectExImpl::GetExtent\n"));
*lpsizel = pT->m_sizeExtent;
return S_OK;
}

// IViewObjectEx
//
STDMETHOD(GetRect)(DWORD /* dwAspect */, LPRECTL /* pRect */)
{
ATLTRACENOTIMPL(_T("IViewObjectExImpl::GetRect"));
}
STDMETHOD(GetViewStatus)(DWORD* pdwStatus)
{
ATLTRACE(_T("IViewObjectExImpl::GetViewStatus\n"));
*pdwStatus =
//          VIEWSTATUS_DVASPECTOPAQUE | VIEWSTATUS_DVASPECTTRANSPARENT |
//          VIEWSTATUS_SOLIDBKGND |
VIEWSTATUS_OPAQUE;

return S_OK;
}
STDMETHOD(QueryHitPoint)(DWORD dwAspect, LPCRECT pRectBounds, POINT ptlLoc, LONG /* lCloseHint */, DWORD *pHitResult)
{
ATLTRACE(_T("IViewObjectExImpl::QueryHitPoint\n"));
if (dwAspect == DVASPECT_CONTENT)
{
*pHitResult = PtInRect(pRectBounds, ptlLoc) ? HITRESULT_HIT : HITRESULT_OUTSIDE;
return S_OK;
}
ATLTRACE(_T("Wrong DVASPECT\n"));
return E_FAIL;
}
STDMETHOD(QueryHitRect)(DWORD dwAspect, LPCRECT pRectBounds, LPCRECT prcLoc, LONG /* lCloseHint */, DWORD* pHitResult)
{
ATLTRACE(_T("IViewObjectExImpl::QueryHitRect\n"));
if (dwAspect == DVASPECT_CONTENT)
{
RECT rc;
*pHitResult = UnionRect(&rc, pRectBounds, prcLoc) ? HITRESULT_HIT : HITRESULT_OUTSIDE;
return S_OK;
}
ATLTRACE(_T("Wrong DVASPECT\n"));
return E_FAIL;
}
STDMETHOD(GetNaturalExtent)(DWORD dwAspect, LONG /* lindex */, DVTARGETDEVICE* /* ptd */, HDC /* hicTargetDev */, DVEXTENTINFO* pExtentInfo , LPSIZEL psizel)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IViewObjectExImpl::GetNaturalExtent\n"));
HRESULT hRes = E_FAIL;
if (dwAspect == DVASPECT_CONTENT)
{
if (pExtentInfo->dwExtentMode == DVEXTENT_CONTENT)
{
*psizel = pT->m_sizeNatural;
hRes = S_OK;
}
}
return hRes;
}

public:
};

//////////////////////////////////////////////////////////////////////////////
// IOleInPlaceObjectWindowlessImpl
//
template <class T>
class ATL_NO_VTABLE IOleInPlaceObjectWindowlessImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleInPlaceObjectWindowlessImpl)

// IOleWindow
//

// Change IOleInPlaceActiveObject::GetWindow as well
STDMETHOD(GetWindow)(HWND* phwnd)
{
ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::GetWindow\n"));
T* pT = static_cast<T*>(this);
HRESULT hRes = E_POINTER;

if (pT->m_bWasOnceWindowless)
return E_FAIL;

if (phwnd != NULL)
{
*phwnd = pT->m_hWnd;
hRes = (*phwnd == NULL) ? E_UNEXPECTED : S_OK;
}
return hRes;
}
STDMETHOD(ContextSensitiveHelp)(BOOL /* fEnterMode */)
{
ATLTRACENOTIMPL(_T("IOleInPlaceObjectWindowlessImpl::ContextSensitiveHelp"));
}

// IOleInPlaceObject
//
STDMETHOD(InPlaceDeactivate)(void)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::InPlaceDeactivate\n"));
return pT->IOleInPlaceObject_InPlaceDeactivate();
}
STDMETHOD(UIDeactivate)(void)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::UIDeactivate\n"));
return pT->IOleInPlaceObject_UIDeactivate();
}
STDMETHOD(SetObjectRects)(LPCRECT prcPos,LPCRECT prcClip)
{
T* pT = static_cast<T*>(this);
ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::SetObjectRects\n"));
return pT->IOleInPlaceObject_SetObjectRects(prcPos, prcClip);
}
STDMETHOD(ReactivateAndUndo)(void)
{
ATLTRACENOTIMPL(_T("IOleInPlaceObjectWindowlessImpl::ReactivateAndUndo"));
}

// IOleInPlaceObjectWindowless
//
STDMETHOD(OnWindowMessage)(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
{
ATLTRACE(_T("IOleInPlaceObjectWindowlessImpl::OnWindowMessage\n"));
T* pT = static_cast<T*>(this);
return pT->ProcessWindowMessage(pT->m_hWnd, msg, wParam, lParam, *plResult);
}

STDMETHOD(GetDropTarget)(IDropTarget** /* ppDropTarget */)
{
ATLTRACENOTIMPL(_T("IOleInPlaceObjectWindowlessImpl::GetDropTarget"));
}
};

//////////////////////////////////////////////////////////////////////////////
// IOleInPlaceActiveObjectImpl
//
template <class T>
class ATL_NO_VTABLE IOleInPlaceActiveObjectImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleInPlaceActiveObjectImpl)

// IOleWindow
//

// Change IOleInPlaceObjectWindowless::GetWindow as well
STDMETHOD(GetWindow)(HWND *phwnd)
{
ATLTRACE(_T("IOleInPlaceActiveObjectImpl::GetWindow\n"));
T* pT = static_cast<T*>(this);
HRESULT hRes = E_POINTER;

if (pT->m_bWasOnceWindowless)
return E_FAIL;

if (phwnd != NULL)
{
*phwnd = pT->m_hWnd;
hRes = (*phwnd == NULL) ? E_UNEXPECTED : S_OK;
}
return hRes;
}
STDMETHOD(ContextSensitiveHelp)(BOOL /* fEnterMode */)
{
ATLTRACENOTIMPL(_T("IOleInPlaceActiveObjectImpl::ContextSensitiveHelp"));
}

// IOleInPlaceActiveObject
//
STDMETHOD(TranslateAccelerator)(LPMSG /* lpmsg */)
{
ATLTRACE(_T("IOleInPlaceActiveObjectImpl::TranslateAccelerator\n"));
return E_NOTIMPL;
}
STDMETHOD(OnFrameWindowActivate)(BOOL /* fActivate */)
{
ATLTRACE(_T("IOleInPlaceActiveObjectImpl::OnFrameWindowActivate\n"));
return S_OK;
}
STDMETHOD(OnDocWindowActivate)(BOOL /* fActivate */)
{
ATLTRACE(_T("IOleInPlaceActiveObjectImpl::OnDocWindowActivate\n"));
return S_OK;
}
STDMETHOD(ResizeBorder)(LPCRECT /* prcBorder */, IOleInPlaceUIWindow* /* pUIWindow */, BOOL /* fFrameWindow */)
{
ATLTRACE(_T("IOleInPlaceActiveObjectImpl::ResizeBorder\n"));
return S_OK;
}
STDMETHOD(EnableModeless)(BOOL /* fEnable */)
{
ATLTRACE(_T("IOleInPlaceActiveObjectImpl::EnableModeless\n"));
return S_OK;
}
};

//////////////////////////////////////////////////////////////////////////////
// ISpecifyPropertyPagesImpl
template <class T>
class ATL_NO_VTABLE ISpecifyPropertyPagesImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(ISpecifyPropertyPagesImpl)

// ISpecifyPropertyPages
//
STDMETHOD(GetPages)(CAUUID* pPages)
{
ATLTRACE(_T("ISpecifyPropertyPagesImpl::GetPages\n"));
T* pT = static_cast<T*>(this);
ATL_PROPMAP_ENTRY* pMap = T::GetPropertyMap();
return pT->ISpecifyPropertyPages_GetPages(pPages, pMap);
}
};

//////////////////////////////////////////////////////////////////////////////
// IPointerInactiveImpl
template <class T>
class ATL_NO_VTABLE IPointerInactiveImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPointerInactiveImpl)

// IPointerInactive
//
STDMETHOD(GetActivationPolicy)(DWORD *pdwPolicy)
{
ATLTRACENOTIMPL(_T("IPointerInactiveImpl::GetActivationPolicy"));
}
STDMETHOD(OnInactiveMouseMove)(LPCRECT pRectBounds, long x, long y, DWORD dwMouseMsg)
{
ATLTRACENOTIMPL(_T("IPointerInactiveImpl::OnInactiveMouseMove"));
}
STDMETHOD(OnInactiveSetCursor)(LPCRECT pRectBounds, long x, long y, DWORD dwMouseMsg, BOOL fSetAlways)
{
ATLTRACENOTIMPL(_T("IPointerInactiveImpl::OnInactiveSetCursor"));
}
};

//////////////////////////////////////////////////////////////////////////////
// IRunnableObjectImpl
template <class T>
class ATL_NO_VTABLE IRunnableObjectImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IRunnableObjectImpl)

// IRunnableObject
//
STDMETHOD(GetRunningClass)(LPCLSID lpClsid)
{
ATLTRACE(_T("IRunnableObjectImpl::GetRunningClass\n"));
T* pT = static_cast<T*>(this);
*lpClsid = GUID_NULL;
return E_UNEXPECTED;
}
STDMETHOD(Run)(LPBINDCTX)
{
ATLTRACE(_T("IRunnableObjectImpl::Run\n"));
return S_OK;
}
virtual BOOL STDMETHODCALLTYPE IsRunning()
{
ATLTRACE(_T("IRunnableObjectImpl::IsRunning\n"));
return TRUE;
}
STDMETHOD(LockRunning)(BOOL /*fLock*/, BOOL /*fLastUnlockCloses*/)
{
ATLTRACE(_T("IRunnableObjectImpl::LockRunning\n"));
return S_OK;
}
STDMETHOD(SetContainedObject)(BOOL /*fContained*/)
{
ATLTRACE(_T("IRunnableObjectImpl::SetContainedObject\n"));
return S_OK;
}
};

//////////////////////////////////////////////////////////////////////////////
// IDataObjectImpl
template <class T>
class ATL_NO_VTABLE IDataObjectImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IDataObjectImpl)

// IDataObject
//
STDMETHOD(GetData)(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
{
ATLTRACE(_T("IDataObjectImpl::GetData\n"));
T* pT = (T*) this;
return pT->IDataObject_GetData(pformatetcIn, pmedium);
}
STDMETHOD(GetDataHere)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */)
{
ATLTRACENOTIMPL(_T("IDataObjectImpl::GetDataHere"));
}
STDMETHOD(QueryGetData)(FORMATETC* /* pformatetc */)
{
ATLTRACENOTIMPL(_T("IDataObjectImpl::QueryGetData"));
}
STDMETHOD(GetCanonicalFormatEtc)(FORMATETC* /* pformatectIn */,FORMATETC* /* pformatetcOut */)
{
ATLTRACENOTIMPL(_T("IDataObjectImpl::GetCanonicalFormatEtc"));
}
STDMETHOD(SetData)(FORMATETC* /* pformatetc */, STGMEDIUM* /* pmedium */, BOOL /* fRelease */)
{
ATLTRACENOTIMPL(_T("IDataObjectImpl::SetData"));
}
STDMETHOD(EnumFormatEtc)(DWORD /* dwDirection */, IEnumFORMATETC** /* ppenumFormatEtc */)
{
ATLTRACENOTIMPL(_T("IDataObjectImpl::EnumFormatEtc"));
}
STDMETHOD(DAdvise)(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink,
DWORD *pdwConnection)
{
ATLTRACE(_T("IDataObjectImpl::DAdvise\n"));
T* pT = static_cast<T*>(this);
HRESULT hr = S_OK;
if (pT->m_spDataAdviseHolder == NULL)
hr = CreateDataAdviseHolder(&pT->m_spDataAdviseHolder);

if (hr == S_OK)
hr = pT->m_spDataAdviseHolder->Advise((IDataObject*)this, pformatetc, advf, pAdvSink, pdwConnection);

return hr;
}
STDMETHOD(DUnadvise)(DWORD dwConnection)
{
ATLTRACE(_T("IDataObjectImpl::DUnadvise\n"));
T* pT = static_cast<T*>(this);
HRESULT hr = S_OK;
if (pT->m_spDataAdviseHolder == NULL)
hr = OLE_E_NOCONNECTION;

else
hr = pT->m_spDataAdviseHolder->Unadvise(dwConnection);
return hr;
}
STDMETHOD(EnumDAdvise)(IEnumSTATDATA **ppenumAdvise)
{
ATLTRACE(_T("IDataObjectImpl::EnumDAdvise\n"));
T* pT = static_cast<T*>(this);
HRESULT hr = E_FAIL;
if (pT->m_spDataAdviseHolder != NULL)
hr = pT->m_spDataAdviseHolder->EnumAdvise(ppenumAdvise);
return hr;
}
};

//////////////////////////////////////////////////////////////////////////////
// IPropertyNotifySinkCP
template <class T, class CDV = CComDynamicUnkArray >
class ATL_NO_VTABLE IPropertyNotifySinkCP :
public IConnectionPointImpl<T, &IID_IPropertyNotifySink, CDV>
{
public:
typedef CFirePropNotifyEvent _ATL_PROP_NOTIFY_EVENT_CLASS;
};

//////////////////////////////////////////////////////////////////////////////
// IObjectSafety
//

template <class T>
class ATL_NO_VTABLE IObjectSafetyImpl
{
public:
IObjectSafetyImpl()
{
m_dwSafety = 0;
}

// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IObjectSafetyImpl)

// IObjectSafety
//
STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
{
ATLTRACE(_T("IObjectSafetyImpl::GetInterfaceSafetyOptions\n"));
if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL)
return E_POINTER;
HRESULT hr = S_OK;
if (riid == IID_IDispatch)
{
*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
*pdwEnabledOptions = m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
}
else
{
*pdwSupportedOptions = 0;
*pdwEnabledOptions = 0;
hr = E_NOINTERFACE;
}
return hr;
}
STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
{
ATLTRACE(_T("IObjectSafetyImpl::SetInterfaceSafetyOptions\n"));
// If we're being asked to set our safe for scripting option then oblige
if (riid == IID_IDispatch)
{
// Store our current safety level to return in GetInterfaceSafetyOptions
m_dwSafety = dwEnabledOptions & dwOptionSetMask;
return S_OK;
}
return E_NOINTERFACE;
}

DWORD m_dwSafety;
};

template <class T>
class ATL_NO_VTABLE IOleLinkImpl
{
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IOleLinkImpl)

STDMETHOD(SetUpdateOptions)(DWORD /* dwUpdateOpt */)
{
ATLTRACENOTIMPL(_T("IOleLinkImpl::SetUpdateOptions"));
}

STDMETHOD(GetUpdateOptions)(DWORD* /* pdwUpdateOpt */)
{
ATLTRACENOTIMPL(_T("IOleLinkImpl::GetUpdateOptions"));
}

STDMETHOD(SetSourceMoniker)(IMoniker* /* pmk */, REFCLSID /* rclsid */)
{
ATLTRACENOTIMPL(_T("IOleLinkImpl::SetSourceMoniker"));
}

STDMETHOD(GetSourceMoniker)(IMoniker** /* ppmk */)
{
ATLTRACENOTIMPL(_T("IOleLinkImpl::GetSourceMoniker"));
};

STDMETHOD(SetSourceDisplayName)(LPCOLESTR /* pszStatusText */)
{
ATLTRACENOTIMPL(_T("IOleLinkImpl::SetSourceDisplayName"));
}

STDMETHOD(GetSourceDisplayName)(LPOLESTR *ppszDisplayName)
{
ATLTRACE(_T("IOleLink::GetSourceDisplayName\n"));
*ppszDisplayName = NULL;
return E_FAIL;
}

STDMETHOD(BindToSource)(DWORD /* bindflags */, IBindCtx* /* pbc */)
{
ATLTRACENOTIMPL(_T("IOleLinkImpl::BindToSource\n"));
};

STDMETHOD(BindIfRunning)()
{
ATLTRACE(_T("IOleLinkImpl::BindIfRunning\n"));
return S_OK;
};

STDMETHOD(GetBoundSource)(IUnknown** /* ppunk */)
{
ATLTRACENOTIMPL(_T("IOleLinkImpl::GetBoundSource"));
};

STDMETHOD(UnbindSource)()
{
ATLTRACENOTIMPL(_T("IOleLinkImpl::UnbindSource"));
};

STDMETHOD(Update)(IBindCtx* /* pbc */)
{
ATLTRACENOTIMPL(_T("IOleLinkImpl::Update"));
};
};

template <class T>
class ATL_NO_VTABLE IBindStatusCallbackImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IBindStatusCallbackImpl)

// IBindStatusCallback
//
STDMETHOD(OnStartBinding)(DWORD /* dwReserved */, IBinding *pBinding)
{
ATLTRACE(_T("IBindStatusCallbackImpl::OnStartBinding\n"));
return S_OK;
}

STDMETHOD(GetPriority)(LONG* /* pnPriority */)
{
ATLTRACENOTIMPL(_T("IBindStatusCallbackImpl::GetPriority"));
}

STDMETHOD(OnLowResource)(DWORD /* reserved */)
{
ATLTRACE(_T("IBindStatusCallbackImpl::OnLowResource\n"));
return S_OK;
}

STDMETHOD(OnProgress)(ULONG /* ulProgress */, ULONG /* ulProgressMax */, ULONG /* ulStatusCode */, LPCWSTR /* szStatusText */)
{
ATLTRACE(_T("IBindStatusCallbackImpl::OnProgress\n"));
return S_OK;
}

STDMETHOD(OnStopBinding)(HRESULT /* hresult */, LPCWSTR /* szError */)
{
ATLTRACE(_T("IBindStatusCallbackImpl::OnStopBinding\n"));
return S_OK;
}

STDMETHOD(GetBindInfo)(DWORD* /* pgrfBINDF */, BINDINFO* /* pBindInfo */)
{
ATLTRACE(_T("IBindStatusCallbackImpl::GetBindInfo\n"));
return S_OK;
}

STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
{
ATLTRACE(_T("IBindStatusCallbackImpl::OnDataAvailable\n"));
return S_OK;
}

STDMETHOD(OnObjectAvailable)(REFIID /* riid */, IUnknown* /* punk */)
{
ATLTRACE(_T("IBindStatusCallbackImpl::OnObjectAvailable\n"));
return S_OK;
}
};

template <class T>
class ATL_NO_VTABLE CBindStatusCallback :
public CComObjectRootEx<T::_ThreadModel::ThreadModelNoCS>,
public IBindStatusCallbackImpl<T>
{
typedef void (T::*ATL_PDATAAVAILABLE)(CBindStatusCallback<T>* pbsc, BYTE* pBytes, DWORD dwSize);

public:

BEGIN_COM_MAP(CBindStatusCallback<T>)
COM_INTERFACE_ENTRY_IID(IID_IBindStatusCallback, IBindStatusCallbackImpl<T>)
END_COM_MAP()

CBindStatusCallback()
{
m_pT = NULL;
m_pFunc = NULL;
}
~CBindStatusCallback()
{
ATLTRACE(_T("~CBindStatusCallback\n"));
}

STDMETHOD(OnStartBinding)(DWORD dwReserved, IBinding *pBinding)
{
ATLTRACE(_T("CBindStatusCallback::OnStartBinding\n"));
m_spBinding = pBinding;
return S_OK;
}

STDMETHOD(GetPriority)(LONG *pnPriority)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback::GetPriority"));
}

STDMETHOD(OnLowResource)(DWORD reserved)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback::OnLowResource"));
}

STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback::OnProgress"));
}

STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR szError)
{
ATLTRACE(_T("CBindStatusCallback::OnStopBinding\n"));
m_spBinding.Release();
return S_OK;
}

STDMETHOD(GetBindInfo)(DWORD *pgrfBINDF, BINDINFO *pbindInfo)
{
ATLTRACE(_T("CBindStatusCallback::GetBindInfo\n"));
*pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE |
BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE;
pbindInfo->cbSize = sizeof(BINDINFO);
pbindInfo->szExtraInfo = NULL;
memset(&pbindInfo->stgmedData, 0, sizeof(STGMEDIUM));
pbindInfo->grfBindInfoF = 0;
pbindInfo->dwBindVerb = BINDVERB_GET;
pbindInfo->szCustomVerb = NULL;
return S_OK;
}

STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
{
ATLTRACE(_T("CBindStatusCallback::OnDataAvailable\n"));
HRESULT hr = S_OK;

// Get the Stream passed
if (BSCF_FIRSTDATANOTIFICATION & grfBSCF)
{
if (!m_spStream && pstgmed->tymed == TYMED_ISTREAM)
{
m_spStream = pstgmed->pstm;
if (m_spStream)
m_spStream->AddRef();
}
}

DWORD dwRead = dwSize - m_dwTotalRead; // Minimum amount available that hasn't been read
DWORD dwActuallyRead = 0;            // Placeholder for amount read during this pull

// If there is some data to be read then go ahead and read them
if (m_spStream)
{
if (dwRead > 0)
{
BYTE* pBytes = NULL;
ATLTRY(pBytes = new BYTE[dwRead + 1]);
if (pBytes == NULL)
return S_FALSE;
hr = m_spStream->Read(pBytes, dwRead, &dwActuallyRead);
if (SUCCEEDED(hr))
{
pBytes[dwActuallyRead] = 0;
if (dwActuallyRead>0)
{
(m_pT->*m_pFunc)(this, pBytes, dwActuallyRead);
m_dwTotalRead += dwActuallyRead;
}
}
delete[] pBytes;
}
}

if (BSCF_LASTDATANOTIFICATION & grfBSCF)
m_spStream.Release();
return hr;
}

STDMETHOD(OnObjectAvailable)(REFIID riid, IUnknown *punk)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback::OnObjectAvailable"));
}

HRESULT _StartAsyncDownload(BSTR bstrURL, IUnknown* pUnkContainer, BOOL bRelative)
{
m_dwTotalRead = 0;
m_dwAvailableToRead = 0;
HRESULT hr = S_OK;
CComQIPtr<IServiceProvider, &IID_IServiceProvider> spServiceProvider(pUnkContainer);
CComPtr<IBindHost> spBindHost;
if (spServiceProvider)
spServiceProvider->QueryService(SID_IBindHost, IID_IBindHost, (void**)&spBindHost);

if (spBindHost == NULL)
{
if (bRelative)
return E_NOINTERFACE;  // relative asked for, but no IBindHost
hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker);
if (SUCCEEDED(hr))
hr = CreateBindCtx(0, &m_spBindCtx);

if (SUCCEEDED(hr))
hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L);
else
m_spMoniker.Release();

if (SUCCEEDED(hr))
{
IStream* pStream;
hr = m_spMoniker->BindToStorage(m_spBindCtx, 0, IID_IStream, (void**)&pStream);
}
}
else
{
hr = CreateBindCtx(0, &m_spBindCtx);
if (SUCCEEDED(hr))
hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L);

if (SUCCEEDED(hr))
{
if (bRelative)
hr = spBindHost->CreateMoniker(bstrURL, m_spBindCtx, &m_spMoniker, 0);
else
hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker);
}

if (SUCCEEDED(hr))
{
IStream* pStream;
hr = spBindHost->MonikerBindToStorage(m_spMoniker, NULL, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), IID_IStream, (void**)&pStream);
ATLTRACE(_T("Bound"));
}
}
return hr;
}

HRESULT StartAsyncDownload(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE)
{
m_pT = pT;
m_pFunc = pFunc;
return  _StartAsyncDownload(bstrURL, pUnkContainer, bRelative);
}

static HRESULT Download(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE)
{
CComObject<CBindStatusCallback<T> > *pbsc;
HRESULT hRes = CComObject<CBindStatusCallback<T> >::CreateInstance(&pbsc);
if (FAILED(hRes))
return hRes;
return pbsc->StartAsyncDownload(pT, pFunc, bstrURL, pUnkContainer, bRelative);
}

CComPtr<IMoniker> m_spMoniker;
CComPtr<IBindCtx> m_spBindCtx;
CComPtr<IBinding> m_spBinding;
CComPtr<IStream> m_spStream;
T* m_pT;
ATL_PDATAAVAILABLE m_pFunc;
DWORD m_dwTotalRead;
DWORD m_dwAvailableToRead;
};

#define IMPLEMENT_STOCKPROP(type, fname, pname, dispid) \
HRESULT STDMETHODCALLTYPE put_##fname(type pname) \
{ \
T* pT = (T*) this; \
if (pT->FireOnRequestEdit(dispid) == S_FALSE) \
return S_FALSE; \
pT->m_##pname = pname; \
pT->m_bRequiresSave = TRUE; \
pT->FireOnChanged(dispid); \
pT->FireViewChange(); \
return S_OK; \
} \
HRESULT STDMETHODCALLTYPE get_##fname(type* p##pname) \
{ \
T* pT = (T*) this; \
*p##pname = pT->m_##pname; \
return S_OK; \
}

#define IMPLEMENT_BOOL_STOCKPROP(fname, pname, dispid) \
HRESULT STDMETHODCALLTYPE put_##fname(VARIANT_BOOL pname) \
{ \
T* pT = (T*) this; \
if (pT->FireOnRequestEdit(dispid) == S_FALSE) \
return S_FALSE; \
pT->m_##pname = pname; \
pT->m_bRequiresSave = TRUE; \
pT->FireOnChanged(dispid); \
pT->FireViewChange(); \
return S_OK; \
} \
HRESULT STDMETHODCALLTYPE get_##fname(VARIANT_BOOL* p##pname) \
{ \
T* pT = (T*) this; \
*p##pname = pT->m_##pname ? VARIANT_TRUE : VARIANT_FALSE; \
return S_OK; \
}

#define IMPLEMENT_BSTR_STOCKPROP(fname, pname, dispid) \
HRESULT STDMETHODCALLTYPE put_##fname(BSTR pname) \
{ \
T* pT = (T*) this; \
if (pT->FireOnRequestEdit(dispid) == S_FALSE) \
return S_FALSE; \
*(&(pT->m_##pname)) = SysAllocString(pname); \
pT->m_bRequiresSave = TRUE; \
pT->FireOnChanged(dispid); \
pT->FireViewChange(); \
return S_OK; \
} \
HRESULT STDMETHODCALLTYPE get_##fname(BSTR* p##pname) \
{ \
T* pT = (T*) this; \
*p##pname = SysAllocString(pT->m_##pname); \
return S_OK; \
}

template < class T, class InterfaceName, const IID* piid, const GUID* plibid>
class ATL_NO_VTABLE CStockPropImpl : public IDispatchImpl< InterfaceName, piid, plibid >
{
public:
// Font
HRESULT STDMETHODCALLTYPE put_Font(IFontDisp* pFont)
{
T* pT = (T*) this;
if (pT->FireOnRequestEdit(DISPID_FONT) == S_FALSE)
return S_FALSE;
pT->m_pFont = 0;
if (pFont)
{
CComQIPtr<IFont, &IID_IFont> p(pFont);
if (p)
{
CComPtr<IFont> pFont;
p->Clone(&pFont);
if (pFont)
pFont->QueryInterface(IID_IFontDisp, (void**) &pT->m_pFont);
}
}
pT->m_bRequiresSave = TRUE;
pT->FireOnChanged(DISPID_FONT);
pT->FireViewChange();
return S_OK;
}
HRESULT STDMETHODCALLTYPE putref_Font(IFontDisp* pFont)
{
T* pT = (T*) this;
if (pT->FireOnRequestEdit(DISPID_FONT) == S_FALSE)
return S_FALSE;
pT->m_pFont = pFont;
pT->m_bRequiresSave = TRUE;
pT->FireOnChanged(DISPID_FONT);
pT->FireViewChange();
return S_OK;
}
HRESULT STDMETHODCALLTYPE get_Font(IFontDisp** ppFont)
{
T* pT = (T*) this;
*ppFont = pT->m_pFont;
if (*ppFont != NULL)
(*ppFont)->AddRef();
return S_OK;
}
// Picture
HRESULT STDMETHODCALLTYPE put_Picture(IPictureDisp* pPicture)
{
T* pT = (T*) this;
if (pT->FireOnRequestEdit(DISPID_PICTURE) == S_FALSE)
return S_FALSE;
pT->m_pPicture = 0;
if (pPicture)
{
CComQIPtr<IPersistStream, &IID_IPersistStream> p(pPicture);
if (p)
{
ULARGE_INTEGER l;
p->GetSizeMax(&l);
HGLOBAL hGlob = GlobalAlloc(GHND, l.LowPart);
if (hGlob)
{
CComPtr<IStream> spStream;
CreateStreamOnHGlobal(hGlob, TRUE, &spStream);
if (spStream)
{
if (SUCCEEDED(p->Save(spStream, FALSE)))
{
LARGE_INTEGER l;
l.QuadPart = 0;
spStream->Seek(l, STREAM_SEEK_SET, NULL);
OleLoadPicture(spStream, l.LowPart, FALSE, IID_IPictureDisp, (void**)&pT->m_pPicture);
}
spStream.Release();
}
GlobalFree(hGlob);
}
}
}
pT->m_bRequiresSave = TRUE;
pT->FireOnChanged(DISPID_PICTURE);
pT->FireViewChange();
return S_OK;
}
HRESULT STDMETHODCALLTYPE putref_Picture(IPictureDisp* pPicture)
{
T* pT = (T*) this;
if (pT->FireOnRequestEdit(DISPID_PICTURE) == S_FALSE)
return S_FALSE;
pT->m_pPicture = pPicture;
pT->m_bRequiresSave = TRUE;
pT->FireOnChanged(DISPID_PICTURE);
pT->FireViewChange();
return S_OK;
}
HRESULT STDMETHODCALLTYPE get_Picture(IPictureDisp** ppPicture)
{
T* pT = (T*) this;
*ppPicture = pT->m_pPicture;
if (*ppPicture != NULL)
(*ppPicture)->AddRef();
return S_OK;
}
// MouseIcon
HRESULT STDMETHODCALLTYPE put_MouseIcon(IPictureDisp* pPicture)
{
T* pT = (T*) this;
if (pT->FireOnRequestEdit(DISPID_MOUSEICON) == S_FALSE)
return S_FALSE;
pT->m_pMouseIcon = 0;
if (pPicture)
{
CComQIPtr<IPersistStream, &IID_IPersistStream> p(pPicture);
if (p)
{
ULARGE_INTEGER l;
p->GetSizeMax(&l);
HGLOBAL hGlob = GlobalAlloc(GHND, l.LowPart);
if (hGlob)
{
CComPtr<IStream> spStream;
CreateStreamOnHGlobal(hGlob, TRUE, &spStream);
if (spStream)
{
if (SUCCEEDED(p->Save(spStream, FALSE)))
{
LARGE_INTEGER l;
l.QuadPart = 0;
spStream->Seek(l, STREAM_SEEK_SET, NULL);
OleLoadPicture(spStream, l.LowPart, FALSE, IID_IPictureDisp, (void**)&pT->m_pMouseIcon);
}
spStream.Release();
}
GlobalFree(hGlob);
}
}
}
pT->m_bRequiresSave = TRUE;
pT->FireOnChanged(DISPID_MOUSEICON);
pT->FireViewChange();
return S_OK;
}
HRESULT STDMETHODCALLTYPE putref_MouseIcon(IPictureDisp* pPicture)
{
T* pT = (T*) this;
if (pT->FireOnRequestEdit(DISPID_MOUSEICON) == S_FALSE)
return S_FALSE;
pT->m_pMouseIcon = pPicture;
pT->m_bRequiresSave = TRUE;
pT->FireOnChanged(DISPID_MOUSEICON);
pT->FireViewChange();
return S_OK;
}
HRESULT STDMETHODCALLTYPE get_MouseIcon(IPictureDisp** ppPicture)
{
T* pT = (T*) this;
*ppPicture = pT->m_pMouseIcon;
if (*ppPicture != NULL)
(*ppPicture)->AddRef();
return S_OK;
}
IMPLEMENT_STOCKPROP(OLE_COLOR, BackColor, clrBackColor, DISPID_BACKCOLOR)
IMPLEMENT_STOCKPROP(OLE_COLOR, BorderColor, clrBorderColor, DISPID_BORDERCOLOR)
IMPLEMENT_STOCKPROP(OLE_COLOR, FillColor, clrFillColor, DISPID_FILLCOLOR)
IMPLEMENT_STOCKPROP(OLE_COLOR, ForeColor, clrForeColor, DISPID_FORECOLOR)
IMPLEMENT_BOOL_STOCKPROP(AutoSize, bAutoSize, DISPID_AUTOSIZE)
IMPLEMENT_BOOL_STOCKPROP(Valid, bValid, DISPID_VALID)
IMPLEMENT_BOOL_STOCKPROP(Enabled, bEnabled, DISPID_ENABLED)
IMPLEMENT_BOOL_STOCKPROP(TabStop, bTabStop, DISPID_TABSTOP)
IMPLEMENT_BOOL_STOCKPROP(BorderVisible, bBorderVisible, DISPID_BORDERVISIBLE)
IMPLEMENT_BSTR_STOCKPROP(Text, bstrText, DISPID_TEXT)
IMPLEMENT_BSTR_STOCKPROP(Caption, bstrCaption, DISPID_CAPTION)
HRESULT STDMETHODCALLTYPE put_Window(long /*hWnd*/)
{
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE get_Window(long* phWnd)
{
T* pT = (T*) this;
*phWnd = (long)pT->m_hWnd;
return S_OK;
}
IMPLEMENT_STOCKPROP(long, BackStyle, nBackStyle, DISPID_BACKSTYLE)
IMPLEMENT_STOCKPROP(long, BorderStyle, nBorderStyle, DISPID_BORDERSTYLE)
IMPLEMENT_STOCKPROP(long, BorderWidth, nBorderWidth, DISPID_BORDERWIDTH)
IMPLEMENT_STOCKPROP(long, DrawMode, nDrawMode, DISPID_DRAWMODE)
IMPLEMENT_STOCKPROP(long, DrawStyle, nDrawStyle, DISPID_DRAWSTYLE)
IMPLEMENT_STOCKPROP(long, DrawWidth, nDrawWidth, DISPID_DRAWWIDTH)
IMPLEMENT_STOCKPROP(long, FillStyle, nFillStyle, DISPID_FILLSTYLE)
IMPLEMENT_STOCKPROP(long, Appearance, nAppearance, DISPID_APPEARANCE)
IMPLEMENT_STOCKPROP(long, MousePointer, nMousePointer, DISPID_MOUSEPOINTER)
IMPLEMENT_STOCKPROP(long, ReadyState, nReadyState, DISPID_READYSTATE)
};

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

#endif // __ATLCTL_H__


ATLIFACE.H

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 3.00.44 */
/* at Thu Nov 21 14:48:45 1996
 */
/* Compiler settings for atliface.idl:
    Os (OptLev=s), W1, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __atliface_h__
#define __atliface_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __IRegistrar_FWD_DEFINED__
#define __IRegistrar_FWD_DEFINED__
typedef interface IRegistrar IRegistrar;
#endif /* __IRegistrar_FWD_DEFINED__ */

/* header files for imported files */
#include "oaidl.h"

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

/****************************************
 * Generated header for interface: __MIDL__intf_0000
 * at Thu Nov 21 14:48:45 1996
 * using MIDL 3.00.44
 ****************************************/
/* [local] */

EXTERN_C const CLSID CLSID_Registrar;

extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_s_ifspec;

#ifndef __IRegistrar_INTERFACE_DEFINED__
#define __IRegistrar_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IRegistrar
 * at Thu Nov 21 14:48:45 1996
 * using MIDL 3.00.44
 ****************************************/
/* [unique][helpstring][dual][uuid][object] */

EXTERN_C const IID IID_IRegistrar;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface IRegistrar : public IUnknown
    {
    public:
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE AddReplacement(
            /* [in] */ LPCOLESTR key,
            /* [in] */ LPCOLESTR item) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE ClearReplacements( void) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE ResourceRegisterSz(
            /* [in] */ LPCOLESTR resFileName,
            /* [in] */ LPCOLESTR szID,
            /* [in] */ LPCOLESTR szType) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE ResourceUnregisterSz(
            /* [in] */ LPCOLESTR resFileName,
            /* [in] */ LPCOLESTR szID,
            /* [in] */ LPCOLESTR szType) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE FileRegister(
            /* [in] */ LPCOLESTR fileName) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE FileUnregister(
            /* [in] */ LPCOLESTR fileName) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE StringRegister(
            /* [in] */ LPCOLESTR data) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE StringUnregister(
            /* [in] */ LPCOLESTR data) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE ResourceRegister(
            /* [in] */ LPCOLESTR resFileName,
            /* [in] */ UINT nID,
            /* [in] */ LPCOLESTR szType) = 0;

        virtual /* [id] */ HRESULT STDMETHODCALLTYPE ResourceUnregister(
            /* [in] */ LPCOLESTR resFileName,
            /* [in] */ UINT nID,
            /* [in] */ LPCOLESTR szType) = 0;

    };

#else /* C style interface */

    typedef struct IRegistrarVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IRegistrar __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IRegistrar __RPC_FAR * This);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddReplacement )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ LPCOLESTR key,
            /* [in] */ LPCOLESTR item);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ClearReplacements )(
            IRegistrar __RPC_FAR * This);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ResourceRegisterSz )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ LPCOLESTR resFileName,
            /* [in] */ LPCOLESTR szID,
            /* [in] */ LPCOLESTR szType);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ResourceUnregisterSz )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ LPCOLESTR resFileName,
            /* [in] */ LPCOLESTR szID,
            /* [in] */ LPCOLESTR szType);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FileRegister )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ LPCOLESTR fileName);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FileUnregister )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ LPCOLESTR fileName);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *StringRegister )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ LPCOLESTR data);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *StringUnregister )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ LPCOLESTR data);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ResourceRegister )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ LPCOLESTR resFileName,
            /* [in] */ UINT nID,
            /* [in] */ LPCOLESTR szType);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ResourceUnregister )(
            IRegistrar __RPC_FAR * This,
            /* [in] */ LPCOLESTR resFileName,
            /* [in] */ UINT nID,
            /* [in] */ LPCOLESTR szType);

        END_INTERFACE
    } IRegistrarVtbl;

    interface IRegistrar
    {
        CONST_VTBL struct IRegistrarVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IRegistrar_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IRegistrar_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IRegistrar_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IRegistrar_AddReplacement(This,key,item)\
    (This)->lpVtbl -> AddReplacement(This,key,item)

#define IRegistrar_ClearReplacements(This)\
    (This)->lpVtbl -> ClearReplacements(This)

#define IRegistrar_ResourceRegisterSz(This,resFileName,szID,szType)\
    (This)->lpVtbl -> ResourceRegisterSz(This,resFileName,szID,szType)

#define IRegistrar_ResourceUnregisterSz(This,resFileName,szID,szType)\
    (This)->lpVtbl -> ResourceUnregisterSz(This,resFileName,szID,szType)

#define IRegistrar_FileRegister(This,fileName)\
    (This)->lpVtbl -> FileRegister(This,fileName)

#define IRegistrar_FileUnregister(This,fileName)\
    (This)->lpVtbl -> FileUnregister(This,fileName)

#define IRegistrar_StringRegister(This,data)\
    (This)->lpVtbl -> StringRegister(This,data)

#define IRegistrar_StringUnregister(This,data)\
    (This)->lpVtbl -> StringUnregister(This,data)

#define IRegistrar_ResourceRegister(This,resFileName,nID,szType)\
    (This)->lpVtbl -> ResourceRegister(This,resFileName,nID,szType)

#define IRegistrar_ResourceUnregister(This,resFileName,nID,szType)\
    (This)->lpVtbl -> ResourceUnregister(This,resFileName,nID,szType)

#endif /* COBJMACROS */

#endif /* C style interface */

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_AddReplacement_Proxy(
    IRegistrar __RPC_FAR * This,
    /* [in] */ LPCOLESTR key,
    /* [in] */ LPCOLESTR item);

void __RPC_STUB IRegistrar_AddReplacement_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_ClearReplacements_Proxy(
    IRegistrar __RPC_FAR * This);

void __RPC_STUB IRegistrar_ClearReplacements_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_ResourceRegisterSz_Proxy(
    IRegistrar __RPC_FAR * This,
    /* [in] */ LPCOLESTR resFileName,
    /* [in] */ LPCOLESTR szID,
    /* [in] */ LPCOLESTR szType);

void __RPC_STUB IRegistrar_ResourceRegisterSz_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_ResourceUnregisterSz_Proxy(
    IRegistrar __RPC_FAR * This,
    /* [in] */ LPCOLESTR resFileName,
    /* [in] */ LPCOLESTR szID,
    /* [in] */ LPCOLESTR szType);

void __RPC_STUB IRegistrar_ResourceUnregisterSz_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_FileRegister_Proxy(
    IRegistrar __RPC_FAR * This,
    /* [in] */ LPCOLESTR fileName);

void __RPC_STUB IRegistrar_FileRegister_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_FileUnregister_Proxy(
    IRegistrar __RPC_FAR * This,
    /* [in] */ LPCOLESTR fileName);

void __RPC_STUB IRegistrar_FileUnregister_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_StringRegister_Proxy(
    IRegistrar __RPC_FAR * This,
    /* [in] */ LPCOLESTR data);

void __RPC_STUB IRegistrar_StringRegister_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_StringUnregister_Proxy(
    IRegistrar __RPC_FAR * This,
    /* [in] */ LPCOLESTR data);

void __RPC_STUB IRegistrar_StringUnregister_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_ResourceRegister_Proxy(
    IRegistrar __RPC_FAR * This,
    /* [in] */ LPCOLESTR resFileName,
    /* [in] */ UINT nID,
    /* [in] */ LPCOLESTR szType);

void __RPC_STUB IRegistrar_ResourceRegister_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [id] */ HRESULT STDMETHODCALLTYPE IRegistrar_ResourceUnregister_Proxy(
    IRegistrar __RPC_FAR * This,
    /* [in] */ LPCOLESTR resFileName,
    /* [in] */ UINT nID,
    /* [in] */ LPCOLESTR szType);

void __RPC_STUB IRegistrar_ResourceUnregister_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IRegistrar_INTERFACE_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif


ATLIFACE.IDL

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

// atl.idl : IDL source for atl.dll
//

cpp_quote("EXTERN_C const CLSID CLSID_Registrar;")

// This file will be processed by the MIDL tool to
// produce the type library (atl.tlb) and marshalling code.
import "oaidl.idl";

[
uuid(44EC053B-400F-11D0-9DCD-00A0C90391D3),
helpstring("IRegistrar Interface"),
pointer_default(unique)
]
interface IRegistrar : IUnknown
{
//////////////////////////////////////////////////////////
// Script based methods
//////////////////////////////////////////////////////////
[id(100)] HRESULT AddReplacement([in] LPCOLESTR key, [in] LPCOLESTR item);
[id(101)] HRESULT ClearReplacements();
[id(102)] HRESULT ResourceRegisterSz([in] LPCOLESTR resFileName, [in] LPCOLESTR szID, [in] LPCOLESTR szType);
[id(103)] HRESULT ResourceUnregisterSz([in] LPCOLESTR resFileName, [in] LPCOLESTR szID, [in] LPCOLESTR szType);
[id(104)] HRESULT FileRegister([in] LPCOLESTR fileName);
[id(105)] HRESULT FileUnregister([in] LPCOLESTR fileName);
[id(106)] HRESULT StringRegister([in] LPCOLESTR data);
[id(107)] HRESULT StringUnregister([in] LPCOLESTR data);
[id(120)] HRESULT ResourceRegister([in] LPCOLESTR resFileName, [in] UINT nID, [in] LPCOLESTR szType);
[id(121)] HRESULT ResourceUnregister([in] LPCOLESTR resFileName, [in] UINT nID, [in] LPCOLESTR szType);
};


ATLIMPL.CPP

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLBASE_H__
#error atlimpl.cpp requires atlbase.h to be included first
#endif

extern "C" const IID IID_IRegistrar = {0x44EC053B,0x400F,0x11D0,{0x9D,0xCD,0x00,0xA0,0xC9,0x03,0x91,0xD3}};
#ifndef _ATL_DLL_IMPL
extern "C" const CLSID CLSID_Registrar = {0x44EC053A,0x400F,0x11D0,{0x9D,0xCD,0x00,0xA0,0xC9,0x03,0x91,0xD3}};
#endif

#include <atlconv.cpp>
#ifdef _DEBUG
#include <stdio.h>
#include <stdarg.h>
#endif

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

// used in thread pooling
UINT CComApartment::ATL_CREATE_OBJECT = 0;

#ifdef __ATLCOM_H__

/////////////////////////////////////////////////////////////////////////////
// AtlReportError

HRESULT WINAPI AtlReportError(const CLSID& clsid, UINT nID, const IID& iid,
HRESULT hRes, HINSTANCE hInst)
{
return AtlSetErrorInfo(clsid, (LPCOLESTR)MAKEINTRESOURCE(nID), 0, NULL, iid, hRes, hInst);
}

HRESULT WINAPI AtlReportError(const CLSID& clsid, UINT nID, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes, HINSTANCE hInst)
{
return AtlSetErrorInfo(clsid, (LPCOLESTR)MAKEINTRESOURCE(nID), dwHelpID,
lpszHelpFile, iid, hRes, hInst);
}

#ifndef OLE2ANSI
HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCSTR lpszDesc,
DWORD dwHelpID, LPCSTR lpszHelpFile, const IID& iid, HRESULT hRes)
{
_ASSERTE(lpszDesc != NULL);
USES_CONVERSION;
return AtlSetErrorInfo(clsid, A2COLE(lpszDesc), dwHelpID, A2CW(lpszHelpFile),
iid, hRes, NULL);
}

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCSTR lpszDesc,
const IID& iid, HRESULT hRes)
{
_ASSERTE(lpszDesc != NULL);
USES_CONVERSION;
return AtlSetErrorInfo(clsid, A2COLE(lpszDesc), 0, NULL, iid, hRes, NULL);
}
#endif

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc,
const IID& iid, HRESULT hRes)
{
return AtlSetErrorInfo(clsid, lpszDesc, 0, NULL, iid, hRes, NULL);
}

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes)
{
return AtlSetErrorInfo(clsid, lpszDesc, dwHelpID, lpszHelpFile, iid, hRes, NULL);
}

#endif //__ATLCOM_H__

/////////////////////////////////////////////////////////////////////////////
// CComBSTR

CComBSTR& CComBSTR::operator=(const CComBSTR& src)
{
if (m_str != src.m_str)
{
if (m_str)
::SysFreeString(m_str);
m_str = src.Copy();
}
return *this;
}

CComBSTR& CComBSTR::operator=(LPCOLESTR pSrc)
{
::SysFreeString(m_str);
m_str = ::SysAllocString(pSrc);
return *this;
}

void CComBSTR::Append(LPCOLESTR lpsz, int nLen)
{
int n1 = Length();
BSTR b = SysAllocStringLen(NULL, n1+nLen);
memcpy(b, m_str, n1*sizeof(OLECHAR));
memcpy(b+n1, lpsz, nLen*sizeof(OLECHAR));
b[n1+nLen] = NULL;
SysFreeString(m_str);
m_str = b;
}

#ifndef OLE2ANSI
void CComBSTR::Append(LPCSTR lpsz)
{
USES_CONVERSION;
LPCOLESTR lpo = A2COLE(lpsz);
Append(lpo, ocslen(lpo));
}

CComBSTR::CComBSTR(LPCSTR pSrc)
{
USES_CONVERSION;
m_str = ::SysAllocString(A2COLE(pSrc));
}

CComBSTR::CComBSTR(int nSize, LPCSTR sz)
{
USES_CONVERSION;
m_str = ::SysAllocStringLen(A2COLE(sz), nSize);
}

CComBSTR& CComBSTR::operator=(LPCSTR pSrc)
{
USES_CONVERSION;
::SysFreeString(m_str);
m_str = ::SysAllocString(A2COLE(pSrc));
return *this;
}
#endif

HRESULT CComBSTR::ReadFromStream(IStream* pStream)
{
_ASSERTE(pStream != NULL);
_ASSERTE(m_str == NULL); // should be empty
ULONG cb;
ULONG cbStrLen;
HRESULT hr = pStream->Read((void*) &cbStrLen, sizeof(cbStrLen), &cb);
if (FAILED(hr))
return hr;
if (cbStrLen != 0)
{
//subtract size for terminating NULL which we wrote out
//since SysAllocStringByteLen overallocates for the NULL
m_str = SysAllocStringByteLen(NULL, cbStrLen-sizeof(OLECHAR));
if (m_str == NULL)
hr = E_OUTOFMEMORY;
else
hr = pStream->Read((void*) m_str, cbStrLen, &cb);
}
return hr;
}

HRESULT CComBSTR::WriteToStream(IStream* pStream)
{
_ASSERTE(pStream != NULL);
ULONG cb;
ULONG cbStrLen = m_str ? SysStringByteLen(m_str)+sizeof(OLECHAR) : 0;
HRESULT hr = pStream->Write((void*) &cbStrLen, sizeof(cbStrLen), &cb);
if (FAILED(hr))
return hr;
return cbStrLen ? pStream->Write((void*) m_str, cbStrLen, &cb) : S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// CComVariant

CComVariant& CComVariant::operator=(BSTR bstrSrc)
{
InternalClear();
vt = VT_BSTR;
bstrVal = ::SysAllocString(bstrSrc);
if (bstrVal == NULL && bstrSrc != NULL)
{
vt = VT_ERROR;
scode = E_OUTOFMEMORY;
}
return *this;
}

CComVariant& CComVariant::operator=(LPCOLESTR lpszSrc)
{
InternalClear();
vt = VT_BSTR;
bstrVal = ::SysAllocString(lpszSrc);

if (bstrVal == NULL && lpszSrc != NULL)
{
vt = VT_ERROR;
scode = E_OUTOFMEMORY;
}
return *this;
}

#ifndef OLE2ANSI
CComVariant& CComVariant::operator=(LPCSTR lpszSrc)
{
USES_CONVERSION;
InternalClear();
vt = VT_BSTR;
bstrVal = ::SysAllocString(A2COLE(lpszSrc));

if (bstrVal == NULL && lpszSrc != NULL)
{
vt = VT_ERROR;
scode = E_OUTOFMEMORY;
}
return *this;
}
#endif

#if _MSC_VER>1020
CComVariant& CComVariant::operator=(bool bSrc)
{
if (vt != VT_BOOL)
{
InternalClear();
vt = VT_BOOL;
}
#pragma warning(disable: 4310) // cast truncates constant value
boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE;
#pragma warning(default: 4310) // cast truncates constant value
return *this;
}
#endif

CComVariant& CComVariant::operator=(int nSrc)
{
if (vt != VT_I4)
{
InternalClear();
vt = VT_I4;
}
lVal = nSrc;

return *this;
}

CComVariant& CComVariant::operator=(BYTE nSrc)
{
if (vt != VT_UI1)
{
InternalClear();
vt = VT_UI1;
}
bVal = nSrc;
return *this;
}

CComVariant& CComVariant::operator=(short nSrc)
{
if (vt != VT_I2)
{
InternalClear();
vt = VT_I2;
}
iVal = nSrc;
return *this;
}

CComVariant& CComVariant::operator=(long nSrc)
{
if (vt != VT_I4)
{
InternalClear();
vt = VT_I4;
}
lVal = nSrc;
return *this;
}

CComVariant& CComVariant::operator=(float fltSrc)
{
if (vt != VT_R4)
{
InternalClear();
vt = VT_R4;
}
fltVal = fltSrc;
return *this;
}

CComVariant& CComVariant::operator=(double dblSrc)
{
if (vt != VT_R8)
{
InternalClear();
vt = VT_R8;
}
dblVal = dblSrc;
return *this;
}

CComVariant& CComVariant::operator=(CY cySrc)
{
if (vt != VT_CY)
{
InternalClear();
vt = VT_CY;
}
cyVal.Hi = cySrc.Hi;
cyVal.Lo = cySrc.Lo;
return *this;
}

CComVariant& CComVariant::operator=(IDispatch* pSrc)
{
InternalClear();
vt = VT_DISPATCH;
pdispVal = pSrc;
// Need to AddRef as VariantClear will Release
if (pdispVal != NULL)
pdispVal->AddRef();
return *this;
}

CComVariant& CComVariant::operator=(IUnknown* pSrc)
{
InternalClear();
vt = VT_UNKNOWN;
punkVal = pSrc;

// Need to AddRef as VariantClear will Release
if (punkVal != NULL)
punkVal->AddRef();
return *this;
}

#if _MSC_VER>1020
bool CComVariant::operator==(const VARIANT& varSrc)
{
if (this == &varSrc)
return true;

// Variants not equal if types don't match
if (vt != varSrc.vt)
return false;

// Check type specific values
switch (vt)
{
case VT_EMPTY:
case VT_NULL:
return true;

case VT_BOOL:
return boolVal == varSrc.boolVal;

case VT_UI1:
return bVal == varSrc.bVal;

case VT_I2:
return iVal == varSrc.iVal;

case VT_I4:
return lVal == varSrc.lVal;

case VT_R4:
return fltVal == varSrc.fltVal;

case VT_R8:
return dblVal == varSrc.dblVal;

case VT_BSTR:
return (::SysStringByteLen(bstrVal) == ::SysStringByteLen(varSrc.bstrVal)) &&
(::memcmp(bstrVal, varSrc.bstrVal, ::SysStringByteLen(bstrVal)) == 0);

case VT_ERROR:
return scode == varSrc.scode;

case VT_DISPATCH:
return pdispVal == varSrc.pdispVal;

case VT_UNKNOWN:
return punkVal == varSrc.punkVal;

default:
_ASSERTE(false);
// fall through
}

return false;
}
#else
BOOL CComVariant::operator==(const VARIANT& varSrc)
{
if (this == &varSrc)
return TRUE;

// Variants not equal if types don't match
if (vt != varSrc.vt)
return FALSE;

// Check type specific values
switch (vt)
{
case VT_EMPTY:
case VT_NULL:
return TRUE;

case VT_BOOL:
return boolVal == varSrc.boolVal;

case VT_UI1:
return bVal == varSrc.bVal;

case VT_I2:
return iVal == varSrc.iVal;

case VT_I4:
return lVal == varSrc.lVal;

case VT_R4:
return fltVal == varSrc.fltVal;

case VT_R8:
return dblVal == varSrc.dblVal;

case VT_BSTR:
return (::SysStringByteLen(bstrVal) == ::SysStringByteLen(varSrc.bstrVal)) &&
(::memcmp(bstrVal, varSrc.bstrVal, ::SysStringByteLen(bstrVal)) == 0);

case VT_ERROR:
return scode == varSrc.scode;

case VT_DISPATCH:
return pdispVal == varSrc.pdispVal;

case VT_UNKNOWN:
return punkVal == varSrc.punkVal;

default:
_ASSERTE(FALSE);
// fall through
}

return FALSE;
}
#endif

HRESULT CComVariant::Attach(VARIANT* pSrc)
{
// Clear out the variant
HRESULT hr = Clear();
if (!FAILED(hr))
{
// Copy the contents and give control to CComVariant
memcpy(this, pSrc, sizeof(VARIANT));
VariantInit(pSrc);
hr = S_OK;
}
return hr;
}

HRESULT CComVariant::Detach(VARIANT* pDest)
{
// Clear out the variant
HRESULT hr = ::VariantClear(pDest);
if (!FAILED(hr))
{
// Copy the contents and remove control from CComVariant
memcpy(pDest, this, sizeof(VARIANT));
vt = VT_EMPTY;
hr = S_OK;
}
return hr;
}

HRESULT CComVariant::ChangeType(VARTYPE vtNew, const VARIANT* pSrc)
{
VARIANT* pVar = const_cast<VARIANT*>(pSrc);
// Convert in place if pSrc is NULL
if (pVar == NULL)
pVar = this;
// Do nothing if doing in place convert and vts not different
return ::VariantChangeType(this, pVar, 0, vtNew);
}

HRESULT CComVariant::InternalClear()
{
HRESULT hr = Clear();
_ASSERTE(SUCCEEDED(hr));
if (FAILED(hr))
{
vt = VT_ERROR;
scode = hr;
}
return hr;
}

void CComVariant::InternalCopy(const VARIANT* pSrc)
{
HRESULT hr = Copy(pSrc);
if (FAILED(hr))
{
vt = VT_ERROR;
scode = hr;
}
}

HRESULT CComVariant::WriteToStream(IStream* pStream)
{
HRESULT hr = pStream->Write(&vt, sizeof(VARTYPE), NULL);
if (FAILED(hr))
return hr;

int cbWrite = 0;
switch (vt)
{
case VT_UNKNOWN:
case VT_DISPATCH:
{
CComPtr<IPersistStream> spStream;
if (punkVal != NULL)
{
hr = punkVal->QueryInterface(IID_IPersistStream, (void**)&spStream);
if (FAILED(hr))
return hr;
}
if (spStream != NULL)
return OleSaveToStream(spStream, pStream);
else
return WriteClassStm(pStream, CLSID_NULL);
}
case VT_UI1:
case VT_I1:
cbWrite = sizeof(BYTE);
break;
case VT_I2:
case VT_UI2:
case VT_BOOL:
cbWrite = sizeof(short);
break;
case VT_I4:
case VT_UI4:
case VT_R4:
case VT_INT:
case VT_UINT:
case VT_ERROR:
cbWrite = sizeof(long);
break;
case VT_R8:
case VT_CY:
case VT_DATE:
cbWrite = sizeof(double);
break;
default:
break;
}
if (cbWrite != 0)
return pStream->Write((void*) &bVal, cbWrite, NULL);

CComBSTR bstrWrite;
CComVariant varBSTR;
if (vt != VT_BSTR)
{
hr = VariantChangeType(&varBSTR, this, VARIANT_NOVALUEPROP, VT_BSTR);
if (FAILED(hr))
return hr;
bstrWrite = varBSTR.bstrVal;
}
else
bstrWrite = bstrVal;

return bstrWrite.WriteToStream(pStream);
}

HRESULT CComVariant::ReadFromStream(IStream* pStream)
{
_ASSERTE(pStream != NULL);
HRESULT hr;
hr = VariantClear(this);
if (FAILED(hr))
return hr;
VARTYPE vtRead;
hr = pStream->Read(&vtRead, sizeof(VARTYPE), NULL);
if (FAILED(hr))
return hr;

vt = vtRead;
int cbRead = 0;
switch (vtRead)
{
case VT_UNKNOWN:
case VT_DISPATCH:
{
punkVal = NULL;
hr = OleLoadFromStream(pStream,
(vtRead == VT_UNKNOWN) ? IID_IUnknown : IID_IDispatch,
(void**)&punkVal);
if (hr == REGDB_E_CLASSNOTREG)
hr = S_OK;
return S_OK;
}
case VT_UI1:
case VT_I1:
cbRead = sizeof(BYTE);
break;
case VT_I2:
case VT_UI2:
case VT_BOOL:
cbRead = sizeof(short);
break;
case VT_I4:
case VT_UI4:
case VT_R4:
case VT_INT:
case VT_UINT:
case VT_ERROR:
cbRead = sizeof(long);
break;
case VT_R8:
case VT_CY:
case VT_DATE:
cbRead = sizeof(double);
break;
default:
break;
}
if (cbRead != 0)
return pStream->Read((void*) &bVal, cbRead, NULL);
CComBSTR bstrRead;

hr = bstrRead.ReadFromStream(pStream);
if (FAILED(hr))
return hr;
vt = VT_BSTR;
bstrVal = bstrRead.Detach();
if (vtRead != VT_BSTR)
hr = ChangeType(vtRead);
return hr;
}

#ifdef __ATLCOM_H__

/////////////////////////////////////////////////////////////////////////////
// CComTypeInfoHolder

void CComTypeInfoHolder::AddRef()
{
EnterCriticalSection(&_Module.m_csTypeInfoHolder);
m_dwRef++;
LeaveCriticalSection(&_Module.m_csTypeInfoHolder);
}

void CComTypeInfoHolder::Release()
{
EnterCriticalSection(&_Module.m_csTypeInfoHolder);
if (--m_dwRef == 0)
{
if (m_pInfo != NULL)
m_pInfo->Release();
m_pInfo = NULL;
}
LeaveCriticalSection(&_Module.m_csTypeInfoHolder);
}

HRESULT CComTypeInfoHolder::GetTI(LCID lcid, ITypeInfo** ppInfo)
{
//If this assert occurs then most likely didn't initialize properly
_ASSERTE(m_plibid != NULL && m_pguid != NULL);
_ASSERTE(ppInfo != NULL);
*ppInfo = NULL;

HRESULT hRes = E_FAIL;
EnterCriticalSection(&_Module.m_csTypeInfoHolder);
if (m_pInfo == NULL)
{
ITypeLib* pTypeLib;
hRes = LoadRegTypeLib(*m_plibid, m_wMajor, m_wMinor, lcid, &pTypeLib);
if (SUCCEEDED(hRes))
{
ITypeInfo* pTypeInfo;
hRes = pTypeLib->GetTypeInfoOfGuid(*m_pguid, &pTypeInfo);
if (SUCCEEDED(hRes))
m_pInfo = pTypeInfo;
pTypeLib->Release();
}
}
*ppInfo = m_pInfo;
if (m_pInfo != NULL)
{
m_pInfo->AddRef();
hRes = S_OK;
}
LeaveCriticalSection(&_Module.m_csTypeInfoHolder);
return hRes;
}

HRESULT CComTypeInfoHolder::GetTypeInfo(UINT /*itinfo*/, LCID lcid,
ITypeInfo** pptinfo)
{
HRESULT hRes = E_POINTER;
if (pptinfo != NULL)
hRes = GetTI(lcid, pptinfo);
return hRes;
}

HRESULT CComTypeInfoHolder::GetIDsOfNames(REFIID /*riid*/, LPOLESTR* rgszNames,
UINT cNames, LCID lcid, DISPID* rgdispid)
{
ITypeInfo* pInfo;
HRESULT hRes = GetTI(lcid, &pInfo);
if (pInfo != NULL)
{
hRes = pInfo->GetIDsOfNames(rgszNames, cNames, rgdispid);
pInfo->Release();
}
return hRes;
}

HRESULT CComTypeInfoHolder::Invoke(IDispatch* p, DISPID dispidMember, REFIID /*riid*/,
LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
SetErrorInfo(0, NULL);
ITypeInfo* pInfo;
HRESULT hRes = GetTI(lcid, &pInfo);
if (pInfo != NULL)
{
hRes = pInfo->Invoke(p, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
pInfo->Release();
}
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// QI implementation

#ifdef _ATL_DEBUG_QI
HRESULT WINAPI AtlDumpIID(REFIID iid, LPCTSTR pszClassName, HRESULT hr)
{
USES_CONVERSION;
CRegKey key;
TCHAR szName[100];
DWORD dwType,dw = sizeof(szName);

LPOLESTR pszGUID = NULL;
StringFromCLSID(iid, &pszGUID);
OutputDebugString(pszClassName);
OutputDebugString(_T(" - "));

// Attempt to find it in the interfaces section
key.Open(HKEY_CLASSES_ROOT, _T("Interface"));
if (key.Open(key, OLE2T(pszGUID)) == S_OK)
{
*szName = 0;
RegQueryValueEx(key.m_hKey, (LPTSTR)NULL, NULL, &dwType, (LPBYTE)szName, &dw);
OutputDebugString(szName);
goto cleanup;
}
// Attempt to find it in the clsid section
key.Open(HKEY_CLASSES_ROOT, _T("CLSID"));
if (key.Open(key, OLE2T(pszGUID)) == S_OK)
{
*szName = 0;
RegQueryValueEx(key.m_hKey, (LPTSTR)NULL, NULL, &dwType, (LPBYTE)szName, &dw);
OutputDebugString(_T("(CLSID\?\?\?) "));
OutputDebugString(szName);
goto cleanup;
}
OutputDebugString(OLE2T(pszGUID));
cleanup:
if (hr != S_OK)
OutputDebugString(_T(" - failed"));
OutputDebugString(_T("\n"));
CoTaskMemFree(pszGUID);
return hr;
}
#endif

HRESULT WINAPI CComObjectRootBase::_Break(void* /* pv */, REFIID iid, void** /* ppvObject */, DWORD /* dw */)
{
iid;
_ATLDUMPIID(iid, _T("Break due to QI for interface "), S_OK);
DebugBreak();
return S_FALSE;
}

HRESULT WINAPI CComObjectRootBase::_NoInterface(void* /* pv */, REFIID /* iid */, void** /* ppvObject */, DWORD /* dw */)
{
return E_NOINTERFACE;
}

HRESULT WINAPI CComObjectRootBase::_Creator(void* pv, REFIID iid, void** ppvObject, DWORD dw)
{
_ATL_CREATORDATA* pcd = (_ATL_CREATORDATA*)dw;
return pcd->pFunc(pv, iid, ppvObject);
}

HRESULT WINAPI CComObjectRootBase::_Delegate(void* pv, REFIID iid, void** ppvObject, DWORD dw)
{
HRESULT hRes = E_NOINTERFACE;
IUnknown* p = *(IUnknown**)((DWORD)pv + dw);
if (p != NULL)
hRes = p->QueryInterface(iid, ppvObject);
return hRes;
}

HRESULT WINAPI CComObjectRootBase::_Chain(void* pv, REFIID iid, void** ppvObject, DWORD dw)
{
_ATL_CHAINDATA* pcd = (_ATL_CHAINDATA*)dw;
void* p = (void*)((DWORD)pv + pcd->dwOffset);
return InternalQueryInterface(p, pcd->pFunc(), iid, ppvObject);
}

HRESULT WINAPI CComObjectRootBase::_Cache(void* pv, REFIID iid, void** ppvObject, DWORD dw)
{
HRESULT hRes = E_NOINTERFACE;
_ATL_CACHEDATA* pcd = (_ATL_CACHEDATA*)dw;
IUnknown** pp = (IUnknown**)((DWORD)pv + pcd->dwOffsetVar);
if (*pp == NULL)
hRes = pcd->pFunc(pv, IID_IUnknown, (void**)pp);
if (*pp != NULL)
hRes = (*pp)->QueryInterface(iid, ppvObject);
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// CComClassFactory

STDMETHODIMP CComClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
REFIID riid, void** ppvObj)
{
_ASSERTE(m_pfnCreateInstance != NULL);
HRESULT hRes = E_POINTER;
if (ppvObj != NULL)
{
*ppvObj = NULL;
// can't ask for anything other than IUnknown when aggregating
_ASSERTE((pUnkOuter == NULL) || InlineIsEqualUnknown(riid));
if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
hRes = CLASS_E_NOAGGREGATION;
else
hRes = m_pfnCreateInstance(pUnkOuter, riid, ppvObj);
}
return hRes;
}

STDMETHODIMP CComClassFactory::LockServer(BOOL fLock)
{
if (fLock)
_Module.Lock();
else
_Module.Unlock();
return S_OK;
}

STDMETHODIMP CComClassFactory2Base::LockServer(BOOL fLock)
{
if (fLock)
_Module.Lock();
else
_Module.Unlock();
return S_OK;
}

#ifndef _ATL_NO_CONNECTION_POINTS
/////////////////////////////////////////////////////////////////////////////
// Connection Points

DWORD CComDynamicUnkArray::Add(IUnknown* pUnk)
{
IUnknown** pp = NULL;
if (m_nSize == 0) // no connections
{
m_pUnk = pUnk;
m_nSize = 1;
return (DWORD)m_pUnk;
}
else if (m_nSize == 1)
{
//create array
pp = (IUnknown**)malloc(sizeof(IUnknown*)*_DEFAULT_VECTORLENGTH);
if (pp == NULL)
return 0;
memset(pp, 0, sizeof(IUnknown*)*_DEFAULT_VECTORLENGTH);
*pp = m_pUnk;
m_ppUnk = pp;
m_nSize = _DEFAULT_VECTORLENGTH;
}
for (pp = begin();pp<end();pp++)
{
if (*pp == NULL)
{
*pp = pUnk;
return (DWORD)pUnk;
}
}
int nAlloc = m_nSize*2;
pp = (IUnknown**)realloc(m_ppUnk, sizeof(IUnknown*)*nAlloc);
if (pp == NULL)
return 0;
m_ppUnk = pp;
memset(&m_ppUnk[m_nSize], 0, sizeof(IUnknown*)*m_nSize);
m_ppUnk[m_nSize] = pUnk;
m_nSize = nAlloc;
return (DWORD)pUnk;
}

BOOL CComDynamicUnkArray::Remove(DWORD dwCookie)
{
IUnknown** pp;
if (dwCookie == NULL)
return FALSE;
if (m_nSize == 0)
return FALSE;
if (m_nSize == 1)
{
if ((DWORD)m_pUnk == dwCookie)
{
m_nSize = 0;
return TRUE;
}
return FALSE;
}
for (pp=begin();pp<end();pp++)
{
if ((DWORD)*pp == dwCookie)
{
*pp = NULL;
return TRUE;
}
}
return FALSE;
}

#endif //!_ATL_NO_CONNECTION_POINTS

#endif //__ATLCOM_H__

/////////////////////////////////////////////////////////////////////////////
// Object Registry Support

static HRESULT WINAPI AtlRegisterProgID(LPCTSTR lpszCLSID, LPCTSTR lpszProgID, LPCTSTR lpszUserDesc)
{
CRegKey keyProgID;
LONG lRes = keyProgID.Create(HKEY_CLASSES_ROOT, lpszProgID);
if (lRes == ERROR_SUCCESS)
{
keyProgID.SetValue(lpszUserDesc);
keyProgID.SetKeyValue(_T("CLSID"), lpszCLSID);
return S_OK;
}
return HRESULT_FROM_WIN32(lRes);
}

void CComModule::AddCreateWndData(_AtlCreateWndData* pData, void* pObject)
{
pData->m_pThis = pObject;
pData->m_dwThreadID = ::GetCurrentThreadId();
::EnterCriticalSection(&m_csWindowCreate);
pData->m_pNext = m_pCreateWndList;
m_pCreateWndList = pData;
::LeaveCriticalSection(&m_csWindowCreate);
}

void* CComModule::ExtractCreateWndData()
{
::EnterCriticalSection(&m_csWindowCreate);
_AtlCreateWndData* pEntry = m_pCreateWndList;
if(pEntry == NULL)
{
::LeaveCriticalSection(&m_csWindowCreate);
return NULL;
}

DWORD dwThreadID = ::GetCurrentThreadId();
_AtlCreateWndData* pPrev = NULL;
while(pEntry != NULL)
{
if(pEntry->m_dwThreadID == dwThreadID)
{
if(pPrev == NULL)
m_pCreateWndList = pEntry->m_pNext;
else
pPrev->m_pNext = pEntry->m_pNext;
::LeaveCriticalSection(&m_csWindowCreate);
return pEntry->m_pThis;
}
pPrev = pEntry;
pEntry = pEntry->m_pNext;
}

::LeaveCriticalSection(&m_csWindowCreate);
return NULL;
}

#ifdef _ATL_STATIC_REGISTRY
// Statically linking to Registry Ponent
HRESULT WINAPI CComModule::UpdateRegistryFromResourceS(UINT nResID, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries)
{
USES_CONVERSION;
CRegObject ro;
TCHAR szModule[_MAX_PATH];
GetModuleFileName(_Module.GetModuleInstance(), szModule, _MAX_PATH);
LPOLESTR pszModule = T2OLE(szModule);
ro.AddReplacement(OLESTR("Module"), pszModule);
if (NULL != pMapEntries)
{
while (NULL != pMapEntries->szKey)
{
_ASSERTE(NULL != pMapEntries->szData);
ro.AddReplacement(pMapEntries->szKey, pMapEntries->szData);
pMapEntries++;
}
}

LPCOLESTR szType = OLESTR("REGISTRY");
return (bRegister) ? ro.ResourceRegister(pszModule, nResID, szType) :
ro.ResourceUnregister(pszModule, nResID, szType);
}

HRESULT WINAPI CComModule::UpdateRegistryFromResourceS(LPCTSTR lpszRes, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries)
{
USES_CONVERSION;
CRegObject ro;
TCHAR szModule[_MAX_PATH];
GetModuleFileName(_Module.GetModuleInstance(), szModule, _MAX_PATH);
LPOLESTR pszModule = T2OLE(szModule);
ro.AddReplacement(OLESTR("Module"), pszModule);
if (NULL != pMapEntries)
{
while (NULL != pMapEntries->szKey)
{
_ASSERTE(NULL != pMapEntries->szData);
ro.AddReplacement(pMapEntries->szKey, pMapEntries->szData);
pMapEntries++;
}
}

LPCOLESTR szType = OLESTR("REGISTRY");
LPCOLESTR pszRes = T2COLE(lpszRes);
return (bRegister) ? ro.ResourceRegisterSz(pszModule, pszRes, szType) :
ro.ResourceUnregisterSz(pszModule, pszRes, szType);
}
#endif // _ATL_STATIC_REGISTRY

HRESULT WINAPI CComModule::UpdateRegistryClass(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID, UINT nDescID, DWORD dwFlags, BOOL bRegister)
{
if (bRegister)
{
return RegisterClassHelper(clsid, lpszProgID, lpszVerIndProgID, nDescID,
dwFlags);
}
else
return UnregisterClassHelper(clsid, lpszProgID, lpszVerIndProgID);
}

HRESULT WINAPI CComModule::RegisterClassHelper(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID, UINT nDescID, DWORD dwFlags)
{
static const TCHAR szProgID[] = _T("ProgID");
static const TCHAR szVIProgID[] = _T("VersionIndependentProgID");
static const TCHAR szLS32[] = _T("LocalServer32");
static const TCHAR szIPS32[] = _T("InprocServer32");
static const TCHAR szThreadingModel[] = _T("ThreadingModel");
static const TCHAR szAUTPRX32[] = _T("AUTPRX32.DLL");
static const TCHAR szApartment[] = _T("Apartment");
static const TCHAR szBoth[] = _T("both");
USES_CONVERSION;
HRESULT hRes = S_OK;
TCHAR szDesc[256];
LoadString(m_hInst, nDescID, szDesc, 256);
TCHAR szModule[_MAX_PATH];
GetModuleFileName(m_hInst, szModule, _MAX_PATH);

LPOLESTR lpOleStr;
StringFromCLSID(clsid, &lpOleStr);
LPTSTR lpsz = OLE2T(lpOleStr);

hRes = AtlRegisterProgID(lpsz, lpszProgID, szDesc);
if (hRes == S_OK)
hRes = AtlRegisterProgID(lpsz, lpszVerIndProgID, szDesc);
LONG lRes = ERROR_SUCCESS;
if (hRes == S_OK)
{
CRegKey key;
LONG lRes = key.Open(HKEY_CLASSES_ROOT, _T("CLSID"));
if (lRes == ERROR_SUCCESS)
{
lRes = key.Create(key, lpsz);
if (lRes == ERROR_SUCCESS)
{
key.SetValue(szDesc);
key.SetKeyValue(szProgID, lpszProgID);
key.SetKeyValue(szVIProgID, lpszVerIndProgID);

if ((m_hInst == NULL) || (m_hInst == GetModuleHandle(NULL))) // register as EXE
key.SetKeyValue(szLS32, szModule);
else
{
key.SetKeyValue(szIPS32, (dwFlags & AUTPRXFLAG) ? szAUTPRX32 : szModule);
LPCTSTR lpszModel = (dwFlags & THREADFLAGS_BOTH) ? szBoth :
(dwFlags & THREADFLAGS_APARTMENT) ? szApartment : NULL;
if (lpszModel != NULL)
key.SetKeyValue(szIPS32, lpszModel, szThreadingModel);
}
}
}
}
CoTaskMemFree(lpOleStr);
if (lRes != ERROR_SUCCESS)
hRes = HRESULT_FROM_WIN32(lRes);
return hRes;
}

HRESULT WINAPI CComModule::UnregisterClassHelper(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID)
{
USES_CONVERSION;
CRegKey key;

key.Attach(HKEY_CLASSES_ROOT);
if (lpszProgID != NULL && lstrcmpi(lpszProgID, _T("")))
key.RecurseDeleteKey(lpszProgID);
if (lpszVerIndProgID != NULL && lstrcmpi(lpszVerIndProgID, _T("")))
key.RecurseDeleteKey(lpszVerIndProgID);
LPOLESTR lpOleStr;
StringFromCLSID(clsid, &lpOleStr);
LPTSTR lpsz = OLE2T(lpOleStr);
if (key.Open(key, _T("CLSID")) == ERROR_SUCCESS)
key.RecurseDeleteKey(lpsz);
CoTaskMemFree(lpOleStr);
return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// CRegKey

LONG CRegKey::Close()
{
LONG lRes = ERROR_SUCCESS;
if (m_hKey != NULL)
{
lRes = RegCloseKey(m_hKey);
m_hKey = NULL;
}
return lRes;
}

LONG CRegKey::Create(HKEY hKeyParent, LPCTSTR lpszKeyName,
LPTSTR lpszClass, DWORD dwOptions, REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecAttr, LPDWORD lpdwDisposition)
{
_ASSERTE(hKeyParent != NULL);
DWORD dw;
HKEY hKey = NULL;
LONG lRes = RegCreateKeyEx(hKeyParent, lpszKeyName, 0,
lpszClass, dwOptions, samDesired, lpSecAttr, &hKey, &dw);
if (lpdwDisposition != NULL)
*lpdwDisposition = dw;
if (lRes == ERROR_SUCCESS)
{
lRes = Close();
m_hKey = hKey;
}
return lRes;
}

LONG CRegKey::Open(HKEY hKeyParent, LPCTSTR lpszKeyName, REGSAM samDesired)
{
_ASSERTE(hKeyParent != NULL);
HKEY hKey = NULL;
LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyName, 0, samDesired, &hKey);
if (lRes == ERROR_SUCCESS)
{
lRes = Close();
_ASSERTE(lRes == ERROR_SUCCESS);
m_hKey = hKey;
}
return lRes;
}

LONG CRegKey::QueryValue(DWORD& dwValue, LPCTSTR lpszValueName)
{
DWORD dwType = NULL;
DWORD dwCount = sizeof(DWORD);
LONG lRes = RegQueryValueEx(m_hKey, (LPTSTR)lpszValueName, NULL, &dwType,
(LPBYTE)&dwValue, &dwCount);
_ASSERTE((lRes!=ERROR_SUCCESS) || (dwType == REG_DWORD));

_ASSERTE((lRes!=ERROR_SUCCESS) || (dwCount == sizeof(DWORD)));
return lRes;
}

LONG CRegKey::QueryValue(LPTSTR szValue, LPCTSTR lpszValueName, DWORD* pdwCount)
{
_ASSERTE(pdwCount != NULL);
DWORD dwType = NULL;
LONG lRes = RegQueryValueEx(m_hKey, (LPTSTR)lpszValueName, NULL, &dwType,
(LPBYTE)szValue, pdwCount);
_ASSERTE((lRes!=ERROR_SUCCESS) || (dwType == REG_SZ) ||
 (dwType == REG_MULTI_SZ) || (dwType == REG_EXPAND_SZ));
return lRes;
}

LONG WINAPI CRegKey::SetValue(HKEY hKeyParent, LPCTSTR lpszKeyName, LPCTSTR lpszValue, LPCTSTR lpszValueName)
{
_ASSERTE(lpszValue != NULL);
CRegKey key;
LONG lRes = key.Create(hKeyParent, lpszKeyName);
if (lRes == ERROR_SUCCESS)
lRes = key.SetValue(lpszValue, lpszValueName);
return lRes;
}

LONG CRegKey::SetKeyValue(LPCTSTR lpszKeyName, LPCTSTR lpszValue, LPCTSTR lpszValueName)
{
_ASSERTE(lpszValue != NULL);
CRegKey key;
LONG lRes = key.Create(m_hKey, lpszKeyName);
if (lRes == ERROR_SUCCESS)
lRes = key.SetValue(lpszValue, lpszValueName);
return lRes;
}

LONG CRegKey::SetValue(DWORD dwValue, LPCTSTR lpszValueName)
{
_ASSERTE(m_hKey != NULL);
return RegSetValueEx(m_hKey, lpszValueName, NULL, REG_DWORD,
(BYTE * const)&dwValue, sizeof(DWORD));
}

HRESULT CRegKey::SetValue(LPCTSTR lpszValue, LPCTSTR lpszValueName)
{
_ASSERTE(lpszValue != NULL);
_ASSERTE(m_hKey != NULL);
return RegSetValueEx(m_hKey, lpszValueName, NULL, REG_SZ,
(BYTE * const)lpszValue, (lstrlen(lpszValue)+1)*sizeof(TCHAR));
}

//RecurseDeleteKey is necessary because on NT RegDeleteKey doesn't work if the
//specified key has subkeys
LONG CRegKey::RecurseDeleteKey(LPCTSTR lpszKey)
{
CRegKey key;
LONG lRes = key.Open(m_hKey, lpszKey);
if (lRes != ERROR_SUCCESS)
return lRes;
FILETIME time;
TCHAR szBuffer[256];
DWORD dwSize = 256;
while (RegEnumKeyEx(key.m_hKey, 0, szBuffer, &dwSize, NULL, NULL, NULL,
&time)==ERROR_SUCCESS)
{
lRes = key.RecurseDeleteKey(szBuffer);
if (lRes != ERROR_SUCCESS)
return lRes;
dwSize = 256;
}
key.Close();
return DeleteSubKey(lpszKey);
}

#ifdef __ATLCOM_H__
#ifndef _ATL_NO_SECURITY

CSecurityDescriptor::CSecurityDescriptor()
{
m_pSD = NULL;
m_pOwner = NULL;
m_pGroup = NULL;
m_pDACL = NULL;
m_pSACL= NULL;
}

CSecurityDescriptor::~CSecurityDescriptor()
{
if (m_pSD)
delete m_pSD;
if (m_pOwner)
free(m_pOwner);
if (m_pGroup)
free(m_pGroup);
if (m_pDACL)
free(m_pDACL);
if (m_pSACL)
free(m_pSACL);
}

HRESULT CSecurityDescriptor::Initialize()
{
if (m_pSD)
{
delete m_pSD;
m_pSD = NULL;
}
if (m_pOwner)
{
free(m_pOwner);
m_pOwner = NULL;
}
if (m_pGroup)
{
free(m_pGroup);
m_pGroup = NULL;
}
if (m_pDACL)
{
free(m_pDACL);
m_pDACL = NULL;
}
if (m_pSACL)
{
free(m_pSACL);
m_pSACL = NULL;
}

ATLTRY(m_pSD = new SECURITY_DESCRIPTOR);
if (!m_pSD)
return E_OUTOFMEMORY;
if (!InitializeSecurityDescriptor(m_pSD, SECURITY_DESCRIPTOR_REVISION))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
delete m_pSD;
m_pSD = NULL;
_ASSERTE(FALSE);
return hr;
}
// Set the DACL to allow EVERYONE
SetSecurityDescriptorDacl(m_pSD, TRUE, NULL, FALSE);
return S_OK;
}

HRESULT CSecurityDescriptor::InitializeFromProcessToken(BOOL bDefaulted)
{
PSID pUserSid;
PSID pGroupSid;
HRESULT hr;

Initialize();
hr = GetProcessSids(&pUserSid, &pGroupSid);
if (FAILED(hr))
return hr;
hr = SetOwner(pUserSid, bDefaulted);
if (FAILED(hr))
return hr;
hr = SetGroup(pGroupSid, bDefaulted);
if (FAILED(hr))
return hr;
return S_OK;
}

HRESULT CSecurityDescriptor::InitializeFromThreadToken(BOOL bDefaulted, BOOL bRevertToProcessToken)
{
PSID pUserSid;
PSID pGroupSid;
HRESULT hr;

Initialize();
hr = GetThreadSids(&pUserSid, &pGroupSid);
if (HRESULT_CODE(hr) == ERROR_NO_TOKEN && bRevertToProcessToken)
hr = GetProcessSids(&pUserSid, &pGroupSid);
if (FAILED(hr))
return hr;
hr = SetOwner(pUserSid, bDefaulted);
if (FAILED(hr))
return hr;
hr = SetGroup(pGroupSid, bDefaulted);
if (FAILED(hr))
return hr;
return S_OK;
}

HRESULT CSecurityDescriptor::SetOwner(PSID pOwnerSid, BOOL bDefaulted)
{
_ASSERTE(m_pSD);

// Mark the SD as having no owner
if (!SetSecurityDescriptorOwner(m_pSD, NULL, bDefaulted))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}

if (m_pOwner)
{
free(m_pOwner);
m_pOwner = NULL;
}

// If they asked for no owner don't do the copy
if (pOwnerSid == NULL)
return S_OK;

// Make a copy of the Sid for the return value
DWORD dwSize = GetLengthSid(pOwnerSid);

m_pOwner = (PSID) malloc(dwSize);
if (!m_pOwner)
{
// Insufficient memory to allocate Sid
_ASSERTE(FALSE);
return E_OUTOFMEMORY;
}
if (!CopySid(dwSize, m_pOwner, pOwnerSid))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
free(m_pOwner);
m_pOwner = NULL;
return hr;
}

_ASSERTE(IsValidSid(m_pOwner));

if (!SetSecurityDescriptorOwner(m_pSD, m_pOwner, bDefaulted))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
free(m_pOwner);
m_pOwner = NULL;
return hr;
}

return S_OK;
}

HRESULT CSecurityDescriptor::SetGroup(PSID pGroupSid, BOOL bDefaulted)
{
_ASSERTE(m_pSD);

// Mark the SD as having no Group
if (!SetSecurityDescriptorGroup(m_pSD, NULL, bDefaulted))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}

if (m_pGroup)
{
free(m_pGroup);
m_pGroup = NULL;
}

// If they asked for no Group don't do the copy
if (pGroupSid == NULL)
return S_OK;

// Make a copy of the Sid for the return value
DWORD dwSize = GetLengthSid(pGroupSid);

m_pGroup = (PSID) malloc(dwSize);
if (!m_pGroup)
{
// Insufficient memory to allocate Sid
_ASSERTE(FALSE);
return E_OUTOFMEMORY;
}
if (!CopySid(dwSize, m_pGroup, pGroupSid))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
free(m_pGroup);
m_pGroup = NULL;
return hr;
}

_ASSERTE(IsValidSid(m_pGroup));

if (!SetSecurityDescriptorGroup(m_pSD, m_pGroup, bDefaulted))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
free(m_pGroup);
m_pGroup = NULL;
return hr;
}

return S_OK;
}

HRESULT CSecurityDescriptor::Allow(LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
HRESULT hr = AddAccessAllowedACEToACL(&m_pDACL, pszPrincipal, dwAccessMask);
if (SUCCEEDED(hr))
SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE);
return hr;
}

HRESULT CSecurityDescriptor::Deny(LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
HRESULT hr = AddAccessDeniedACEToACL(&m_pDACL, pszPrincipal, dwAccessMask);
if (SUCCEEDED(hr))
SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE);
return hr;
}

HRESULT CSecurityDescriptor::Revoke(LPCTSTR pszPrincipal)
{
HRESULT hr = RemovePrincipalFromACL(m_pDACL, pszPrincipal);
if (SUCCEEDED(hr))
SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE);
return hr;
}

HRESULT CSecurityDescriptor::GetProcessSids(PSID* ppUserSid, PSID* ppGroupSid)
{
BOOL bRes;
HRESULT hr;
HANDLE hToken = NULL;
if (ppUserSid)
*ppUserSid = NULL;
if (ppGroupSid)
*ppGroupSid = NULL;
bRes = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
if (!bRes)
{
// Couldn't open process token
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}
hr = GetTokenSids(hToken, ppUserSid, ppGroupSid);
return hr;
}

HRESULT CSecurityDescriptor::GetThreadSids(PSID* ppUserSid, PSID* ppGroupSid, BOOL bOpenAsSelf)
{
BOOL bRes;
HRESULT hr;
HANDLE hToken = NULL;
if (ppUserSid)
*ppUserSid = NULL;
if (ppGroupSid)
*ppGroupSid = NULL;
bRes = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, bOpenAsSelf, &hToken);
if (!bRes)
{
// Couldn't open thread token
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
hr = GetTokenSids(hToken, ppUserSid, ppGroupSid);
return hr;
}

HRESULT CSecurityDescriptor::GetTokenSids(HANDLE hToken, PSID* ppUserSid, PSID* ppGroupSid)
{
DWORD dwSize;
HRESULT hr;
PTOKEN_USER ptkUser = NULL;
PTOKEN_PRIMARY_GROUP ptkGroup = NULL;

if (ppUserSid)
*ppUserSid = NULL;
if (ppGroupSid)
*ppGroupSid = NULL;

if (ppUserSid)
{
// Get length required for TokenUser by specifying buffer length of 0
GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
hr = GetLastError();
if (hr != ERROR_INSUFFICIENT_BUFFER)
{
// Expected ERROR_INSUFFICIENT_BUFFER
_ASSERTE(FALSE);
hr = HRESULT_FROM_WIN32(hr);
goto failed;
}

ptkUser = (TOKEN_USER*) malloc(dwSize);
if (!ptkUser)
{
// Insufficient memory to allocate TOKEN_USER
_ASSERTE(FALSE);
hr = E_OUTOFMEMORY;
goto failed;
}
// Get Sid of process token.
if (!GetTokenInformation(hToken, TokenUser, ptkUser, dwSize, &dwSize))
{
// Couldn't get user info
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
goto failed;
}

// Make a copy of the Sid for the return value
dwSize = GetLengthSid(ptkUser->User.Sid);

PSID pSid = (PSID) malloc(dwSize);
if (!pSid)
{
// Insufficient memory to allocate Sid
_ASSERTE(FALSE);
hr = E_OUTOFMEMORY;
goto failed;
}
if (!CopySid(dwSize, pSid, ptkUser->User.Sid))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
goto failed;
}

_ASSERTE(IsValidSid(pSid));
*ppUserSid = pSid;
free(ptkUser);
}
if (ppGroupSid)
{
// Get length required for TokenPrimaryGroup by specifying buffer length of 0
GetTokenInformation(hToken, TokenPrimaryGroup, NULL, 0, &dwSize);
hr = GetLastError();
if (hr != ERROR_INSUFFICIENT_BUFFER)
{
// Expected ERROR_INSUFFICIENT_BUFFER
_ASSERTE(FALSE);
hr = HRESULT_FROM_WIN32(hr);
goto failed;
}

ptkGroup = (TOKEN_PRIMARY_GROUP*) malloc(dwSize);
if (!ptkGroup)
{
// Insufficient memory to allocate TOKEN_USER
_ASSERTE(FALSE);
hr = E_OUTOFMEMORY;
goto failed;
}
// Get Sid of process token.
if (!GetTokenInformation(hToken, TokenPrimaryGroup, ptkGroup, dwSize, &dwSize))
{
// Couldn't get user info
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
goto failed;
}

// Make a copy of the Sid for the return value
dwSize = GetLengthSid(ptkGroup->PrimaryGroup);

PSID pSid = (PSID) malloc(dwSize);
if (!pSid)
{
// Insufficient memory to allocate Sid
_ASSERTE(FALSE);
hr = E_OUTOFMEMORY;
goto failed;
}
if (!CopySid(dwSize, pSid, ptkGroup->PrimaryGroup))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
goto failed;
}

_ASSERTE(IsValidSid(pSid));

*ppGroupSid = pSid;
free(ptkGroup);
}

return S_OK;

failed:
if (ptkUser)
free(ptkUser);
if (ptkGroup)
free (ptkGroup);
return hr;
}

HRESULT CSecurityDescriptor::GetCurrentUserSID(PSID *ppSid)
{
HANDLE tkHandle;

if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tkHandle))
{
TOKEN_USER *tkUser;
DWORD tkSize;
DWORD sidLength;

// Call to get size information for alloc
GetTokenInformation(tkHandle, TokenUser, NULL, 0, &tkSize);
tkUser = (TOKEN_USER *) malloc(tkSize);

// Now make the real call
if (GetTokenInformation(tkHandle, TokenUser, tkUser, tkSize, &tkSize))
{
sidLength = GetLengthSid(tkUser->User.Sid);
*ppSid = (PSID) malloc(sidLength);

memcpy(*ppSid, tkUser->User.Sid, sidLength);
CloseHandle(tkHandle);

free(tkUser);
return S_OK;
}
else
{
free(tkUser);
return HRESULT_FROM_WIN32(GetLastError());
}
}
return HRESULT_FROM_WIN32(GetLastError());
}

HRESULT CSecurityDescriptor::GetPrincipalSID(LPCTSTR pszPrincipal, PSID *ppSid)
{
HRESULT hr;
LPTSTR pszRefDomain = NULL;
DWORD dwDomainSize = 0;
DWORD dwSidSize = 0;
SID_NAME_USE snu;

// Call to get size info for alloc
LookupAccountName(NULL, pszPrincipal, *ppSid, &dwSidSize, pszRefDomain, &dwDomainSize, &snu);

hr = GetLastError();
if (hr != ERROR_INSUFFICIENT_BUFFER)
return HRESULT_FROM_WIN32(hr);

ATLTRY(pszRefDomain = new TCHAR[dwDomainSize]);
if (pszRefDomain == NULL)
return E_OUTOFMEMORY;

*ppSid = (PSID) malloc(dwSidSize);
if (*ppSid != NULL)
{
if (!LookupAccountName(NULL, pszPrincipal, *ppSid, &dwSidSize, pszRefDomain, &dwDomainSize, &snu))
{
free(*ppSid);
*ppSid = NULL;
delete[] pszRefDomain;
return HRESULT_FROM_WIN32(GetLastError());
}
delete[] pszRefDomain;
return S_OK;
}
delete[] pszRefDomain;
return E_OUTOFMEMORY;
}

HRESULT CSecurityDescriptor::Attach(PSECURITY_DESCRIPTOR pSelfRelativeSD)
{
PACL    pDACL = NULL;
PACL    pSACL = NULL;
BOOL    bDACLPresent, bSACLPresent;
BOOL    bDefaulted;
PACL    m_pDACL = NULL;
ACCESS_ALLOWED_ACE* pACE;
HRESULT hr;
PSID    pUserSid;
PSID    pGroupSid;

hr = Initialize();
if(FAILED(hr))
return hr;

// get the existing DACL.
if (!GetSecurityDescriptorDacl(pSelfRelativeSD, &bDACLPresent, &pDACL, &bDefaulted))
goto failed;

if (bDACLPresent)
{
if (pDACL)
{
// allocate new DACL.
m_pDACL = (PACL) malloc(pDACL->AclSize);
if (!m_pDACL)
goto failed;

// initialize the DACL
if (!InitializeAcl(m_pDACL, pDACL->AclSize, ACL_REVISION))
goto failed;

// copy the ACES
for (int i = 0; i < pDACL->AceCount; i++)
{
if (!GetAce(pDACL, i, (void **)&pACE))
goto failed;

if (!AddAccessAllowedAce(m_pDACL, ACL_REVISION, pACE->Mask, (PSID)&(pACE->SidStart)))
goto failed;
}

if (!IsValidAcl(m_pDACL))
goto failed;
}

// set the DACL
if (!SetSecurityDescriptorDacl(m_pSD, m_pDACL ? TRUE : FALSE, m_pDACL, bDefaulted))
goto failed;
}

// get the existing SACL.
if (!GetSecurityDescriptorSacl(pSelfRelativeSD, &bSACLPresent, &pSACL, &bDefaulted))
goto failed;

if (bSACLPresent)
{
if (pSACL)
{
// allocate new SACL.
m_pSACL = (PACL) malloc(pSACL->AclSize);
if (!m_pSACL)
goto failed;

// initialize the SACL
if (!InitializeAcl(m_pSACL, pSACL->AclSize, ACL_REVISION))
goto failed;

// copy the ACES
for (int i = 0; i < pSACL->AceCount; i++)
{
if (!GetAce(pSACL, i, (void **)&pACE))
goto failed;

if (!AddAccessAllowedAce(m_pSACL, ACL_REVISION, pACE->Mask, (PSID)&(pACE->SidStart)))
goto failed;
}

if (!IsValidAcl(m_pSACL))
goto failed;
}

// set the SACL
if (!SetSecurityDescriptorSacl(m_pSD, m_pSACL ? TRUE : FALSE, m_pSACL, bDefaulted))
goto failed;
}

if (!GetSecurityDescriptorOwner(m_pSD, &pUserSid, &bDefaulted))
goto failed;

if (FAILED(SetOwner(pUserSid, bDefaulted)))
goto failed;

if (!GetSecurityDescriptorGroup(m_pSD, &pGroupSid, &bDefaulted))
goto failed;

if (FAILED(SetGroup(pGroupSid, bDefaulted)))
goto failed;

if (!IsValidSecurityDescriptor(m_pSD))
goto failed;

return hr;

failed:
if (m_pDACL)
free(m_pDACL);
if (m_pSD)
free(m_pSD);
return E_UNEXPECTED;
}

HRESULT CSecurityDescriptor::AttachObject(HANDLE hObject)
{
HRESULT hr;
DWORD dwSize = 0;
PSECURITY_DESCRIPTOR pSD = NULL;

GetKernelObjectSecurity(hObject, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION, pSD, 0, &dwSize);

hr = GetLastError();
if (hr != ERROR_INSUFFICIENT_BUFFER)
return HRESULT_FROM_WIN32(hr);

pSD = (PSECURITY_DESCRIPTOR) malloc(dwSize);

if (!GetKernelObjectSecurity(hObject, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION, pSD, dwSize, &dwSize))
{
hr = HRESULT_FROM_WIN32(GetLastError());
free(pSD);
return hr;
}

hr = Attach(pSD);
free(pSD);
return hr;
}

HRESULT CSecurityDescriptor::CopyACL(PACL pDest, PACL pSrc)
{
ACL_SIZE_INFORMATION aclSizeInfo;
LPVOID pAce;
ACE_HEADER *aceHeader;

if (pSrc == NULL)
return S_OK;

if (!GetAclInformation(pSrc, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
return HRESULT_FROM_WIN32(GetLastError());

// Copy all of the ACEs to the new ACL
for (UINT i = 0; i < aclSizeInfo.AceCount; i++)
{
if (!GetAce(pSrc, i, &pAce))
return HRESULT_FROM_WIN32(GetLastError());

aceHeader = (ACE_HEADER *) pAce;

if (!AddAce(pDest, ACL_REVISION, 0xffffffff, pAce, aceHeader->AceSize))
return HRESULT_FROM_WIN32(GetLastError());
}

return S_OK;
}

HRESULT CSecurityDescriptor::AddAccessDeniedACEToACL(PACL *ppAcl, LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
ACL_SIZE_INFORMATION aclSizeInfo;
int aclSize;
DWORD returnValue;
PSID principalSID;
PACL oldACL, newACL;

oldACL = *ppAcl;

returnValue = GetPrincipalSID(pszPrincipal, &principalSID);
if (FAILED(returnValue))
return returnValue;

aclSizeInfo.AclBytesInUse = 0;
if (*ppAcl != NULL)
GetAclInformation(oldACL, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_DENIED_ACE) + GetLengthSid(principalSID) - sizeof(DWORD);

ATLTRY(newACL = (PACL) new BYTE[aclSize]);

if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

if (!AddAccessDeniedAce(newACL, ACL_REVISION2, dwAccessMask, principalSID))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

returnValue = CopyACL(newACL, oldACL);
if (FAILED(returnValue))
{
free(principalSID);
return returnValue;
}

*ppAcl = newACL;

if (oldACL != NULL)
free(oldACL);
free(principalSID);
return S_OK;
}

HRESULT CSecurityDescriptor::AddAccessAllowedACEToACL(PACL *ppAcl, LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
ACL_SIZE_INFORMATION aclSizeInfo;
int aclSize;
DWORD returnValue;
PSID principalSID;
PACL oldACL, newACL;

oldACL = *ppAcl;

returnValue = GetPrincipalSID(pszPrincipal, &principalSID);
if (FAILED(returnValue))
return returnValue;

aclSizeInfo.AclBytesInUse = 0;
if (*ppAcl != NULL)
GetAclInformation(oldACL, (LPVOID) &aclSizeInfo, (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(principalSID) - sizeof(DWORD);

ATLTRY(newACL = (PACL) new BYTE[aclSize]);

if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

returnValue = CopyACL(newACL, oldACL);
if (FAILED(returnValue))
{
free(principalSID);
return returnValue;
}

if (!AddAccessAllowedAce(newACL, ACL_REVISION2, dwAccessMask, principalSID))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

*ppAcl = newACL;

if (oldACL != NULL)
free(oldACL);
free(principalSID);
return S_OK;
}

HRESULT CSecurityDescriptor::RemovePrincipalFromACL(PACL pAcl, LPCTSTR pszPrincipal)
{
ACL_SIZE_INFORMATION aclSizeInfo;
ULONG i;
LPVOID ace;
ACCESS_ALLOWED_ACE *accessAllowedAce;
ACCESS_DENIED_ACE *accessDeniedAce;
SYSTEM_AUDIT_ACE *systemAuditAce;
PSID principalSID;
DWORD returnValue;
ACE_HEADER *aceHeader;

returnValue = GetPrincipalSID(pszPrincipal, &principalSID);
if (FAILED(returnValue))
return returnValue;

GetAclInformation(pAcl, (LPVOID) &aclSizeInfo, (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

for (i = 0; i < aclSizeInfo.AceCount; i++)
{
if (!GetAce(pAcl, i, &ace))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

aceHeader = (ACE_HEADER *) ace;

if (aceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
{
accessAllowedAce = (ACCESS_ALLOWED_ACE *) ace;

if (EqualSid(principalSID, (PSID) &accessAllowedAce->SidStart))
{
DeleteAce(pAcl, i);
free(principalSID);
return S_OK;
}
} else

if (aceHeader->AceType == ACCESS_DENIED_ACE_TYPE)
{
accessDeniedAce = (ACCESS_DENIED_ACE *) ace;

if (EqualSid(principalSID, (PSID) &accessDeniedAce->SidStart))
{
DeleteAce(pAcl, i);
free(principalSID);
return S_OK;
}
} else

if (aceHeader->AceType == SYSTEM_AUDIT_ACE_TYPE)
{
systemAuditAce = (SYSTEM_AUDIT_ACE *) ace;

if (EqualSid(principalSID, (PSID) &systemAuditAce->SidStart))
{
DeleteAce(pAcl, i);
free(principalSID);
return S_OK;
}
}
}
free(principalSID);
return S_OK;
}

HRESULT CSecurityDescriptor::SetPrivilege(LPCTSTR privilege, BOOL bEnable, HANDLE hToken)
{
HRESULT hr;
TOKEN_PRIVILEGES tpPrevious;
TOKEN_PRIVILEGES tp;
DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);
LUID luid;

// if no token specified open process token
if (hToken == 0)
{
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}
}

if (!LookupPrivilegeValue(NULL, privilege, &luid ))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;

if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}

tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;

if (bEnable)
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
else
tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes);

if (!AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}
return S_OK;
}

#endif //_ATL_NO_SECURITY
#endif //__ATLCOM_H__

#ifdef _DEBUG

void _cdecl AtlTrace(LPCTSTR lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);

int nBuf;
TCHAR szBuffer[512];

nBuf = _vstprintf(szBuffer, lpszFormat, args);
_ASSERTE(nBuf < sizeof(szBuffer));

OutputDebugString(szBuffer);
va_end(args);
}
#endif

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

///////////////////////////////////////////////////////////////////////////////
//All Global stuff goes below this line
///////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// Minimize CRT
// Specify DllMain as EntryPoint
// Turn off exception handling
// Define _ATL_MIN_CRT

#ifdef _ATL_MIN_CRT
/////////////////////////////////////////////////////////////////////////////
// Startup Code

#if defined(_WINDLL) || defined(_USRDLL)

// Declare DllMain
extern "C" BOOL WINAPI DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved);

extern "C" BOOL WINAPI _DllMainCRTStartup(HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved)
{
return DllMain(hDllHandle, dwReason, lpReserved);
}

#else

// wWinMain is not defined in winbase.h.
extern "C" int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd);

#define SPACECHAR   _T(' ')
#define DQUOTECHAR  _T('\"')

#ifdef _UNICODE
extern "C" void wWinMainCRTStartup()
#else // _UNICODE
extern "C" void WinMainCRTStartup()
#endif // _UNICODE
{
LPTSTR lpszCommandLine = ::GetCommandLine();
if(lpszCommandLine == NULL)
::ExitProcess((UINT)-1);

// Skip past program name (first token in command line).
// Check for and handle quoted program name.
if(*lpszCommandLine == DQUOTECHAR)
{
// Scan, and skip over, subsequent characters until
// another double-quote or a null is encountered.
do
{
lpszCommandLine = ::CharNext(lpszCommandLine);
}
while((*lpszCommandLine != DQUOTECHAR) && (*lpszCommandLine != _T('\0')));

// If we stopped on a double-quote (usual case), skip over it.
if(*lpszCommandLine == DQUOTECHAR)
lpszCommandLine = ::CharNext(lpszCommandLine);
}
else
{
while(*lpszCommandLine > SPACECHAR)
lpszCommandLine = ::CharNext(lpszCommandLine);
}

// Skip past any white space preceeding the second token.
while(*lpszCommandLine && (*lpszCommandLine <= SPACECHAR))
lpszCommandLine = ::CharNext(lpszCommandLine);

STARTUPINFO StartupInfo;
StartupInfo.dwFlags = 0;
::GetStartupInfo(&StartupInfo);

int nRet = _tWinMain(::GetModuleHandle(NULL), NULL, lpszCommandLine,
(StartupInfo.dwFlags & STARTF_USESHOWWINDOW) ?
StartupInfo.wShowWindow : SW_SHOWDEFAULT);

::ExitProcess((UINT)nRet);
}

#endif // defined(_WINDLL) | defined(_USRDLL)

/////////////////////////////////////////////////////////////////////////////
// Heap Allocation

#ifndef _DEBUG

#ifndef _MERGE_PROXYSTUB
//rpcproxy.h does the same thing as this
int __cdecl _purecall()
{
DebugBreak();
return 0;
}
#endif

extern "C" const int _fltused = 0;

void* __cdecl malloc(size_t n)
{
if (_Module.m_hHeap == NULL)
{
_Module.m_hHeap = HeapCreate(0, 0, 0);
if (_Module.m_hHeap == NULL)
return NULL;
}
_ASSERTE(_Module.m_hHeap != NULL);

#ifdef _MALLOC_ZEROINIT
return HeapAlloc(_Module.m_hHeap, HEAP_ZERO_MEMORY, n);
#else
return HeapAlloc(_Module.m_hHeap, 0, n);
#endif
}

void* __cdecl calloc(size_t n, size_t s)
{
return malloc(n * s);
}

void* __cdecl realloc(void* p, size_t n)
{
_ASSERTE(_Module.m_hHeap != NULL);
#ifdef _MALLOC_ZEROINIT
return (p == NULL) ? malloc(n) : HeapReAlloc(_Module.m_hHeap, HEAP_ZERO_MEMORY, p, n);
#else
return (p == NULL) ? malloc(n) : HeapReAlloc(_Module.m_hHeap, 0, p, n);
#endif
}

void __cdecl free(void* p)
{
_ASSERTE(_Module.m_hHeap != NULL);
HeapFree(_Module.m_hHeap, 0, p);
}

void* __cdecl operator new(size_t n)
{
return malloc(n);
}

void __cdecl operator delete(void* p)
{
free(p);
}

#endif  //_DEBUG

#endif //_ATL_MIN_CRT

#ifndef _ATL_DLL

#ifndef ATL_NO_NAMESPACE
#ifndef _ATL_DLL_IMPL
namespace ATL
{
#endif
#endif

/////////////////////////////////////////////////////////////////////////////
// statics

static UINT WINAPI AtlGetDirLen(LPCOLESTR lpszPathName)
{
_ASSERTE(lpszPathName != NULL);

// always capture the complete file name including extension (if present)
LPCOLESTR lpszTemp = lpszPathName;
for (LPCOLESTR lpsz = lpszPathName; *lpsz != NULL; )
{
LPCOLESTR lp = CharNextO(lpsz);
// remember last directory/drive separator
if (*lpsz == OLESTR('\\') || *lpsz == OLESTR('/') || *lpsz == OLESTR(':'))
lpszTemp = lp;
lpsz = lp;
}

return lpszTemp-lpszPathName;
}

/////////////////////////////////////////////////////////////////////////////
// QI support

ATLAPI AtlInternalQueryInterface(void* pThis,
const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
{
_ASSERTE(pThis != NULL);
// First entry in the com map should be a simple map entry
_ASSERTE(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
if (ppvObject == NULL)
return E_POINTER;
*ppvObject = NULL;
if (InlineIsEqualUnknown(iid)) // use first interface
{
IUnknown* pUnk = (IUnknown*)((int)pThis+pEntries->dw);
pUnk->AddRef();
*ppvObject = pUnk;
return S_OK;
}
while (pEntries->pFunc != NULL)
{
BOOL bBlind = (pEntries->piid == NULL);
if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))
{
if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY) //offset
{
_ASSERTE(!bBlind);
IUnknown* pUnk = (IUnknown*)((int)pThis+pEntries->dw);
pUnk->AddRef();
*ppvObject = pUnk;
return S_OK;
}
else //actual function call
{
HRESULT hRes = pEntries->pFunc(pThis,
iid, ppvObject, pEntries->dw);
if (hRes == S_OK || (!bBlind && FAILED(hRes)))
return hRes;
}
}
pEntries++;

}
return E_NOINTERFACE;
}

/////////////////////////////////////////////////////////////////////////////
// Smart Pointer helpers

ATLAPI_(IUnknown*) AtlComPtrAssign(IUnknown** pp, IUnknown* lp)
{
if (lp != NULL)
lp->AddRef();
if (*pp)
(*pp)->Release();
*pp = lp;
return lp;
}

ATLAPI_(IUnknown*) AtlComQIPtrAssign(IUnknown** pp, IUnknown* lp, REFIID riid)
{
IUnknown* pTemp = *pp;
lp->QueryInterface(riid, (void**)pp);
if (pTemp)
pTemp->Release();
return *pp;
}

/////////////////////////////////////////////////////////////////////////////
// Inproc Marshaling helpers

ATLAPI AtlFreeMarshalStream(IStream* pStream)
{
if (pStream != NULL)
{
CoReleaseMarshalData(pStream);
pStream->Release();
}
return S_OK;
}

ATLAPI AtlMarshalPtrInProc(IUnknown* pUnk, const IID& iid, IStream** ppStream)
{
HRESULT hRes = CreateStreamOnHGlobal(NULL, TRUE, ppStream);
if (SUCCEEDED(hRes))
{
hRes = CoMarshalInterface(*ppStream, iid,
pUnk, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLESTRONG);
if (FAILED(hRes))
{
(*ppStream)->Release();
*ppStream = NULL;
}
}
return hRes;
}

ATLAPI AtlUnmarshalPtr(IStream* pStream, const IID& iid, IUnknown** ppUnk)
{
*ppUnk = NULL;
HRESULT hRes = E_INVALIDARG;
if (pStream != NULL)
{
LARGE_INTEGER l;
l.QuadPart = 0;
pStream->Seek(l, STREAM_SEEK_SET, NULL);
hRes = CoUnmarshalInterface(pStream, iid, (void**)ppUnk);
}
return hRes;
}

ATLAPI_(BOOL) AtlWaitWithMessageLoop(HANDLE hEvent)
{
DWORD dwRet;
MSG msg;

while(1)
{
dwRet = MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT);

if (dwRet == WAIT_OBJECT_0)
return TRUE;    // The event was signaled

if (dwRet != WAIT_OBJECT_0 + 1)
break;          // Something else happened

// There is one or more window message available. Dispatch them
while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
return TRUE; // Event is now signaled.
}
}
return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Connection Point Helpers

ATLAPI AtlAdvise(IUnknown* pUnkCP, IUnknown* pUnk, const IID& iid, LPDWORD pdw)
{
CComPtr<IConnectionPointContainer> pCPC;
CComPtr<IConnectionPoint> pCP;
HRESULT hRes = pUnkCP->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
if (SUCCEEDED(hRes))
hRes = pCPC->FindConnectionPoint(iid, &pCP);
if (SUCCEEDED(hRes))
hRes = pCP->Advise(pUnk, pdw);
return hRes;
}

ATLAPI AtlUnadvise(IUnknown* pUnkCP, const IID& iid, DWORD dw)
{
CComPtr<IConnectionPointContainer> pCPC;
CComPtr<IConnectionPoint> pCP;
HRESULT hRes = pUnkCP->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
if (SUCCEEDED(hRes))
hRes = pCPC->FindConnectionPoint(iid, &pCP);
if (SUCCEEDED(hRes))
hRes = pCP->Unadvise(dw);
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// IDispatch Error handling

ATLAPI AtlSetErrorInfo(const CLSID& clsid, LPCOLESTR lpszDesc, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes, HINSTANCE hInst)
{
USES_CONVERSION;
TCHAR szDesc[1024];
szDesc[0] = NULL;
// For a valid HRESULT the id should be in the range [0x0200, 0xffff]
if (HIWORD(lpszDesc) == 0) //id
{
UINT nID = LOWORD((DWORD)lpszDesc);
_ASSERTE((nID >= 0x0200 && nID <= 0xffff) || hRes != 0);
if (LoadString(hInst, nID, szDesc, 1024) == 0)
{
_ASSERTE(FALSE);
lstrcpy(szDesc, _T("Unknown Error"));
}
lpszDesc = T2OLE(szDesc);
if (hRes == 0)
hRes = MAKE_HRESULT(3, FACILITY_ITF, nID);
}

CComPtr<ICreateErrorInfo> pICEI;
if (SUCCEEDED(CreateErrorInfo(&pICEI)))
{
CComPtr<IErrorInfo> pErrorInfo;
pICEI->SetGUID(iid);
LPOLESTR lpsz;
ProgIDFromCLSID(clsid, &lpsz);
if (lpsz != NULL)
pICEI->SetSource(lpsz);
if (dwHelpID != 0 && lpszHelpFile != NULL)
{
pICEI->SetHelpContext(dwHelpID);
pICEI->SetHelpFile(const_cast<LPOLESTR>(lpszHelpFile));
}
CoTaskMemFree(lpsz);
pICEI->SetDescription((LPOLESTR)lpszDesc);
if (SUCCEEDED(pICEI->QueryInterface(IID_IErrorInfo, (void**)&pErrorInfo)))
SetErrorInfo(0, pErrorInfo);
}
//#ifdef _DEBUG
//  USES_CONVERSION;
//  ATLTRACE(_T("AtlReportError: Description=\"%s\" returning %x\n"), OLE2CT(lpszDesc), hRes);
//#endif
return (hRes == 0) ? DISP_E_EXCEPTION : hRes;
}

/////////////////////////////////////////////////////////////////////////////
// Module

//Although these functions are big, they are only used once in a module
//so we should make them inline.

ATLAPI AtlModuleInit(_ATL_MODULE* pM, _ATL_OBJMAP_ENTRY* p, HINSTANCE h)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
if (pM->cbSize < sizeof(_ATL_MODULE))
return E_INVALIDARG;
pM->m_pObjMap = p;
pM->m_hInst = pM->m_hInstTypeLib = pM->m_hInstResource = h;
pM->m_nLockCnt=0L;
pM->m_hHeap = NULL;
InitializeCriticalSection(&pM->m_csTypeInfoHolder);
InitializeCriticalSection(&pM->m_csWindowCreate);
InitializeCriticalSection(&pM->m_csObjMap);
return S_OK;
}

ATLAPI AtlModuleRegisterClassObjects(_ATL_MODULE* pM, DWORD dwClsContext, DWORD dwFlags)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
HRESULT hRes = S_OK;
while (pEntry->pclsid != NULL && hRes == S_OK)
{
hRes = pEntry->RegisterClassObject(dwClsContext, dwFlags);
pEntry++;
}
return hRes;
}

ATLAPI AtlModuleRevokeClassObjects(_ATL_MODULE* pM)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
HRESULT hRes = S_OK;
while (pEntry->pclsid != NULL && hRes == S_OK)
{
hRes = pEntry->RevokeClassObject();
pEntry++;
}
return hRes;
}

ATLAPI AtlModuleGetClassObject(_ATL_MODULE* pM, REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
HRESULT hRes = S_OK;
if (ppv == NULL)
return E_POINTER;
while (pEntry->pclsid != NULL)
{
if (InlineIsEqualGUID(rclsid, *pEntry->pclsid))
{
if (pEntry->pCF == NULL)
{
EnterCriticalSection(&pM->m_csObjMap);
if (pEntry->pCF == NULL)
hRes = pEntry->pfnGetClassObject(pEntry->pfnCreateInstance, IID_IUnknown, (LPVOID*)&pEntry->pCF);
LeaveCriticalSection(&pM->m_csObjMap);
}
if (pEntry->pCF != NULL)
hRes = pEntry->pCF->QueryInterface(riid, ppv);
break;
}
pEntry++;
}
if (*ppv == NULL && hRes == S_OK)
hRes = CLASS_E_CLASSNOTAVAILABLE;
return hRes;
}

ATLAPI AtlModuleTerm(_ATL_MODULE* pM)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_hInst != NULL);
if (pM->m_pObjMap != NULL)
{
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
while (pEntry->pclsid != NULL)
{
if (pEntry->pCF != NULL)
pEntry->pCF->Release();
pEntry->pCF = NULL;
pEntry++;
}
}
DeleteCriticalSection(&pM->m_csTypeInfoHolder);
DeleteCriticalSection(&pM->m_csWindowCreate);
DeleteCriticalSection(&pM->m_csObjMap);
if (pM->m_hHeap != NULL)
HeapDestroy(pM->m_hHeap);
return S_OK;
}

ATLAPI AtlModuleRegisterServer(_ATL_MODULE* pM, BOOL bRegTypeLib, const CLSID* pCLSID)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_hInst != NULL);
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
HRESULT hRes = S_OK;
for (;pEntry->pclsid != NULL; pEntry++)
{
if (pCLSID == NULL)
{
if (pEntry->pfnGetObjectDescription() != NULL)
continue;
}
else
{
if (!IsEqualGUID(*pCLSID, *pEntry->pclsid))
continue;
}
hRes = pEntry->pfnUpdateRegistry(TRUE);
if (FAILED(hRes))
break;
}
if (SUCCEEDED(hRes) && bRegTypeLib)
hRes = AtlModuleRegisterTypeLib(pM, 0);
return hRes;
}

ATLAPI AtlModuleUnregisterServer(_ATL_MODULE* pM, const CLSID* pCLSID)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_hInst != NULL);
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
for (;pEntry->pclsid != NULL; pEntry++)
{
if (pCLSID == NULL)
{
if (pEntry->pfnGetObjectDescription() != NULL)
continue;
}
else
{
if (!IsEqualGUID(*pCLSID, *pEntry->pclsid))
continue;
}
pEntry->pfnUpdateRegistry(FALSE); //unregister
}
return S_OK;
}

ATLAPI AtlModuleUpdateRegistryFromResourceD(_ATL_MODULE* pM, LPCOLESTR lpszRes,
BOOL bRegister, struct _ATL_REGMAP_ENTRY* pMapEntries, IRegistrar* pReg)
{
USES_CONVERSION;
_ASSERTE(pM != NULL);
HRESULT hRes = S_OK;
CComPtr<IRegistrar> p;
if (pReg != NULL)
p = pReg;
else
{
hRes = CoCreateInstance(CLSID_Registrar, NULL,
CLSCTX_INPROC_SERVER, IID_IRegistrar, (void**)&p);
}
if (SUCCEEDED(hRes))
{
TCHAR szModule[_MAX_PATH];
GetModuleFileName(pM->m_hInst, szModule, _MAX_PATH);
p->AddReplacement(OLESTR("Module"), T2OLE(szModule));

if (NULL != pMapEntries)
{
while (NULL != pMapEntries->szKey)
{
_ASSERTE(NULL != pMapEntries->szData);
p->AddReplacement((LPOLESTR)pMapEntries->szKey, (LPOLESTR)pMapEntries->szData);
pMapEntries++;
}
}
LPCOLESTR szType = OLESTR("REGISTRY");
GetModuleFileName(pM->m_hInstResource, szModule, _MAX_PATH);
LPOLESTR pszModule = T2OLE(szModule);
if (HIWORD(lpszRes)==0)
{
if (bRegister)
hRes = p->ResourceRegister(pszModule, ((UINT)LOWORD((DWORD)lpszRes)), szType);
else
hRes = p->ResourceUnregister(pszModule, ((UINT)LOWORD((DWORD)lpszRes)), szType);
}
else
{
if (bRegister)
hRes = p->ResourceRegisterSz(pszModule, lpszRes, szType);
else
hRes = p->ResourceUnregisterSz(pszModule, lpszRes, szType);
}

}
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// TypeLib Support

ATLAPI AtlModuleRegisterTypeLib(_ATL_MODULE* pM, LPCOLESTR lpszIndex)
{
_ASSERTE(pM != NULL);
USES_CONVERSION;
_ASSERTE(pM->m_hInstTypeLib != NULL);
TCHAR szModule[_MAX_PATH+10];
OLECHAR szDir[_MAX_PATH];
GetModuleFileName(pM->m_hInstTypeLib, szModule, _MAX_PATH);
if (lpszIndex != NULL)
lstrcat(szModule, OLE2CT(lpszIndex));
ITypeLib* pTypeLib;
LPOLESTR lpszModule = T2OLE(szModule);
HRESULT hr = LoadTypeLib(lpszModule, &pTypeLib);
if (!SUCCEEDED(hr))
{
// typelib not in module, try <module>.tlb instead
LPTSTR lpszExt = NULL;
LPTSTR lpsz;
for (lpsz = szModule; *lpsz != NULL; lpsz = CharNext(lpsz))
{
if (*lpsz == _T('.'))
lpszExt = lpsz;
}
if (lpszExt == NULL)
lpszExt = lpsz;
lstrcpy(lpszExt, _T(".tlb"));
lpszModule = T2OLE(szModule);
hr = LoadTypeLib(lpszModule, &pTypeLib);
}
if (SUCCEEDED(hr))
{
ocscpy(szDir, lpszModule);
szDir[AtlGetDirLen(szDir)] = 0;
hr = ::RegisterTypeLib(pTypeLib, lpszModule, szDir);
}
if (pTypeLib != NULL)
pTypeLib->Release();
return hr;
}

#ifndef ATL_NO_NAMESPACE
#ifndef _ATL_DLL_IMPL
}; //namespace ATL
#endif
#endif

#endif //!_ATL_DLL


ATLWIN.CPP

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLWIN_H__
#error atlwin.cpp requires atlwin.h to be included first
#endif

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

/////////////////////////////////////////////////////////////////////////////
// CWndProcThunk

void CWndProcThunk::Init(WNDPROC proc, void* pThis)
{
#if defined(_M_PPC)
#error PPC not yet implemented
#elif defined (_M_ALPHA)
thunk.ldah_at = 0x279f0000 | HIWORD(proc);
thunk.ldah_a0 = 0x261f0000 | HIWORD(pThis);
thunk.lda_at = 0x239c0000 | LOWORD(proc);
thunk.lda_a0 = 0x22100000 | LOWORD(pThis);
thunk.jmp = 0x6bfc0000;
#elif defined (_M_IX86)
thunk.m_mov = 0xb9;
thunk.m_this = (DWORD)pThis;
thunk.m_jmp = 0xe9;
thunk.m_relproc = (int)proc - ((int)this+sizeof(_WndProcThunk));
#endif
}

/////////////////////////////////////////////////////////////////////////////
// CWindow

HWND CWindow::GetDescendantWindow(int nID) const
{
_ASSERTE(::IsWindow(m_hWnd));

// GetDlgItem recursive (return first found)
// breadth-first for 1 level, then depth-first for next level

// use GetDlgItem since it is a fast USER function
HWND hWndChild, hWndTmp;
CWindow wnd;
if((hWndChild = ::GetDlgItem(m_hWnd, nID)) != NULL)
{
if(::GetTopWindow(hWndChild) != NULL)
{
// children with the same ID as their parent have priority
wnd.Attach(hWndChild);
hWndTmp = wnd.GetDescendantWindow(nID);
if(hWndTmp != NULL)
return hWndTmp;
}
return hWndChild;
}

// walk each child
for(hWndChild = ::GetTopWindow(m_hWnd); hWndChild != NULL;
hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
{
wnd.Attach(hWndChild);
hWndTmp = wnd.GetDescendantWindow(nID);
if(hWndTmp != NULL)
return hWndTmp;
}

return NULL;    // not found
}

void CWindow::SendMessageToDescendants(UINT message, WPARAM wParam/*= 0*/, LPARAM lParam/*= 0*/, BOOL bDeep/* = TRUE*/)
{
CWindow wnd;
for(HWND hWndChild = ::GetTopWindow(m_hWnd); hWndChild != NULL;
hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
{
::SendMessage(hWndChild, message, wParam, lParam);

if(bDeep && ::GetTopWindow(hWndChild) != NULL)
{
// send to child windows after parent
wnd.Attach(hWndChild);
wnd.SendMessageToDescendants(message, wParam, lParam, bDeep);
}
}
}

BOOL CWindow::CenterWindow(HWND hWndCenter/* = NULL*/)
{
_ASSERTE(::IsWindow(m_hWnd));

// determine owner window to center against
DWORD dwStyle = GetStyle();
if(hWndCenter == NULL)
{
if(dwStyle & WS_CHILD)
hWndCenter = ::GetParent(m_hWnd);
else
hWndCenter = ::GetWindow(m_hWnd, GW_OWNER);
}

// get coordinates of the window relative to its parent
RECT rcDlg;
::GetWindowRect(m_hWnd, &rcDlg);
RECT rcArea;
RECT rcCenter;
HWND hWndParent;
if(!(dwStyle & WS_CHILD))
{
// don't center against invisible or minimized windows
if(hWndCenter != NULL)
{
DWORD dwStyle = ::GetWindowLong(hWndCenter, GWL_STYLE);
if(!(dwStyle & WS_VISIBLE) || (dwStyle & WS_MINIMIZE))
hWndCenter = NULL;
}

// center within screen coordinates
::SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);

if(hWndCenter == NULL)
rcCenter = rcArea;
else
::GetWindowRect(hWndCenter, &rcCenter);
}
else
{
// center within parent client coordinates
hWndParent = ::GetParent(m_hWnd);
_ASSERTE(::IsWindow(hWndParent));

::GetClientRect(hWndParent, &rcArea);
_ASSERTE(::IsWindow(hWndCenter));
::GetClientRect(hWndCenter, &rcCenter);
::MapWindowPoints(hWndCenter, hWndParent, (POINT*)&rcCenter, 2);
}

int DlgWidth = rcDlg.right - rcDlg.left;
int DlgHeight = rcDlg.bottom - rcDlg.top;

// find dialog's upper left based on rcCenter
int xLeft = (rcCenter.left + rcCenter.right) / 2 - DlgWidth / 2;
int yTop = (rcCenter.top + rcCenter.bottom) / 2 - DlgHeight / 2;

// if the dialog is outside the screen, move it inside
if(xLeft < rcArea.left)
xLeft = rcArea.left;
else if(xLeft + DlgWidth > rcArea.right)
xLeft = rcArea.right - DlgWidth;

if(yTop < rcArea.top)
yTop = rcArea.top;
else if(yTop + DlgHeight > rcArea.bottom)
yTop = rcArea.bottom - DlgHeight;

// map screen coordinates to child coordinates
return ::SetWindowPos(m_hWnd, NULL, xLeft, yTop, -1, -1,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}

BOOL CWindow::ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags)
{
_ASSERTE(::IsWindow(m_hWnd));

DWORD dwStyle = ::GetWindowLong(m_hWnd, GWL_STYLE);
DWORD dwNewStyle = (dwStyle & ~dwRemove) | dwAdd;
if(dwStyle == dwNewStyle)
return FALSE;

::SetWindowLong(m_hWnd, GWL_STYLE, dwNewStyle);
if(nFlags != 0)
{
::SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | nFlags);
}

return TRUE;
}

BOOL CWindow::ModifyStyleEx(DWORD dwRemove, DWORD dwAdd, UINT nFlags)
{
_ASSERTE(::IsWindow(m_hWnd));

DWORD dwStyle = ::GetWindowLong(m_hWnd, GWL_EXSTYLE);
DWORD dwNewStyle = (dwStyle & ~dwRemove) | dwAdd;
if(dwStyle == dwNewStyle)
return FALSE;

::SetWindowLong(m_hWnd, GWL_EXSTYLE, dwNewStyle);
if(nFlags != 0)
{
::SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | nFlags);
}

return TRUE;
}

BOOL CWindow::GetWindowText(BSTR& bstrText)
{
USES_CONVERSION;
_ASSERTE(::IsWindow(m_hWnd));

int nLen = ::GetWindowTextLength(m_hWnd);
if(nLen == 0)
return FALSE;

LPTSTR lpszText = (LPTSTR)_alloca((nLen+1)*sizeof(TCHAR));

if(!::GetWindowText(m_hWnd, lpszText, nLen+1))
return FALSE;

bstrText = ::SysAllocString(T2OLE(lpszText));

return (bstrText != NULL) ? TRUE : FALSE;
}

HWND CWindow::GetTopLevelParent() const
{
_ASSERTE(::IsWindow(m_hWnd));

HWND hWndParent = m_hWnd;
HWND hWndTmp;
while((hWndTmp = ::GetParent(hWndParent)) != NULL)
hWndParent = hWndTmp;

return hWndParent;
}

HWND CWindow::GetTopLevelWindow() const
{
_ASSERTE(::IsWindow(m_hWnd));

HWND hWndParent = m_hWnd;
HWND hWndTmp = hWndParent;

while(hWndTmp != NULL)
{
hWndTmp = (::GetWindowLong(hWndParent, GWL_STYLE) & WS_CHILD) ? ::GetParent(hWndParent) : ::GetWindow(hWndParent, GW_OWNER);
hWndParent = hWndTmp;
}

return hWndParent;
}

/////////////////////////////////////////////////////////////////////////////
// CDynamicChain

CDynamicChain::~CDynamicChain()
{
if(m_pChainEntry != NULL)
{
for(int i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] != NULL)
delete m_pChainEntry[i];
}

delete [] m_pChainEntry;
}
}

BOOL CDynamicChain::SetChainEntry(DWORD dwChainID, CMessageMap* pObject, DWORD dwMsgMapID /* = 0 */)
{
// first search for an existing entry

for(int i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] != NULL && m_pChainEntry[i]->m_dwChainID == dwChainID)
{
m_pChainEntry[i]->m_pObject = pObject;
m_pChainEntry[i]->m_dwMsgMapID = dwMsgMapID;
return TRUE;
}
}

// create a new one

ATL_CHAIN_ENTRY* pEntry = NULL;
ATLTRY(pEntry = new ATL_CHAIN_ENTRY);

if(pEntry == NULL)
return FALSE;

// search for an empty one

for(i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] == NULL)
{
m_pChainEntry[i] = pEntry;
return TRUE;
}
}

// add a new one

ATL_CHAIN_ENTRY** ppNew = NULL;
ATLTRY(ppNew = new ATL_CHAIN_ENTRY*[m_nEntries + 1]);

if(ppNew == NULL)
{
delete pEntry;
return FALSE;
}

pEntry->m_dwChainID = dwChainID;
pEntry->m_pObject = pObject;
pEntry->m_dwMsgMapID = dwMsgMapID;

if(m_pChainEntry != NULL)
{
memcpy(ppNew, m_pChainEntry, m_nEntries * sizeof(ATL_CHAIN_ENTRY*));
delete [] m_pChainEntry;
}

m_pChainEntry = ppNew;

m_pChainEntry[m_nEntries] = pEntry;

m_nEntries++;

return TRUE;
}

BOOL CDynamicChain::RemoveChainEntry(DWORD dwChainID)
{
for(int i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] != NULL && m_pChainEntry[i]->m_dwChainID == dwChainID)
{
delete m_pChainEntry[i];
m_pChainEntry[i] = NULL;
return TRUE;
}
}

return FALSE;
}

BOOL CDynamicChain::CallChain(DWORD dwChainID, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult)
{
for(int i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] != NULL && m_pChainEntry[i]->m_dwChainID == dwChainID)
return (m_pChainEntry[i]->m_pObject)->ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, m_pChainEntry[i]->m_dwMsgMapID);
}

return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CWndClassInfo

ATOM CWndClassInfo::Register(WNDPROC* pProc)
{
if (m_atom == 0)
{
::EnterCriticalSection(&_Module.m_csWindowCreate);
if(m_atom == 0)
{
HINSTANCE hInst = _Module.GetModuleInstance();
if (m_lpszOrigName != NULL)
{
_ASSERTE(pProc != NULL);
LPCTSTR lpsz = m_wc.lpszClassName;
WNDPROC proc = m_wc.lpfnWndProc;

WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
if(!::GetClassInfoEx(NULL, m_lpszOrigName, &wc))
{
::LeaveCriticalSection(&_Module.m_csWindowCreate);
return 0;
}
memcpy(&m_wc, &wc, sizeof(WNDCLASSEX));
pWndProc = m_wc.lpfnWndProc;
m_wc.lpszClassName = lpsz;
m_wc.lpfnWndProc = proc;
}
else
{
m_wc.hCursor = ::LoadCursor(m_bSystemCursor ? NULL : hInst,
m_lpszCursorID);
}

m_wc.hInstance = hInst;
if (m_wc.lpszClassName == NULL)
{
wsprintf(m_szAutoName, _T("ATL:%8.8X"), (DWORD)&m_wc);
m_wc.lpszClassName = m_szAutoName;
}
WNDCLASSEX wcTemp;
memcpy(&wcTemp, &m_wc, sizeof(WNDCLASSEX));
m_atom = ::GetClassInfoEx(m_wc.hInstance, m_szAutoName, &wcTemp);
if (m_atom == 0)
m_atom = ::RegisterClassEx(&m_wc);
}
::LeaveCriticalSection(&_Module.m_csWindowCreate);
}

if (m_lpszOrigName != NULL)
{
_ASSERTE(pProc != NULL);
_ASSERTE(pWndProc != NULL);
*pProc = pWndProc;
}
return m_atom;
}

/////////////////////////////////////////////////////////////////////////////
// CWindowImpl

LRESULT CALLBACK CWindowImplBase::StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CWindowImplBase* pT = (CWindowImplBase*)_Module.ExtractCreateWndData();
_ASSERTE(pT != NULL);
pT->m_hWnd = hWnd;
pT->m_thunk.Init(WindowProc, pT);
WNDPROC pProc = (WNDPROC)&(pT->m_thunk.thunk);
::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);
// if somebody has subclassed us already we can't do anything,
// so discard return value from SetWindowLong
return pProc(hWnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK CWindowImplBase::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
#ifdef _M_IX86
__asm mov dword ptr[hWnd], ecx
#endif
CWindowImplBase* pT = (CWindowImplBase*)hWnd;
LRESULT lRes;
BOOL bRet = pT->ProcessWindowMessage(pT->m_hWnd, uMsg, wParam, lParam, lRes, 0);
if(uMsg == WM_NCDESTROY)
pT->m_hWnd = NULL;
if(bRet)
return lRes;
return pT->DefWindowProc(uMsg, wParam, lParam);
}

HWND CWindowImplBase::Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName,
DWORD dwStyle, DWORD dwExStyle, UINT nID, ATOM atom)
{
_ASSERTE(m_hWnd == NULL);

if(atom == 0)
return NULL;

_Module.AddCreateWndData(&m_thunk.cd, this);

if(nID == 0 && (dwStyle & WS_CHILD))
nID = (UINT)this;

HWND hWnd = ::CreateWindowEx(dwExStyle, (LPCTSTR)MAKELONG(atom, 0), szWindowName,
dwStyle, rcPos.left, rcPos.top, rcPos.right - rcPos.left,
rcPos.bottom - rcPos.top, hWndParent, (HMENU)nID,
_Module.GetModuleInstance(), NULL);

_ASSERTE(m_hWnd == hWnd);

return hWnd;
}

BOOL CWindowImplBase::SubclassWindow(HWND hWnd)
{
_ASSERTE(m_hWnd == NULL);
_ASSERTE(::IsWindow(hWnd));
m_thunk.Init(WindowProc, this);
WNDPROC pProc = (WNDPROC)&(m_thunk.thunk);
WNDPROC pfnWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);
if(pfnWndProc == NULL)
return FALSE;
m_pfnSuperWindowProc = pfnWndProc;
m_hWnd = hWnd;
return TRUE;
}

HWND CWindowImplBase::UnsubclassWindow()
{
_ASSERTE(m_hWnd != NULL);

if(!::SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_pfnSuperWindowProc))
return NULL;

m_pfnSuperWindowProc = ::DefWindowProc;

HWND hWnd = m_hWnd;
m_hWnd = NULL;

return hWnd;
}

/////////////////////////////////////////////////////////////////////////////
// CDialogImplBase

LRESULT CALLBACK CDialogImplBase::StartDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CDialogImplBase* pT = (CDialogImplBase*)_Module.ExtractCreateWndData();
_ASSERTE(pT != NULL);
pT->m_hWnd = hWnd;
pT->m_thunk.Init(DialogProc, pT);
WNDPROC pProc = (WNDPROC)&(pT->m_thunk.thunk);
WNDPROC pOldProc = (WNDPROC)::SetWindowLong(hWnd, DWL_DLGPROC, (LONG)pProc);
// check if somebody has subclassed us already since we don't hold onto it
ATLTRACE(_T("Subclassing through a hook discarded.\n"));
return pProc(hWnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK CDialogImplBase::DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
#ifdef _M_IX86
__asm mov dword ptr[hWnd], ecx
#endif
CDialogImplBase* pT = (CDialogImplBase*)hWnd;
LRESULT lRes;
if(pT->ProcessWindowMessage(pT->m_hWnd, uMsg, wParam, lParam, lRes, 0))
{
switch (uMsg)
{
case WM_COMPAREITEM:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
case WM_INITDIALOG:
case WM_QUERYDRAGICON:
return lRes;
break;
}
if (lRes != 0)
::SetWindowLong(pT->m_hWnd, DWL_MSGRESULT, lRes);
return TRUE;
}
return FALSE;
}

BOOL CDialogImplBase::EndDialog(int nRetCode)
{
_ASSERTE(m_hWnd);
return ::EndDialog(m_hWnd, nRetCode);
}

/////////////////////////////////////////////////////////////////////////////
// CContainedWindow

LRESULT CALLBACK CContainedWindow::StartWindowProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
CContainedWindow* pThis = (CContainedWindow*)_Module.ExtractCreateWndData();
_ASSERTE(pThis != NULL);
pThis->m_hWnd = hWnd;
pThis->m_thunk.Init(pThis->WindowProc, pThis);
WNDPROC pProc = (WNDPROC)&(pThis->m_thunk.thunk);
WNDPROC pOldProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);
// check if somebody has subclassed us already since we don't hold onto it
ATLTRACE(_T("Subclassing through a hook discarded.\n"));
return pProc(hWnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK CContainedWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
#ifdef _M_IX86
__asm mov dword ptr[hWnd], ecx
#endif
CContainedWindow* pT = (CContainedWindow*)hWnd;
_ASSERTE(pT->m_hWnd != NULL);
_ASSERTE(pT->m_pObject != NULL);

LRESULT lRes;
BOOL bRet = pT->m_pObject->ProcessWindowMessage(pT->m_hWnd, uMsg, wParam, lParam, lRes, pT->m_dwMsgMapID);
if(uMsg == WM_NCDESTROY)
pT->m_hWnd = NULL;
if(bRet)
return lRes;
return pT->DefWindowProc(uMsg, wParam, lParam);
}

ATOM CContainedWindow::RegisterWndSuperclass()
{
ATOM atom = 0;
LPTSTR szBuff = (LPTSTR)_alloca((lstrlen(m_lpszClassName) + 14) * sizeof(TCHAR));

WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);

if(::GetClassInfoEx(NULL, m_lpszClassName, &wc))
{
m_pfnSuperWindowProc = wc.lpfnWndProc;

wsprintf(szBuff, _T("ATL:%s"), m_lpszClassName);

WNDCLASSEX wc1;
wc1.cbSize = sizeof(WNDCLASSEX);
atom = (ATOM)::GetClassInfoEx(_Module.GetModuleInstance(), szBuff, &wc1);

if(atom == 0)   // register class
{
wc.lpszClassName = szBuff;
wc.lpfnWndProc = StartWindowProc;
wc.hInstance = _Module.GetModuleInstance();

atom = ::RegisterClassEx(&wc);
}
}

return atom;
}

HWND CContainedWindow::Create(HWND hWndParent, RECT& rcPos,
LPCTSTR szWindowName, DWORD dwStyle, DWORD dwExStyle, UINT nID)
{
_ASSERTE(m_hWnd == NULL);

ATOM atom = RegisterWndSuperclass();
if(atom == 0)
return NULL;

_Module.AddCreateWndData(&m_thunk.cd, this);

if(nID == 0 && (dwStyle & WS_CHILD))
nID = (UINT)this;

HWND hWnd = ::CreateWindowEx(dwExStyle, (LPCTSTR)MAKELONG(atom, 0), szWindowName,
dwStyle,
rcPos.left, rcPos.top,
rcPos.right - rcPos.left,
rcPos.bottom - rcPos.top,
hWndParent, (HMENU)nID,
_Module.GetModuleInstance(), this);

_ASSERTE(m_hWnd == hWnd);
return hWnd;
}

BOOL CContainedWindow::SubclassWindow(HWND hWnd)
{
_ASSERTE(m_hWnd == NULL);
_ASSERTE(::IsWindow(hWnd));

m_thunk.Init(WindowProc, this);
WNDPROC pProc = (WNDPROC)&m_thunk.thunk;
WNDPROC pfnWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);
if(pfnWndProc == NULL)
return FALSE;
m_pfnSuperWindowProc = pfnWndProc;
m_hWnd = hWnd;
return TRUE;
}

HWND CContainedWindow::UnsubclassWindow()
{
_ASSERTE(m_hWnd != NULL);

if(!::SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_pfnSuperWindowProc))
return NULL;
m_pfnSuperWindowProc = ::DefWindowProc;

HWND hWnd = m_hWnd;
m_hWnd = NULL;

return hWnd;
}

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

///////////////////////////////////////////////////////////////////////////////
//All Global stuff goes below this line
///////////////////////////////////////////////////////////////////////////////


ATLWIN.H

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLWIN_H__
#define __ATLWIN_H__

#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLBASE_H__
#error atlwin.h requires atlbase.h to be included first
#endif

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

/////////////////////////////////////////////////////////////////////////////
// Forward declarations

class CWindow;
class CMessageMap;
class CDynamicChain;
class CWndClassInfo;
template <class T> class CWindowImpl;
template <class T> class CDialogImpl;
class CContainedWindow;

/////////////////////////////////////////////////////////////////////////////
// CWindow - client side for a Windows window

class CWindow
{
public:
HWND m_hWnd;

CWindow(HWND hWnd = NULL)
{
m_hWnd = hWnd;
}

CWindow& operator=(HWND hWnd)
{
m_hWnd = hWnd;
return *this;
}

void Attach(HWND hWndNew)
{
_ASSERTE(::IsWindow(hWndNew));
m_hWnd = hWndNew;
}

HWND Detach()
{
HWND hWnd = m_hWnd;
m_hWnd = NULL;
return hWnd;
}

BOOL DestroyWindow()
{
_ASSERTE(::IsWindow(m_hWnd));

if(!::DestroyWindow(m_hWnd))
return FALSE;

m_hWnd = NULL;
return TRUE;
}

// Attributes

operator HWND() const { return m_hWnd; }

DWORD GetStyle() const
{
_ASSERTE(::IsWindow(m_hWnd));
return (DWORD)::GetWindowLong(m_hWnd, GWL_STYLE);
}

DWORD GetExStyle() const
{
_ASSERTE(::IsWindow(m_hWnd));
return (DWORD)::GetWindowLong(m_hWnd, GWL_EXSTYLE);
}

BOOL ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0);
BOOL ModifyStyleEx(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0);

LONG GetWindowLong(int nIndex) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetWindowLong(m_hWnd, nIndex);
}

LONG SetWindowLong(int nIndex, LONG dwNewLong)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetWindowLong(m_hWnd, nIndex, dwNewLong);
}

WORD GetWindowWord(int nIndex) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetWindowWord(m_hWnd, nIndex);
}

WORD SetWindowWord(int nIndex, WORD wNewWord)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetWindowWord(m_hWnd, nIndex, wNewWord);
}

// Message Functions

LRESULT SendMessage(UINT message, WPARAM wParam = 0, LPARAM lParam = 0)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SendMessage(m_hWnd,message,wParam,lParam);
}

BOOL PostMessage(UINT message, WPARAM wParam = 0, LPARAM lParam = 0)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::PostMessage(m_hWnd,message,wParam,lParam);
}

BOOL SendNotifyMessage(UINT message, WPARAM wParam = 0, LPARAM lParam = 0)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SendNotifyMessage(m_hWnd, message, wParam, lParam);
}

// Window Text Functions

BOOL SetWindowText(LPCTSTR lpszString)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetWindowText(m_hWnd, lpszString);
}

int GetWindowText(LPTSTR lpszStringBuf, int nMaxCount) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetWindowText(m_hWnd, lpszStringBuf, nMaxCount);
}

int GetWindowTextLength() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetWindowTextLength(m_hWnd);
}

BOOL GetWindowText(BSTR& bstrText);

// Font Functions

void SetFont(HFONT hFont, BOOL bRedraw = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
::SendMessage(m_hWnd, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw, 0));
}

HFONT GetFont() const
{
_ASSERTE(::IsWindow(m_hWnd));
return (HFONT)::SendMessage(m_hWnd, WM_GETFONT, 0, 0);
}

// Menu Functions (non-child windows only)

HMENU GetMenu() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetMenu(m_hWnd);
}

BOOL SetMenu(HMENU hMenu)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetMenu(m_hWnd, hMenu);
}

BOOL DrawMenuBar()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::DrawMenuBar(m_hWnd);
}

HMENU GetSystemMenu(BOOL bRevert) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetSystemMenu(m_hWnd, bRevert);
}

BOOL HiliteMenuItem(HMENU hMenu, UINT uItemHilite, UINT uHilite)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::HiliteMenuItem(m_hWnd, hMenu, uItemHilite, uHilite);
}

// Window Size and Position Functions

BOOL IsIconic() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::IsIconic(m_hWnd);
}

BOOL IsZoomed() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::IsZoomed(m_hWnd);
}

BOOL MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint);
}

BOOL MoveWindow(LPCRECT lpRect, BOOL bRepaint = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::MoveWindow(m_hWnd, lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, bRepaint);
}

BOOL SetWindowPos(HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetWindowPos(m_hWnd, hWndInsertAfter, x, y, cx, cy, nFlags);
}

BOOL SetWindowPos(HWND hWndInsertAfter, LPCRECT lpRect, UINT nFlags)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetWindowPos(m_hWnd, hWndInsertAfter, lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, nFlags);
}

UINT ArrangeIconicWindows()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ArrangeIconicWindows(m_hWnd);
}

BOOL BringWindowToTop()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::BringWindowToTop(m_hWnd);
}

BOOL GetWindowRect(LPRECT lpRect) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetWindowRect(m_hWnd, lpRect);
}

BOOL GetClientRect(LPRECT lpRect) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetClientRect(m_hWnd, lpRect);
}

BOOL GetWindowPlacement(WINDOWPLACEMENT FAR* lpwndpl) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetWindowPlacement(m_hWnd, lpwndpl);
}

BOOL SetWindowPlacement(const WINDOWPLACEMENT FAR* lpwndpl)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetWindowPlacement(m_hWnd, lpwndpl);
}

// Coordinate Mapping Functions

BOOL ClientToScreen(LPPOINT lpPoint) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ClientToScreen(m_hWnd, lpPoint);
}

BOOL ClientToScreen(LPRECT lpRect) const
{
_ASSERTE(::IsWindow(m_hWnd));
if(!::ClientToScreen(m_hWnd, (LPPOINT)lpRect))
return FALSE;
return ::ClientToScreen(m_hWnd, ((LPPOINT)lpRect)+1);
}

BOOL ScreenToClient(LPPOINT lpPoint) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ScreenToClient(m_hWnd, lpPoint);
}

BOOL ScreenToClient(LPRECT lpRect) const
{
_ASSERTE(::IsWindow(m_hWnd));
if(!::ScreenToClient(m_hWnd, (LPPOINT)lpRect))
return FALSE;
return ::ScreenToClient(m_hWnd, ((LPPOINT)lpRect)+1);
}

int MapWindowPoints(HWND hWndTo, LPPOINT lpPoint, UINT nCount) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::MapWindowPoints(m_hWnd, hWndTo, lpPoint, nCount);
}

int MapWindowPoints(HWND hWndTo, LPRECT lpRect) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::MapWindowPoints(m_hWnd, hWndTo, (LPPOINT)lpRect, 2);
}

// Update and Painting Functions

HDC BeginPaint(LPPAINTSTRUCT lpPaint)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::BeginPaint(m_hWnd, lpPaint);
}

void EndPaint(LPPAINTSTRUCT lpPaint)
{
_ASSERTE(::IsWindow(m_hWnd));
::EndPaint(m_hWnd, lpPaint);
}

HDC GetDC()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetDC(m_hWnd);
}

HDC GetWindowDC()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetWindowDC(m_hWnd);
}

int ReleaseDC(HDC hDC)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ReleaseDC(m_hWnd, hDC);
}

void Print(HDC hDC, DWORD dwFlags) const
{
_ASSERTE(::IsWindow(m_hWnd));
::SendMessage(m_hWnd, WM_PRINT, (WPARAM)hDC, dwFlags);
}

void PrintClient(HDC hDC, DWORD dwFlags) const
{
_ASSERTE(::IsWindow(m_hWnd));
::SendMessage(m_hWnd, WM_PRINTCLIENT, (WPARAM)hDC, dwFlags);
}

BOOL UpdateWindow()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::UpdateWindow(m_hWnd);
}

void SetRedraw(BOOL bRedraw = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
::SendMessage(m_hWnd, WM_SETREDRAW, (WPARAM)bRedraw, 0);
}

BOOL GetUpdateRect(LPRECT lpRect, BOOL bErase = FALSE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetUpdateRect(m_hWnd, lpRect, bErase);
}

int GetUpdateRgn(HRGN hRgn, BOOL bErase = FALSE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetUpdateRgn(m_hWnd, hRgn, bErase);
}

BOOL Invalidate(BOOL bErase = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::InvalidateRect(m_hWnd, NULL, bErase);
}

BOOL InvalidateRect(LPCRECT lpRect, BOOL bErase = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::InvalidateRect(m_hWnd, lpRect, bErase);
}

void InvalidateRgn(HRGN hRgn, BOOL bErase = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
::InvalidateRgn(m_hWnd, hRgn, bErase);
}

BOOL ValidateRect(LPCRECT lpRect)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ValidateRect(m_hWnd, lpRect);
}

BOOL ValidateRgn(HRGN hRgn)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ValidateRgn(m_hWnd, hRgn);
}

BOOL ShowWindow(int nCmdShow)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ShowWindow(m_hWnd, nCmdShow);
}

BOOL IsWindowVisible() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::IsWindowVisible(m_hWnd);
}

BOOL ShowOwnedPopups(BOOL bShow = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ShowOwnedPopups(m_hWnd, bShow);
}

HDC GetDCEx(HRGN hRgnClip, DWORD flags)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetDCEx(m_hWnd, hRgnClip, flags);
}

BOOL LockWindowUpdate(BOOL bLock = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::LockWindowUpdate(bLock ? m_hWnd : NULL);
}

BOOL RedrawWindow(LPCRECT lpRectUpdate = NULL, HRGN hRgnUpdate = NULL, UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::RedrawWindow(m_hWnd, lpRectUpdate, hRgnUpdate, flags);
}

// Timer Functions

UINT SetTimer(UINT nIDEvent, UINT nElapse, void (CALLBACK* lpfnTimer)(HWND, UINT, UINT, DWORD))
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetTimer(m_hWnd, nIDEvent, nElapse, (TIMERPROC)lpfnTimer);
}

BOOL KillTimer(UINT nIDEvent)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::KillTimer(m_hWnd, nIDEvent);
}

// Window State Functions

BOOL IsWindowEnabled() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::IsWindowEnabled(m_hWnd);
}

BOOL EnableWindow(BOOL bEnable = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::EnableWindow(m_hWnd, bEnable);
}

HWND SetActiveWindow()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetActiveWindow(m_hWnd);
}

HWND SetCapture()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetCapture(m_hWnd);
}

HWND SetFocus()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetFocus(m_hWnd);
}

// Dialog-Box Item Functions

BOOL CheckDlgButton(int nIDButton, UINT nCheck)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::CheckDlgButton(m_hWnd, nIDButton, nCheck);
}

BOOL CheckRadioButton(int nIDFirstButton, int nIDLastButton, int nIDCheckButton)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::CheckRadioButton(m_hWnd, nIDFirstButton, nIDLastButton, nIDCheckButton);
}

int DlgDirList(LPTSTR lpPathSpec, int nIDListBox, int nIDStaticPath, UINT nFileType)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::DlgDirList(m_hWnd, lpPathSpec, nIDListBox, nIDStaticPath, nFileType);
}

int DlgDirListComboBox(LPTSTR lpPathSpec, int nIDComboBox, int nIDStaticPath, UINT nFileType)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::DlgDirListComboBox(m_hWnd, lpPathSpec, nIDComboBox, nIDStaticPath, nFileType);
}

BOOL DlgDirSelect(LPTSTR lpString, int nCount, int nIDListBox)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::DlgDirSelectEx(m_hWnd, lpString, nCount, nIDListBox);
}

BOOL DlgDirSelectComboBox(LPTSTR lpString, int nCount, int nIDComboBox)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::DlgDirSelectComboBoxEx(m_hWnd, lpString, nCount, nIDComboBox);
}

UINT GetDlgItemInt(int nID, BOOL* lpTrans = NULL, BOOL bSigned = TRUE) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetDlgItemInt(m_hWnd, nID, lpTrans, bSigned);
}

UINT GetDlgItemText(int nID, LPTSTR lpStr, int nMaxCount) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetDlgItemText(m_hWnd, nID, lpStr, nMaxCount);
}

BOOL GetDlgItemText(int nID, BSTR& bstrText) const
{
_ASSERTE(::IsWindow(m_hWnd));

HWND hWndCtl = GetDlgItem(nID);
if(hWndCtl == NULL)
return FALSE;

return CWindow(hWndCtl).GetWindowText(bstrText);
}

HWND GetNextDlgGroupItem(HWND hWndCtl, BOOL bPrevious = FALSE) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetNextDlgGroupItem(m_hWnd, hWndCtl, bPrevious);
}

HWND GetNextDlgTabItem(HWND hWndCtl, BOOL bPrevious = FALSE) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetNextDlgTabItem(m_hWnd, hWndCtl, bPrevious);
}

UINT IsDlgButtonChecked(int nIDButton) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::IsDlgButtonChecked(m_hWnd, nIDButton);
}

LRESULT SendDlgItemMessage(int nID, UINT message, WPARAM wParam = 0, LPARAM lParam = 0)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SendDlgItemMessage(m_hWnd, nID, message, wParam, lParam);
}

BOOL SetDlgItemInt(int nID, UINT nValue, BOOL bSigned = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetDlgItemInt(m_hWnd, nID, nValue, bSigned);
}

BOOL SetDlgItemText(int nID, LPCTSTR lpszString)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetDlgItemText(m_hWnd, nID, lpszString);
}

// Scrolling Functions

int GetScrollPos(int nBar) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetScrollPos(m_hWnd, nBar);
}

BOOL GetScrollRange(int nBar, LPINT lpMinPos, LPINT lpMaxPos) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetScrollRange(m_hWnd, nBar, lpMinPos, lpMaxPos);
}

BOOL ScrollWindow(int xAmount, int yAmount, LPCRECT lpRect = NULL, LPCRECT lpClipRect = NULL)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ScrollWindow(m_hWnd, xAmount, yAmount, lpRect, lpClipRect);
}

int ScrollWindowEx(int dx, int dy, LPCRECT lpRectScroll, LPCRECT lpRectClip, HRGN hRgnUpdate, LPRECT lpRectUpdate, UINT flags)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ScrollWindowEx(m_hWnd, dx, dy, lpRectScroll, lpRectClip, hRgnUpdate, lpRectUpdate, flags);
}

int SetScrollPos(int nBar, int nPos, BOOL bRedraw = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetScrollPos(m_hWnd, nBar, nPos, bRedraw);
}

BOOL SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetScrollRange(m_hWnd, nBar, nMinPos, nMaxPos, bRedraw);
}

BOOL ShowScrollBar(UINT nBar, BOOL bShow = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ShowScrollBar(m_hWnd, nBar, bShow);
}

BOOL EnableScrollBar(UINT uSBFlags, UINT uArrowFlags = ESB_ENABLE_BOTH)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::EnableScrollBar(m_hWnd, uSBFlags, uArrowFlags);
}

// Window Access Functions

HWND ChildWindowFromPoint(POINT point) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ChildWindowFromPoint(m_hWnd, point);
}

HWND ChildWindowFromPointEx(POINT point, UINT uFlags) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ChildWindowFromPointEx(m_hWnd, point, uFlags);
}

HWND GetTopWindow() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetTopWindow(m_hWnd);
}

HWND GetWindow(UINT nCmd) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetWindow(m_hWnd, nCmd);
}

HWND GetLastActivePopup() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetLastActivePopup(m_hWnd);
}

BOOL IsChild(HWND hWnd) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::IsChild(m_hWnd, hWnd);
}

HWND GetParent() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetParent(m_hWnd);
}

HWND SetParent(HWND hWndNewParent)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetParent(m_hWnd, hWndNewParent);
}

// Window Tree Access

int GetDlgCtrlID() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetDlgCtrlID(m_hWnd);
}

int SetDlgCtrlID(int nID)
{
_ASSERTE(::IsWindow(m_hWnd));
return (int)::SetWindowLong(m_hWnd, GWL_ID, nID);
}

HWND GetDlgItem(int nID) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetDlgItem(m_hWnd, nID);
}

HWND GetDescendantWindow(int nID) const;

void SendMessageToDescendants(UINT message, WPARAM wParam = 0, LPARAM lParam = 0, BOOL bDeep = TRUE);

// Alert Functions

BOOL FlashWindow(BOOL bInvert)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::FlashWindow(m_hWnd, bInvert);
}

int MessageBox(LPCTSTR lpszText, LPCTSTR lpszCaption = NULL, UINT nType = MB_OK)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::MessageBox(m_hWnd, lpszText, lpszCaption, nType);
}

// Clipboard Functions

BOOL ChangeClipboardChain(HWND hWndNewNext)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ChangeClipboardChain(m_hWnd, hWndNewNext);
}

HWND SetClipboardViewer()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetClipboardViewer(m_hWnd);
}

BOOL OpenClipboard()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::OpenClipboard(m_hWnd);
}

// Caret Functions

BOOL CreateCaret(HBITMAP hBitmap)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::CreateCaret(m_hWnd, hBitmap, 0, 0);
}

BOOL CreateSolidCaret(int nWidth, int nHeight)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::CreateCaret(m_hWnd, (HBITMAP)0, nWidth, nHeight);
}

BOOL CreateGrayCaret(int nWidth, int nHeight)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::CreateCaret(m_hWnd, (HBITMAP)1, nWidth, nHeight);
}

BOOL HideCaret()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::HideCaret(m_hWnd);
}

BOOL ShowCaret()
{
_ASSERTE(::IsWindow(m_hWnd));
return ::ShowCaret(m_hWnd);
}

// Drag-Drop Functions

void DragAcceptFiles(BOOL bAccept = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd)); ::DragAcceptFiles(m_hWnd, bAccept);
}

// Icon Functions

HICON SetIcon(HICON hIcon, BOOL bBigIcon = TRUE)
{
_ASSERTE(::IsWindow(m_hWnd));
return (HICON)::SendMessage(m_hWnd, WM_SETICON, bBigIcon, (LPARAM)hIcon);
}

HICON GetIcon(BOOL bBigIcon = TRUE) const
{
_ASSERTE(::IsWindow(m_hWnd));
return (HICON)::SendMessage(m_hWnd, WM_GETICON, bBigIcon, 0);
}

// Help Functions

BOOL WinHelp(LPCTSTR lpszHelp, UINT nCmd = HELP_CONTEXT, DWORD dwData = 0)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::WinHelp(m_hWnd, lpszHelp, nCmd, dwData);
}

BOOL SetWindowContextHelpId(DWORD dwContextHelpId)
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SetWindowContextHelpId(m_hWnd, dwContextHelpId);
}

DWORD GetWindowContextHelpId() const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::GetWindowContextHelpId(m_hWnd);
}

// Hot Key Functions

int SetHotKey(WORD wVirtualKeyCode, WORD wModifiers)
{
_ASSERTE(::IsWindow(m_hWnd));
return (int)::SendMessage(m_hWnd, WM_SETHOTKEY, MAKEWORD(wVirtualKeyCode, wModifiers), 0);
}

DWORD GetHotKey(WORD& /* wVirtualKeyCode */, WORD& /* wModifiers */) const
{
_ASSERTE(::IsWindow(m_hWnd));
return ::SendMessage(m_hWnd, WM_GETHOTKEY, 0, 0);
}

// Misc. Operations

BOOL CenterWindow(HWND hWndCenter = NULL);

HWND GetTopLevelParent() const;
HWND GetTopLevelWindow() const;
};

/////////////////////////////////////////////////////////////////////////////
// WindowProc thunks

#if defined(_M_PPC)
#error PPC not yet implemented
#elif defined (_M_ALPHA)
// For ALPHA we will stick the this pointer into a0, which is where
// the HWND is.  However, we don't actually need the HWND so this is OK.
#pragma pack(push,4)
struct _WndProcThunk //this should come out to 20 bytes
{
DWORD ldah_at;      //  ldah    at, HIWORD(func)
DWORD ldah_a0;      //  ldah    a0, HIWORD(this)
DWORD lda_at;       //  lda     at, LOWORD(func)(at)
DWORD lda_a0;       //  lda     a0, LOWORD(this)(a0)
DWORD jmp;          //  jmp     zero,(at),0
};
#pragma pack(pop)
#elif defined (_M_IX86)
#pragma pack(push,1)
struct _WndProcThunk
{
BYTE    m_mov;          // mov ecx, pThis
DWORD   m_this;         //
BYTE    m_jmp;          // jmp WndProc
DWORD   m_relproc;      // relative jmp
};
#pragma pack(pop)
#else
#error Only PPC, ALPHA, and X86 supported
#endif

class CWndProcThunk
{
public:
union
{
_AtlCreateWndData cd;
_WndProcThunk thunk;
};
void Init(WNDPROC proc, void* pThis);
};

/////////////////////////////////////////////////////////////////////////////
// CMessageMap - abstract class that provides an interface for message maps

class ATL_NO_VTABLE CMessageMap
{
public:
virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
LRESULT& lResult, DWORD dwMsgMapID) = 0;
};

/////////////////////////////////////////////////////////////////////////////
// Message map

#define BEGIN_MSG_MAP(theClass) \
public: \
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \
{ \
BOOL bHandled = TRUE; \
hWnd; \
switch(dwMsgMapID) \
{ \
case 0:

#define ALT_MSG_MAP(msgMapID) \
break; \
case msgMapID:

#define MESSAGE_HANDLER(msg, func) \
if(uMsg == msg) \
{ \
bHandled = TRUE; \
lResult = func(uMsg, wParam, lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define MESSAGE_RANGE_HANDLER(msgFirst, msgLast, func) \
if(uMsg >= msgFirst && uMsg <= msgLast) \
{ \
bHandled = TRUE; \
lResult = func(uMsg, wParam, lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define COMMAND_HANDLER(id, code, func) \
if(uMsg == WM_COMMAND && id == LOWORD(wParam) && code == HIWORD(wParam)) \
{ \
bHandled = TRUE; \
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define COMMAND_ID_HANDLER(id, func) \
if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \
{ \
bHandled = TRUE; \
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define COMMAND_CODE_HANDLER(code, func) \
if(uMsg == WM_COMMAND && code == HIWORD(wParam)) \
{ \
bHandled = TRUE; \
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define COMMAND_RANGE_HANDLER(idFirst, idLast, func) \
if(uMsg == WM_COMMAND && LOWORD(wParam) >= idFirst  && LOWORD(wParam) <= idLast) \
{ \
bHandled = TRUE; \
lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define NOTIFY_HANDLER(id, cd, func) \
if(uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom && cd == ((LPNMHDR)lParam)->code) \
{ \
bHandled = TRUE; \
lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define NOTIFY_ID_HANDLER(id, func) \
if(uMsg == WM_NOTIFY && id == ((LPNMHDR)lParam)->idFrom) \
{ \
bHandled = TRUE; \
lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define NOTIFY_CODE_HANDLER(cd, func) \
if(uMsg == WM_NOTIFY && cd == ((LPNMHDR)lParam)->code) \
{ \
bHandled = TRUE; \
lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define NOTIFY_RANGE_HANDLER(idFirst, idLast, func) \
if(uMsg == WM_NOTIFY && ((LPNMHDR)lParam)->idFrom >= idFirst && ((LPNMHDR)lParam)->idFrom <= idLast) \
{ \
bHandled = TRUE; \
lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \
if(bHandled) \
return TRUE; \
}

#define CHAIN_MSG_MAP(theChainClass) \
{ \
if(theChainClass::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) \
return TRUE; \
}

#define CHAIN_MSG_MAP_MEMBER(theChainMember) \
{ \
if(theChainMember.ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) \
return TRUE; \
}

#define CHAIN_MSG_MAP_ALT(theChainClass, msgMapID) \
{ \
if(theChainClass::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, msgMapID)) \
return TRUE; \
}

#define CHAIN_MSG_MAP_ALT_MEMBER(theChainMember, msgMapID) \
{ \
if(theChainMember.ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, msgMapID)) \
return TRUE; \
}

#define CHAIN_MSG_MAP_DYNAMIC(dynaChainID) \
{ \
if(CDynamicChain::CallChain(dynaChainID, hWnd, uMsg, wParam, lParam, lResult)) \
return TRUE; \
}

#define CHAIN_MSG_MAP_ALT_DYNAMIC(dynaChainID, msgMapID) \
{ \
if(CDynamicChain::CallChain(dynaChainID, hWnd, uMsg, wParam, lParam, lResult, msgMapID)) \
return TRUE; \
}

#define END_MSG_MAP() \
break; \
default: \
ATLTRACE(_T("Invalid message map ID (%i)\n"), dwMsgMapID); \
_ASSERTE(FALSE); \
break; \
} \
return FALSE; \
}

// Handler prototypes:
//  LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
//  LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
//  LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);

/////////////////////////////////////////////////////////////////////////////
// CDynamicChain - provides support for dynamic chaining

class CDynamicChain
{
public:
struct ATL_CHAIN_ENTRY
{
DWORD m_dwChainID;
CMessageMap* m_pObject;
DWORD m_dwMsgMapID;
};

int m_nEntries;
ATL_CHAIN_ENTRY** m_pChainEntry;

CDynamicChain() : m_nEntries(0), m_pChainEntry(NULL)
{ }

~CDynamicChain();
BOOL SetChainEntry(DWORD dwChainID, CMessageMap* pObject, DWORD dwMsgMapID = 0);
BOOL RemoveChainEntry(DWORD dwChainID);
BOOL CallChain(DWORD dwChainID, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult);
};

/////////////////////////////////////////////////////////////////////////////
// CWndClassInfo - Manages Windows class information

class CWndClassInfo
{
public:
WNDCLASSEX m_wc;
LPCTSTR m_lpszOrigName;
WNDPROC pWndProc;
LPCTSTR m_lpszCursorID;
BOOL m_bSystemCursor;
ATOM m_atom;
TCHAR m_szAutoName[13];
ATOM Register(WNDPROC*);
};

#define DECLARE_WND_CLASS(WndClassName) \
static CWndClassInfo& GetWndClassInfo() \
{ \
static CWndClassInfo wc = \
{ \
{ sizeof(WNDCLASSEX), CS_HREDRAW|CS_VREDRAW, StartWindowProc, \
  0, 0, 0, 0, 0, (HBRUSH)(COLOR_WINDOW+1), 0, WndClassName, 0 }, \
NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \
}; \
return wc; \
}

#define DECLARE_WND_SUPERCLASS(WndClassName, OrigWndClassName) \
static CWndClassInfo& GetWndClassInfo() \
{ \
static CWndClassInfo wc = \
{ \
{ sizeof(WNDCLASSEX), NULL, StartWindowProc, \
  0, 0, 0, 0, 0, NULL, 0, WndClassName, 0 }, \
OrigWndClassName, NULL, NULL, TRUE, 0, _T("") \
}; \
return wc; \
}

/////////////////////////////////////////////////////////////////////////////
// CWindowImpl - Implements a window

class ATL_NO_VTABLE CWindowImplBase : public CWindow, public CMessageMap
{
public:
CWndProcThunk m_thunk;
WNDPROC m_pfnSuperWindowProc;

CWindowImplBase() : m_pfnSuperWindowProc(::DefWindowProc)
{}

static LRESULT CALLBACK StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName,

DWORD dwStyle, DWORD dwExStyle, UINT nID, ATOM atom);
BOOL SubclassWindow(HWND hWnd);
HWND UnsubclassWindow();

LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
#ifdef STRICT
return ::CallWindowProc(m_pfnSuperWindowProc, m_hWnd, uMsg, wParam, lParam);
#else
return ::CallWindowProc((FARPROC)m_pfnSuperWindowProc, m_hWnd, uMsg, wParam, lParam);
#endif
}
};

template <class T>
class ATL_NO_VTABLE CWindowImpl : public CWindowImplBase
{
public:
DECLARE_WND_CLASS(NULL)

HWND Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL,
DWORD dwStyle = WS_CHILD | WS_VISIBLE, DWORD dwExStyle = 0,
UINT nID = 0)
{
ATOM atom = T::GetWndClassInfo().Register(&m_pfnSuperWindowProc);
return CWindowImplBase::Create(hWndParent, rcPos, szWindowName, dwStyle, dwExStyle,
nID, atom);
}
};

/////////////////////////////////////////////////////////////////////////////
// CDialog - Implements a dialog box

class ATL_NO_VTABLE CDialogImplBase : public CWindow, public CMessageMap
{
public:
CWndProcThunk m_thunk;

static LRESULT CALLBACK StartDialogProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL EndDialog(int nRetCode);
};

template <class T>
class ATL_NO_VTABLE CDialogImpl : public CDialogImplBase
{
public:
int DoModal(HWND hWndParent = ::GetActiveWindow())
{
_ASSERTE(m_hWnd == NULL);
_Module.AddCreateWndData(&m_thunk.cd, (CDialogImplBase*)this);
int nRet = ::DialogBoxParam(_Module.GetResourceInstance(),
MAKEINTRESOURCE(T::IDD),
hWndParent,
(DLGPROC)T::StartDialogProc,
NULL);
m_hWnd = NULL;
return nRet;
}

HWND Create(HWND hWndParent)
{
_ASSERTE(m_hWnd == NULL);
_Module.AddCreateWndData(&m_thunk.cd, (CDialogImplBase*)this);
HWND hWnd = ::CreateDialogParam(_Module.GetResourceInstance(),
MAKEINTRESOURCE(T::IDD),
hWndParent,
(DLGPROC)T::StartDialogProc,
NULL);
_ASSERTE(m_hWnd == hWnd);
return hWnd;
}
};

/////////////////////////////////////////////////////////////////////////////
// CContainedWindow - Implements a contained window

class CContainedWindow : public CWindow
{
public:
CWndProcThunk m_thunk;
LPTSTR m_lpszClassName;
WNDPROC m_pfnSuperWindowProc;
CMessageMap* m_pObject;
DWORD m_dwMsgMapID;

CContainedWindow(LPTSTR lpszClassName, CMessageMap* pObject, DWORD dwMsgMapID = 0)
: m_lpszClassName(lpszClassName),
m_pfnSuperWindowProc(::DefWindowProc),
m_pObject(pObject), m_dwMsgMapID(dwMsgMapID)
{ }

void SwitchMessageMap(DWORD dwMsgMapID)
{
m_dwMsgMapID = dwMsgMapID;
}

static LRESULT CALLBACK StartWindowProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
ATOM RegisterWndSuperclass();
HWND Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL,
DWORD dwStyle = WS_CHILD | WS_VISIBLE, DWORD dwExStyle = 0,
UINT nID = 0);
BOOL SubclassWindow(HWND hWnd);
HWND UnsubclassWindow();

LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
#ifdef STRICT
return ::CallWindowProc(m_pfnSuperWindowProc, m_hWnd, uMsg, wParam, lParam);
#else
return ::CallWindowProc((FARPROC)m_pfnSuperWindowProc, m_hWnd, uMsg, wParam, lParam);
#endif
}
};

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

#endif // __ATLWIN_H__


BUILD_.H

// ATL Julian date build numbers (YDDD)

#define _ATL_BUILD 7024
#define _ATL_USER_BUILD "7024"
#define _ATL_PRODUCT_VERSION 5,0,0,0
#define _ATL_FILE_VERSION 2,00
#define _ATL_USER_PRODVER "5.00.0000"
#define _ATL_USER_FILEVER "2.00."


STATREG.CPP

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

///////////////////////////////////////
#define RET_ON_ERROR(x) \
if (FAILED(hr = x))\
return hr;
///////////////////////////////////////
#define BREAK_ON_ERROR(x) \
if (FAILED(hr = x))\
break;
///////////////////////////////////////
#ifdef _DEBUG
#define REPORT_ERROR(name, func) \
if (func != ERROR_SUCCESS)\
ATLTRACE(_T("NON CRITICAL ERROR : %s failed\n"), name);
#define REG_TRACE_RECOVER() \
if (!bRecover) \
ATLTRACE(_T("Opened Key %s\n"), szToken); \
else \
ATLTRACE(_T("Ignoring Open key on %s : In Recovery mode\n"), szToken);
#else //!_DEBUG
#define REG_TRACE_RECOVER()
#define REPORT_ERROR(name, func) \
func;
#endif //_DEBUG

///////////////////////////////////////
#define MAX_TYPE            MAX_VALUE
#define MAX_VALUE           4096

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

class CParseBuffer
{
public:
int nPos;
int nSize;
LPTSTR p;
CParseBuffer(int nInitial);
~CParseBuffer() {CoTaskMemFree(p);}
BOOL AddChar(TCHAR ch);
BOOL AddString(LPCOLESTR lpsz);
LPTSTR Detach();

};

LPCTSTR   rgszNeverDelete[] = //Component Catagories
{
_T("CLSID"), _T("TYPELIB")
};

const int   cbNeverDelete = sizeof(rgszNeverDelete) / sizeof(LPCTSTR*);

static LPTSTR StrChr(LPTSTR lpsz, TCHAR ch)
{
LPTSTR p = NULL;
while (*lpsz)
{
if (*lpsz == ch)
{
p = lpsz;
break;
}
lpsz = CharNext(lpsz);
}
return p;
}

static HKEY WINAPI HKeyFromString(LPTSTR szToken)
{
struct keymap
{
LPCTSTR lpsz;
HKEY hkey;
};
static const keymap map[] = {
{_T("HKCR"), HKEY_CLASSES_ROOT},
{_T("HKCU"), HKEY_CURRENT_USER},
{_T("HKLM"), HKEY_LOCAL_MACHINE},
{_T("HKU"),  HKEY_USERS},
{_T("HKPD"), HKEY_PERFORMANCE_DATA},
{_T("HKDD"), HKEY_DYN_DATA},
{_T("HKCC"), HKEY_CURRENT_CONFIG},
{_T("HKEY_CLASSES_ROOT"), HKEY_CLASSES_ROOT},
{_T("HKEY_CURRENT_USER"), HKEY_CURRENT_USER},
{_T("HKEY_LOCAL_MACHINE"), HKEY_LOCAL_MACHINE},
{_T("HKEY_USERS"), HKEY_USERS},
{_T("HKEY_PERFORMANCE_DATA"), HKEY_PERFORMANCE_DATA},
{_T("HKEY_DYN_DATA"), HKEY_DYN_DATA},
{_T("HKEY_CURRENT_CONFIG"), HKEY_CURRENT_CONFIG}
};

for (int i=0;i<sizeof(map)/sizeof(keymap);i++)
{
if (!lstrcmpi(szToken, map[i].lpsz))
return map[i].hkey;
}
return NULL;
}

static HKEY HKeyFromCompoundString(LPTSTR szToken, LPTSTR& szTail)
{
if (NULL == szToken)
return NULL;

LPTSTR lpsz = StrChr(szToken, chDirSep);

if (NULL == lpsz)
return NULL;

szTail = CharNext(lpsz);
*lpsz = chEOS;
HKEY hKey = HKeyFromString(szToken);
*lpsz = chDirSep;
return hKey;
}

static LPVOID QueryValue(HKEY hKey, LPCTSTR szValName, DWORD& dwType)
{
DWORD dwCount = 0;

if (RegQueryValueEx(hKey, szValName, NULL, &dwType, NULL, &dwCount) != ERROR_SUCCESS)
{
ATLTRACE(_T("RegQueryValueEx failed for Value %s\n"), szValName);
return NULL;
}

if (!dwCount)
{
ATLTRACE(_T("RegQueryValueEx returned 0 bytes\n"));
return NULL;
}

// Not going to Check for fail on CoTaskMemAlloc & RegQueryValueEx as NULL
// will be returned regardless if anything failed

LPVOID pData = CoTaskMemAlloc(dwCount);
RegQueryValueEx(hKey, szValName, NULL, &dwType, (LPBYTE) pData, &dwCount);
return pData;
}

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

HRESULT CRegParser::GenerateError(UINT nID)
{
//  m_re.m_nID   = nID;
//  m_re.m_cLines = m_cLines;
return DISP_E_EXCEPTION;
}

CRegParser::CRegParser(CRegObject* pRegObj)
{
m_pRegObj           = pRegObj;
m_pchCur            = NULL;
m_cLines            = 1;
}

BOOL CRegParser::IsSpace(TCHAR ch)
{
switch (ch)
{
case chSpace:
case chTab:
case chCR:
case chLF:
return TRUE;
}

return FALSE;
}

void CRegParser::IncrementLinePos()
{
m_pchCur = CharNext(m_pchCur);
if (chLF == *m_pchCur)
IncrementLineCount();
}

void CRegParser::SkipWhiteSpace()
{
while(IsSpace(*m_pchCur))
IncrementLinePos();
}

HRESULT CRegParser::NextToken(LPTSTR szToken)
{
USES_CONVERSION;

UINT ichToken = 0;

SkipWhiteSpace();

// NextToken cannot be called at EOS
if (chEOS == *m_pchCur)
return GenerateError(E_ATL_UNEXPECTED_EOS);

// handle quoted value / key
if (chQuote == *m_pchCur)
{
LPCTSTR szOrig = szToken;

IncrementLinePos(); // Skip Quote

while (chEOS != *m_pchCur && !EndOfVar())
{
if (chQuote == *m_pchCur) // If it is a quote that means we must skip it
IncrementLinePos();   // as it has been escaped

LPTSTR pchPrev = m_pchCur;
IncrementLinePos();

if (szToken + sizeof(WORD) >= MAX_VALUE + szOrig)
return GenerateError(E_ATL_VALUE_TOO_LARGE);
for (int i = 0; pchPrev+i < m_pchCur; i++, szToken++)
*szToken = *(pchPrev+i);
}

if (chEOS == *m_pchCur)
{
ATLTRACE(_T("NextToken : Unexpected End of File\n"));
return GenerateError(E_ATL_UNEXPECTED_EOS);
}

*szToken = chEOS;
IncrementLinePos(); // Skip end quote
}

else
{   // Handle non-quoted ie parse up till first "White Space"
while (chEOS != *m_pchCur && !IsSpace(*m_pchCur))
{
LPTSTR pchPrev = m_pchCur;
IncrementLinePos();
for (int i = 0; pchPrev+i < m_pchCur; i++, szToken++)
*szToken = *(pchPrev+i);
}

*szToken = chEOS;
}
return S_OK;
}

static BOOL VTFromRegType(LPCTSTR szValueType, VARTYPE& vt)
{
struct typemap
{
LPCTSTR lpsz;
VARTYPE vt;
};
static const typemap map[] = {
{szStringVal, VT_BSTR},
{szDwordVal,  VT_I4}
};

for (int i=0;i<sizeof(map)/sizeof(typemap);i++)
{
if (!lstrcmpi(szValueType, map[i].lpsz))
{
vt = map[i].vt;
return TRUE;
}
}

return FALSE;

}

HRESULT CRegParser::AddValue(CRegKey& rkParent,LPCTSTR szValueName, LPTSTR szToken)
{
USES_CONVERSION;
HRESULT hr;

TCHAR       szTypeToken[MAX_TYPE];
VARTYPE     vt;
LONG        lRes = ERROR_SUCCESS;
UINT        nIDRes = 0;

RET_ON_ERROR(NextToken(szTypeToken))
if (!VTFromRegType(szTypeToken, vt))
{
ATLTRACE(_T("%s Type not supported\n"), szTypeToken);
return GenerateError(E_ATL_TYPE_NOT_SUPPORTED);
}

TCHAR szValue[MAX_VALUE];
SkipWhiteSpace();
RET_ON_ERROR(NextToken(szValue));
long lVal;

switch (vt)
{
case VT_BSTR:
lRes = rkParent.SetValue(szValue, szValueName);
ATLTRACE(_T("Setting Value %s at %s\n"), szValue, !szValueName ? _T("default") : szValueName);
break;
case VT_I4:
VarI4FromStr(T2OLE(szValue), 0, 0, &lVal);
lRes = rkParent.SetValue(lVal, szValueName);
ATLTRACE(_T("Setting Value %d at %s\n"), lVal, !szValueName ? _T("default") : szValueName);
break;
}

if (ERROR_SUCCESS != lRes)
{
nIDRes = E_ATL_VALUE_SET_FAILED;
hr = HRESULT_FROM_WIN32(lRes);
}

RET_ON_ERROR(NextToken(szToken))

return S_OK;
}

BOOL CRegParser::CanForceRemoveKey(LPCTSTR szKey)
{
for (int iNoDel = 0; iNoDel < cbNeverDelete; iNoDel++)
if (!lstrcmpi(szKey, rgszNeverDelete[iNoDel]))
 return FALSE;                       // We cannot delete it

return TRUE;
}

BOOL CRegParser::HasSubKeys(HKEY hkey)
{
DWORD       cbSubKeys = 0;

if (FAILED(RegQueryInfoKey(hkey, NULL, NULL, NULL,
   &cbSubKeys, NULL, NULL,
   NULL, NULL, NULL, NULL, NULL)))
{
ATLTRACE(_T("Should not be here!!\n"));
_ASSERTE(FALSE);
return FALSE;
}

return cbSubKeys > 0;
}

BOOL CRegParser::HasValues(HKEY hkey)
{
DWORD       cbValues = 0;

LONG lResult = RegQueryInfoKey(hkey, NULL, NULL, NULL,
  NULL, NULL, NULL,
  &cbValues, NULL, NULL, NULL, NULL);
if (ERROR_SUCCESS != lResult)
{
ATLTRACE(_T("RegQueryInfoKey Failed "));
_ASSERTE(FALSE);
return FALSE;
}

if (1 == cbValues)
{
DWORD cbData = 0;
lResult = RegQueryValueEx(hkey, NULL, NULL, NULL, NULL, &cbData);

if (ERROR_SUCCESS == lResult)
return !cbData;
else
return TRUE;
}

return cbValues > 0;
}

HRESULT CRegParser::SkipAssignment(LPTSTR szToken)
{
HRESULT hr;
TCHAR szValue[MAX_VALUE];

if (*szToken == chEquals)
{
RET_ON_ERROR(NextToken(szToken))
// Skip assignment
SkipWhiteSpace();
RET_ON_ERROR(NextToken(szValue));
RET_ON_ERROR(NextToken(szToken))
}

return S_OK;
}

HRESULT CRegParser::RegisterSubkeys(HKEY hkParent, BOOL bRegister, BOOL bRecover)
{
CRegKey keyCur;
TCHAR   szToken[MAX_VALUE];
LONG    lRes;
TCHAR   szKey[MAX_VALUE];
BOOL    bDelete = TRUE;
BOOL    bInRecovery = bRecover;
HRESULT hr = S_OK;

ATLTRACE(_T("Num Els = %d\n"), cbNeverDelete);
RET_ON_ERROR(NextToken(szToken))  // Should be key name

while (*szToken != chRightBracket) // Continue till we see a }
{
BOOL bTokenDelete = !lstrcmpi(szToken, szDelete);

if (!lstrcmpi(szToken, szForceRemove) || bTokenDelete)
{
BREAK_ON_ERROR(NextToken(szToken))

if (bRegister)
{
CRegKey rkForceRemove;

if (StrChr(szToken, chDirSep) != NULL)
return GenerateError(E_ATL_COMPOUND_KEY);

if (CanForceRemoveKey(szToken))
{
rkForceRemove.Attach(hkParent);
rkForceRemove.RecurseDeleteKey(szToken);
rkForceRemove.Detach();
}
if (bTokenDelete)
{
BREAK_ON_ERROR(NextToken(szToken))
BREAK_ON_ERROR(SkipAssignment(szToken))
goto EndCheck;
}
}

}

if (!lstrcmpi(szToken, szNoRemove))
{
bDelete = FALSE;    // set even for register
BREAK_ON_ERROR(NextToken(szToken))
}

if (!lstrcmpi(szToken, szValToken)) // need to add a value to hkParent
{
TCHAR  szValueName[_MAX_PATH];

BREAK_ON_ERROR(NextToken(szValueName))
BREAK_ON_ERROR(NextToken(szToken))

if (*szToken != chEquals)
return GenerateError(E_ATL_EXPECTING_EQUAL);

if (bRegister)
{
CRegKey rk;

rk.Attach(hkParent);
hr = AddValue(rk, szValueName, szToken);
rk.Detach();

if (FAILED(hr))
return hr;

goto EndCheck;
}
else
{
if (!bRecover)
{
ATLTRACE(_T("Deleting %s\n"), szValueName);
REPORT_ERROR(_T("RegDeleteValue"), RegDeleteValue(hkParent, szValueName))
}

BREAK_ON_ERROR(SkipAssignment(szToken)) // Strip off type
continue;  // can never have a subkey
}
}

if (StrChr(szToken, chDirSep) != NULL)
return GenerateError(E_ATL_COMPOUND_KEY);

if (bRegister)
{
lRes = keyCur.Open(hkParent, szToken, KEY_ALL_ACCESS);
if (ERROR_SUCCESS != lRes)
{
// Failed all access try read only
lRes = keyCur.Open(hkParent, szToken, KEY_READ);
if (ERROR_SUCCESS != lRes)
{
// Finally try creating it
ATLTRACE(_T("Creating key %s\n"), szToken);
lRes = keyCur.Create(hkParent, szToken);
if (ERROR_SUCCESS != lRes)
return GenerateError(E_ATL_CREATE_KEY_FAILED);
}
}

BREAK_ON_ERROR(NextToken(szToken))

if (*szToken == chEquals)
BREAK_ON_ERROR(AddValue(keyCur, NULL, szToken)) // NULL == default
}
else
{
if (!bRecover && keyCur.Open(hkParent, szToken) != ERROR_SUCCESS)
bRecover = TRUE;

// TRACE out Key open status and if in recovery mode
REG_TRACE_RECOVER()

// Remember Subkey
lstrcpyn(szKey, szToken, _MAX_PATH);

// If in recovery mode

if (bRecover || HasSubKeys(keyCur) || HasValues(keyCur))
{
BREAK_ON_ERROR(NextToken(szToken))
BREAK_ON_ERROR(SkipAssignment(szToken))

if (*szToken == chLeftBracket)
{
BREAK_ON_ERROR(RegisterSubkeys(keyCur.m_hKey, bRegister, bRecover))
if (bRecover) // Turn off recovery if we are done
{
bRecover = bInRecovery;
ATLTRACE(_T("Ending Recovery Mode\n"));
BREAK_ON_ERROR(NextToken(szToken))
BREAK_ON_ERROR(SkipAssignment(szToken))
continue;
}
}

if (!bRecover && HasSubKeys(keyCur))
{
// See if the KEY is in the NeverDelete list and if so, don't
if (CanForceRemoveKey(szKey))
{
ATLTRACE(_T("Deleting non-empty subkey %s by force\n"), szKey);
REPORT_ERROR(_T("RecurseDeleteKey"), keyCur.RecurseDeleteKey(szKey))
}
BREAK_ON_ERROR(NextToken(szToken))
continue;
}

if (bRecover)
continue;
}

if (!bRecover && keyCur.Close() != ERROR_SUCCESS)
   return GenerateError(E_ATL_CLOSE_KEY_FAILED);

if (!bRecover && bDelete)
{
ATLTRACE(_T("Deleting Key %s\n"), szKey);
REPORT_ERROR(_T("RegDeleteKey"), RegDeleteKey(hkParent, szKey))
}

BREAK_ON_ERROR(NextToken(szToken))
BREAK_ON_ERROR(SkipAssignment(szToken))
}

EndCheck:

if (bRegister)
{
if (*szToken == chLeftBracket)
{
BREAK_ON_ERROR(RegisterSubkeys(keyCur.m_hKey, bRegister, FALSE))
BREAK_ON_ERROR(NextToken(szToken))
}
}
}

return hr;
}

LPTSTR CParseBuffer::Detach()
{
LPTSTR lp = p;
p = NULL;
return lp;
}

CParseBuffer::CParseBuffer(int nInitial)
{
nPos = 0;
nSize = nInitial;
p = (LPTSTR) CoTaskMemAlloc(nSize*sizeof(TCHAR));
}

BOOL CParseBuffer::AddString(LPCOLESTR lpsz)
{
USES_CONVERSION;
LPCTSTR lpszT = OLE2CT(lpsz);
while (*lpszT)
{
AddChar(*lpszT);
lpszT++;
}
return TRUE;
}

BOOL CParseBuffer::AddChar(TCHAR ch)
{
if (nPos == nSize) // realloc
{
nSize *= 2;
p = (LPTSTR) CoTaskMemRealloc(p, nSize*sizeof(TCHAR));
}
p[nPos++] = ch;
return TRUE;
}

HRESULT CRegParser::PreProcessBuffer(LPTSTR lpszReg, LPTSTR* ppszReg)
{
USES_CONVERSION;
_ASSERTE(lpszReg != NULL);
_ASSERTE(ppszReg != NULL);
*ppszReg = NULL;
int nSize = lstrlen(lpszReg)*2;
CParseBuffer pb(nSize);
if (pb.p == NULL)
return E_OUTOFMEMORY;
m_pchCur = lpszReg;
HRESULT hr = S_OK;

while (*m_pchCur != NULL) // look for end
{
if (*m_pchCur == _T('%'))
{
IncrementLinePos();
if (*m_pchCur == _T('%'))
pb.AddChar(*m_pchCur);
else
{
LPTSTR lpszNext = StrChr(m_pchCur, _T('%'));
if (lpszNext == NULL)
{
ATLTRACE(_T("Error no closing % found\n"));
hr = GenerateError(E_ATL_UNEXPECTED_EOS);
break;
}
int nLength = lpszNext - m_pchCur;
if (nLength > 31)
{
hr = E_FAIL;
break;
}
TCHAR buf[32];
lstrcpyn(buf, m_pchCur, nLength+1);
LPCOLESTR lpszVar = m_pRegObj->StrFromMap(buf);
if (lpszVar == NULL)
{
hr = GenerateError(E_ATL_NOT_IN_MAP);
break;
}
pb.AddString(lpszVar);
while (m_pchCur != lpszNext)
IncrementLinePos();
}
}
else
pb.AddChar(*m_pchCur);
IncrementLinePos();
}
pb.AddChar(NULL);
if (SUCCEEDED(hr))
*ppszReg = pb.Detach();
return hr;
}

HRESULT CRegParser::RegisterBuffer(LPTSTR szBuffer, BOOL bRegister)
{
TCHAR   szToken[_MAX_PATH];
HRESULT hr = S_OK;

LPTSTR szReg;
hr = PreProcessBuffer(szBuffer, &szReg);
if (FAILED(hr))
return hr;

m_pchCur = szReg;

// Preprocess szReg

while (chEOS != *m_pchCur)
{
BREAK_ON_ERROR(NextToken(szToken))
HKEY hkBase;
if ((hkBase = HKeyFromString(szToken)) == NULL)
{
ATLTRACE(_T("HKeyFromString failed on %s\n"), szToken);
hr = GenerateError(E_ATL_BAD_HKEY);
break;
}

BREAK_ON_ERROR(NextToken(szToken))

if (chLeftBracket != *szToken)
{
ATLTRACE(_T("Syntax error, expecting a {, found a %s\n"), szToken);
hr = GenerateError(E_ATL_MISSING_OPENKEY_TOKEN);
break;
}
if (bRegister)
{
LPTSTR szRegAtRegister = m_pchCur;
hr = RegisterSubkeys(hkBase, bRegister);
if (FAILED(hr))
{
ATLTRACE(_T("Failed to register, cleaning up!\n"));
m_pchCur = szRegAtRegister;
RegisterSubkeys(hkBase, FALSE);
break;
}
}
else
{
BREAK_ON_ERROR(RegisterSubkeys(hkBase, bRegister))
}

SkipWhiteSpace();
}
CoTaskMemFree(szReg);
return hr;
}

HRESULT CExpansionVector::Add(LPCOLESTR lpszKey, LPCOLESTR lpszValue)
{
USES_CONVERSION;
HRESULT hr = S_OK;

EXPANDER* pExpand = NULL;
ATLTRY(pExpand = new EXPANDER);
if (pExpand == NULL)
return E_OUTOFMEMORY;

DWORD cbKey = (ocslen(lpszKey)+1)*sizeof(OLECHAR);
DWORD cbValue = (ocslen(lpszValue)+1)*sizeof(OLECHAR);
pExpand->szKey = (LPOLESTR)CoTaskMemAlloc(cbKey);
pExpand->szValue = (LPOLESTR)CoTaskMemAlloc(cbValue);
if (pExpand->szKey == NULL || pExpand->szValue == NULL)
{
CoTaskMemFree(pExpand->szKey);
CoTaskMemFree(pExpand->szValue);
delete pExpand;
return E_OUTOFMEMORY;
}
memcpy(pExpand->szKey, lpszKey, cbKey);
memcpy(pExpand->szValue, lpszValue, cbValue);

if (m_cEls == m_nSize)
{
m_nSize*=2;
m_p = (EXPANDER**)realloc(m_p, m_nSize*sizeof(EXPANDER*));
}

if (NULL != m_p)
{
m_p[m_cEls] = pExpand;
m_cEls++;
}
else
hr = E_OUTOFMEMORY;

return hr;

}

LPCOLESTR CExpansionVector::Find(LPTSTR lpszKey)
{
USES_CONVERSION;
for (int iExpand = 0; iExpand < m_cEls; iExpand++)
{
if (!lstrcmpi(OLE2T(m_p[iExpand]->szKey), lpszKey)) //are equal
return m_p[iExpand]->szValue;
}
return NULL;
}

HRESULT CExpansionVector::ClearReplacements()
{
for (int iExpand = 0; iExpand < m_cEls; iExpand++)
{
EXPANDER* pExp = m_p[iExpand];
CoTaskMemFree(pExp->szValue);
CoTaskMemFree(pExp->szKey);
delete pExp;
}
m_cEls = 0;
return S_OK;
}

HRESULT CRegObject::GenerateError(UINT nID)
{
//  re.m_nID    = nID;
//  re.m_cLines = -1;

return DISP_E_EXCEPTION;
}

HRESULT STDMETHODCALLTYPE CRegObject::AddReplacement(LPCOLESTR lpszKey, LPCOLESTR lpszItem)
{
m_csMap.Lock();
HRESULT hr = m_RepMap.Add(lpszKey, lpszItem);
m_csMap.Unlock();
return hr;
}

HRESULT CRegObject::RegisterFromResource(LPCOLESTR bstrFileName, LPCTSTR szID,
 LPCTSTR szType, BOOL bRegister)
{
USES_CONVERSION;

HRESULT     hr;
CRegParser  parser(this);
HINSTANCE   hInstResDll;
HRSRC       hrscReg;
HGLOBAL     hReg;
DWORD       dwSize;
LPSTR       szRegA;
LPTSTR      szReg;

hInstResDll = LoadLibraryEx(OLE2CT(bstrFileName), NULL, LOAD_LIBRARY_AS_DATAFILE);

if (NULL == hInstResDll)
{
ATLTRACE(_T("Failed to LoadLibrary on %s\n"), OLE2CT(bstrFileName));
hr = HRESULT_FROM_WIN32(GetLastError());
goto ReturnHR;
}

hrscReg = FindResource((HMODULE)hInstResDll, szID, szType);

if (NULL == hrscReg)
{
ATLTRACE(_T("Failed to FindResource on ID:%s TYPE:%s\n"), szID, szType);
hr = HRESULT_FROM_WIN32(GetLastError());
goto ReturnHR;
}

hReg = LoadResource((HMODULE)hInstResDll, hrscReg);

if (NULL == hReg)
{
ATLTRACE(_T("Failed to LoadResource \n"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto ReturnHR;
}

dwSize = SizeofResource((HMODULE)hInstResDll, hrscReg);
szRegA = (LPSTR)hReg;
if (szRegA[dwSize] != NULL)
{
szRegA = (LPSTR)_alloca(dwSize+1);
memcpy(szRegA, (void*)hReg, dwSize+1);
szRegA[dwSize] = NULL;
}

szReg = A2T(szRegA);

#ifdef _DEBUG
OutputDebugString(szReg); //would call ATLTRACE but szReg is > 512 bytes
OutputDebugString(_T("\n"));
#endif //_DEBUG

hr = parser.RegisterBuffer(szReg, bRegister);

ReturnHR:

if (NULL != hInstResDll)
FreeLibrary((HMODULE)hInstResDll);
return hr;
}

HRESULT STDMETHODCALLTYPE CRegObject::ResourceRegister(LPCOLESTR szFileName, UINT nID, LPCOLESTR szType)
{
USES_CONVERSION;
return RegisterFromResource(szFileName, MAKEINTRESOURCE(nID), OLE2CT(szType), TRUE);
}

HRESULT STDMETHODCALLTYPE CRegObject::ResourceRegisterSz(LPCOLESTR szFileName, LPCOLESTR szID, LPCOLESTR szType)
{
USES_CONVERSION;

if (szID == NULL || szType == NULL)
return E_INVALIDARG;

return RegisterFromResource(szFileName, OLE2CT(szID), OLE2CT(szType), TRUE);
}

HRESULT STDMETHODCALLTYPE CRegObject::ResourceUnregister(LPCOLESTR szFileName, UINT nID, LPCOLESTR szType)
{
USES_CONVERSION;
return RegisterFromResource(szFileName, MAKEINTRESOURCE(nID), OLE2CT(szType), FALSE);
}

HRESULT STDMETHODCALLTYPE CRegObject::ResourceUnregisterSz(LPCOLESTR szFileName, LPCOLESTR szID, LPCOLESTR szType)
{
USES_CONVERSION;
if (szID == NULL || szType == NULL)
return E_INVALIDARG;

return RegisterFromResource(szFileName, OLE2CT(szID), OLE2CT(szType), FALSE);
}

HRESULT CRegObject::RegisterWithString(LPCOLESTR bstrData, BOOL bRegister)
{
USES_CONVERSION;
CRegParser  parser(this);

LPCTSTR szReg = OLE2CT(bstrData);

#ifdef _DEBUG
OutputDebugString(szReg); //would call ATLTRACE but szReg is > 512 bytes
OutputDebugString(_T("\n"));
#endif //_DEBUG

HRESULT hr = parser.RegisterBuffer((LPTSTR)szReg, bRegister);

return hr;
}

HRESULT CRegObject::ClearReplacements()
{
m_csMap.Lock();
HRESULT hr = m_RepMap.ClearReplacements();
m_csMap.Unlock();
return hr;
}

LPCOLESTR CRegObject::StrFromMap(LPTSTR lpszKey)
{
m_csMap.Lock();
LPCOLESTR lpsz = m_RepMap.Find(lpszKey);
if (lpsz == NULL) // not found!!
ATLTRACE(_T("Map Entry not found\n"));
m_csMap.Unlock();
return lpsz;
}

HRESULT CRegObject::MemMapAndRegister(LPCOLESTR bstrFileName, BOOL bRegister)
{
USES_CONVERSION;

CRegParser  parser(this);

HANDLE hFile = CreateFile(OLE2CT(bstrFileName), GENERIC_READ, 0, NULL,
  OPEN_EXISTING,
  FILE_ATTRIBUTE_READONLY,
  NULL);

if (NULL == hFile)
{
ATLTRACE(_T("Failed to CreateFile on %s\n"), OLE2CT(bstrFileName));
return HRESULT_FROM_WIN32(GetLastError());
}

DWORD cbFile = GetFileSize(hFile, NULL); // No HiOrder DWORD required

HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);

if (NULL == hMapping)
{
ATLTRACE(_T("Failed to CreateFileMapping\n"));
return HRESULT_FROM_WIN32(GetLastError());
}

LPVOID pMap = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);

if (NULL == pMap)
{
ATLTRACE(_T("Failed to MapViewOfFile\n"));
return HRESULT_FROM_WIN32(GetLastError());
}

LPTSTR szReg = A2T((char*)pMap);

if (chEOS != szReg[cbFile]) //ensure buffer is NULL terminated
{
ATLTRACE(_T("ERROR : Bad or missing End of File\n"));
return E_FAIL; // make a real error
}

#ifdef _DEBUG
OutputDebugString(szReg); //would call ATLTRACE but szReg is > 512 bytes
OutputDebugString(_T("\n"));
#endif //_DEBUG

HRESULT hRes = parser.RegisterBuffer(szReg, bRegister);

//  if (FAILED(hRes))
//      re = parser.GetRegException();

UnmapViewOfFile(pMap);
CloseHandle(hMapping);
CloseHandle(hFile);

return hRes;
}

HRESULT STDMETHODCALLTYPE CRegObject::FileRegister(LPCOLESTR bstrFileName)
{
return MemMapAndRegister(bstrFileName, TRUE);
}

HRESULT STDMETHODCALLTYPE CRegObject::FileUnregister(LPCOLESTR bstrFileName)
{
return MemMapAndRegister(bstrFileName, FALSE);
}

HRESULT STDMETHODCALLTYPE CRegObject::StringRegister(LPCOLESTR bstrData)
{
return RegisterWithString(bstrData, TRUE);
}

HRESULT STDMETHODCALLTYPE CRegObject::StringUnregister(LPCOLESTR bstrData)
{
return RegisterWithString(bstrData, FALSE);
}

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif


STATREG.H

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __STATREG_H
#define __STATREG_H

#define E_ATL_REGISTRAR_DESC              0x0201
#define E_ATL_NOT_IN_MAP                  0x0202
#define E_ATL_UNEXPECTED_EOS              0x0203
#define E_ATL_VALUE_SET_FAILED            0x0204
#define E_ATL_RECURSE_DELETE_FAILED       0x0205
#define E_ATL_EXPECTING_EQUAL             0x0206
#define E_ATL_CREATE_KEY_FAILED           0x0207
#define E_ATL_DELETE_KEY_FAILED           0x0208
#define E_ATL_OPEN_KEY_FAILED             0x0209
#define E_ATL_CLOSE_KEY_FAILED            0x020A
#define E_ATL_UNABLE_TO_COERCE            0x020B
#define E_ATL_BAD_HKEY                    0x020C
#define E_ATL_MISSING_OPENKEY_TOKEN       0x020D
#define E_ATL_CONVERT_FAILED              0x020E
#define E_ATL_TYPE_NOT_SUPPORTED          0x020F
#define E_ATL_COULD_NOT_CONCAT            0x0210
#define E_ATL_COMPOUND_KEY                0x0211
#define E_ATL_INVALID_MAPKEY              0x0212
#define E_ATL_UNSUPPORTED_VT              0x0213
#define E_ATL_VALUE_GET_FAILED            0x0214
#define E_ATL_VALUE_TOO_LARGE             0x0215
#define E_ATL_MISSING_VALUE_DELIMETER     0x0216
#define E_ATL_DATA_NOT_BYTE_ALIGNED       0x0217

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

const TCHAR  chSemiColon         = _T(';');
const TCHAR  chDirSep            = _T('\\');
const TCHAR  chEscape            = _T('\\');
const TCHAR  chComma             = _T(',');
const TCHAR  chDelete            = _T('~');
const TCHAR  chEOS               = _T('\0');
const TCHAR  chTab               = _T('\t');
const TCHAR  chLF                = _T('\n');
const TCHAR  chCR                = _T('\r');
const TCHAR  chSpace             = _T(' ');
const TCHAR  chRightBracket      = _T('}');
const TCHAR  chLeftBracket       = _T('{');
const TCHAR  chVarLead           = _T('%');
const TCHAR  chQuote             = _T('\'');
const TCHAR  chEquals            = _T('=');
//const LPCTSTR  szRightBracket  = _T("}");
//const LPCTSTR  szLeftBracket   = _T("{");
//const LPCTSTR  szEquals            = _T("=");
//const LPCTSTR  szDirSep          = _T("\\");
const LPCTSTR  szStringVal       = _T("S");
const LPCTSTR  szDwordVal        = _T("D");
const LPCTSTR  szBinaryVal       = _T("B");
const LPCTSTR  szValToken        = _T("Val");
const LPCTSTR  szForceRemove     = _T("ForceRemove");
const LPCTSTR  szNoRemove        = _T("NoRemove");
const LPCTSTR  szDelete          = _T("Delete");

struct EXPANDER
{
LPOLESTR    szKey;
LPOLESTR    szValue;
};

class CExpansionVector
{
public:
CExpansionVector()
{
m_cEls = 0;
m_nSize=10;
m_p=(EXPANDER**)malloc(m_nSize*sizeof(EXPANDER*));
}
HRESULT Add(LPCOLESTR lpszKey, LPCOLESTR lpszValue);
LPCOLESTR Find(LPTSTR lpszKey);
HRESULT ClearReplacements();

private:
EXPANDER** m_p;
int m_cEls;
int m_nSize;
};

#if defined(_ATL_DLL) | defined(_ATL_DLL_IMPL)
class ATL_NO_VTABLE CRegObject
 : public IRegistrar
#else
class CRegObject
#endif
{
public:

~CRegObject(){ClearReplacements();}
HRESULT FinalConstruct() {return S_OK;}
void FinalRelease() {}

// Map based methods
HRESULT STDMETHODCALLTYPE AddReplacement(LPCOLESTR lpszKey, LPCOLESTR lpszItem);
HRESULT STDMETHODCALLTYPE ClearReplacements();
LPCOLESTR StrFromMap(LPTSTR lpszKey);

// Register via a given mechanism
HRESULT STDMETHODCALLTYPE ResourceRegister(LPCOLESTR pszFileName, UINT nID, LPCOLESTR pszType);
HRESULT STDMETHODCALLTYPE ResourceRegisterSz(LPCOLESTR pszFileName, LPCOLESTR pszID, LPCOLESTR pszType);
HRESULT STDMETHODCALLTYPE ResourceUnregister(LPCOLESTR pszFileName, UINT nID, LPCOLESTR pszType);
HRESULT STDMETHODCALLTYPE ResourceUnregisterSz(LPCOLESTR pszFileName, LPCOLESTR pszID, LPCOLESTR pszType);
HRESULT STDMETHODCALLTYPE FileRegister(LPCOLESTR pszFileName);
HRESULT STDMETHODCALLTYPE FileUnregister(LPCOLESTR pszFileName);
HRESULT STDMETHODCALLTYPE StringRegister(LPCOLESTR pszData);
HRESULT STDMETHODCALLTYPE StringUnregister(LPCOLESTR pszData);

protected:

HRESULT MemMapAndRegister(LPCOLESTR pszFileName, BOOL bRegister);
HRESULT RegisterFromResource(LPCOLESTR pszFileName, LPCTSTR pszID, LPCTSTR pszType, BOOL bRegister);
HRESULT RegisterWithString(LPCOLESTR pszData, BOOL bRegister);

static HRESULT GenerateError(UINT nID);

CExpansionVector                                m_RepMap;
CComObjectThreadModel::AutoCriticalSection      m_csMap;
};

class CRegParser
{
public:
CRegParser(CRegObject* pRegObj);

HRESULT  PreProcessBuffer(LPTSTR lpszReg, LPTSTR* ppszReg);
HRESULT  RegisterBuffer(LPTSTR szReg, BOOL bRegister);

protected:

void    SkipWhiteSpace();
HRESULT NextToken(LPTSTR szToken);
HRESULT AddValue(CRegKey& rkParent,LPCTSTR szValueName, LPTSTR szToken);
BOOL    CanForceRemoveKey(LPCTSTR szKey);
BOOL    HasSubKeys(HKEY hkey);
BOOL    HasValues(HKEY hkey);
HRESULT RegisterSubkeys(HKEY hkParent, BOOL bRegister, BOOL bInRecovery = FALSE);
BOOL    IsSpace(TCHAR ch);
void    IncrementLinePos();
void    IncrementLineCount(){m_cLines++;}

LPTSTR  m_pchCur;
int     m_cLines;

CRegObject*     m_pRegObj;

HRESULT GenerateError(UINT nID);
HRESULT HandleReplacements(LPTSTR& szToken);
HRESULT SkipAssignment(LPTSTR szToken);

BOOL    EndOfVar() { return chQuote == *m_pchCur && chQuote != *CharNext(m_pchCur); }

};

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

#endif //__STATREG_H


WINRES.H

// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

// winres.h - Windows resource definitions
//  extracted from WINUSER.H and COMMCTRL.H

#ifdef _AFX_MINREBUILD
#pragma component(minrebuild, off)
#endif

#define VS_VERSION_INFO     1

#ifdef APSTUDIO_INVOKED
#define APSTUDIO_HIDDEN_SYMBOLS // Ignore following symbols
#endif

#ifndef WINVER
#define WINVER 0x0400   // default to Windows Version 4.0
#endif

#include <winresrc.h>

#ifdef _MAC
#define DS_WINDOWSUI    0x8000L
#endif

// operation messages sent to DLGINIT
#define LB_ADDSTRING    (WM_USER+1)
#define CB_ADDSTRING    (WM_USER+3)

#ifdef APSTUDIO_INVOKED
#undef APSTUDIO_HIDDEN_SYMBOLS
#endif

#ifdef IDC_STATIC
#undef IDC_STATIC
#endif
#define IDC_STATIC      (-1)

#ifdef _AFX_MINREBUILD
#pragma component(minrebuild, on)
#endif


HTMLDLG.CPP

/**************************************************************************
   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.

   Copyright 1997 Microsoft Corporation.  All Rights Reserved.
**************************************************************************/

/**************************************************************************

   File:          HTMLDlg.cpp

**************************************************************************/

/**************************************************************************
   Include Files
**************************************************************************/

#include <windows.h>
#include <urlmon.h>
#include <mshtmhst.h>

#define ARRAYSIZE(a)    (sizeof(a)/sizeof((a)[0]))

int BSTRToLocal(LPTSTR pLocal, BSTR pWide, DWORD dwChars);
int LocalToBSTR(BSTR pWide, LPTSTR pLocal, DWORD dwChars);

/**************************************************************************

   WinMain()

**************************************************************************/

int PASCAL WinMain(  HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
HINSTANCE   hinstMSHTML = LoadLibrary(TEXT("MSHTML.DLL"));

if(hinstMSHTML)
   {
   SHOWHTMLDIALOGFN  *pfnShowHTMLDialog;

   pfnShowHTMLDialog = (SHOWHTMLDIALOGFN*)GetProcAddress(hinstMSHTML, TEXT("ShowHTMLDialog"));

   if(pfnShowHTMLDialog)
      {
      IMoniker *pmk;
      TCHAR    szTemp[MAX_PATH*2];
      OLECHAR  bstr[MAX_PATH*2];

      lstrcpy(szTemp, TEXT("res://"));
      GetModuleFileName(hInstance, szTemp + lstrlen(szTemp), ARRAYSIZE(szTemp) - lstrlen(szTemp));
      lstrcat(szTemp, TEXT("/HTML_RESOURCE"));

      LocalToBSTR(bstr, szTemp, ARRAYSIZE(bstr));

      CreateURLMoniker(NULL, bstr, &pmk);

      if(pmk)
         {
         HRESULT  hr;
         VARIANT  varArgs, varReturn;

         VariantInit(&varReturn);

         varArgs.vt = VT_BSTR;
         varArgs.bstrVal = SysAllocString(L"Argument 1;Argument 2;Argument 3;Argument 4");

         hr = (*pfnShowHTMLDialog)(NULL, pmk, &varArgs, NULL, &varReturn);

         VariantClear(&varArgs);

         pmk->Release();

         if(SUCCEEDED(hr))
            {
            switch(varReturn.vt)
               {
               case VT_BSTR:
                  {
                  TCHAR szData[MAX_PATH];

                  BSTRToLocal(szData, varReturn.bstrVal, ARRAYSIZE(szData));

                  wsprintf(szTemp, TEXT("The selected item was \"%s\"."), szData);

                  VariantClear(&varReturn);
                  }
                  break;

               default:
                  lstrcpy(szTemp, TEXT("Cancel was selected."));
                  break;
               }
            MessageBox(NULL, szTemp, TEXT("HTML Dialog Sample"), MB_OK | MB_ICONINFORMATION);
            }
         else
            MessageBox(NULL, TEXT("ShowHTMLDialog Failed."), TEXT("HTML Dialog Sample"), MB_OK | MB_ICONERROR);

         }
      }
   FreeLibrary(hinstMSHTML);
   }

return 0;
}

/**************************************************************************

   BSTRToLocal()

**************************************************************************/

int BSTRToLocal(LPTSTR pLocal, BSTR pWide, DWORD dwChars)
{
*pLocal = 0;

#ifdef UNICODE
lstrcpyn(pLocal, pWide, dwChars);
#else
WideCharToMultiByte( CP_ACP,
                     0,
                     pWide,
                     -1,
                     pLocal,
dwChars,
                     NULL,
                     NULL);
#endif

return lstrlen(pLocal);
}

/**************************************************************************

   LocalToBSTR()

**************************************************************************/

int LocalToBSTR(BSTR pWide, LPTSTR pLocal, DWORD dwChars)
{
*pWide = 0;

#ifdef UNICODE
lstrcpyn(pWide, pLocal, dwChars);
#else
MultiByteToWideChar( CP_ACP,
                     0,
                     pLocal,
                     -1,
                     pWide,
                     dwChars);
#endif

return lstrlenW(pWide);
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by HTMLDlg.rc
//

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        108
#define _APS_NEXT_COMMAND_VALUE         105
#define _APS_NEXT_CONTROL_VALUE         208
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


IELNK.CPP

// IELnk.cpp : Implementation of DLL Exports.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f IELnkps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "IELnk.h"

#include "IELnk_i.c"
#include "IELnkCtl.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_IELnkCtl, CIELnkCtl)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
_Module.UnregisterServer();
return S_OK;
}


IELNK.IDL

#include <olectl.h> // IELnk.idl : IDL source for IELnk.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (IELnk.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(51319FCF-F334-11D0-9DD5-30E803C10E27),
dual,
helpstring("IIELnkCtl Interface"),
pointer_default(unique)
]
interface IIELnkCtl : IDispatch
{
[propget, id(1), helpstring("property Flags")] HRESULT Flags([out, retval] long *pVal);
[propput, id(1), helpstring("property Flags")] HRESULT Flags([in] long newVal);
[propget, id(2), helpstring("property HLink")] HRESULT HLink([out, retval] BSTR *pVal);
[propput, id(2), helpstring("property HLink")] HRESULT HLink([in] BSTR newVal);
[propget, id(3), helpstring("property Location")] HRESULT Location([out, retval] BSTR *pVal);
[propput, id(3), helpstring("property Location")] HRESULT Location([in] BSTR newVal);
[propget, id(4), helpstring("property Mode")] HRESULT Mode([out, retval] long *pVal);
[propput, id(4), helpstring("property Mode")] HRESULT Mode([in] long newVal);
[propget, id(5), helpstring("property TargetFrame")] HRESULT TargetFrame([out, retval] BSTR *pVal);
[propput, id(5), helpstring("property TargetFrame")] HRESULT TargetFrame([in] BSTR newVal);
[id(6), helpstring("method Go")] HRESULT Go();
[id(7), helpstring("method Forward")] HRESULT Forward();
[id(8), helpstring("method Back")] HRESULT Back();
};
[
uuid(51319FC0-F334-11D0-9DD5-30E803C10E27),
version(1.0),
helpstring("IELnk 1.0 Type Library")
]
library IELNKLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

[
uuid(51319FD1-F334-11D0-9DD5-30E803C10E27),
helpstring("IELnkCtl Class")
]
coclass IELnkCtl
{
[default] interface IIELnkCtl;
};
};


IELNKCTL.CPP

// IELnkCtl.cpp : Implementation of CIELnkCtl
#include "stdafx.h"
#include "IELnk.h"
#include "IELnkCtl.h"
#include <hlink.h>

/////////////////////////////////////////////////////////////////////////////
// CIELnkCtl
STDMETHODIMP CIELnkCtl::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_IIELnkCtl,
};
for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}

HRESULT CIELnkCtl::OnDraw(ATL_DRAWINFO& di)
{
RECT& rc = *(RECT*)di.prcBounds;
HDC hdc  = di.hdcDraw;
HBRUSH    hOldBrush, hBrush;
    HPEN      hOldPen, hPen;

    hPen = (HPEN)GetStockObject(BLACK_PEN);
    hOldPen = (HPEN)SelectObject(hdc, hPen);
    hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
    hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);

Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);

#ifndef _UNICODE
if(m_pANSIHLink)
DrawText(hdc, m_pANSIHLink, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
else
DrawText(hdc, "Unable to Display URL", -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
#else
DrawText(hdc, m_strHLink, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
#endif

    SelectObject(hdc, hOldPen);
    SelectObject(hdc, hOldBrush);

return S_OK;
}

STDMETHODIMP CIELnkCtl::get_Flags(long * pVal)
{
*pVal = m_lFlags;

return S_OK;
}

STDMETHODIMP CIELnkCtl::put_Flags(long newVal)
{
if (newVal == 0 || newVal == 1)
{
m_lMode = newVal;

return S_OK;
}
else
return Error(_T("The navigation flag must be either 0 or 1."));
}

STDMETHODIMP CIELnkCtl::get_HLink(BSTR * pVal)
{
*pVal = m_strHLink;

return S_OK;
}

STDMETHODIMP CIELnkCtl::put_HLink(BSTR newVal)
{
m_strHLink = newVal;

#ifndef _UNICODE
unsigned int nURLLength = m_strHLink.Length();

if(m_pANSIHLink)
free(m_pANSIHLink);

// Adjust for the terminating NULL character when allocating space
m_pANSIHLink = (char *)calloc(nURLLength + 1, 1);

wcstombs(m_pANSIHLink, m_strHLink, nURLLength);
#endif

return S_OK;
}

STDMETHODIMP CIELnkCtl::get_Location(BSTR * pVal)
{
*pVal = m_strLocation;

return S_OK;
}

STDMETHODIMP CIELnkCtl::put_Location(BSTR newVal)
{
m_strLocation = newVal;

return S_OK;
}

STDMETHODIMP CIELnkCtl::get_Mode(long * pVal)
{
*pVal = m_lMode;

return S_OK;
}

STDMETHODIMP CIELnkCtl::put_Mode(long newVal)
{
if (newVal >= 0 && newVal <= 7)
{
m_lMode = newVal;

return S_OK;
}
else
return Error(_T("The Mode value must be in the range of 0 through 7."));
}

STDMETHODIMP CIELnkCtl::get_TargetFrame(BSTR * pVal)
{
*pVal = m_strTargetFrame;

return S_OK;
}

STDMETHODIMP CIELnkCtl::put_TargetFrame(BSTR newVal)
{
m_strTargetFrame = newVal;

return S_OK;
}

STDMETHODIMP CIELnkCtl::GetInterfaceSafetyOptions(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
{
ATLTRACE(_T("IObjectSafetyImpl::GetInterfaceSafetyOptions\n"));
if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL)
return E_POINTER;
HRESULT hr = S_OK;

if (riid == IID_IDispatch)
{
*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
*pdwEnabledOptions = m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
}
else if (riid == IID_IPersistPropertyBag)
{
*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
*pdwEnabledOptions = m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_DATA;
}
else
{
*pdwSupportedOptions = 0;
*pdwEnabledOptions = 0;
hr = E_NOINTERFACE;
}
return hr;
}

STDMETHODIMP CIELnkCtl::SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
{
ATLTRACE(_T("IObjectSafetyImpl::SetInterfaceSafetyOptions\n"));

// If we're being asked to set our safe for scripting or
// safe for initialization options then oblige
if (riid == IID_IDispatch || riid == IID_IPersistPropertyBag)
{
// Store our current safety level to return in GetInterfaceSafetyOptions
m_dwSafety = dwEnabledOptions & dwOptionSetMask;
return S_OK;
}

return E_NOINTERFACE;
}

STDMETHODIMP CIELnkCtl::Go()
{
int nNavFlag;
HRESULT hr;

// m_lFlags values:
//
// 0 - Navigate in a new window
// 1 - Navigate in the current window

if( m_lFlags == 0 )
nNavFlag = HLNF_OPENINNEWWINDOW;
else if( m_lFlags == 1)
nNavFlag = HLNF_INTERNALJUMP;
else
return Error(_T("The navigation flag must be 0 or 1."));

switch (m_lMode)
{
case 0 :
hr = HlinkSimpleNavigateToString(
!m_strHLink ? NULL : (BSTR)m_strHLink,
!m_strLocation ? NULL : (BSTR)m_strLocation,
!m_strTargetFrame ? NULL : (BSTR)m_strTargetFrame,
GetUnknown(),
NULL,
NULL,
nNavFlag,
0);
break;

case 1 :
HlinkSimpleNavigateToString(
!m_strHLink ? NULL : (BSTR)m_strHLink,
!m_strLocation ? NULL : (BSTR)m_strLocation,
!m_strTargetFrame ? NULL : (BSTR)m_strTargetFrame,
NULL,
NULL,
NULL,
nNavFlag,
0);
break;

case 2 :
HlinkNavigateString(
GetUnknown(),
!m_strHLink ? NULL : (BSTR)m_strHLink);
break;

case 3 :
HlinkNavigateString(
NULL,
!m_strHLink ? NULL : (BSTR)m_strHLink);
break;

case 4 :
{
IMoniker *pmTarget;

::CreateURLMoniker(
0,
!m_strHLink ? NULL : (BSTR)m_strHLink,
&pmTarget);

HlinkSimpleNavigateToMoniker(
pmTarget,
!m_strLocation ? NULL : (BSTR)m_strLocation,
!m_strTargetFrame ? NULL : (BSTR)m_strTargetFrame,
GetUnknown(),
NULL,
NULL,
nNavFlag,
0);
}
break;

case 5 :
{
IMoniker *pmTarget;

::CreateURLMoniker(
0,
!m_strHLink ? NULL : (BSTR)m_strHLink,
&pmTarget);

HlinkSimpleNavigateToMoniker(
pmTarget,
!m_strLocation ? NULL : (BSTR)m_strLocation,
!m_strTargetFrame ? NULL : (BSTR)m_strTargetFrame,
NULL,
NULL,
NULL,
nNavFlag,
0);
}
break;

case 6 :
{
IMoniker *pmTarget;

::CreateURLMoniker(
0,
!m_strHLink ? NULL : (BSTR)m_strHLink,
&pmTarget);

HlinkNavigateMoniker(
GetUnknown(),
pmTarget);
}
break;

case 7 :
{
IMoniker *pmTarget;

::CreateURLMoniker(
0,
!m_strHLink ? NULL : (BSTR)m_strHLink,
&pmTarget);

HlinkNavigateMoniker(
NULL,
pmTarget);
}
break;

default:
return Error(_T("The Mode value must be in the range of 0 through 7."));
}

return S_OK;
}

STDMETHODIMP CIELnkCtl::Forward()
{
HlinkGoForward(GetUnknown());

return S_OK;
}

STDMETHODIMP CIELnkCtl::Back()
{
HlinkGoBack(GetUnknown());

return S_OK;
}


IELNKCTL.H

// IELnkCtl.h : Declaration of the CIELnkCtl

#ifndef __IELNKCTL_H_
#define __IELNKCTL_H_

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// CIELnkCtl
class ATL_NO_VTABLE CIELnkCtl :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CIELnkCtl, &CLSID_IELnkCtl>,
public CComControl<CIELnkCtl>,
public IDispatchImpl<IIELnkCtl, &IID_IIELnkCtl, &LIBID_IELNKLib>,
public IProvideClassInfo2Impl<&CLSID_IELnkCtl, NULL, &LIBID_IELNKLib>,
public IPersistStreamInitImpl<CIELnkCtl>,
public IPersistStorageImpl<CIELnkCtl>,
public IQuickActivateImpl<CIELnkCtl>,
public IOleControlImpl<CIELnkCtl>,
public IOleObjectImpl<CIELnkCtl>,
public IOleInPlaceActiveObjectImpl<CIELnkCtl>,
public IViewObjectExImpl<CIELnkCtl>,
public IOleInPlaceObjectWindowlessImpl<CIELnkCtl>,
public IDataObjectImpl<CIELnkCtl>,
public ISupportErrorInfo,
public ISpecifyPropertyPagesImpl<CIELnkCtl>,
public IPersistPropertyBagImpl<CIELnkCtl>,
public IObjectSafetyImpl<CIELnkCtl>
{
public:
CIELnkCtl()
{
#ifndef _UNICODE
m_pANSIHLink = NULL;
#endif

m_lFlags = 1;
m_lMode = 0;
}

~CIELnkCtl()
{
#ifndef _UNICODE
if(m_pANSIHLink)
free(m_pANSIHLink);
#endif
}

DECLARE_REGISTRY_RESOURCEID(IDR_IELNKCTL)

BEGIN_COM_MAP(CIELnkCtl)
COM_INTERFACE_ENTRY(IIELnkCtl)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY_IMPL(IOleControl)
COM_INTERFACE_ENTRY_IMPL(IOleObject)
COM_INTERFACE_ENTRY_IMPL(IQuickActivate)
COM_INTERFACE_ENTRY_IMPL(IPersistStorage)
COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
COM_INTERFACE_ENTRY_IMPL(ISpecifyPropertyPages)
COM_INTERFACE_ENTRY_IMPL(IDataObject)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY_IMPL(IPersistPropertyBag)
COM_INTERFACE_ENTRY_IMPL(IObjectSafety)
END_COM_MAP()

BEGIN_PROPERTY_MAP(CIELnkCtl)
// Example entries
// PROP_ENTRY("Property Description", dispid, clsid)
PROP_ENTRY("Flags", 1, CLSID_NULL)
PROP_ENTRY("HLink", 2, CLSID_NULL)
PROP_ENTRY("Location", 3, CLSID_NULL)
PROP_ENTRY("Mode", 4, CLSID_NULL)
PROP_ENTRY("TargetFrame", 5, CLSID_NULL)
PROP_PAGE(CLSID_StockColorPage)
END_PROPERTY_MAP()

BEGIN_MSG_MAP(CIELnkCtl)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
END_MSG_MAP()

// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IViewObjectEx
STDMETHOD(GetViewStatus)(DWORD* pdwStatus)
{
ATLTRACE(_T("IViewObjectExImpl::GetViewStatus\n"));
*pdwStatus = VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE;
return S_OK;
}

// IIELnkCtl
public:
STDMETHOD(Back)();
STDMETHOD(Forward)();
STDMETHOD(Go)();
STDMETHOD(get_TargetFrame)(/*[out, retval]*/ BSTR *pVal);
STDMETHOD(put_TargetFrame)(/*[in]*/ BSTR newVal);
STDMETHOD(get_Mode)(/*[out, retval]*/ long *pVal);
STDMETHOD(put_Mode)(/*[in]*/ long newVal);
STDMETHOD(get_Location)(/*[out, retval]*/ BSTR *pVal);
STDMETHOD(put_Location)(/*[in]*/ BSTR newVal);
STDMETHOD(get_HLink)(/*[out, retval]*/ BSTR *pVal);
STDMETHOD(put_HLink)(/*[in]*/ BSTR newVal);
STDMETHOD(get_Flags)(/*[out, retval]*/ long *pVal);
STDMETHOD(put_Flags)(/*[in]*/ long newVal);
HRESULT OnDraw(ATL_DRAWINFO& di);

STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions);
STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions);

private:

CComBSTR m_strHLink;
CComBSTR m_strLocation;
CComBSTR m_strTargetFrame;
long  m_lFlags;
long  m_lMode;

#ifndef _UNICODE
char * m_pANSIHLink;
#endif

};

#endif //__IELNKCTL_H_


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by IELnk.rc
//
#define IDS_PROJNAME                    100
#define IDR_IELNKCTL                    101

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           102
#endif
#endif


STDAFX.H

// stdafx.h : include file for standard system include files,
//      or project specific include files that are used frequently,
//      but are changed infrequently

#if !defined(AFX_STDAFX_H__51319FC4_F334_11D0_9DD5_30E803C10E27__INCLUDED_)
#define AFX_STDAFX_H__51319FC4_F334_11D0_9DD5_30E803C10E27__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#define STRICT

#define _WIN32_WINNT 0x0400
#define _ATL_APARTMENT_THREADED

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>
#include <atlctl.h>
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__51319FC4_F334_11D0_9DD5_30E803C10E27__INCLUDED)


README.TXT

IEMime Sample
=============

IEMime demonstrates MIME type registration and MIME type recognition of downloads.

The control starts an asynchronous download, usually by asking its
bind-host to MonikerBindToStorage on its behalf. During the download proces, IEMime
is notified of the MIME type on its bind-status-callback. IEMime displays this MIME type.

Main points of interest:

* For purposes of demonstration, IEMime uses a somewhat atypical scheme for
  downloading during initialization. Typically, a control which downloads asynchronous
  data based on a data-path property (URL) would proceed with the download as soon
  as it possibly can. The ReadyState property would then be used to signal to MSHTML as
  the control container that the control was done downloading whatever data it needed
  asynchronously. MSHTML would continue to show progress and UI such as the spinning
  icon until the control signalled READYSTATE_COMPLETE. In most cases, any events
  signalling the download would happen before the page was ready to receive them. In
  the case of IEMime, this would cause the Complete() event to not fire on page-startup.

  Instead, IEMime waits until the container is totally ready to receive events before
  starting its download. That way, script can catch Complete() even for downloads kicked off
  by <PARAM URL=> persistent properties. However, this gets us into a deadlocked
  situation of sorts -- the container is waiting for our control to signal complete
  while we're waiting for the container to go complete. We avoid this problem by
  signalling "COMPLETE" as soon as our properties are loaded and then transitioning back to
  "INTERACTIVE" once we start our asynchronous download. This solution is acceptable --
  it is okay for a control to change ReadyState back to LOADED or INTERACTIVE while it
  is re-downloading a data file or re-evaluating a query, for example. We always go back
  to INTERACTIVE when doing an asynchronous download.

  The traditional means for a control to know when its container is up and running and
  can receive events is to watch IOleControl::FreezeEvents until the freeze count
  transitions from 1 to 0. This is where IEMime kicks off the download by calling Reload().

* IEMime adds to the basic ATL class CBindStatusCallback by adding some extra overrides.
  IEMime's CBindStatusCallback2 passes the FORMATETC and STGMEDIUM on to the using class
  (CIEMimeCtl) during OnData, because these structures are what we are really interested in.
  The original CBindStatusCallback did not provide these to the using class.

  IEMime also calls up to two funcs in the using class, PreBindMoniker & OnBindingFailure.
  The class design here could be improved, as this requires the using class to implement
  these funcs instead of pass in func-pointers such as with OnData. In our case,
  we want to handle both so it isn't a big deal.

* PreBindMoniker is intended to allow the using class to talk to the IBindCtx and IMoniker
  interfaces before the upcoming bind process. In our case, we use it to demonstrate
  the use of the RegisterMediaType, CreateFormatEnumerator, and RegisterFormatEnumerator APIs.
  If the "Media" property has been set on the control, then IEMime uses this custom
  string to Register a new Media Type. This nets a Clipboard format for that media type. Then,
  IEMime creates a format enumerator that enumerates ONLY this custom clipformat and registers
  that format enumerator on the bindctx. The net effect of this is that IE will use this
  custom type in the HTTP "Accept" header supplied to remote servers to indicate the
  accepted types that we're looking for. Feel free to experiment with different servers
  and different requested types.

* IEMime marks itself as safe-for-persistence on the IPersistPropertyBag interface. This
  is necessary to allow controls to receive Property persistence through <PARAM> tags.
  The standard ATL IObjectSafetyImpl doesn't support this automatically, so we override
  Set/GetInterfaceSafetyOptions and do the right magic.

* Miscellaneous Note #1: When asking a BindHost to do a binding via MonikerBindToStorage,
  the IBindStatusCallback registered on the IBindCtx will not be used in most cases and
  will get dumped. It's pretty important to supply the IBindStatusCallback interface as the
  third parameter to MonikerBindToStorage.

* Miscellaneous Note #2: IProvideClassInfo(2) must be implemented by a control if it wants
  IE/MSHTML to connect the control's event sink for scripting.


CPIEMIME.H

////////////////////////////////////////////////////////////////////////////// // CProxyDIEMimeEvents template <class T> class CProxyDIEMimeEvents : public IConnectionPointImpl<T, &DIID__IEMimeEvents, CComDynamicUnkArray> { public: //methods: //DIEMimeEvents : IDispatch public: void Fire_Complete() { VARIANT varResult; VariantInit(&varResult); T* pT = (T*)this; pT->Lock(); IUnknown** pp = m_vec.begin(); while (pp < m_vec.end()) { if (*pp != NULL) { DISPPARAMS disp = { NULL, NULL, 0, 0 }; IDispatch* pDispatch = reinterpret_cast<IDispatch*>(*pp); pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL); } pp++; } pT->Unlock();
}  };


DISPIDS.H

#ifndef __DISPIDS_H__
#define __DISPIDS_H__

#define DISPID_URL 1
#define DISPID_MEDIA 2
#define DISPID_CFFORMAT 3
#define DISPID_RELOAD 5

#endif // __DISPIDS_H__


IEMIME.CPP

// IEMime.cpp : Implementation of DLL Exports.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f IEMimeps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "IEMime.h"

#include "IEMime_i.c"
#include "IEMimeCtl.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_IEMimeCtl, CIEMimeCtl)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
_Module.UnregisterServer();
return S_OK;
}


IEMIME.IDL

#include <olectl.h>

#include "dispids.h" // IEMime.idl : IDL source for IEMime.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (IEMime.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(C60BC918-F40E-11D0-B038-00C04FB9C24A),
dual,
helpstring("IIEMimeCtl Interface"),
pointer_default(unique)
]
interface IIEMimeCtl : IDispatch
{
[propget, id(DISPID_URL), helpstring("property URL")] HRESULT URL([out, retval] BSTR *pVal);
[propput, id(DISPID_URL), helpstring("property URL")] HRESULT URL([in] BSTR newVal);
[propget, id(DISPID_MEDIA), helpstring("property Media")] HRESULT Media([out, retval] BSTR *pVal);
[propput, id(DISPID_MEDIA), helpstring("property Media")] HRESULT Media([in] BSTR newVal);
[propget, id(DISPID_CFFORMAT), helpstring("property Cfformat")] HRESULT Cfformat([out, retval] BSTR *pVal);
[propget, id(DISPID_READYSTATE), helpstring("Stock property ReadyState")] HRESULT ReadyState([out, retval] long *pnReadyState);
[id(DISPID_ABOUTBOX), helpstring("method AboutBox")] HRESULT AboutBox();
[id(DISPID_RELOAD), helpstring("method Reload")] HRESULT Reload();
};
[
uuid(C60BC90B-F40E-11D0-B038-00C04FB9C24A),
version(1.0),
helpstring("IEMime 1.0 Type Library")
]
library IEMIMELib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

    [
        uuid(0f4a7b40-a295-11cf-a3a9-00a0c9034920),
helpstring("Event interface for IEMime control")
    ]
    dispinterface _IEMimeEvents
{
        properties:
methods:
[id(1), helpstring("event complete - fired when IEMime is done downloading")]
void Complete();
    };

[
uuid(C60BC919-F40E-11D0-B038-00C04FB9C24A),
helpstring("IEMimeCtl Class")
]
coclass IEMimeCtl
{
[default] interface IIEMimeCtl;
[default, source] interface _IEMimeEvents;
};
};


IEMIMECTL.CPP

// IEMimeCtl.cpp : Implementation of CIEMimeCtl
#include "stdafx.h"

#include "IEMime.h"
#include "IEMimeCtl.h"

#include "dispids.h"

/////////////////////////////////////////////////////////////////////////////
// CIEMimeCtl

HRESULT CIEMimeCtl::OnDraw(ATL_DRAWINFO& di)
{
RECT& rc = *(RECT*)di.prcBounds;

HDC hdc  = di.hdcDraw;
HBRUSH    hOldBrush, hBrush;
    HPEN      hOldPen, hPen;
hPen = (HPEN)GetStockObject(BLACK_PEN);
    hOldPen = (HPEN)SelectObject(hdc, hPen);
    hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
    hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);

SetBkMode(di.hdcDraw,TRANSPARENT);
SetTextColor(di.hdcDraw,RGB(0,0,255));
if (!m_bstrCFFormat) // Empty - don't draw text
return S_OK;

ATLTRACE(_T("    m_bstrCFFormat=%s\n"),(LPTSTR)m_bstrCFFormat);
rc.left += 5; // small left margin
DrawText(di.hdcDraw, m_bstrCFFormat, m_bstrCFFormat.length(), &rc,
DT_LEFT | DT_WORD_ELLIPSIS | DT_SINGLELINE);

SelectObject(hdc, hOldPen);
    SelectObject(hdc, hOldBrush);

return S_OK;
}

STDMETHODIMP CIEMimeCtl::get_URL(BSTR * pVal)
{
//*pVal = m_bstrURL.copy();
// NOTE: _bstr_t::copy() has a BUG in VC5.0 that is fixed in VS5 SP1. See KB Q151491.
*pVal = ::SysAllocString(static_cast<const wchar_t*>(m_bstrURL));

return S_OK;
}

STDMETHODIMP CIEMimeCtl::put_URL(BSTR newVal)
{
m_bstrURL = newVal;

FireOnChanged(DISPID_URL);

if (!m_bstrURL) // NULL string
return S_OK;

if (READYSTATE_LOADING != m_nReadyState)  // Wait until control is done loading before
Reload();  // firing off download

    return S_OK;
}

STDMETHODIMP CIEMimeCtl::get_Media(BSTR * pVal)
{
//*pVal = m_bstrMedia.copy();
// NOTE: _bstr_t::copy() has a BUG in VC5.0 that is fixed in VS5 SP1. See KB Q151491.
*pVal = ::SysAllocString(static_cast<const wchar_t*>(m_bstrMedia));

return S_OK;
}

STDMETHODIMP CIEMimeCtl::put_Media(BSTR newVal)
{
m_bstrMedia = newVal;

FireOnChanged(DISPID_MEDIA);

    return S_OK;
}

STDMETHODIMP CIEMimeCtl::get_Cfformat(BSTR * pVal)
{
*pVal = m_bstrCFFormat.copy();

return S_OK;
}

STDMETHODIMP CIEMimeCtl::AboutBox()
{
// The following is necessary to prepare the frame for showing a modal dialog box
HRESULT hr;
OLEINPLACEFRAMEINFO frameInfo;
RECT rcPos, rcClip;
CComPtr<IOleInPlaceFrame> spInPlaceFrame;
CComPtr<IOleInPlaceUIWindow> spInPlaceUIWindow;
frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO);
HWND hwndFrame = NULL;

if (m_spInPlaceSite)
{
m_spInPlaceSite->GetWindowContext(&spInPlaceFrame,
&spInPlaceUIWindow, &rcPos, &rcClip, &frameInfo);
if (spInPlaceFrame)
{
spInPlaceFrame->GetWindow(&hwndFrame);
hr = spInPlaceFrame->EnableModeless(FALSE);
if (FAILED(hr))
return S_OK;  // Failed, but we don't want to send errors back to script
}
}
// -------------------------------------------------------------------------------

    ::MessageBox(hwndFrame, _T("This is My Control"), _T("About iemim"), MB_OK | MB_TASKMODAL);

// Tell frame our modal dialog box has gone away
if (spInPlaceFrame)
{
spInPlaceFrame->EnableModeless(TRUE);
}
// --------------------------------------------

return S_OK;
}

STDMETHODIMP CIEMimeCtl::Reload()
{
if (!m_bstrURL) // NO OP
return S_OK;
else
{
m_nReadyState = READYSTATE_INTERACTIVE;
FireOnChanged(DISPID_READYSTATE);
CBindStatusCallback2<CIEMimeCtl>::Download(this, OnData, m_bstrURL, m_spClientSite, FALSE);
}

return S_OK;
}

HRESULT CIEMimeCtl::PreBindMoniker(CComPtr<IBindCtx> pBindCtx, CComPtr<IMoniker> pMoniker)
{
HRESULT hr = S_OK;

if (!(m_bstrMedia)) // No MIME registration of our own, default
return S_OK;    // will give us an accept-type of "*/*"

LPSTR rgszTypes[1];
CLIPFORMAT cfFormats[1];
cfFormats[0] = CF_NULL;

// Register m_bstrMedia as a media-type. This will either give us
// the CLIPFORMAT of an existing media-type or create a new one for us.
rgszTypes[0] = (LPSTR)(m_bstrMedia);
if (SUCCEEDED(hr))
hr = RegisterMediaTypes(1, (LPSTR*)&rgszTypes, cfFormats);

FORMATETC rgfmtetc[1] =
{ {CF_NULL, NULL, DVASPECT_CONTENT, -1, TYMED_NULL} };
IEnumFORMATETC *pMyEtc = NULL;

// Create and register a FORMATETC enumerator for our (potentially custom)
// media type. Basically this has the equivalent effect of telling URLMON
// to use this media type for the HTTP Accept-types header.
// (this won't come into play for file://)
rgfmtetc[0].cfFormat = cfFormats[0];
hr = CreateFormatEnumerator (1, rgfmtetc, &pMyEtc);
if (SUCCEEDED(hr) && pMyEtc)
{
hr = RegisterFormatEnumerator(pBindCtx, pMyEtc, 0);
_ASSERTE(SUCCEEDED(hr));
}

return hr;
}

void CIEMimeCtl::OnBindingFailure(HRESULT hr, LPCWSTR szError)
{
if (NULL != szError && NULL != *szError)
m_bstrCFFormat = szError;
else
{
// Convert HR to human-readable string
if (INET_E_NO_VALID_MEDIA == hr)
m_bstrCFFormat = _T("<Resource at URL does not match Media type>");
else
m_bstrCFFormat = _T("<Failed to download>");
}

m_nReadyState = READYSTATE_COMPLETE;
FireOnChanged(DISPID_READYSTATE);
FireViewChange();

}

//OnData will be used as a callback functin by the CBindStatusCallback object.
//OnData will be called periodically with data from the asynchronous transfer
void CIEMimeCtl::OnData(CBindStatusCallback2<CIEMimeCtl>* pbsc, BYTE* pBytes, DWORD dwSize,
DWORD grfBSCF, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
{
static TCHAR formatname[MAX_PATH];

GetClipboardFormatName(pformatetc->cfFormat, formatname, MAX_PATH);
m_bstrCFFormat = formatname;

FireOnChanged(DISPID_CFFORMAT);

if (grfBSCF & BSCF_LASTDATANOTIFICATION)
{
Fire_Complete(); // Fire the "Complete" event
m_nReadyState = READYSTATE_COMPLETE;
FireOnChanged(DISPID_READYSTATE);
FireViewChange();
}

}

// From ATLCTL.H, line 842
STDMETHODIMP CIEMimeCtl::FreezeEvents(BOOL bFreeze)
{
ATLTRACE(_T("IOleControlImpl::FreezeEvents\n"));
if (bFreeze)
m_nFreezeEvents++;
else
{
m_nFreezeEvents--;
// Modified this so that we wait until container is
// set before downloading
if (!m_nFreezeEvents)
{
// Let's go!
Reload();
}
}

return S_OK;
}


IEMIMECTL.H

// IEMimeCtl.h : Declaration of the CIEMimeCtl

#ifndef __IEMIMECTL_H_
#define __IEMIMECTL_H_

#include "resource.h"       // main symbols

#include "CPIEMime.h" // Event interface Connection Point
#include "BSCbck.h"   // Bind-status-callback download implementation
// We're using VC5 COM support (_bstr_t and smart pointers)
#include <comdef.h>

/////////////////////////////////////////////////////////////////////////////
// CIEMimeCtl
class ATL_NO_VTABLE CIEMimeCtl :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CIEMimeCtl,&CLSID_IEMimeCtl>,
public CComControl<CIEMimeCtl>,
public IDispatchImpl<IIEMimeCtl, &IID_IIEMimeCtl, &LIBID_IEMIMELib>,
public IPersistStreamInitImpl<CIEMimeCtl>,
public IOleControlImpl<CIEMimeCtl>,
public IOleObjectImpl<CIEMimeCtl>,
public IOleInPlaceActiveObjectImpl<CIEMimeCtl>,
public IViewObjectExImpl<CIEMimeCtl>,
public IConnectionPointContainerImpl<CIEMimeCtl>,
public IOleInPlaceObjectWindowlessImpl<CIEMimeCtl>,
public IPersistPropertyBagImpl<CIEMimeCtl>,
public IPerPropertyBrowsingImpl<CIEMimeCtl>,
public IObjectSafetyImpl<CIEMimeCtl>,
public CProxyDIEMimeEvents<CIEMimeCtl>,
public IPropertyNotifySinkCP<CIEMimeCtl, CComDynamicUnkArray>,
public IProvideClassInfo2Impl<&CLSID_IEMimeCtl, &DIID__IEMimeEvents, &LIBID_IEMIMELib>
{
public:
CIEMimeCtl()
{
m_nReadyState = READYSTATE_UNINITIALIZED;
}

DECLARE_REGISTRY_RESOURCEID(IDR_IEMIMECTL)

BEGIN_COM_MAP(CIEMimeCtl)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IIEMimeCtl)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY_IMPL(IOleControl)
COM_INTERFACE_ENTRY_IMPL(IOleObject)
COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IPersist, IPersistPropertyBag)
COM_INTERFACE_ENTRY_IMPL(IPersistPropertyBag)
COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
COM_INTERFACE_ENTRY_IMPL(IObjectSafety)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
COM_INTERFACE_ENTRY_IID(IID_IProvideClassInfo, IProvideClassInfo2)
END_COM_MAP()

BEGIN_PROPERTY_MAP(CIEMimeCtl)
PROP_ENTRY("URL", 1, CLSID_NULL)
PROP_ENTRY("Media", 2, CLSID_NULL)
END_PROPERTY_MAP()

BEGIN_CONNECTION_POINT_MAP(CIEMimeCtl)
CONNECTION_POINT_ENTRY(DIID__IEMimeEvents)
CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink)
END_CONNECTION_POINT_MAP()

BEGIN_MSG_MAP(CIEMimeCtl)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
END_MSG_MAP()

// IViewObjectEx
STDMETHOD(GetViewStatus)(DWORD* pdwStatus)
{
ATLTRACE(_T("IViewObjectExImpl::GetViewStatus\n"));
*pdwStatus = VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE;
return S_OK;
}

// IIEMimeCtl
public:
STDMETHOD(Reload)();
STDMETHOD(AboutBox)();
STDMETHOD(get_Cfformat)(/*[out, retval]*/ BSTR *pVal);
//STDMETHOD(put_Cfformat)(/*[in]*/ BSTR newVal); // PUT not supported
STDMETHOD(get_Media)(/*[out, retval]*/ BSTR *pVal);
STDMETHOD(put_Media)(/*[in]*/ BSTR newVal);
STDMETHOD(get_URL)(/*[out, retval]*/ BSTR *pVal);
STDMETHOD(put_URL)(/*[in]*/ BSTR newVal);
HRESULT OnDraw(ATL_DRAWINFO& di);

// IOleControl override
STDMETHOD(FreezeEvents)(BOOL bFreeze);

// IPersistPropertybag override
STDMETHOD(Load)(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
{
ATLTRACE(_T("IPersistPropertyBagImpl::Load\n"));
ATL_PROPMAP_ENTRY* pMap = GetPropertyMap();
_ASSERTE(pMap != NULL);
m_nReadyState = READYSTATE_LOADING; // ADDED
FireOnChanged(DISPID_READYSTATE);
HRESULT hr = IPersistPropertyBag_Load(pPropBag, pErrorLog, pMap);
m_nReadyState = READYSTATE_COMPLETE;// ADDED
// Saying we're complete lets MSHTML proceed. See README.TXT for details.
FireOnChanged(DISPID_READYSTATE);
return hr;
}

void OnData(CBindStatusCallback2<CIEMimeCtl>* pbsc, BYTE* pBytes, DWORD dwSize,
DWORD grfBSCF, FORMATETC *pformatetc, STGMEDIUM *pstgmed);

HRESULT PreBindMoniker(CComPtr<IBindCtx> pBindCtx, CComPtr<IMoniker> pMoniker);

void OnBindingFailure(HRESULT hr, LPCWSTR szError);

// IObjectSafetyImpl doesn't support safe-for-persistence naturally.
// See Microsoft Knowledge Base article Q168371 for more details on the following
STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid,
DWORD *pdwSupportedOptions,
DWORD *pdwEnabledOptions)
{
   ATLTRACE(_T("CObjectSafetyImpl::GetInterfaceSafetyOptions\n"));
   if (!pdwSupportedOptions || !pdwEnabledOptions)
  return E_FAIL;
   LPUNKNOWN pUnk;
   if (_InternalQueryInterface (riid, (void**)&pUnk) == E_NOINTERFACE)
  // Our object doesn't even support this interface.
  return E_NOINTERFACE;
   else
   {
  // Cleanup after ourselves.
  pUnk->Release();
  pUnk = NULL;
   }
   if (riid == IID_IDispatch)
   {
  *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
  *pdwEnabledOptions = m_dwSafety &
   INTERFACESAFE_FOR_UNTRUSTED_CALLER;
  return S_OK;
// IEMime only supports persistence via IPersistPropertyBag
   }
   else if (riid == IID_IPersistPropertyBag)
   {
  *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
  *pdwEnabledOptions = m_dwSafety &
   INTERFACESAFE_FOR_UNTRUSTED_DATA;
  return S_OK;
   }
   else
   {
  // No other interfaces in this control are
  // safe for initializing or scripting.
  *pdwSupportedOptions = 0;
  *pdwEnabledOptions = 0;
  return E_FAIL;
   }
}

   STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid,
                                     DWORD dwOptionSetMask,
                                     DWORD dwEnabledOptions)
   {
   ATLTRACE(_T("CObjectSafetyImpl::SetInterfaceSafetyOptions\n"));
   if (!dwOptionSetMask && !dwEnabledOptions) return E_FAIL;
   LPUNKNOWN pUnk;
   if (_InternalQueryInterface (riid, (void**)&pUnk) == E_NOINTERFACE)
  // Our object doesn't even support this interface.
  return E_NOINTERFACE;
   else
   {
  // Cleanup after ourselves.
  pUnk->Release();
  pUnk = NULL;
   }
   // Store our current safety level to return in
   // GetInterfaceSafetyOptions
   m_dwSafety |= dwEnabledOptions & dwOptionSetMask;
   if ((riid == IID_IDispatch) &&
   (m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER))
  return S_OK;
   else if ((riid == IID_IPersistPropertyBag) &&
 (m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_DATA))
  return S_OK;
   else
  return E_FAIL;
   }

// ReadyState property - not using CStockPropImpl because we only want this one stock-prop
//                       and we only want it to be read-only
long m_nReadyState;

HRESULT STDMETHODCALLTYPE get_ReadyState(long* pnReadyState)
{
ATLTRACE(_T("CIEMimeCTL - get_ReadyState - %d\n"),m_nReadyState);

*pnReadyState = m_nReadyState;
return S_OK;
}

public:
_bstr_t m_bstrURL;
_bstr_t m_bstrMedia;
_bstr_t m_bstrCFFormat;

};

#endif //__IEMIMECTL_H_


BSCBCK.H

// CBindStatusCallback2 -- we want a little more info than it is giving us, such as
//      C&P'ed from ATL -- see ADDED below for additons, CHANGED for changes...

// PreBindMoniker() & OnBindingFailure() must be implemented in using class T

template <class T>
class ATL_NO_VTABLE CBindStatusCallback2 :
public CComObjectRootEx<T::_ThreadModel::ThreadModelNoCS>,
public IBindStatusCallbackImpl<T>
{
// CHANGED from CBindStatusCallback
typedef void (T::*ATL_PDATAAVAILABLE)(CBindStatusCallback2<T>* pbsc, BYTE* pBytes, DWORD dwSize,
DWORD grfBSCF, FORMATETC *pformatetc, STGMEDIUM *pstgmed);
// -------

public:

BEGIN_COM_MAP(CBindStatusCallback2<T>)
COM_INTERFACE_ENTRY_IID(IID_IBindStatusCallback, IBindStatusCallbackImpl<T>)
END_COM_MAP()

CBindStatusCallback2()
{
m_pT = NULL;
m_pFunc = NULL;
}
~CBindStatusCallback2()
{
ATLTRACE(_T("~CBindStatusCallback2\n"));
}

STDMETHOD(OnStartBinding)(DWORD dwReserved, IBinding *pBinding)
{
ATLTRACE(_T("CBindStatusCallback2::OnStartBinding\n"));
m_spBinding = pBinding;
return S_OK;
}

STDMETHOD(GetPriority)(LONG *pnPriority)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback2::GetPriority"));
}

STDMETHOD(OnLowResource)(DWORD reserved)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback2::OnLowResource"));
}

STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
//ATLTRACENOTIMPL(_T("CBindStatusCallback2::OnProgress"));
ATLTRACE(_T("CBindStatusCallback2::OnProgress : progress=%d / %d - status=%x\n"), ulProgress, ulProgressMax, ulStatusCode);
_bstr_t strStatusText = szStatusText;
ATLTRACE(_T("     status-text=%s\n"),(LPCTSTR)strStatusText);
return E_NOTIMPL;
}

STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR szError)
{
ATLTRACE(_T("CBindStatusCallback2::OnStopBinding\n"));

if (FAILED(hresult))
m_pT->OnBindingFailure(hresult, szError);

m_spBinding.Release();// ADDED
m_spBindCtx.Release();
m_spMoniker.Release();

return S_OK;
}

STDMETHOD(GetBindInfo)(DWORD *pgrfBINDF, BINDINFO *pbindInfo)
{
ATLTRACE(_T("CBindStatusCallback2::GetBindInfo\n"));
if (!pbindInfo || !pbindInfo->cbSize || !pgrfBINDF)
return E_INVALIDARG;
*pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE |
BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE;

// NOTE: BINDINFO has changed sizes between IE3 and IE4
// Relying on a compiled sizeof(BINDINFO) can cause trouble.
// The following changes will propagate to later versions of
// ATL.

// Remember incoming cbSize
ULONG cbSize = pbindInfo->cbSize;

memset(pbindInfo, 0, cbSize);

// Restore cbSize
pbindInfo->cbSize = cbSize;
pbindInfo->dwBindVerb = BINDVERB_GET;

return S_OK;
}

STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
{
ATLTRACE(_T("CBindStatusCallback2::OnDataAvailable\n"));
HRESULT hr = S_OK;

// Get the Stream passed
if (BSCF_FIRSTDATANOTIFICATION & grfBSCF)
{
if (!m_spStream && pstgmed->tymed == TYMED_ISTREAM)
{
m_spStream = pstgmed->pstm;
/*if (m_spStream)
m_spStream->AddRef();*/ // CHANGED
}
}

DWORD dwRead = dwSize - m_dwTotalRead; // Minimum amount available that hasn't been read
DWORD dwActuallyRead = 0;              // Placeholder for amount read during this pull

// If there is some data to be read then go ahead and read them
if (m_spStream)
{
if (dwRead > 0)
{
BYTE* pBytes = NULL;
ATLTRY(pBytes = new BYTE[dwRead + 1]);
if (pBytes == NULL)
return S_FALSE;
hr = m_spStream->Read(pBytes, dwRead, &dwActuallyRead);
if (SUCCEEDED(hr))
{
pBytes[dwActuallyRead] = 0;
if (dwActuallyRead>0)
{
// CHANGED
(m_pT->*m_pFunc)(this, pBytes, dwActuallyRead, grfBSCF, pformatetc, pstgmed);
// -------
m_dwTotalRead += dwActuallyRead;
}
}
delete[] pBytes;
}
}

if (BSCF_LASTDATANOTIFICATION & grfBSCF)
m_spStream.Release();
return hr;
}

STDMETHOD(OnObjectAvailable)(REFIID riid, IUnknown *punk)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback2::OnObjectAvailable"));
}

HRESULT _StartAsyncDownload(BSTR bstrURL, IUnknown* pUnkContainer, BOOL bRelative)
{
m_dwTotalRead = 0;
m_dwAvailableToRead = 0;
HRESULT hr = S_OK;
CComQIPtr<IServiceProvider, &IID_IServiceProvider> spServiceProvider(pUnkContainer);
CComPtr<IBindHost> spBindHost;
if (spServiceProvider)
spServiceProvider->QueryService(SID_IBindHost, IID_IBindHost, (void**)&spBindHost);

if (spBindHost == NULL)
{
if (bRelative)
return E_NOINTERFACE;  // relative asked for, but no IBindHost
hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker);

if (SUCCEEDED(hr))
hr = CreateBindCtx(0, &m_spBindCtx);

if (SUCCEEDED(hr))
hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L);
else
m_spMoniker.Release();

// ADDED to CBindStatusCallback
if (SUCCEEDED(hr))
hr = m_pT->PreBindMoniker(m_spBindCtx, m_spMoniker);
// -----

if (SUCCEEDED(hr))
{
IStream* pStream;
hr = m_spMoniker->BindToStorage(m_spBindCtx, 0, IID_IStream, (void**)&pStream);
if (pStream != NULL) // ADDED
pStream->Release();
}
}
else
{
hr = CreateBindCtx(0, &m_spBindCtx);

/*if (SUCCEEDED(hr))
hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L);*/
// If we're working through the IBindHost interface, we should specify the
// IBSC on MonikerBindToStorage, not registered on the IBindCtx

if (SUCCEEDED(hr))
{
if (bRelative)
hr = spBindHost->CreateMoniker(bstrURL, m_spBindCtx, &m_spMoniker, 0);
else
hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker);
}

// ADDED
if (SUCCEEDED(hr))
hr = m_pT->PreBindMoniker(m_spBindCtx, m_spMoniker);
// -----

if (SUCCEEDED(hr))
{
IStream* pStream;
hr = spBindHost->MonikerBindToStorage(m_spMoniker, m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), IID_IStream, (void**)&pStream);
if (pStream != NULL) // ADDED
pStream->Release();
ATLTRACE(_T("Bound"));
}
}
return hr;
}

HRESULT StartAsyncDownload(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE)
{
m_pT = pT;
m_pFunc = pFunc;
return  _StartAsyncDownload(bstrURL, pUnkContainer, bRelative);
}

static HRESULT Download(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE)
{
CComObject<CBindStatusCallback2<T> > *pbsc;
HRESULT hRes = CComObject<CBindStatusCallback2<T> >::CreateInstance(&pbsc);
if (FAILED(hRes))
return hRes;
return pbsc->StartAsyncDownload(pT, pFunc, bstrURL, pUnkContainer, bRelative);
}

CComPtr<IMoniker> m_spMoniker;
CComPtr<IBindCtx> m_spBindCtx;
CComPtr<IBinding> m_spBinding;
CComPtr<IStream> m_spStream;
T* m_pT;
ATL_PDATAAVAILABLE m_pFunc;
DWORD m_dwTotalRead;
DWORD m_dwAvailableToRead;
};


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by IEMime.rc
//
#define IDS_PROJNAME                    100
#define IDR_IEMIMECTL                   101

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        202
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           102
#endif
#endif


STDAFX.H

// stdafx.h : include file for standard system include files,
//      or project specific include files that are used frequently,
//      but are changed infrequently

#if !defined(AFX_STDAFX_H__C60BC90F_F40E_11D0_B038_00C04FB9C24A__INCLUDED_)
#define AFX_STDAFX_H__C60BC90F_F40E_11D0_B038_00C04FB9C24A__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#define STRICT

#define _WIN32_WINNT 0x0400
#define _ATL_APARTMENT_THREADED

#define _ATL_DEBUG_QI

#undef ATL_MIN_CRT // Can't use this, we're using VC5 COM support

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>
#include <atlctl.h>
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__C60BC90F_F40E_11D0_B038_00C04FB9C24A__INCLUDED)


README.TXT

=======================================================================

Release notes for the IISLog Sample of the Internet Client Software
Development Kit:

Copyright (c) Microsoft Corporation, 1997
=======================================================================

Description:

The IISLog sample demonstrates how to receive and parse the IE4 user
logging profile from the server side. When the user subscribes to a
channel which has hit count logging specified and they later browse to
those pages, IE40 will log the hit counts and post them back to the
server when the channel subscribed to gets updated. The user can
globally turn logging off from the control panel UI if desired. IE40
uses the W3C extended server log file format and may encode a posted
string as directed in the CDF file associated with the channel
subscribed to. This sample shows how to parse the extended server log
file format and merge to a W3C flexible log file format. This sample
also shows how to decode a posted string if it is encoded using
pluggable protocol filters. IISLog.htm is a sample page to post a string
to logparse.dll on the server side. The posted string should simulate
the extended server log file format.

Building procedure:

The Logparse ISAPI DLL was created and built using VC++ 5.0 ISAPI
extension wizard. VC++ 5.0 is required to build this sample. It links
to urlmon.lib to obtain the pluggable protocol filters.

Install the ISAPI DLL:

You need IIS 4.0 or greater to run this sample. Copy LogParse.dll to
that server's scripts directory. A detailed description of how to
install an ISAPI DLL can be found in the MSDN library.


LOGPARSE.CPP

// LOGPARSE.CPP - Implementation file for your Internet Server
//    LogParse Extension

#include "stdafx.h"
#include "LogParse.h"
#include "urlmon.h"

///////////////////////////////////////////////////////////////////////
// The one and only CWinApp object
// NOTE: You may remove this object if you alter your project to no
// longer use MFC in a DLL.

CWinApp theApp;

#define USE_DECODER 1

#define MAX_SIZE        2048
#define LOG_BUF_SIZE    512
#define NUMLOG          23

TCHAR   LogEntries[NUMLOG][LOG_BUF_SIZE] = {
        "",                 //0 remote addr
        "-",                //1 user name, "-" means anonymous
        "",                 //2 request date
        "",                 //3 request time
        "W3SVC",            //4 service type (default = W3SVC)
        "",                 //5 server name
        "",                 //6 server ip
        "0",                //7 elapsed time (default = 0)
        "0",                //8 bytes received (default = 0)
        "0",                //9 bytes sent (default = 0)
        "200",              //a service response code (default = 200)
        "0",                //b system (NT) response code (default = 0)
        "GET",              //c method type  (HEAD,POST, default = GET)
        "",                 //d uri
        "",                 //e user agent
        "-",                //f referer (default = "-" means unknown)
        "",                 //10 cookie
        "-",                //11 query string ("-" means no query string)
        "",                 //12 duration
        "",                 //13 context
        "",                 //14 client cache
        "-",                //15 metatag
        "-"                 //16 additional parameters (ignored)
    };

///////////////////////////////////////////////////////////////////////
// command-parsing map

BEGIN_PARSE_MAP(CLogParseExtension, CHttpServer)
// TODO: insert your ON_PARSE_COMMAND() and
// ON_PARSE_COMMAND_PARAMS() here to hook up your commands.
// For example:
DEFAULT_PARSE_COMMAND(LogParse, CLogParseExtension)
ON_PARSE_COMMAND(LogParse, CLogParseExtension, ITS_PSTR)
ON_PARSE_COMMAND_PARAMS("input_text=default")
END_PARSE_MAP(CLogParseExtension)

///////////////////////////////////////////////////////////////////////
// The one and only CLogParseExtension object

CLogParseExtension theExtension;

///////////////////////////////////////////////////////////////////////
// CLogParseExtension implementation

CLogParseExtension::CLogParseExtension()
{
m_hFile = NULL;
m_ContentLen = 0;
m_szEncoding = NULL;
    m_cbURILen = 0;
    m_fInLog = FALSE;
}

CLogParseExtension::~CLogParseExtension()
{
if (m_szEncoding)
GlobalFree(m_szEncoding);

}

BOOL CLogParseExtension::GetExtensionVersion(HSE_VERSION_INFO* pVer)
{
// Call default implementation for initialization
CHttpServer::GetExtensionVersion(pVer);

// Load description string
TCHAR sz[HSE_MAX_EXT_DLL_NAME_LEN+1];
ISAPIVERIFY(::LoadString(AfxGetResourceHandle(),
IDS_SERVER, sz, HSE_MAX_EXT_DLL_NAME_LEN));
_tcscpy(pVer->lpszExtensionDesc, sz);
return TRUE;
}

///////////////////////////////////////////////////////////////////////
// CLogParseExtension command handlers

// pszString : post string (using FORM)
void CLogParseExtension::LogParse(CHttpServerContext* pCtxt, LPTSTR pszString)
{

StartContent(pCtxt);
WriteTitle(pCtxt);

*pCtxt << _T("Server DLL, just shown that we were here. \r\n");

    // real work starts
    DWORD   err;
    DWORD   dwLen = sizeof(DWORD);
    char    buf[LOG_BUF_SIZE];
    DWORD   cbsize = LOG_BUF_SIZE;
    BOOL    bret;
BOOL    fEncoded = FALSE;

    // get server variables
    if (!pCtxt->GetServerVariable("CONTENT_LENGTH", buf, &dwLen) ||
        !pCtxt->GetServerVariable("REQUEST_METHOD", (void *)LogEntries[12], &cbsize) )
    {
        err = GetLastError();
        *pCtxt << _T("LogParse failed: couldn't get server variables.\r\n");
        goto _exithere;
    }

    if (lstrcmp(LogEntries[12], _T("POST")))
    {
        *pCtxt << _T("LogParse failed: wrong request method.\r\n");
        goto _exithere;
    }
    else
    {
cbsize = LOG_BUF_SIZE;
m_szEncoding = (LPTSTR)GlobalAlloc(LPTR, LOG_BUF_SIZE);
if (!m_szEncoding)
{
*pCtxt << _T("LogParse failed: couldn't allocate g_szEncoding \r\n");
goto _exithere;
}

        if (pCtxt->GetServerVariable("HTTP_CONTENT_TRANSFER_ENCODING", (void *)m_szEncoding, &cbsize))
fEncoded = TRUE;

if (*pszString == NULL)
{
*pCtxt << _T("LogParse: no posting string \r\n");
goto _exithere;
}

bret = OpenLogFile(pCtxt);
if (!bret)
{
*pCtxt << _T("LogParse: create/open file failed \r\n");
goto _exithere;
}

dwLen = AppendContentToLogfile(pCtxt, pszString, atoi(buf), fEncoded);

ISAPITRACE1("LogParse: server logged   %d bytes\n", dwLen);
ISAPITRACE1("LogParse: encoding by %s\n", m_szEncoding);

    }

_exithere:
EndContent(pCtxt);

}

BOOL CLogParseExtension::OpenLogFile(CHttpServerContext* pCtxt)
{
SYSTEMTIMEst;
    TCHAR   szLogPath[MAX_PATH];
    DWORD   cbpath = MAX_PATH;
TCHARpszfile[MAX_PATH];
    HKEY    hkey;

// generate filename based on date
GetLocalTime(&st);
    wsprintf(pszfile, "ie%.2d%.2d%.2d.log", st.wYear, st.wMonth, st.wDay);

    if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, g_szRegW3SVC, 0, KEY_READ, &hkey))
    {
        *pCtxt << _T("LogParse failed: RegOpenKeyEx\r\n");
    return FALSE;
    }

if (ERROR_SUCCESS != RegQueryValueEx(hkey, g_szRegLogFileDirectory,
                                             NULL, NULL, (LPBYTE)szLogPath, &cbpath))
{
    *pCtxt << _T("LogParse failed: GetLogFileDirectory.\r\n");
        RegCloseKey(hkey);
    return FALSE;
    }

    RegCloseKey(hkey);

    lstrcpy(m_szLogFile, szLogPath);
    lstrcat(m_szLogFile, g_szLogFilePath);
lstrcat(m_szLogFile, pszfile);

    m_hFile = CreateFile(m_szLogFile, GENERIC_READ|GENERIC_WRITE, 0, NULL,
        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if (m_hFile == INVALID_HANDLE_VALUE)
        return FALSE;

SetFilePointer(m_hFile, 0, NULL, FILE_END);

return TRUE;
}

DWORD CLogParseExtension::AppendContentToLogfile(CHttpServerContext* pCtxt, LPCTSTR pszContent, DWORD ContentLen, BOOL fEncoded)
{
LPTSTR  pszDecContent = NULL;
LPTSTR  pszLog = NULL;
    DWORD   dwWritten = 0;

if (fEncoded)
{
DWORD   dwlen;
//decoding
dwlen = ContentLen * 4;
pszDecContent = (LPTSTR)GlobalAlloc(LPTR, dwlen);
if (DecodeContent(pCtxt, pszContent, ContentLen, pszDecContent, dwlen))
pszLog = pszDecContent;
        else
            return 0;       // failed to decompress the data somehow
}
else
{
pszLog = (LPTSTR)pszContent;
m_ContentLen = lstrlenA(pszLog);
}

//transform to W3C flexible log format
dwWritten = ConvertToW3CFlexible(pCtxt, pszLog);

if (pszDecContent)
GlobalFree(pszDecContent);

    return dwWritten;
}

// we need to extract out the decoder's name from "x-filtername" string
BOOL CLogParseExtension::DecodeContent(CHttpServerContext* pCtxt, LPCTSTR pszEncodedString,
   DWORD cbEnc, LPTSTR pszDecString, DWORD cbDec)
{
#ifdef USE_DECODER
LPWSTRpwszEncoding = NULL;
LPTSTR  pszEncoding = m_szEncoding;
DWORD   dwlen;
BOOL    bInDash = FALSE;
BOOLbret = FALSE;
IEncodingFilterFactory* pEflt = NULL;
    IDataFilter*    pDF = NULL;

//parse encoding header string
while (*pszEncoding != 0)
{
if (bInDash)
break;

if (*pszEncoding == '-')
bInDash = TRUE;

pszEncoding ++;
};

dwlen = (lstrlenA(pszEncoding) + 1) * sizeof(WCHAR);
pwszEncoding = (LPWSTR)GlobalAlloc(LPTR, dwlen);

//find/load decoder
    if (!pwszEncoding)
    {
*pCtxt << _T("LogParse: DecodeContent mem error \r\n");
goto _exitDecode;
}

dwlen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszEncoding, lstrlenA(pszEncoding),
pwszEncoding, dwlen);

if (dwlen == 0)
{
*pCtxt << _T("LogParse : DecodeContent wide char convert failed\r\n");
goto _exitDecode;
}

    CoCreateInstance(CLSID_StdEncodingFilterFac, NULL, CLSCTX_INPROC_SERVER,
                                    IID_IEncodingFilterFactory, (void**)&pEflt);

if (pEflt)
    {
pEflt->GetDefaultFilter(pwszEncoding, L"text", &pDF);
        if (pDF)
        {
            LONG lInUsed = 0;
            LONG lOutUsed = 0;
            HRESULT hrCode = NOERROR;

            hrCode = pDF->DoEncode(
                                   0,
                                   cbEnc,
                                   (BYTE *)pszEncodedString,
                                   cbDec,
                                   (BYTE *)pszDecString,
                                   cbEnc,
                                   &lInUsed,
                                   &lOutUsed,
                                   0);

            if (SUCCEEDED(hrCode))
            {
                m_ContentLen = (DWORD)lOutUsed;
bret = TRUE;
            }
            pDF->Release();

}

        pEflt->Release();

}

_exitDecode:
if (pwszEncoding)
GlobalFree(pwszEncoding);

return bret;
#else
return TRUE;
#endif
}

DWORD CLogParseExtension::ConvertToW3CFlexible(CHttpServerContext* pCtxt, LPTSTR pszClientLog)
{

#ifdef _DEBUG
#define NUMVARS 20

  char varnames[NUMVARS][30] = {
    "AUTH_TYPE",
    "CONTENT_LENGTH",
    "CONTENT_TYPE",
    "PATH_INFO",
    "PATH_TRANSLATED",
    "QUERY_STRING",
    "REMOTE_ADDR",
    "REMOTE_HOST",
    "REMOTE_USER",
    "UNMAPPED_REMOTE_USER",
    "REQUEST_METHOD",
    "SCRIPT_NAME",
    "SERVER_NAME",
    "SERVER_PORT",
    "SERVER_PORT_SECURE",
    "SERVER_PROTOCOL",
    "SERVER_SOFTWARE",
    "ALL_HTTP",
    "HTTP_ACCEPT",
    "URL"
  };

  char buf[1024];
  unsigned long bufsize;

  for (int index = 0; index < NUMVARS; index++) {

    bufsize = 1024;
    if (pCtxt->GetServerVariable (&varnames[index][0], buf, &bufsize)) {

      ISAPITRACE2("[%s] = [%s]\n", &varnames[index], buf);
    }
    else {

      ISAPITRACE1("[%s] = (Oops!)\n", varnames[index]);
    }
  }
#endif

    DWORD  dwFixedBytes = 0;
    DWORD  dwTotalBytes = 0;
    TCHAR  szSpace[] = " ";
    LPTSTR pszServerLog;
    DWORD  dwCount = m_ContentLen + 1;  // over 1 from end
    DWORD  dwmove;

    dwFixedBytes = ProcessFixedLogFields(pCtxt);
    dwFixedBytes = dwFixedBytes + NUMLOG*lstrlen(szSpace) + lstrlen(LogEntries[4])
                + 6*lstrlen(LogEntries[7]) + lstrlen(LogEntries[10]);

    // parse client log
    m_cbURILen = 0;
    do
    {
        DWORD   dwthisclientlog = 0;
        DWORD   dwthislog = 0;
        DWORD   dwwritten;

        dwthisclientlog = ProcessClientLog(pCtxt, pszClientLog, &dwmove);
        if (dwthisclientlog == 0)
            break;

        dwthislog = dwFixedBytes + dwthisclientlog;

        // fill in temp buffer
        pszServerLog = (LPTSTR)GlobalAlloc(LPTR, dwthislog);
        if (!pszServerLog)
        {
            ISAPITRACE0("LogParse: mem alloc failed, pszServerLog\n");
            continue;
        }

        int ilog, i;
        int index = 0;
        for (ilog=0; ilog<NUMLOG; ilog++)
        {
            for (i=0; LogEntries[ilog][i]; i++, index++)
                pszServerLog[index] = LogEntries[ilog][i];

            // Add space
            pszServerLog[index++] = ' ';
        }

        // null terminated
        pszServerLog[index] = 0;

        // write to server log file
        WriteFile(m_hFile, pszServerLog, index, &dwwritten, NULL);
        dwTotalBytes += dwwritten;
        GlobalFree(pszServerLog);

        // keep track of end of string
        dwCount -= dwmove;
        if (dwCount == 0)
            break;

        pszClientLog += dwmove;

    } while (TRUE);

CloseHandle(m_hFile);

    return dwTotalBytes;
}

#define NUMSERVER   7
#define NUMCLIENT   5

char Varnames[NUMSERVER][20] = {
    "REMOTE_ADDR",
    "REMOTE_USER",
    "SERVER_NAME",
    "SERVER_PORT",
    "HTTP_USER_AGENT",
    "HTTP_REFERER",
    "QUERY_STRING"
    };

int SvrIdx[NUMSERVER+NUMCLIENT] = {0, 1, 5, 6, 14, 15, 17, 19, 20, 2, 3, 18};

DWORD CLogParseExtension::ProcessFixedLogFields(CHttpServerContext* pCtxt)
{
    DWORD   dwBytes;
    DWORD   cbsize;
    char    buf[1024];

    // remote addr
    dwBytes = 0;
    for (int i=0; i<NUMSERVER; i++)
    {
        cbsize = LOG_BUF_SIZE;
        if (!pCtxt->GetServerVariable(Varnames[i], buf, &cbsize))
        {
            ISAPITRACE1("LogParse: can't get %s\n", Varnames[i]);
        }
        else
        {
            if (*buf)
                lstrcpy(LogEntries[SvrIdx[i]], buf);
        }
        dwBytes += lstrlen(LogEntries[SvrIdx[i]]);
    }

    // method type
    dwBytes += lstrlen(LogEntries[12]);

    // cookie
    cbsize = LOG_BUF_SIZE;
    if (!GetUserCookie(pCtxt, LogEntries[16], &cbsize))
    {
        ISAPITRACE0("LogParse: can't get request method\n");
        cbsize = 0;
    }
    dwBytes += cbsize;

    return dwBytes;
}

BOOL CLogParseExtension::GetUserCookie(CHttpServerContext* pCtxt, LPTSTR pszCookie, LPDWORD pcb)
{
    BOOL    bret = FALSE;
    DWORD   sizeof_cookies = sizeof_header;
    char *  cookie;

    cookie = (char *)GlobalAlloc(LPTR, sizeof_cookies);
    if (!cookie)
        return bret;

    char * interse_cookie = &cookie[0];   // interse' cookie

    // Obtain the list of cookies.
    if (pCtxt->GetServerVariable("ALL_HTTP", interse_cookie, &sizeof_cookies))
    {
        if (interse_cookie = strstr (interse_cookie, "HTTP_COOKIE:"))
        {
            // Does the client already have a Interse' cookie? Also, remove cookies
            // to the left of the Interse' cookie.
            //
            if (interse_cookie = strstr (interse_cookie, interselog_cookiename))
            {
                // Remove cookies to the right of Interse' cookie.
                //
                char * next_cookie = strstr (interse_cookie, ";");
                if (next_cookie) *next_cookie = 0;
                *pcb = lstrlen(interse_cookie);
                if (*pcb >= LOG_BUF_SIZE)
                {
                    ISAPITRACE0("LogParse: cookie memory overflow!!\n");
                }
                lstrcpy(pszCookie, interse_cookie);
                bret = TRUE;
            }
            else
            {
                *pszCookie = 0;
                *pcb = 0;
            }
        }
        else
        {
            *pszCookie = 0;
            *pcb = 0;
        }
    }

    return bret;
}

/*
 * client log is in "extended server log format"
 * #Fields: s-URI
 * http://www.msnbc.com/news/66784.asp
 * #Fields: x-context x-cache x-date x-time x-duration x-meta
 * N 1 06-10-1997 14:03:54 00:12:32 customestring
 * N 1 06-11-1997 10:13:04 00:05:13
 * N 1 06-17-1997 04:07:15 00:11:33
 */

DWORD CLogParseExtension::ProcessClientLog(CHttpServerContext* pCtxt, LPTSTR pszClientLog, LPDWORD pMove)
{
    char*   pszNext;
    DWORD   cbBytes = 0;
    LPTSTR  pszInbuf = pszClientLog;

    if (m_cbURILen == 0)
    {
        // look for URI
        if (*pszClientLog == ' ')
            pszClientLog ++;

        if (memicmp(pszClientLog, g_szURIFields, lstrlen(g_szURIFields)))
            return 0;

        pszClientLog += lstrlen(g_szURIFields);
        pszNext = strstr(pszClientLog, " ");
        if (pszNext)
            *pszNext = 0;

        lstrcpy(LogEntries[13], pszClientLog);
        m_cbURILen = pszNext - pszClientLog;
        pszClientLog += m_cbURILen;
        pszClientLog ++;    // the space  (pNext)
    }

    cbBytes = m_cbURILen;

    // next line should be either log or #Fields: x-context.... or end_of_string
    if (!memicmp(pszClientLog, g_szFields, lstrlen(g_szFields)))
    {
        pszClientLog = strstr(pszClientLog, g_szMeta);
        pszClientLog += lstrlen(g_szMeta);
        if (*pszClientLog == ' ')
            pszClientLog ++;        // the 1st space in log cache!!
    }
    else if (*pszClientLog == 0)
    {
        // end of log string
        return 0;
    }

    for (int i = NUMSERVER; i < NUMSERVER+NUMCLIENT; i ++)
    {
        pszNext = strstr(pszClientLog, " ");
        if (pszNext)
            *pszNext = 0;
        lstrcpy(LogEntries[SvrIdx[i]], pszClientLog);
        cbBytes += lstrlen(LogEntries[SvrIdx[i]]);
        pszClientLog += lstrlen(LogEntries[SvrIdx[i]]);
        pszClientLog ++ ;   // the space
    }

    // look for URI
    if (!memicmp(pszClientLog, g_szURIFields, lstrlen(g_szURIFields)))
    {
        m_cbURILen = 0;
    }
    //
    *pMove = pszClientLog - pszInbuf;
    return cbBytes;

}

// Do not edit the following lines, which are needed by ClassWizard.
#ifdef PROCESS_MESSAGE
BEGIN_MESSAGE_MAP(CLogParseExtension, CHttpServer)
//{{AFX_MSG_MAP(CLogParseExtension)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif

///////////////////////////////////////////////////////////////////////
// If your extension will not use MFC, you'll need this code to make
// sure the extension objects can find the resource handle for the
// module.  If you convert your extension to not be dependent on MFC,
// remove the comments arounn the following AfxGetResourceHandle()
// and DllMain() functions, as well as the g_hInstance global.

/****

static HINSTANCE g_hInstance;

HINSTANCE AFXISAPI AfxGetResourceHandle()
{
return g_hInstance;
}

BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason,
LPVOID lpReserved)
{
if (ulReason == DLL_PROCESS_ATTACH)
{
g_hInstance = hInst;
}

return TRUE;
}

****/

/* W3C flexible log file format */
/*
157.56.230.204//remote addr
-//user name, "-" means anonymous
06/17/97//request date
19:51:44//request time
W3SVC//service type (default = W3SVC)
A282527//server name
157.56.230.168//server ip
340//elapsed time (default = 0)
450//bytes received (default = 0)
123//bytes sent (default = 0)
200//service response code (default = 200)
0//system (NT) response code (default = 0)
GET//method type  (HEAD,POST, default = GET)
/impsrvr/index.html//uri
Mozilla/2.0 (compatible; MSIE 3.02; WIndows NT)//user agent
-//referer (default = "-" means unknown)
INTERSE=157222862038521//cookie
-//query string ("-" means no query string)
00:00:18//duration
N//context
1//client cache
-//additional parameters (ignored)
*/


LOGPARSE.H

#if !defined(AFX_LOGPARSE_H__F7BCDEE3_DE91_11D0_BB1C_00C04FB66F63__INCLUDED_)
#define AFX_LOGPARSE_H__F7BCDEE3_DE91_11D0_BB1C_00C04FB66F63__INCLUDED_

// LOGPARSE.H - Header file for your Internet Server
//    LogParse Extension

#include "resource.h"

const GUID CLSID_StdEncodingFilterFac = {0x54c37cd0,0xd944,0x11d0,{0xa9,0xf4,0x00,0x60,0x97,0x94,0x23,0x11}};
const GUID IID_IEncodingFilterFactory = {0x70bdde00,0xc18e,0x11d0,{0xa9,0xce,0x00,0x60,0x97,0x94,0x23,0x11}};

static const TCHAR c_szEncodingType[] = _T("HTTP_CONTENT_TRANSFER_ENCODING");

static const TCHAR g_szRegW3SVC[]
          = _T("SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters");
static const TCHAR g_szRegLogFileDirectory[]
          = _T("LogFileDirectory");
static const TCHAR g_szLogFilePath[]
          = _T("\\ie4\\");

static const TCHAR g_szFields[]    = _T("#Fields: ");
static const TCHAR g_szURIFields[] = _T("#Fields: s-URI ");
static const TCHAR g_szURI[]       = _T("s-URI ");
static const TCHAR g_szContext[]   = _T("x-context ");
static const TCHAR g_szCache[]     = _T("x-cache ");
static const TCHAR g_szDate[]      = _T("x-date ");
static const TCHAR g_szTime[]      = _T("x-time ");
static const TCHAR g_szDuration[]  = _T("x-duration ");
static const TCHAR g_szMeta[]      = _T("x-meta ");

#define interselog_cookiename "INTERSE"

#define sizeof_header   4096
/*
 * The "name" of the cookie assigned to each user. We strongly recommend that
 * the default value (INTERSE) be used. This value can be set to anything, but
 * once set, should never be changed. If you have used previous versions of
 * this Inters  ISAPI extension and have used a different cookie name before,
 * you will need to edit the cookie name to specify the name that you had used
 * with the previous release.
 *
 */

class CLogParseExtension : public CHttpServer
{
public:
CLogParseExtension();
~CLogParseExtension();

// Overrides
// ClassWizard generated virtual function overrides
// NOTE - the ClassWizard will add and remove member functions here.
//    DO NOT EDIT what you see in these blocks of generated code !
//{{AFX_VIRTUAL(CLogParseExtension)
public:
virtual BOOL GetExtensionVersion(HSE_VERSION_INFO* pVer);
//}}AFX_VIRTUAL

// TODO: Add handlers for your commands here.
// For example:

    void    LogParse(CHttpServerContext* pCtxt, LPTSTR pszString);

    DECLARE_PARSE_MAP()

    protected:
    BOOL    OpenLogFile(CHttpServerContext* pCtxt);
    DWORD   AppendContentToLogfile(CHttpServerContext* pCtxt, LPCTSTR pszContent, DWORD ContentLen, BOOL fEncoded);
    BOOL    DecodeContent(CHttpServerContext* pCtxt, LPCTSTR pszEncodedString, DWORD cbEncode,
                            LPTSTR pszDecString, DWORD cbDecode);
    DWORD   ConvertToW3CFlexible(CHttpServerContext* pCtxt, LPTSTR pszClientLog);
    DWORD   ProcessFixedLogFields(CHttpServerContext* pCtxt);
    BOOL    GetUserCookie(CHttpServerContext* pCtxt, LPTSTR pszCookie, LPDWORD pcb);
    DWORD   ProcessClientLog(CHttpServerContext* pCtxt, LPTSTR pszClientLog, LPDWORD pMove);

    private:
    HANDLE  m_hFile;
    DWORD   m_ContentLen;
    LPTSTR  m_szEncoding;
    TCHAR   m_szLogFile[MAX_PATH*2];

    DWORD   m_cbURILen;
    DWORD   m_cbLogLen;
    BOOL    m_fInLog;
    //{{AFX_MSG(CLogParseExtension)
//}}AFX_MSG
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_LOGPARSE_H__F7BCDEE3_DE91_11D0_BB1C_00C04FB66F63__INCLUDED)


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by LogParse.rc
//

#define IDS_SERVER        102

#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_CONTROL_VALUE         103
#define _APS_NEXT_SYMED_VALUE           103
#define _APS_NEXT_COMMAND_VALUE         32768


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//LogParse.pch will be the pre-compiled header
//stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"


STDAFX.H

#if !defined(AFX_STDAFX_H__F7BCDEE9_DE91_11D0_BB1C_00C04FB66F63__INCLUDED_)
#define AFX_STDAFX_H__F7BCDEE9_DE91_11D0_BB1C_00C04FB66F63__INCLUDED_

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#include <afx.h>
#include <afxwin.h>
#include <afxmt.h>// for synchronization objects
#include <afxext.h>
#include <afxisapi.h>

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__F7BCDEE9_DE91_11D0_BB1C_00C04FB66F63__INCLUDED)


README.TXT

//-------------------------------------------------------------------------
// Readme.txt
//-------------------------------------------------------------------------
// Objvw: A sample that illustrates using IE's Automation and scripting interfaces
// from a contained control
//-------------------------------------------------------------------------
// (C) Copyright 1992-1996 by Microsoft Corporation.  All rights reserved.
//
// 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.
//-------------------------------------------------------------------------

PURPOSE
=======

This sample has been updated for IE4.0. It no longer uses the Base Control
framework.  For the sake of simplicity, the sample is not backwards compatible
with IE 3.X.  The original ObjVw sample can be referenced for information on
the IE 3.0 object model.

   - illustrates accessing IE's Automation model and scripting model from within a
     contained ATL control.

   - illustrates enumerating all ActiveX controls and links in a page and sub
     pages.  This includes an algorithm for recursively enumerating floating
     frames and framesets.

   - illustrate implementing an event sink in a ATL control.

   - illustrates using the DocumentComplete event to identify when pages have been
     completely loaded.  DocumentComplete is fired for each frame in a document.
     DocumentComplete returns the IDispatch for the document that has completed
     loading.  The outermost frame will always fire last so by comparing the
     IDispatch pointer returned by DocumentComplete with the IDispatch of the
     outer frame, it is possible to tell when all frames have completely loaded.

   - Illustrates marking an ATL control "Safe for scripting".

FILES SUPPLIED
==============
     This is a standard VC 5.0 project.  All code relevant to the sample is located
     in ObjVwCtl.cpp and ObjVwCtl.h.

RUNNING THE SAMPLE
==================
     Compile the sample with VC 5.0 ( this will also register the control ).  Open
     the file TestCtl.htm.  This file contains an instance of the control and links
     to several test pages.

SINKING EVENTS
==============
     This ATL control directly implements the DWebBrowserEvents2 interface.  An
     alternative approach would be to implement the sink as a separate object and
     use CComObject::CreateObject to instantiate the sink.  The event sink is
     connected and disconnected in the CObjVw::SetClientSite function.


OBJVW.CPP

// ObjVw.cpp : Implementation of DLL Exports.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f ObjVwps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "ObjVw.h"

#include "ObjVw_i.c"
#include "ObjVwCtl.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_ObjVw, CObjVw)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
_Module.UnregisterServer();
return S_OK;
}


OBJVW.IDL

#include <olectl.h> // ObjVw.idl : IDL source for ObjVw.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (ObjVw.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(5DC52A0F-F33E-11D0-8A4F-00C04FB9C3A5),
dual,
helpstring("IObjVw Interface"),
pointer_default(unique)
]
interface IObjVw : IDispatch
{
[id(1), helpstring("method Recalc")] HRESULT Recalc();
};

    [
      uuid(34A715A0-6587-11D0-924A-0020AFC7AC4D),
      helpstring("Web Browser Control events interface"),
      hidden
    ]
    interface DWebBrowserEvents2 : IDispatch
{
            [id(0x00000066), helpstring("Statusbar text changed.")]
            HRESULT StatusTextChange([in] BSTR Text);
            [id(0x0000006c), helpstring("Fired when download progress is updated.")]
            HRESULT ProgressChange(
                            [in] long Progress,
                            [in] long ProgressMax);
            [id(0x00000069), helpstring("The enabled state of a command changed.")]
            HRESULT CommandStateChange(
                            [in] long Command,
                            [in] VARIANT_BOOL Enable);
            [id(0x0000006a), helpstring("Download of a page started.")]
            HRESULT DownloadBegin();
            [id(0x00000068), helpstring("Download of page complete.")]
            HRESULT DownloadComplete();
            [id(0x00000071), helpstring("Document title changed.")]
            HRESULT TitleChange([in] BSTR Text);
            [id(0x00000070), helpstring("Fired when the PutProperty method has been called.")]
            HRESULT PropertyChange([in] BSTR szProperty);
            [id(0x000000fa), helpstring("Fired before navigate occurs in the given WebBrowser (window or frameset element). The processing of this navigation may be modified.")]
            HRESULT BeforeNavigate2(
                            [in] IDispatch* pDisp,
                            [in] VARIANT* URL,
                            [in] VARIANT* Flags,
                            [in] VARIANT* TargetFrameName,
                            [in] VARIANT* PostData,
                            [in] VARIANT* Headers,
                            [out] VARIANT_BOOL* Cancel);
            [id(0x000000fb), helpstring("A new, hidden, non-navigated WebBrowser window is needed.")]
            HRESULT NewWindow2(
                            [out] IDispatch** ppDisp,
                            [out] VARIANT_BOOL* Cancel);
            [id(0x000000fc), helpstring("Fired when the document being navigated to becomes visible and enters the navigation stack.")]
            HRESULT NavigateComplete2(
                            [in] IDispatch* pDisp,
                            [in] VARIANT* URL);
            [id(0x00000103), helpstring("Fired when the document being navigated to reaches ReadyState_Complete.")]
            HRESULT DocumentComplete(
                            [in] IDispatch* pDisp,
                            [in] VARIANT* URL);
            [id(0x000000fd), helpstring("Fired when application is quiting.")]
            HRESULT OnQuit();
            [id(0x000000fe), helpstring("Fired when the window should be shown/hidden")]
            HRESULT OnVisible([in] VARIANT_BOOL Visible);
            [id(0x000000ff), helpstring("Fired when the toolbar  should be shown/hidden")]
            HRESULT OnToolBar([in] VARIANT_BOOL ToolBar);
            [id(0x00000100), helpstring("Fired when the menubar should be shown/hidden")]
            HRESULT OnMenuBar([in] VARIANT_BOOL MenuBar);
            [id(0x00000101), helpstring("Fired when the statusbar should be shown/hidden")]
            HRESULT OnStatusBar([in] VARIANT_BOOL StatusBar);
            [id(0x00000102), helpstring("Fired when fullscreen mode should be on/off")]
            HRESULT OnFullScreen([in] VARIANT_BOOL FullScreen);
            [id(0x00000104), helpstring("Fired when theater mode should be on/off")]
            HRESULT OnTheaterMode([in] VARIANT_BOOL TheaterMode);
    };

[
uuid(42BE256D-F339-11D0-8A4E-00C04FB9C3A5),
version(1.0),
helpstring("ObjVw 1.0 Type Library")
]
library OBJVWLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

[
uuid(5DC52A12-F33E-11D0-8A4F-00C04FB9C3A5),
helpstring("ObjVw Class")
]
coclass ObjVw
{
[default] interface IObjVw;
interface DWebBrowserEvents2;
};
};


OBJVWCTL.CPP

// ObjVwCtl.cpp : Implementation of CObjVw
#include "stdafx.h"
#include "ObjVw.h"
#include "ObjVwCtl.h"

/////////////////////////////////////////////////////////////////////////////
// CObjVw

STDMETHODIMP CObjVw::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_IObjVw,
};
for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}

HRESULT CObjVw::SetClientSite(IOleClientSite *pClientSite)
{
HRESULT hr = S_OK;

if(NULL != pClientSite)
{
ATLTRACE(_T("SetClientSite called\n"));

if(m_spClientSite)
m_spClientSite->Release();

m_spClientSite = pClientSite;  // This AddRefs the client site
}
else
{
_ASSERT(0 != m_dwAdviseCookie);
_ASSERT(m_spWebBrowser != NULL);

ATLTRACE(_T("SetClientSite NULL called\n"));

hr = AtlUnadvise( m_spWebBrowser, __uuidof(SHDocVw::DWebBrowserEvents2),
m_dwAdviseCookie );

m_spWebBrowser.Release();

returnS_OK;
}

try
{
IServiceProviderPtr spSP((LPOLECLIENTSITE)m_spClientSite);

if(NULL == spSP)
return hr;

spSP->QueryService(__uuidof(SHDocVw::IWebBrowserApp),
__uuidof(SHDocVw::IWebBrowser2), (void**)&m_spWebBrowser);

hr = AtlAdvise(m_spWebBrowser, GetControllingUnknown(),
__uuidof(SHDocVw::DWebBrowserEvents2), &m_dwAdviseCookie);
}
catch(...)
{
return S_FALSE;
}

return hr;
}

HRESULT CObjVw::OnDraw(ATL_DRAWINFO& di)
{
return S_OK;
}

STDMETHODIMP CObjVw::Recalc()
{
EnumFrames();
return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// CIEDriveView message handlers

void CObjVw::EnumFrames()
{
// Empty the tree view
TreeView_DeleteAllItems(m_ctlSysTreeView32.m_hWnd);

try
{
MSHTML::IHTMLDocument2Ptr spDocument(m_spWebBrowser->GetDocument());
RecurseWindows(spDocument);
}
catch(_com_error Error)
{
ATLTRACE(Error.ErrorMessage());
}
catch(...)
{
ATLTRACE(_T("Unspecified exception thrown in OnTestEnumlinks\n"));
}

HTREEITEM hFirst = TreeView_GetRoot(m_ctlSysTreeView32.m_hWnd);
TreeView_Expand(m_ctlSysTreeView32.m_hWnd, hFirst, TVE_EXPAND);
}

void CObjVw::RecurseWindows(MSHTML::IHTMLDocument2Ptr spDocument, LPVOID pvTreeParent)
{
// I am passing in a pointer to the parent node to insert items in the Tree View.
// Obviously this is specific to this application and has little to do with
// enumerating frames in a document.

LPVOID ppvTree = NULL;

if(spDocument == NULL)
// Not an HTLM document
return;

try
{
// Insert a root node for this page.
_bstr_t bstrDocTitle = spDocument->Gettitle();

_bstr_t bstrNoTitle("");
if(bstrDocTitle == bstrNoTitle)
bstrDocTitle = "Untitled Document";

InsertTreeItem(bstrDocTitle, pvTreeParent, &ppvTree);

// Find out if this page has a "BODY".  If it does, it will not have a
// <FRAMESET> but may still have a floating frame.

MSHTML::IHTMLElementPtr spBodyElement = spDocument->Getbody();

_bstr_t bstrBody("BODY");
_bstr_t bstrTagName = spBodyElement->GettagName();

if(bstrBody == bstrTagName)
{
ATLTRACE(_T("Found a body\n"));

GetPageContents(spDocument, ppvTree);
}

MSHTML::IHTMLFramesCollection2Ptr spFrameset = spDocument->Getframes();

for(long i = 0; i < spFrameset->Getlength(); i++)
{
_variant_t va(i, VT_I4);

MSHTML::IHTMLWindow2Ptr spWindow2Next = spFrameset->item(&va);
MSHTML::IHTMLDocument2Ptr spNextDocPtr;
try
{
// This will die in the case of a cross frame security violation.
// It would be a security risk to allow a page to access the contents
// of a page in another domain or zone.

spNextDocPtr = spWindow2Next->Getdocument();
}
catch(_com_error Error)
{
HRESULT hr = 0x80070005;

ATLTRACE(Error.ErrorMessage());
if(hr == Error.Error())
InsertTreeItem(_T("Page - Access Denied"), ppvTree, NULL);

continue;
}
catch(...)
{
ATLTRACE(_T("Unspecified exception thrown in RecurseWindows\n"));
continue;
}

RecurseWindows(spNextDocPtr, ppvTree);
}
}
catch(_com_error Error)
{
ATLTRACE(Error.ErrorMessage());
}
catch(...)
{
ATLTRACE(_T("Unspecified exception thrown in RecurseWindows\n"));
}
}

void CObjVw::GetPageContents(MSHTML::IHTMLDocument2Ptr spDocument, LPVOID pvTreeParent)
{
try
{
MSHTML::IHTMLElementPtr spAnElement;
MSHTML::IHTMLElementCollectionPtr spAllElements = spDocument->Getall();

for(long i = 0; i < spAllElements->Getlength(); i++)
{
_variant_t va(i, VT_I4);
spAnElement = spAllElements->item(va);

_bstr_t bstrIsControl("OBJECT");
_bstr_t bstrIsLink("A");
_bstr_t bstrTagName = spAnElement->GettagName();

if(bstrTagName == bstrIsControl)
{
// This will get you any ActiveX controls in a page.  It is possible
// to call methods and properties of the  control off the IHTMLElementPtr.

////This is for illustration only
//{
//OLECHAR* rgwzCaption[] = {OLESTR("Caption")};
//DISPID rgdispidCaption[1];
//
//_bstr_t bstrLabel1("Label1");
//if(spAnElement->Getid() == bstrLabel1)
//{
//IDispatchPtr pDispatch(spAnElement);
//HRESULT hr = pDispatch->GetIDsOfNames(IID_NULL, rgwzCaption, 1,
//LOCALE_SYSTEM_DEFAULT, rgdispidCaption);
//
//if(SUCCEEDED(hr))
//{
//// You would call Invoke at this point
//}
//}
//}

_bstr_t bstrTagID("ActiveX Control - ");
bstrTagID += spAnElement->Getid();

InsertTreeItem(bstrTagID, pvTreeParent, NULL);
}
if(bstrTagName == bstrIsLink)
{
MSHTML::IHTMLAnchorElementPtr spAnchor(spAnElement);
_bstr_t bstrHref = spAnchor->Gethref();
InsertTreeItem(bstrHref, pvTreeParent, NULL);
}
}
}
catch(_com_error Error)
{
ATLTRACE(Error.ErrorMessage());
}
catch(...)
{
ATLTRACE(_T("Unspecified exception thrown in GetPageContents\n"));
}
}

bool CObjVw::InsertTreeItem(LPTSTR pszItemText, LPVOID pvTreeParent, LPVOID* ppvTree)
{
    TV_ITEM         tvitem;
    TV_INSERTSTRUCT tvis;
    HTREEITEM*      ppTree = (HTREEITEM*)ppvTree;
    HTREEITEM       pTreeParent = (HTREEITEM)pvTreeParent;
    HTREEITEM       pretTree;

    tvitem.mask = TVIF_TEXT;
    tvitem.pszText = pszItemText;
    tvitem.cchTextMax = _tcslen(pszItemText);
    tvis.item = tvitem;
    tvis.hInsertAfter = (HTREEITEM)TVI_LAST;
    tvis.hParent = pTreeParent;

    pretTree = (HTREEITEM)::SendMessage(m_ctlSysTreeView32.m_hWnd, TVM_INSERTITEM, 0, (LPARAM)&tvis);
    if(!pretTree)
        return false;

    if ( ppvTree )
        (*ppvTree) = (LPVOID)pretTree;

    return true;
}

// DWebBrowserEvents2

STDMETHODIMP CObjVw::StatusTextChange(BSTR Text)
{
ATLTRACE(_T("StatusTextChange called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::ProgressChange(long Progress, long ProgressMax)
{
ATLTRACE(_T("ProgressChange called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::CommandStateChange(long Command, VARIANT_BOOL Enable)
{
ATLTRACE(_T("CommandStateChange called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::DownloadBegin()
{
ATLTRACE(_T("DownloadBegin called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::DownloadComplete()
{
ATLTRACE(_T("DownloadComplete called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::TitleChange(BSTR Text)
{
ATLTRACE(_T("TitleChange called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::PropertyChange(BSTR szProperty)
{
ATLTRACE(_T("PropertyChange called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::BeforeNavigate2(IDispatch* pDisp, VARIANT* URL, VARIANT* Flags,
VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers,
VARIANT_BOOL* Cancel)
{
ATLTRACE(_T("BeforeNavigate2 called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::NewWindow2(IDispatch** ppDisp, VARIANT_BOOL* Cancel)
{
ATLTRACE(_T("NewWindow2 called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::NavigateComplete2(IDispatch* pDisp, VARIANT* URL)
{
ATLTRACE(_T("NavigateComplete2 called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::DocumentComplete(IDispatch* pDisp, VARIANT* URL)
{
// DocumentComplete is fired for each frame that has completed loading.
// The outer most frame will fire last.

ATLTRACE(_T("DocumentComplete called\n"));

if(m_spWebBrowser == pDisp)
{
EnumFrames();
}

return S_OK;
}

STDMETHODIMP CObjVw::OnQuit()
{
ATLTRACE(_T("OnQuit called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::OnVisible(VARIANT_BOOL Visible)
{
ATLTRACE(_T("OnVisible called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::OnToolBar(VARIANT_BOOL ToolBar)
{
ATLTRACE(_T("OnToolBar called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::OnMenuBar(VARIANT_BOOL MenuBar)
{
ATLTRACE(_T("OnMenuBar called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::OnStatusBar(VARIANT_BOOL StatusBar)
{
ATLTRACE(_T("OnStatusBar called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::OnFullScreen(VARIANT_BOOL FullScreen)
{
ATLTRACE(_T("OnFullScreen called\n"));
return S_OK;
}

STDMETHODIMP CObjVw::OnTheaterMode(VARIANT_BOOL TheaterMode)
{
ATLTRACE(_T("OnTheaterMode called\n"));
return S_OK;
}


OBJVWCTL.H

// ObjVwCtl.h : Declaration of the CObjVw

#ifndef __OBJVW_H_
#define __OBJVW_H_

#include "resource.h"       // main symbols
#include <commctrl.h>

#import "mshtml.dll"
#import "shdocvw.dll" exclude("tagREADYSTATE")

/////////////////////////////////////////////////////////////////////////////
// CObjVw
class ATL_NO_VTABLE CObjVw :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CObjVw,&CLSID_ObjVw>,
public CComControl<CObjVw>,
public IDispatchImpl<IObjVw, &IID_IObjVw, &LIBID_OBJVWLib>,
public IDispatchImpl<DWebBrowserEvents2, &IID_DWebBrowserEvents2, &LIBID_OBJVWLib>,
public IPersistStreamInitImpl<CObjVw>,
public IOleControlImpl<CObjVw>,
public IOleObjectImpl<CObjVw>,
public IOleInPlaceActiveObjectImpl<CObjVw>,
public IViewObjectExImpl<CObjVw>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CObjVw>,
public IOleInPlaceObjectWindowlessImpl<CObjVw>,
public IObjectSafetyImpl<CObjVw>
{
public:
CContainedWindow m_ctlSysTreeView32;
SHDocVw::IWebBrowserPtr m_spWebBrowser;
DWORD m_dwAdviseCookie;

CObjVw() :
m_ctlSysTreeView32(_T("SysTreeView32"), this, 1)
{
m_bWindowOnly = TRUE;
m_dwAdviseCookie = 0;
}

void EnumFrames();
void RecurseWindows(MSHTML::IHTMLDocument2Ptr spDocument, LPVOID pvTreeParent = NULL);
void GetPageContents(MSHTML::IHTMLDocument2Ptr spDocument, LPVOID pvTreeParent);
bool CObjVw::GetControlInfo(IUnknownPtr spUnk, LPSTR szClassInfo);

bool InsertTreeItem(LPTSTR pszItemText, LPVOID pvTreeParent, LPVOID* ppvTree);

DECLARE_REGISTRY_RESOURCEID(IDR_OBJVW)
DECLARE_GET_CONTROLLING_UNKNOWN()

BEGIN_COM_MAP(CObjVw)
COM_INTERFACE_ENTRY2(IDispatch, IObjVw)
COM_INTERFACE_ENTRY_IID(IID_IObjVw, IObjVw)
COM_INTERFACE_ENTRY_IID(IID_DWebBrowserEvents2, DWebBrowserEvents2)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY_IMPL(IOleControl)
COM_INTERFACE_ENTRY_IMPL(IOleObject)
COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
COM_INTERFACE_ENTRY_IMPL(IObjectSafety)
END_COM_MAP()

BEGIN_PROPERTY_MAP(CObjVw)
// Example entries
// PROP_ENTRY("Property Description", dispid, clsid)
// PROP_PAGE(CLSID_StockColorPage)
END_PROPERTY_MAP()

BEGIN_CONNECTION_POINT_MAP(CObjVw)
END_CONNECTION_POINT_MAP()

BEGIN_MSG_MAP(CObjVw)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
ALT_MSG_MAP(1)
// Replace this with message map entries for subclassed SysTreeView32
END_MSG_MAP()

LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
RECT rc;
GetWindowRect(&rc);
rc.right -= rc.left;
rc.bottom -= rc.top;
rc.top = rc.left = 0;

m_ctlSysTreeView32.Create(m_hWnd, rc, _T(""),
WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES | TVS_HASBUTTONS);

return 0;
}
STDMETHOD(SetObjectRects)(LPCRECT prcPos,LPCRECT prcClip)
{
IOleInPlaceObjectWindowlessImpl<CObjVw>::SetObjectRects(prcPos, prcClip);
int cx, cy;
cx = prcPos->right - prcPos->left;
cy = prcPos->bottom - prcPos->top;
::SetWindowPos(m_ctlSysTreeView32.m_hWnd, NULL, 0,
0, cx, cy, SWP_NOZORDER | SWP_NOACTIVATE);
return S_OK;
}

STDMETHOD(SetClientSite)(IOleClientSite* pClientSite);

// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IViewObjectEx
STDMETHOD(GetViewStatus)(DWORD* pdwStatus)
{
ATLTRACE(_T("IViewObjectExImpl::GetViewStatus\n"));
*pdwStatus = VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE;
return S_OK;
}

// IObjVw
public:
HRESULT OnDraw(ATL_DRAWINFO& di);

// DWebBrowserEvents2
public:
STDMETHOD(Recalc)();
STDMETHOD(StatusTextChange)(BSTR Text);
STDMETHOD(ProgressChange)(long Progress, long ProgressMax);
STDMETHOD(CommandStateChange)(long Command, VARIANT_BOOL Enable);
STDMETHOD(DownloadBegin)();
STDMETHOD(DownloadComplete)();
STDMETHOD(TitleChange)(BSTR Text);
STDMETHOD(PropertyChange)(BSTR szProperty);
STDMETHOD(BeforeNavigate2)(IDispatch* pDisp, VARIANT* URL, VARIANT* Flags,
VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers,
VARIANT_BOOL* Cancel);
STDMETHOD(NewWindow2)(IDispatch** ppDisp, VARIANT_BOOL* Cancel);
STDMETHOD(NavigateComplete2)(IDispatch* pDisp, VARIANT* URL);
STDMETHOD(DocumentComplete)(IDispatch* pDisp, VARIANT* URL);
STDMETHOD(OnQuit)();
STDMETHOD(OnVisible)(VARIANT_BOOL Visible);
STDMETHOD(OnToolBar)(VARIANT_BOOL ToolBar);
STDMETHOD(OnMenuBar)(VARIANT_BOOL MenuBar);
STDMETHOD(OnStatusBar)(VARIANT_BOOL StatusBar);
STDMETHOD(OnFullScreen)(VARIANT_BOOL FullScreen);
STDMETHOD(OnTheaterMode)(VARIANT_BOOL TheaterMode);
};

#endif //__OBJVW_H_


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by ObjVw.rc
//
#define IDS_PROJNAME                    100
#define IDR_OBJVW                       101

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           103
#endif
#endif


STDAFX.H

// stdafx.h : include file for standard system include files,
//      or project specific include files that are used frequently,
//      but are changed infrequently

#if !defined(AFX_STDAFX_H__42BE2571_F339_11D0_8A4E_00C04FB9C3A5__INCLUDED_)
#define AFX_STDAFX_H__42BE2571_F339_11D0_8A4E_00C04FB9C3A5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#define STRICT

#define _WIN32_WINNT 0x0400
#define _ATL_APARTMENT_THREADED

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>
#include <atlctl.h>
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__42BE2571_F339_11D0_8A4E_00C04FB9C3A5__INCLUDED)


README.TXT

======================================================
Notes for Percy, the IpersistHistory Control
======================================================

Percy is an ATL2.0 control that demonstrates the use of IPersistHistory so that state can be retained if the page containing the control is returned to using the back button.

Percy is a subclassed edit control. Having built the control load percyctl.htm into a browser to see it work.
On navigation away from the page IPersistHistory::SaveHistory() is called supplying a stream into which data should be saved. On return IPersistHistory::LoadHistory() is called so that state can be reloaded.

Note: Only nineteen bytes of data are loaded in the LoadHistory implementation of Percy to load into a temporary buffer.

Percy also implements IObjectSafety specifically to allow IPersistHistory to be used without any security warning dialogs, as Percy is a safe control.


PERCY.CPP

// Percy.cpp : Implementation of DLL Exports.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f Percyps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "Percy.h"

#include "Percy_i.c"
#include "percyctl.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_percyctl, Cpercyctl)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
_Module.UnregisterServer();
return S_OK;
}


PERCY.H

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 3.01.75 */
/* at Sat Jul 26 13:34:45 1997
 */
/* Compiler settings for Percy.idl:
    Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __Percy_h__
#define __Percy_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __Ipercyctl_FWD_DEFINED__
#define __Ipercyctl_FWD_DEFINED__
typedef interface Ipercyctl Ipercyctl;
#endif /* __Ipercyctl_FWD_DEFINED__ */

#ifndef __percyctl_FWD_DEFINED__
#define __percyctl_FWD_DEFINED__

#ifdef __cplusplus
typedef class percyctl percyctl;
#else
typedef struct percyctl percyctl;
#endif /* __cplusplus */

#endif /* __percyctl_FWD_DEFINED__ */

/* header files for imported files */
#include "oaidl.h"
#include "ocidl.h"

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

#ifndef __Ipercyctl_INTERFACE_DEFINED__
#define __Ipercyctl_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: Ipercyctl
 * at Sat Jul 26 13:34:45 1997
 * using MIDL 3.01.75
 ****************************************/
/* [unique][helpstring][dual][uuid][object] */

EXTERN_C const IID IID_Ipercyctl;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("4912B21E-05B0-11D1-AF9A-00A0C909FE5F")
    Ipercyctl : public IDispatch
    {
    public:
    };

#else /* C style interface */

    typedef struct IpercyctlVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            Ipercyctl __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            Ipercyctl __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            Ipercyctl __RPC_FAR * This);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )(
            Ipercyctl __RPC_FAR * This,
            /* [out] */ UINT __RPC_FAR *pctinfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )(
            Ipercyctl __RPC_FAR * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )(
            Ipercyctl __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID __RPC_FAR *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )(
            Ipercyctl __RPC_FAR * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
            /* [out] */ VARIANT __RPC_FAR *pVarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /* [out] */ UINT __RPC_FAR *puArgErr);

        END_INTERFACE
    } IpercyctlVtbl;

    interface Ipercyctl
    {
        CONST_VTBL struct IpercyctlVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define Ipercyctl_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define Ipercyctl_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define Ipercyctl_Release(This)\
    (This)->lpVtbl -> Release(This)

#define Ipercyctl_GetTypeInfoCount(This,pctinfo)\
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)

#define Ipercyctl_GetTypeInfo(This,iTInfo,lcid,ppTInfo)\
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)

#define Ipercyctl_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)\
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)

#define Ipercyctl_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)\
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)

#endif /* COBJMACROS */

#endif /* C style interface */

#endif /* __Ipercyctl_INTERFACE_DEFINED__ */

#ifndef __PERCYLib_LIBRARY_DEFINED__
#define __PERCYLib_LIBRARY_DEFINED__

/****************************************
 * Generated header for library: PERCYLib
 * at Sat Jul 26 13:34:45 1997
 * using MIDL 3.01.75
 ****************************************/
/* [helpstring][version][uuid] */

EXTERN_C const IID LIBID_PERCYLib;

#ifdef __cplusplus
EXTERN_C const CLSID CLSID_percyctl;

class DECLSPEC_UUID("F1D8212E-059E-11D1-AF9A-00A0C909FE5F")
percyctl;
#endif
#endif /* __PERCYLib_LIBRARY_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif


PERCY.IDL

#include <olectl.h> // Percy.idl : IDL source for Percy.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (Percy.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(4912B21E-05B0-11D1-AF9A-00A0C909FE5F),
dual,
helpstring("Ipercyctl Interface"),
pointer_default(unique)
]
interface Ipercyctl : IDispatch
{
};
[
uuid(4912B211-05B0-11D1-AF9A-00A0C909FE5F),
version(1.0),
helpstring("Percy 1.0 Type Library")
]
library PERCYLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

[
uuid(F1D8212E-059E-11D1-AF9A-00A0C909FE5F),
helpstring("percyctl Class")
]
coclass percyctl
{
[default] interface Ipercyctl;
};
};


PERCYCTL.CPP

// percyctl.cpp : Implementation of Cpercyctl
#include "stdafx.h"
#include "Percy.h"
#include "percyctl.h"

/////////////////////////////////////////////////////////////////////////////
// Cpercyctl

HRESULT Cpercyctl::OnDraw(ATL_DRAWINFO& di)
{
return S_OK;
}


PERCYCTL.H

// percyctl.h : Declaration of the Cpercyctl

#ifndef __PERCYCTL_H_
#define __PERCYCTL_H_

#include "resource.h"       // main symbols
#include "perhist.h"

/////////////////////////////////////////////////////////////////////////////
// IPersistHistoryImpl

template <class T>
class ATL_NO_VTABLE IPersistHistoryImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistStreamInitImpl)

// IPersist
STDMETHOD(GetClassID)(CLSID *pClassID)
{
ATLTRACE(_T("IPersistStreamInitImpl::GetClassID\n"));
T* pT = static_cast<T*>(this);
*pClassID = pT->GetObjectCLSID();
return S_OK;
}

STDMETHOD(LoadHistory)( IStream __RPC_FAR *pStream,
            /* [in] */ IBindCtx __RPC_FAR *pbc)
{ return S_OK;}

STDMETHOD(SaveHistory)( IStream __RPC_FAR *pStream)
{ return S_OK;}

STDMETHOD(SetPositionCookie)( DWORD dwPositioncookie)
{ return S_OK;}

STDMETHOD(GetPositionCookie)( DWORD dwPositioncookie)
{ return S_OK;}

};

/////////////////////////////////////////////////////////////////////////////
// Cpercyctl
class ATL_NO_VTABLE Cpercyctl :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<Cpercyctl,&CLSID_percyctl>,
public CComControl<Cpercyctl>,
public IDispatchImpl<Ipercyctl, &IID_Ipercyctl, &LIBID_PERCYLib>,
public IPersistStreamInitImpl<Cpercyctl>,
public IOleControlImpl<Cpercyctl>,
public IOleObjectImpl<Cpercyctl>,
public IOleInPlaceActiveObjectImpl<Cpercyctl>,
public IViewObjectExImpl<Cpercyctl>,
public IOleInPlaceObjectWindowlessImpl<Cpercyctl>,
public IObjectSafetyImpl<Cpercyctl>,
public IPersistHistoryImpl<Cpercyctl>
{
public:
CContainedWindow m_ctlEdit;

char m_pstr[20];

Cpercyctl():m_ctlEdit(_T("Edit"), this, 1)
{

m_bWindowOnly = TRUE;
m_pstr[0] = 0;
}

DECLARE_REGISTRY_RESOURCEID(IDR_PERCYCTL)

BEGIN_COM_MAP(Cpercyctl)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(Ipercyctl)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY_IMPL(IOleControl)
COM_INTERFACE_ENTRY_IMPL(IOleObject)
COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
COM_INTERFACE_ENTRY_IMPL(IObjectSafety)
COM_INTERFACE_ENTRY_IMPL(IPersistHistory)
END_COM_MAP()

BEGIN_PROPERTY_MAP(Cpercyctl)
// Example entries
// PROP_ENTRY("Property Description", dispid, clsid)
// PROP_PAGE(CLSID_StockColorPage)
END_PROPERTY_MAP()

BEGIN_MSG_MAP(Cpercyctl)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
ALT_MSG_MAP(1)
// Replace this with message map entries for subclassed Edit
END_MSG_MAP()

LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
RECT rc;
GetWindowRect(&rc);
rc.right -= rc.left;
rc.bottom -= rc.top;
rc.top = rc.left = 0;
m_ctlEdit.Create(m_hWnd, rc, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER);
// Set the initial value to whatever was passed to us using IPersistHistory
::SetWindowText( m_ctlEdit.m_hWnd, m_pstr);
return 0;
}
STDMETHOD(SetObjectRects)(LPCRECT prcPos,LPCRECT prcClip)
{
IOleInPlaceObjectWindowlessImpl<Cpercyctl>::SetObjectRects(prcPos, prcClip);
int cx, cy;
cx = prcPos->right - prcPos->left;
cy = prcPos->bottom - prcPos->top;
::SetWindowPos(m_ctlEdit.m_hWnd, NULL, 0,
0, cx, cy, SWP_NOZORDER | SWP_NOACTIVATE);
return S_OK;
}

// IViewObjectEx
STDMETHOD(GetViewStatus)(DWORD* pdwStatus)
{
ATLTRACE(_T("IViewObjectExImpl::GetViewStatus\n"));
*pdwStatus = VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE;
return S_OK;
}

// IPersistHistory
STDMETHOD(LoadHistory)( IStream __RPC_FAR *pStream,
            /* [in] */ IBindCtx __RPC_FAR *pbc)
{
// LoadHistory is called if the browser has anything to  persist ino the control
// we just put that into our string for later use when the edit control get's initialised
pStream->Read( m_pstr,19,NULL);
return S_OK;
}

STDMETHOD(SaveHistory)( IStream __RPC_FAR *pStream)
{
// Get the content from the edit control.
// and write it to the stream supplied
::GetWindowText(m_ctlEdit.m_hWnd, m_pstr, 19);
pStream->Write( m_pstr,19, NULL);
return S_OK;
}

// IObjectSafety so we support IPersistHistory as safe as well.
STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
{
ATLTRACE(_T("IObjectSafetyImpl::GetInterfaceSafetyOptions\n"));
if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL)
return E_POINTER;
HRESULT hr = S_OK;
if (riid == IID_IDispatch || riid == IID_IPersistHistory)
{
*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
*pdwEnabledOptions = m_dwSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
}
else
{
*pdwSupportedOptions = 0;
*pdwEnabledOptions = 0;
hr = E_NOINTERFACE;
}
return hr;
}
STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
{
ATLTRACE(_T("IObjectSafetyImpl::SetInterfaceSafetyOptions\n"));
// If we're being asked to set our safe for scripting option then oblige
if (riid == IID_IDispatch || riid == IID_IPersistHistory)
{
// Store our current safety level to return in GetInterfaceSafetyOptions
m_dwSafety = dwEnabledOptions & dwOptionSetMask;
return S_OK;
}
else

return E_NOINTERFACE;
}

// Ipercyctl
public:

HRESULT OnDraw(ATL_DRAWINFO& di);

};

#endif //__PERCYCTL_H_


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Percy.rc
//
#define IDS_PROJNAME                    100
#define IDR_PERCYCTL                    101

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           102
#endif
#endif


STDAFX.H

// stdafx.h : include file for standard system include files,
//      or project specific include files that are used frequently,
//      but are changed infrequently

#if !defined(AFX_STDAFX_H__4912B215_05B0_11D1_AF9A_00A0C909FE5F__INCLUDED_)
#define AFX_STDAFX_H__4912B215_05B0_11D1_AF9A_00A0C909FE5F__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#define STRICT

#define _WIN32_WINNT 0x0400
#define _ATL_APARTMENT_THREADED

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>
#include <atlctl.h>
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__4912B215_05B0_11D1_AF9A_00A0C909FE5F__INCLUDED)


README.TXT

The proftest.htm file contained in this directory shows an example of an HTML file
using the Profile Assistant to automatically populate fields on the form presented
to the user. For more information on the Profile Assistant, refer to the tools
documentation in the "Delivering Content to the Web" section.


LANGCODE.CPP

//+-----------------------------------------------------------------------
//
//  Tabular Data Control Language Support
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       LangCode.cpp
//
//  Contents:   Implementation of the LocalNameToLCID() function.
//
//------------------------------------------------------------------------

#include "stdafx.h"
#include "LangCode.h"
#include "wch.h"

//=======================================================================//

typedef struct
{
    LCID    lcid;       // DWORD Locale
    WCHAR   *pwchAbbrev;// ISO 369 abbreviated (no hyphen) language string
    WCHAR   *pwchFull;  // ISO 369 standard accept language string
}
    LANGPROP;

static LANGPROP langs[] =
{
    { 0x0436, L"af",    L"af"   }, //Afrikaans
    { 0x041c, L"sq",    L"sq"   }, //Albanian
    { 0x0401, L"ar-sa", L"arsa" }, // Arabic(Saudi Arabia)
    { 0x0801, L"ar-iq", L"ariq" }, // Arabic(Iraq)
    { 0x0C01, L"ar-eg", L"areg" }, // Arabic(Egypt)
    { 0x1001, L"ar-ly", L"arly" }, // Arabic(Libya)
    { 0x1401, L"ar-dz", L"ardz" }, // Arabic(Algeria)
    { 0x1801, L"ar-ma", L"arma" }, // Arabic(Morocco)
    { 0x1C01, L"ar-tn", L"artn" }, // Arabic(Tunisia)
    { 0x2001, L"ar-om", L"arom" }, // Arabic(Oman)
    { 0x2401, L"ar-ye", L"arye" }, // Arabic(Yemen)
    { 0x2801, L"ar-sy", L"arsy" }, // Arabic(Syria)
    { 0x2C01, L"ar-jo", L"arjo" }, // Arabic(Jordan)
    { 0x3001, L"ar-lb", L"arlb" }, // Arabic(Lebanon)
    { 0x3401, L"ar-kw", L"arkw" }, // Arabic(Kuwait)
    { 0x3801, L"ar-ae", L"arae" }, // Arabic(U.A.E.)
    { 0x3C01, L"ar-bh", L"arbh" }, // Arabic(Bahrain)
    { 0x4001, L"ar-qa", L"arqa" }, // Arabic(Qatar)
    { 0x042D, L"eu",    L"eu"   }, // Basque
    { 0x0402, L"bg",    L"bg"   }, // Bulgarian
    { 0x0423, L"be",    L"be"   }, // Belarusian
    { 0x0403, L"ca",    L"ca"   }, // Catalan
    { 0x0404, L"zh-tw", L"zhtw" }, // Chinese(Taiwan Region)
    { 0x0804, L"zh-cn", L"zhcn" }, // Chinese(PRC)
    { 0x0C04, L"zh-hk", L"zhhk" }, // Chinese(Hong Kong SAR, PRC)
    { 0x1004, L"zh-sg", L"zhsg" }, // Chinese(Singapore)
    { 0x041a, L"hr",    L"hr"   }, // Croatian
    { 0x0405, L"cs",    L"cs"   }, // Czech
    { 0x0406, L"da",    L"da"   }, // Danish
    { 0x0413, L"nl",    L"nl"   }, // Dutch(Standard)
    { 0x0813, L"nl-be", L"nlbe" }, // Dutch(Belgian)
    { 0x0009, L"en",    L"en"   }, // English
    { 0x0409, L"en-us", L"enus" }, // English(United States)
    { 0x0809, L"en-gb", L"engb" }, // English(British)
    { 0x0c09, L"en-au", L"enau" }, // English(Australian)
    { 0x1009, L"en-ca", L"enca" }, // English(Canadian)
    { 0x1409, L"en-nz", L"ennz" }, // English(New Zealand)
    { 0x1809, L"en-ie", L"enie" }, // English(Ireland)
    { 0x1c09, L"en-za", L"enza" }, // English(South Africa)
    { 0x2009, L"en-jm", L"enjm" }, // English(Jamaica)
    { 0x2409, L"en",    L"en"   }, // English(Caribbean)
    { 0x2809, L"en-bz", L"enbz" }, // English(Belize)
    { 0x2c09, L"en-tt", L"entt" }, // English(Trinidad)
    { 0x0425, L"et",    L"et"   }, // Estonian
    { 0x0438, L"fo",    L"fo"   }, // Faeroese
    { 0x0429, L"fa",    L"fa"   }, // Farsi
    { 0x040b, L"fi",    L"fi"   }, // Finnish
    { 0x040c, L"fr",    L"fr"   }, // French(Standard)
    { 0x080c, L"fr-be", L"frbe" }, // French(Belgian)
    { 0x0c0c, L"fr-ca", L"frca" }, // French(Canadian)
    { 0x100c, L"fr-ch", L"frch" }, // French(Swiss)
    { 0x140c, L"fr-lu", L"frlu" }, // French(Luxembourg)
    { 0x043c, L"gd",    L"gd"   }, // Gaelic(Scots)
    { 0x083c, L"gd-ie", L"gdie" }, // Gaelic(Irish)
    { 0x0407, L"de",    L"de"   }, // German(Standard)
    { 0x0807, L"de-ch", L"dech" }, // German(Swiss)
    { 0x0c07, L"de-at", L"deat" }, // German(Austrian)
    { 0x1007, L"de-lu", L"delu" }, // German(Luxembourg)
    { 0x1407, L"de-li", L"deli" }, // German(Liechtenstein)
    { 0x0408, L"el",    L"el"   }, // Greek
    { 0x040D, L"he",    L"he"   }, // Hebrew
    { 0x0439, L"hi",    L"hi"   }, // Hindi
    { 0x040e, L"hu",    L"hu"   }, // Hungarian
    { 0x040F, L"is",    L"is"   }, // Icelandic
    { 0x0421, L"in",    L"in"   }, // Indonesian
    { 0x0410, L"it",    L"it"   }, // Italian(Standard)
    { 0x0810, L"it-ch", L"itch" }, // Italian(Swiss)
    { 0x0411, L"ja",    L"ja"   }, // Japanese
    { 0x0412, L"ko",    L"ko"   }, // Korean
    { 0x0812, L"ko",    L"ko"   }, // Korean(Johab)
    { 0x0426, L"lv",    L"lv"   }, // Latvian
    { 0x0427, L"lt",    L"lt"   }, // Lithuanian
    { 0x042f, L"mk",    L"mk"   }, // Macedonian
    { 0x043e, L"ms",    L"ms"   }, // Malaysian
    { 0x043a, L"mt",    L"mt"   }, // Maltese
    { 0x0414, L"no",    L"no"   }, // Norwegian(Bokmal)
    { 0x0814, L"no",    L"no"   }, // Norwegian(Nynorsk)
    { 0x0415, L"pl",    L"pl"   }, // Polish
    { 0x0416, L"pt-br", L"ptbr" }, // Portuguese(Brazilian)
    { 0x0816, L"pt",    L"pt"   }, // Portuguese(Standard)
    { 0x0417, L"rm",    L"rm"   }, // Rhaeto-Romanic
    { 0x0418, L"ro",    L"ro"   }, // Romanian
    { 0x0818, L"ro-mo", L"romo" }, // Romanian(Moldavia)
    { 0x0419, L"ru",    L"ru"   }, // Russian
    { 0x0819, L"ru-mo", L"rumo" }, // Russian(Moldavia)
    { 0x043b, L"sz",    L"sz"   }, // Sami(Lappish)
    { 0x0c1a, L"sr",    L"sr"   }, // Serbian(Cyrillic)
    { 0x081a, L"sr",    L"sr"   }, // Serbian(Latin)
    { 0x041b, L"sk",    L"sk"   }, // Slovak
    { 0x0424, L"sl",    L"sl"   }, // Slovenian
    { 0x042e, L"sb",    L"sb"   }, // Sorbian
    { 0x040a, L"es",    L"es"   }, // Spanish(Spain - Traditional Sort)
    { 0x080a, L"es-mx", L"esmx" }, // Spanish(Mexican)
    { 0x0c0a, L"es",    L"es"   }, // Spanish(Spain - Modern Sort)
    { 0x100a, L"es-gt", L"esgt" }, // Spanish(Guatemala)
    { 0x140a, L"es-cr", L"escr" }, // Spanish(Costa Rica)
    { 0x180a, L"es-pa", L"espa" }, // Spanish(Panama)
    { 0x1c0a, L"es-do", L"esdo" }, // Spanish(Dominican Republic)
    { 0x200a, L"es-ve", L"esve" }, // Spanish(Venezuela)
    { 0x240a, L"es-co", L"esco" }, // Spanish(Colombia)
    { 0x280a, L"es-pe", L"espe" }, // Spanish(Peru)
    { 0x2c0a, L"es-ar", L"esar" }, // Spanish(Argentina)
    { 0x300a, L"es-ec", L"esec" }, // Spanish(Ecuador)
    { 0x340a, L"es-cl", L"escl" }, // Spanish(Chile)
    { 0x380a, L"es-uy", L"esuy" }, // Spanish(Uruguay)
    { 0x3c0a, L"es-py", L"espy" }, // Spanish(Paraguay)
    { 0x400a, L"es-bo", L"esbo" }, // Spanish(Bolivia)
    { 0x440a, L"es-sv", L"essv" }, // Spanish(El Salvador)
    { 0x480a, L"es-hn", L"eshn" }, // Spanish(Honduras)
    { 0x4c0a, L"es-ni", L"esni" }, // Spanish(Nicaragua)
    { 0x500a, L"es-pr", L"espr" }, // Spanish(Puerto Rico)
    { 0x0430, L"sx",    L"sx"   }, // Sutu
    { 0x041D, L"sv",    L"sv"   }, // Swedish
    { 0x081d, L"sv-fi", L"svfi" }, // Swedish(Finland)
    { 0x041E, L"th",    L"th"   }, // Thai
    { 0x0431, L"ts",    L"ts"   }, // Tsonga
    { 0x0432, L"tn",    L"tn"   }, // Tswana
    { 0x041f, L"tr",    L"tr"   }, // Turkish
    { 0x0422, L"uk",    L"uk"   }, // Ukrainian
    { 0x0420, L"ur",    L"ur"   }, // Urdu
    { 0x0433, L"ve",    L"ve"   }, // Venda
    { 0x042a, L"vi",    L"vi"   }, // Vietnamese
    { 0x0434, L"xh",    L"xh"   }, // Xhosa
    { 0x043d, L"ji",    L"ji"   }, // Yiddish
    { 0x0435, L"zu",    L"zu"   }, // Zulu
};

//------------------------------------------------------------------------
//
//  Function:   LocalNametoLCID()
//
//  Synopsis:   Searches for the locale ID corresponding to the given
//              ISO 369 locale name.
//
//  Arguments:  pwchLocaleName    Name of locale to match.
//
//  Returns:    Locale ID if a match is found
//              NULL Locale (0x0000) if no match is found
//
//------------------------------------------------------------------------

LCID LocalNameToLCID(LPWCH pwchLocaleName)
{
    int i;
    LCID    lcid    = 0x0000;

    for (i = 0; i < sizeof(langs) / sizeof(langs[0]); i++)
    {
        LANGPROP    *pLang  = &langs[i];

        if (wch_icmp(pwchLocaleName, pLang->pwchAbbrev) == 0 ||
            wch_icmp(pwchLocaleName, pLang->pwchFull) == 0)
        {
            lcid = pLang->lcid;
            break;
        }
    }
    return lcid;
}


LANGCODE.H

//+-----------------------------------------------------------------------
//
//  TDC Array
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       LangCode.h
//
//  Contents:   Declaration of the LocalNameToLCID() function
//
//------------------------------------------------------------------------

#ifndef LANGCODE_H
#define LANGCODE_H

extern LCID LocalNameToLCID(LPWCH pwchLocaleName);

#endif


MLANG.IDL

//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992-1997.
//
//  File: mlang.idl
//
//  Contents: IMultiLanguage interface definition
//
//
//--------------------------------------------------------------------------

cpp_quote("//=--------------------------------------------------------------------------=")
cpp_quote("// MLang.h")
cpp_quote("//=--------------------------------------------------------------------------=")
cpp_quote("// (C) Copyright 1995-1997 Microsoft Corporation.  All Rights Reserved.")
cpp_quote("//")
cpp_quote("// THIS CODE AND INFORMATION IS PROVIDED \"AS IS\" WITHOUT WARRANTY OF")
cpp_quote("// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO")
cpp_quote("// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A")
cpp_quote("// PARTICULAR PURPOSE.")
cpp_quote("//=--------------------------------------------------------------------------=")
cpp_quote("")
cpp_quote("#pragma comment(lib,\"uuid.lib\")")
cpp_quote("")
cpp_quote("//--------------------------------------------------------------------------")
cpp_quote("// IMultiLanguage Interfaces.")
cpp_quote("")

#ifndef DO_NO_IMPORTS
import "unknwn.idl";
#endif

[
    uuid(275c23e0-3747-11d0-9fea-00aa003f8646),
    helpstring("MultiLanguage Object Model"),
    lcid(0x0000),
    version(0.1)
]
library MultiLanguage
{
    importlib("stdole2.tlb");

    [
      object,
      uuid(275c23e3-3747-11d0-9fea-00aa003f8646),
      pointer_default(unique)
    ]
    interface IEnumCodePage : IUnknown
    {
        const USHORT MAX_MIMECP_NAME    = 65;
        const USHORT MAX_MIMECSET_NAME  = 50;
        const USHORT MAX_MIMEFACE_NAME  = 32;

        typedef enum tagMIMECONTF
        {
            MIMECONTF_MAILNEWS          = 1,
            MIMECONTF_BROWSER           = 2,
            MIMECONTF_MINIMAL           = 4,
            MIMECONTF_VALID             = 256
        } MIMECONTF;

        typedef struct tagMIMECPINFO
        {
            DWORD   dwFlags;
            UINT    uiCodePage;
            UINT    uiFamilyCodePage;
            WCHAR   wszDescription[MAX_MIMECP_NAME];
            WCHAR   wszWebCharset[MAX_MIMECSET_NAME];
            WCHAR   wszHeaderCharset[MAX_MIMECSET_NAME];
            WCHAR   wszBodyCharset[MAX_MIMECSET_NAME];
            WCHAR   wszFixedWidthFont[MAX_MIMEFACE_NAME];
            WCHAR   wszProportionalFont[MAX_MIMEFACE_NAME];
            BYTE    bGDICharset;
        } MIMECPINFO, *PMIMECPINFO;

        typedef struct tagMIMECSETINFO
        {
            UINT    uiCodePage;
            UINT    uiInternetEncoding;
            WCHAR   wszCharset[MAX_MIMECSET_NAME];
        } MIMECSETINFO, *PMIMECSETINFO;

        typedef [unique] IEnumCodePage *LPENUMCODEPAGE;

        HRESULT Clone
        (
            [out] IEnumCodePage **ppEnum
        );

        HRESULT Next
        (
            [in]  ULONG         celt,
            [out] PMIMECPINFO   rgelt,
            [out] ULONG         *pceltFetched
        );

        HRESULT Reset();

        HRESULT Skip
        (
            [in]  ULONG         celt
        );
    };

    [
      object,
      uuid(275c23e1-3747-11d0-9fea-00aa003f8646),
      pointer_default(unique)
    ]
    interface IMultiLanguage : IUnknown
    {
        typedef [unique] IMultiLanguage *LPMULTILANGUAGE;

        HRESULT GetNumberOfCodePageInfo
        (
            [out] UINT          *pcCodePage
        );

        HRESULT GetCodePageInfo
        (
            [in]  UINT          uiCodePage,
            [out] PMIMECPINFO   pCodePageInfo
        );

        HRESULT GetFamilyCodePage
        (
            [in]  UINT          uiCodePage,
            [out] UINT          *puiFamilyCodePage
        );

        HRESULT EnumCodePages
        (
            [in]  DWORD         grfFlags,
            [out] IEnumCodePage **ppEnumCodePage
        );

        HRESULT GetCharsetInfo
        (
            [in]  BSTR          Charset,
            [out] PMIMECSETINFO pCharsetInfo
        );

        HRESULT IsConvertible
        (
            [in]  DWORD         dwSrcEncoding,
            [in]  DWORD         dwDstEncoding
        );

        HRESULT ConvertString
        (
            [in]  DWORD         dwSrcEncoding,
            [in]  DWORD         dwDstEncoding,
            [in]  BYTE          *pSrcStr,
            [in,out]  UINT      *pcSrcSize,
            [in]  BYTE          *pDstStr,
            [in,out]  UINT      *pcDstSize
        );

        HRESULT ConvertStringToUnicode
        (
            [in]  DWORD         dwEncoding,
            [in]  CHAR          *pSrcStr,
            [in,out]  UINT      *pcSrcSize,
            [in]  WCHAR         *pDstStr,
            [in,out]  UINT      *pcDstSize
        );

        HRESULT ConvertStringFromUnicode
        (
            [in]  DWORD         dwEncoding,
            [in]  WCHAR         *pSrcStr,
            [in,out]  UINT      *pcSrcSize,
            [in]  CHAR          *pDstStr,
            [in,out]  UINT      *pcDstSize
        );
    };

    [
      uuid(275c23e2-3747-11d0-9fea-00aa003f8646),
    ]
    coclass CMultiLanguage
    {
        [default] interface IMultiLanguage;
    };

}


MSDATSRC.H

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 3.02.88 */
/* at Sat Jun 14 18:54:39 1997
 */
/* Compiler settings for msdatsrc.idl:
    Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"

#ifndef __msdatsrc_h__
#define __msdatsrc_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __DataSourceListener_FWD_DEFINED__
#define __DataSourceListener_FWD_DEFINED__
typedef interface DataSourceListener DataSourceListener;
#endif     /* __DataSourceListener_FWD_DEFINED__ */

#ifndef __DataSource_FWD_DEFINED__
#define __DataSource_FWD_DEFINED__
typedef interface DataSource DataSource;
#endif     /* __DataSource_FWD_DEFINED__ */

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

/****************************************
 * Generated header for interface: __MIDL_itf_msdatsrc_0000
 * at Sat Jun 14 18:54:39 1997
 * using MIDL 3.02.88
 ****************************************/
/* [local] */

// For non-VC5 compilers
#if _MSC_VER >= 1100
#define DECLSPEC_UUID(x)    __declspec(uuid(x))
#else
#define DECLSPEC_UUID(x)
#endif

// C/C++ interface names
#define IDataSource        DataSource
#define IDataSourceListener    DataSourceListener

// Component category guids
EXTERN_C const IID CATID_DataSource;
EXTERN_C const IID CATID_DataConsumer;

extern RPC_IF_HANDLE __MIDL_itf_msdatsrc_0000_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_msdatsrc_0000_v0_0_s_ifspec;

#ifndef __MSDATASRC_LIBRARY_DEFINED__
#define __MSDATASRC_LIBRARY_DEFINED__

/****************************************
 * Generated header for library: MSDATASRC
 * at Sat Jun 14 18:54:39 1997
 * using MIDL 3.02.88
 ****************************************/
/* [helpstring][version][uuid] */

typedef /* [uuid] */ BSTR DataMember;

EXTERN_C const IID LIBID_MSDATASRC;

#ifndef __DataSourceListener_INTERFACE_DEFINED__
#define __DataSourceListener_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: DataSourceListener
 * at Sat Jun 14 18:54:39 1997
 * using MIDL 3.02.88
 ****************************************/
/* [object][hidden][uuid] */

EXTERN_C const IID IID_DataSourceListener;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("7c0ffab2-cd84-11d0-949a-00a0c91110ed")
    DataSourceListener : public IUnknown
    {
    public:
        virtual /* [hidden] */ HRESULT STDMETHODCALLTYPE dataMemberChanged(
            /* [in] */ DataMember bstrDM) = 0;

        virtual /* [hidden] */ HRESULT STDMETHODCALLTYPE dataMemberAdded(
            /* [in] */ DataMember bstrDM) = 0;

        virtual /* [hidden] */ HRESULT STDMETHODCALLTYPE dataMemberRemoved(
            /* [in] */ DataMember bstrDM) = 0;

    };

#else     /* C style interface */

    typedef struct DataSourceListenerVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            DataSourceListener __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            DataSourceListener __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            DataSourceListener __RPC_FAR * This);

        /* [hidden] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *dataMemberChanged )(
            DataSourceListener __RPC_FAR * This,
            /* [in] */ DataMember bstrDM);

        /* [hidden] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *dataMemberAdded )(
            DataSourceListener __RPC_FAR * This,
            /* [in] */ DataMember bstrDM);

        /* [hidden] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *dataMemberRemoved )(
            DataSourceListener __RPC_FAR * This,
            /* [in] */ DataMember bstrDM);

        END_INTERFACE
    } DataSourceListenerVtbl;

    interface DataSourceListener
    {
        CONST_VTBL struct DataSourceListenerVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define DataSourceListener_QueryInterface(This,riid,ppvObject)    \
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define DataSourceListener_AddRef(This)    \
    (This)->lpVtbl -> AddRef(This)

#define DataSourceListener_Release(This)    \
    (This)->lpVtbl -> Release(This)

#define DataSourceListener_dataMemberChanged(This,bstrDM)    \
    (This)->lpVtbl -> dataMemberChanged(This,bstrDM)

#define DataSourceListener_dataMemberAdded(This,bstrDM)    \
    (This)->lpVtbl -> dataMemberAdded(This,bstrDM)

#define DataSourceListener_dataMemberRemoved(This,bstrDM)    \
    (This)->lpVtbl -> dataMemberRemoved(This,bstrDM)

#endif /* COBJMACROS */

#endif     /* C style interface */

/* [hidden] */ HRESULT STDMETHODCALLTYPE DataSourceListener_dataMemberChanged_Proxy(
    DataSourceListener __RPC_FAR * This,
    /* [in] */ DataMember bstrDM);

void __RPC_STUB DataSourceListener_dataMemberChanged_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [hidden] */ HRESULT STDMETHODCALLTYPE DataSourceListener_dataMemberAdded_Proxy(
    DataSourceListener __RPC_FAR * This,
    /* [in] */ DataMember bstrDM);

void __RPC_STUB DataSourceListener_dataMemberAdded_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [hidden] */ HRESULT STDMETHODCALLTYPE DataSourceListener_dataMemberRemoved_Proxy(
    DataSourceListener __RPC_FAR * This,
    /* [in] */ DataMember bstrDM);

void __RPC_STUB DataSourceListener_dataMemberRemoved_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif     /* __DataSourceListener_INTERFACE_DEFINED__ */

#ifndef __DataSource_INTERFACE_DEFINED__
#define __DataSource_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: DataSource
 * at Sat Jun 14 18:54:39 1997
 * using MIDL 3.02.88
 ****************************************/
/* [object][uuid] */

EXTERN_C const IID IID_DataSource;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("7c0ffab3-cd84-11d0-949a-00a0c91110ed")
    DataSource : public IUnknown
    {
    public:
        virtual /* [restricted][hidden] */ HRESULT STDMETHODCALLTYPE getDataMember(
            /* [in] */ DataMember bstrDM,
            /* [in] */ REFIID riid,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppunk) = 0;

        virtual /* [hidden] */ HRESULT STDMETHODCALLTYPE getDataMemberName(
            /* [in] */ long lIndex,
            /* [retval][out] */ DataMember __RPC_FAR *pbstrDM) = 0;

        virtual /* [hidden] */ HRESULT STDMETHODCALLTYPE getDataMemberCount(
            /* [retval][out] */ long __RPC_FAR *plCount) = 0;

        virtual /* [hidden] */ HRESULT STDMETHODCALLTYPE addDataSourceListener(
            /* [in] */ DataSourceListener __RPC_FAR *pDSL) = 0;

        virtual /* [hidden] */ HRESULT STDMETHODCALLTYPE removeDataSourceListener(
            /* [in] */ DataSourceListener __RPC_FAR *pDSL) = 0;

    };

#else     /* C style interface */

    typedef struct DataSourceVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            DataSource __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            DataSource __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            DataSource __RPC_FAR * This);

        /* [restricted][hidden] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *getDataMember )(
            DataSource __RPC_FAR * This,
            /* [in] */ DataMember bstrDM,
            /* [in] */ REFIID riid,
            /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppunk);

        /* [hidden] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *getDataMemberName )(
            DataSource __RPC_FAR * This,
            /* [in] */ long lIndex,
            /* [retval][out] */ DataMember __RPC_FAR *pbstrDM);

        /* [hidden] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *getDataMemberCount )(
            DataSource __RPC_FAR * This,
            /* [retval][out] */ long __RPC_FAR *plCount);

        /* [hidden] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *addDataSourceListener )(
            DataSource __RPC_FAR * This,
            /* [in] */ DataSourceListener __RPC_FAR *pDSL);

        /* [hidden] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *removeDataSourceListener )(
            DataSource __RPC_FAR * This,
            /* [in] */ DataSourceListener __RPC_FAR *pDSL);

        END_INTERFACE
    } DataSourceVtbl;

    interface DataSource
    {
        CONST_VTBL struct DataSourceVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define DataSource_QueryInterface(This,riid,ppvObject)    \
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define DataSource_AddRef(This)    \
    (This)->lpVtbl -> AddRef(This)

#define DataSource_Release(This)    \
    (This)->lpVtbl -> Release(This)

#define DataSource_getDataMember(This,bstrDM,riid,ppunk)    \
    (This)->lpVtbl -> getDataMember(This,bstrDM,riid,ppunk)

#define DataSource_getDataMemberName(This,lIndex,pbstrDM)    \
    (This)->lpVtbl -> getDataMemberName(This,lIndex,pbstrDM)

#define DataSource_getDataMemberCount(This,plCount)    \
    (This)->lpVtbl -> getDataMemberCount(This,plCount)

#define DataSource_addDataSourceListener(This,pDSL)    \
    (This)->lpVtbl -> addDataSourceListener(This,pDSL)

#define DataSource_removeDataSourceListener(This,pDSL)    \
    (This)->lpVtbl -> removeDataSourceListener(This,pDSL)

#endif /* COBJMACROS */

#endif     /* C style interface */

/* [restricted][hidden] */ HRESULT STDMETHODCALLTYPE DataSource_getDataMember_Proxy(
    DataSource __RPC_FAR * This,
    /* [in] */ DataMember bstrDM,
    /* [in] */ REFIID riid,
    /* [retval][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppunk);

void __RPC_STUB DataSource_getDataMember_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [hidden] */ HRESULT STDMETHODCALLTYPE DataSource_getDataMemberName_Proxy(
    DataSource __RPC_FAR * This,
    /* [in] */ long lIndex,
    /* [retval][out] */ DataMember __RPC_FAR *pbstrDM);

void __RPC_STUB DataSource_getDataMemberName_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [hidden] */ HRESULT STDMETHODCALLTYPE DataSource_getDataMemberCount_Proxy(
    DataSource __RPC_FAR * This,
    /* [retval][out] */ long __RPC_FAR *plCount);

void __RPC_STUB DataSource_getDataMemberCount_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [hidden] */ HRESULT STDMETHODCALLTYPE DataSource_addDataSourceListener_Proxy(
    DataSource __RPC_FAR * This,
    /* [in] */ DataSourceListener __RPC_FAR *pDSL);

void __RPC_STUB DataSource_addDataSourceListener_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

/* [hidden] */ HRESULT STDMETHODCALLTYPE DataSource_removeDataSourceListener_Proxy(
    DataSource __RPC_FAR * This,
    /* [in] */ DataSourceListener __RPC_FAR *pDSL);

void __RPC_STUB DataSource_removeDataSourceListener_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif     /* __DataSource_INTERFACE_DEFINED__ */

#endif /* __MSDATASRC_LIBRARY_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif


NOTIFY.CPP

//+-----------------------------------------------------------------------
//
//  TDC / STD Notifications
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       Notify.cpp
//
//  Contents:   Implementation of the CEventBroker class.
//              This class translates internal TDC / STD events into
//              appropriate notifications for the external world.
//
//------------------------------------------------------------------------

#include "stdafx.h"
#include "STD.h"
#include "TDC.h"
#include <MLang.h>
#include "Notify.h"
#include "TDCParse.h"
#include "TDCArr.h"
#include "SimpData.h"
#include "TDCIds.h"
#include "TDCCtl.h"

//------------------------------------------------------------------------
//
//  Method:    CEventBroker()
//
//  Synopsis:  Class constructor
//
//  Arguments: None
//
//------------------------------------------------------------------------

CEventBroker::CEventBroker(CTDCCtl *pReadyStateControl)
{
    m_cRef = 1;
    m_pSTDEvents = NULL;
// ;begin_internal
    m_pDATASRCListener = NULL;
// ;end_internal
    m_pDataSourceListener = NULL;
    m_pBSC = NULL;

    //  Can't AddRef this control, since it has a ref on this object;
    //  would lead to circular refs & zombie objects.
    //
    m_pReadyStateControl = pReadyStateControl;

    // When we're born, we'd better be born READYSTATE_COMPLETE.
    // If and when a query starts, we can go READYSTATE_LOADED.
    m_lReadyState = READYSTATE_COMPLETE;
}

CEventBroker::~CEventBroker()
{

    SetDataSourceListener(NULL);
// ;begin_internal
    SetDATASRCListener(NULL);
// ;end_internal
    SetSTDEvents(NULL);
}

//+-----------------------------------------------------------------------
//
//  Method:    AddRef()
//
//  Synopsis:  Implements part of the standard IUnknown COM interface.
//               (Adds a reference to this COM object)
//
//  Arguments: None
//
//  Returns:   Number of references to this COM object.
//
//+-----------------------------------------------------------------------

STDMETHODIMP_(ULONG)
CEventBroker::AddRef ()
{
    return ++m_cRef;
}

//+-----------------------------------------------------------------------
//
//  Method:    Release()
//
//  Synopsis:  Implements part of the standard IUnknown COM interface.
//               (Removes a reference to this COM object)
//
//  Arguments: None
//
//  Returns:   Number of remaining references to this COM object.
//             0 if the COM object is no longer referenced.
//
//+-----------------------------------------------------------------------

STDMETHODIMP_(ULONG)
CEventBroker::Release ()
{
    ULONG retval;

    retval = --m_cRef;

    if (m_cRef == 0)
    {
        m_cRef = 0xffff;
        delete this;
    }

    return retval;
}

//------------------------------------------------------------------------
//
//  Method:    GetReadyState()
//
//  Synopsis:  Returns the current ReadyState in the supplied pointer.
//
//  Arguments: plReadyState    Pointer to space to hold ReadyState result
//
//  Returns:   S_OK indicating success.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::GetReadyState(LONG *plReadyState)
{
    *plReadyState = m_lReadyState;
    return S_OK;
}

//------------------------------------------------------------------------
//
//  Method:    UpdateReadySTate()
//
//  Synopsis:  Update our ReadyState and FireOnChanged iif it changed
//
//  Arguments: lReadyState    new ReadyState
//
//  Returns:   S_OK indicating success.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::UpdateReadyState(LONG lReadyState)
{
    // If we're actually stopping something, then fire READYSTATE_COMPLETE
    if (m_lReadyState != lReadyState)
    {
        m_lReadyState = lReadyState;
        if (m_pReadyStateControl != NULL)
        {
            m_pReadyStateControl->FireOnChanged(DISPID_READYSTATE);
            m_pReadyStateControl->FireOnReadyStateChanged();
        }
    }

    return S_OK;
}

//------------------------------------------------------------------------
//
//  Method:    SetDataSourceListener()
//
//  Synopsis:  Sets the COM object which should receive DATASRC
//             notification events.
//
//  Arguments: pDataSourceLIstener  Pointer to COM object to receive notification
//                               events, or NULL if no notifications to be sent.
//
//  Returns:   S_OK indicating success.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::SetDataSourceListener(DataSourceListener *pDataSourceListener)
{
    // If we've changed/reset the data source listener, make sure we don't
    // think we've fired dataMemberChanged on it yet.
    ClearInterface(&m_pDataSourceListener);

    if (pDataSourceListener != NULL)
    {
        m_pDataSourceListener = pDataSourceListener;
        m_pDataSourceListener->AddRef();
    }
    return S_OK;
}

// ;begin_internal
//------------------------------------------------------------------------
//
//  Method:    SetDATASRCListener()
//
//  Synopsis:  Sets the COM object which should receive DATASRC
//             notification events.
//
//  Arguments: pDATASRCLIstener  Pointer to COM object to receive notification
//                               events, or NULL if no notifications to be sent.
//
//  Returns:   S_OK indicating success.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::SetDATASRCListener(DATASRCListener *pDATASRCListener)
{
    // If we've changed/reset the data source listener, make sure we don't
    // think we've fired dataMemberChanged on it yet.
    ClearInterface(&m_pDATASRCListener);

    if (pDATASRCListener != NULL)
    {
        m_pDATASRCListener = pDATASRCListener;
        m_pDATASRCListener->AddRef();
    }
    return S_OK;
}
// ;end_internal

//------------------------------------------------------------------------
//
//  Method:    SetSTDEvents()
//
//  Synopsis:  Sets the COM object which should receive DATASRC
//             notification events.
//
//  Arguments: pSTDEvents     Pointer to COM object to receive notification
//                            events, or NULL if no notifications to be sent.
//
//  Returns:   S_OK indicating success.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::SetSTDEvents(OLEDBSimpleProviderListener *pSTDEvents)
{
    ClearInterface(&m_pSTDEvents);

    if (pSTDEvents != NULL)
    {
        m_pSTDEvents = pSTDEvents;
        m_pSTDEvents->AddRef();
    }
    return S_OK;
}

//------------------------------------------------------------------------
//
//  Method:    aboutToChangeCell()
//
//  Synopsis:  Notifies anyone who wants to know that a particular cell
//             is about to change.
//
//  Arguments: iRow           Row number of the cell that has changed.
//             iCol           Column number of the cell that has changed.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::aboutToChangeCell(LONG iRow, LONG iCol)
{
    HRESULT hr = S_OK;

    _ASSERT(iRow >= 0);
    _ASSERT(iCol >= 1);
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->aboutToChangeCell(iRow, iCol);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    CellChanged()
//
//  Synopsis:  Notifies anyone who wants to know that a particular cell
//             has changed.
//
//  Arguments: iRow           Row number of the cell that has changed.
//             iCol           Column number of the cell that has changed.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::cellChanged(LONG iRow, LONG iCol)
{
    HRESULT hr = S_OK;

    _ASSERT(iRow >= 0);
    _ASSERT(iCol >= 1);
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->cellChanged(iRow, iCol);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    RowChanged()
//
//  Synopsis:  Notifies anyone who wants to know that a particular row
//             has changed.
//
//  Arguments: iRow           Number of the row that has changed.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::RowChanged(LONG iRow)
{
    HRESULT hr = S_OK;

    _ASSERT(iRow >= 0);
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->cellChanged(iRow, -1);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    ColChanged()
//
//  Synopsis:  Notifies anyone who wants to know that a particular column
//             has changed.
//
//  Arguments: iCol           Number of the column that has changed.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::ColChanged(LONG iCol)
{
    HRESULT hr = S_OK;

    _ASSERT(iCol > 0);
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->cellChanged(-1, iCol);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    aboutToDeleteRows()
//
//  Synopsis:  Notifies anyone who wants to know that a some rows
//             have been deleted.
//
//  Arguments: iRowStart      Number of row on which deletion started.
//             iRowCount      Number of rows deleted.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::aboutToDeleteRows(LONG iRowStart, LONG iRowCount)
{
    HRESULT hr = S_OK;

    _ASSERT(iRowStart >= 0);
    _ASSERT(iRowCount > 0);
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->aboutToDeleteRows(iRowStart, iRowCount);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    deletedRows()
//
//  Synopsis:  Notifies anyone who wants to know that a some rows
//             have been deleted.
//
//  Arguments: iRowStart      Number of row on which deletion started.
//             iRowCount      Number of rows deleted.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::deletedRows(LONG iRowStart, LONG iRowCount)
{
    HRESULT hr = S_OK;

    _ASSERT(iRowStart >= 0);
    _ASSERT(iRowCount > 0);
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->deletedRows(iRowStart, iRowCount);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    aboutToInsertRows()
//
//  Synopsis:  Notifies anyone who wants to know that a some rows
//             have been inserted.
//
//  Arguments: iRowStart      Number of row on which insertion started.
//             iRowCount      Number of rows inserted.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::aboutToInsertRows(LONG iRowStart, LONG iRowCount)
{
    HRESULT hr = S_OK;

    _ASSERT(iRowStart >= 0);
    _ASSERT(iRowCount > 0);
    if (m_pSTDEvents != NULL)
            m_pSTDEvents->aboutToInsertRows(iRowStart, iRowCount);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    insertedRows()
//
//  Synopsis:  Notifies anyone who wants to know that a some rows
//             have been inserted.
//
//  Arguments: iRowStart      Number of row on which insertion started.
//             iRowCount      Number of rows inserted.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::insertedRows(LONG iRowStart, LONG iRowCount)
{
    HRESULT hr = S_OK;

    _ASSERT(iRowStart >= 0);
    _ASSERT(iRowCount > 0);
    if (m_pSTDEvents != NULL)
            m_pSTDEvents->insertedRows(iRowStart, iRowCount);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    rowsAvailable()
//
//  Synopsis:  Notifies anyone who wants to know that a some rows
//             have arrived.  Although this is very similar to insertedRows
//             we want to preserve the distinction between rows that
//             arrive on the wire and an insert operation that might be
//             performed while some data is still downloading.
//
//  Arguments: iRowStart      Number of row on which insertion started.
//             iRowCount      Number of rows inserted.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::rowsAvailable(LONG iRowStart, LONG iRowCount)
{
    HRESULT hr = S_OK;

    _ASSERT(iRowStart >= 0);
    _ASSERT(iRowCount > 0);
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->rowsAvailable(iRowStart, iRowCount);
    return hr;
}

// ;begin_internal
#ifdef NEVER
//------------------------------------------------------------------------
//
//  Method:    DeletedCols()
//
//  Synopsis:  Notifies anyone who wants to know that a some columns
//             have been deleted.
//
//  Arguments: iColStart      Number of column on which deletion started.
//             iColCount      Number of columns deleted.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::DeletedCols(LONG iColStart, LONG iColCount)
{
    HRESULT hr = S_OK;

    _ASSERT(iColStart > 0);
    _ASSERT(iColCount > 0);
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->DeletedColumns(iColStart, iColCount);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    InsertedCols()
//
//  Synopsis:  Notifies anyone who wants to know that a some columns
//             have been inserted.
//
//  Arguments: iColStart      Number of column on which insertion started.
//             iColCount      Number of columns inserted.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::InsertedCols(LONG iColStart, LONG iColCount)
{
    HRESULT hr = S_OK;

    _ASSERT(iColStart > 0);
    _ASSERT(iColCount > 0);
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->InsertedColumns(iColStart, iColCount);
    return hr;
}
#endif
// ;end_internal

//------------------------------------------------------------------------
//
//  Method:    STDLoadStarted()
//
//  Synopsis:  Notifies anyone who wants to know that the STD control
//             has begun loading its data.
//
//  Arguments: pBSC      Pointer to data-retrieval object.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::STDLoadStarted(CComObject<CMyBindStatusCallback<CTDCCtl> > *pBSC, boolean fAppending)
{
    HRESULT hr = S_OK;

    m_pBSC = pBSC;
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    STDLoadCompleted()
//
//  Synopsis:  Notifies anyone who wants to know that the STD control
//             has loaded all of its data.
//             Note this function should be idempotent -- i.e. it may be
//             called more than once in synchronous cases, once when the
//             transfer actually completes, and again as soon as the event
//             sink is actually hooked up in order to fire the transferComplete
//             event.
//
//  Arguments: None.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::STDLoadCompleted()
{
    HRESULT hr = S_OK;

    m_pBSC = NULL;
    if (m_pSTDEvents != NULL)
        hr = m_pSTDEvents->transferComplete(OSPXFER_COMPLETE);
    UpdateReadyState(READYSTATE_COMPLETE);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    STDLoadStopped()
//
//  Synopsis:  Notifies anyone who wants to know that the STD control
//             has aborted the data load operation.
//
//  Arguments: OSPXFER giving reason for stop
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::STDLoadStopped()
{
    HRESULT hr = S_OK;

    if (m_pBSC && m_pBSC->m_spBinding)
    {
        hr = m_pBSC->m_spBinding->Abort();
        m_pBSC = NULL;
    }

    // Right now, any error results in not returning an STD object,
    // therefore we should not fire transfer complete.
    if (m_pSTDEvents)
        hr = m_pSTDEvents->transferComplete(OSPXFER_ABORT);

    UpdateReadyState(READYSTATE_COMPLETE);

    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    STDLoadedHeader()
//
//  Synopsis:  Notifies anyone who wants to know that the STD control
//             has loaded its header row.
//
//  Arguments: None.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::STDLoadedHeader()
{
    HRESULT hr = S_OK;

    hr = STDDataSetChanged();

    UpdateReadyState(READYSTATE_INTERACTIVE);
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    STDSortFilterCompleted()
//
//  Synopsis:  Notifies anyone who wants to know that the STD control
//             has refiltered / resorted its data.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CEventBroker::STDDataSetChanged()
{
    HRESULT hr = S_OK;

    if (m_pDataSourceListener != NULL)
        hr = m_pDataSourceListener->dataMemberChanged(NULL);
// ;begin_internal
    if (m_pDATASRCListener != NULL)
        hr = m_pDATASRCListener->datasrcChanged(NULL, TRUE);
// ;end_internal
    return hr;
}


NOTIFY.H

//+-----------------------------------------------------------------------
//
//  TDC / STD Notifications
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       Notify.h
//
//  Contents:   Declaration of the CEventBroker class.
//              This class translates internal TDC / STD events into
//              appropriate notifications for the external world.
//
//------------------------------------------------------------------------

#include "msdatsrc.h"

template <class T> class CMyBindStatusCallback;
class CTDCCtl;

interface DATASRCListener : public IUnknown
{
    STDMETHOD(datasrcChanged)(BSTR bstrQualifier, BOOL fDataAvail);
};

//------------------------------------------------------------------------
//
//  CEventBroker
//
//  This class translates internal STD / TDC events into appropriate
//  notifications for the outside world.
//
//------------------------------------------------------------------------

class CEventBroker
{
public:
    STDMETHOD_(ULONG,AddRef)    (THIS);
    STDMETHOD_(ULONG,Release)   (THIS);
// ;begin_internal
    STDMETHOD(SetDATASRCListener)(DATASRCListener *);
// ;end_internal
    STDMETHOD(SetDataSourceListener)(DataSourceListener *);
    STDMETHOD(SetSTDEvents)(OLEDBSimpleProviderListener *);
    inline DataSourceListener *GetDataSourceListener();
    inline DATASRCListener *GetDATASRCListener();
    inline OLEDBSimpleProviderListener *GetSTDEvents();

    CEventBroker(CTDCCtl *pReadyStateControl);
    ~CEventBroker();

    STDMETHOD(aboutToChangeCell)(LONG iRow, LONG iCol);
    STDMETHOD(cellChanged)(LONG iRow, LONG iCol);
    STDMETHOD(aboutToDeleteRows)(LONG iRowStart, LONG iRowCount);
    STDMETHOD(deletedRows)(LONG iRowStart, LONG iRowCount);
    STDMETHOD(aboutToInsertRows)(LONG iRowStart, LONG iRowCount);
    STDMETHOD(insertedRows)(LONG iRowStart, LONG iRowCount);
    STDMETHOD(rowsAvailable)(LONG iRowStart, LONG iRowCount);

    STDMETHOD(RowChanged)(LONG iRow);
    STDMETHOD(ColChanged)(LONG iCol);
// ;begin_internal
#ifdef NEVER
    STDMETHOD(DeletedCols)(LONG iColStart, LONG iColCount);
    STDMETHOD(InsertedCols)(LONG iColStart, LONG iColCount);
#endif
// ;end_internal
    STDMETHOD(STDLoadStarted)(CComObject<CMyBindStatusCallback<CTDCCtl> > *pBSC,
                              boolean fAppending);
    STDMETHOD(STDLoadCompleted)();
    STDMETHOD(STDLoadStopped)();
    STDMETHOD(STDLoadedHeader)();
    STDMETHOD(STDDataSetChanged)();

    STDMETHOD(GetReadyState)(LONG *plReadyState);
    STDMETHOD(UpdateReadyState)(LONG lReadyState);
    CMyBindStatusCallback<CTDCCtl> *m_pBSC;

private:
    ULONG                    m_cRef;         // interface reference count
    DataSourceListener      *m_pDataSourceListener;
// ;begin_internal
    DATASRCListener         *m_pDATASRCListener;
// ;end_internal
    OLEDBSimpleProviderListener *m_pSTDEvents;
    LONG                     m_lReadyState;
    CTDCCtl                  *m_pReadyStateControl;
};

inline DataSourceListener *CEventBroker::GetDataSourceListener()
{
    return m_pDataSourceListener;
}

// ;begin_internal
inline DATASRCListener *CEventBroker::GetDATASRCListener()
{
    return m_pDATASRCListener;
}
// ;end_internal

inline OLEDBSimpleProviderListener *CEventBroker::GetSTDEvents()
{
    return m_pSTDEvents;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by TDC.rc
//
#define IDS_PROJNAME    100
#define IDR_TDCCtl  101
#define IDR_SimpleTabularData   102
#define IDI_TDCICON 203

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        205
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           102
#endif
#endif


SIMPDATA.CPP

//+-----------------------------------------------------------------------
//
//  Simple Tabular Data Object
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       SimpData.cpp
//
//  Contents:   Implementation of the CSimpleTabularData COM object.
//
//------------------------------------------------------------------------

#include "stdafx.h"
#include "STD.h"
#include "TDC.h"
#include <MLang.h>
#include "Notify.h"
#include "TDCParse.h"
#include "TDCArr.h"
#include "SimpData.h"
#include "TDCIds.h"

//------------------------------------------------------------------------
//
//  Method:    CSimpleTabularData() - Constructor
//
//  Synopsis:  Clears internal data.  Due to the COM model, the
//             member function "Create" should be called to actually
//             initialize the STD data structures.
//------------------------------------------------------------------------

CSimpleTabularData::CSimpleTabularData() : m_cRef(1), m_pTDCArr(NULL)
{
}

//------------------------------------------------------------------------
//
//  Method:    Create()
//
//  Synopsis:  Initializes STD.
//             This method should be called after an STD object is contructed
//             to initialize it.
//
//  Arguments: pTDCArr      Pointer to the TDCArr object containing the
//                          data for this control.
//
//  Returns:   Nothing.
//
//------------------------------------------------------------------------

void
CSimpleTabularData::Create(CTDCArr *pTDCArr)
{
    m_pTDCArr = pTDCArr;
    if (m_pTDCArr != NULL)
        m_pTDCArr->AddRef();
}

//------------------------------------------------------------------------
//
//  Method:    ~CSimpleTabularData()
//
//  Synopsis:  Destructor
//
//------------------------------------------------------------------------

CSimpleTabularData::~CSimpleTabularData()
{
    if (m_pTDCArr != NULL)
        m_pTDCArr->Release();
}

//------------------------------------------------------------------------
//
//  Method:    Invalidate()
//
//  Synopsis:  Marks the current interface as invalid.  References to
//             the TDCArr object are released.  Subsequent calls to the
//             SimpleTabularData interface will return the code E_INVALID.
//
//  Arguments: None.
//
//  Returns:   S_OK to indicate success.
//
//------------------------------------------------------------------------

STDMETHODIMP
CSimpleTabularData::Invalidate()
{
    if (m_pTDCArr != NULL)
    {
        m_pTDCArr->Release();
        m_pTDCArr = NULL;
    }
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////
//
//        Implementation of IUnknown COM interface.
//        -----------------------------------------
//
//////////////////////////////////////////////////////////////////////////

//+-----------------------------------------------------------------------
//
//  Method:    QueryInterface()
//
//  Synopsis:  Implements part of the standard IUnknown COM interface.
//               (Returns a pointer to this COM object)
//
//  Arguments: riid          GUID to recognise
//             ppv           Pointer to this COM object [OUT]
//
//  Returns:   S_OK upon success.
//             E_NOINTERFACE if queried for an unrecognised interface.
//
//+-----------------------------------------------------------------------

STDMETHODIMP
CSimpleTabularData::QueryInterface (REFIID riid, LPVOID * ppv)
{
    HRESULT hr;

    _ASSERTE(ppv != NULL);

    // This is the non-delegating IUnknown implementation
    if (riid == IID_IUnknown || riid == IID_OLEDBSimpleProvider)
    {
        *ppv = this;
        ((LPUNKNOWN)*ppv)->AddRef();
        hr = S_OK;
    }
    else
    {
        *ppv = NULL;
        hr = E_NOINTERFACE;
    }

#ifdef _ATL_DEBUG_QI
    AtlDumpIID(riid, _T("CSimpleTabularData"), hr);
#endif
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Method:    AddRef()
//
//  Synopsis:  Implements part of the standard IUnknown COM interface.
//               (Adds a reference to this COM object)
//
//  Arguments: None
//
//  Returns:   Number of references to this COM object.
//
//+-----------------------------------------------------------------------

STDMETHODIMP_(ULONG)
CSimpleTabularData::AddRef ()
{
    return ++m_cRef;
}

//+-----------------------------------------------------------------------
//
//  Method:    Release()
//
//  Synopsis:  Implements part of the standard IUnknown COM interface.
//               (Removes a reference to this COM object)
//
//  Arguments: None
//
//  Returns:   Number of remaining references to this COM object.
//             0 if the COM object is no longer referenced.
//
//+-----------------------------------------------------------------------

STDMETHODIMP_(ULONG)
CSimpleTabularData::Release ()
{
    ULONG  retval;

    m_cRef -= 1;
    retval = m_cRef;
    if (!m_cRef)
    {
        m_cRef = 0xffff;    //MM: Use this 'flag' for debug?
        delete this;
    }
    return retval;
}


SIMPDATA.H

//+-----------------------------------------------------------------------
//
//  Simple Tabular Data Object
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       SimpData.h
//
//  Contents:   Declaration of the CSimpleTabularData COM object.
//
//------------------------------------------------------------------------

#include "resource.h"       // main symbols
#include "std.h"

//------------------------------------------------------------------------
//
//  Class:     CSimpleTabularData
//
//  Synopsis:  This is the STD interface presented to the outside world.
//             The calls are passed directly to the attached TDCArr object.
//             This allows the CSimpleTabularData interfaces to expire
//             independently of the CTDC control.
//
//------------------------------------------------------------------------

class CSimpleTabularData : public OLEDBSimpleProvider
{
public:
    CSimpleTabularData();
    ~CSimpleTabularData();

    //  This method is used to initialise the object once after construction
    void Create(CTDCArr *pTDCArr);

    //  This method is used to mark the interface as invalid
    STDMETHOD(Invalidate)();

    // IUnknown

    STDMETHOD(QueryInterface)   (REFIID, LPVOID FAR*);
    STDMETHOD_(ULONG,AddRef)    (THIS);
    STDMETHOD_(ULONG,Release)   (THIS);

// These methods and members implement the ISimpleTabularData interface
//
private:
    CTDCArr *m_pTDCArr;
    ULONG   m_cRef;         // interface reference count

public:

    //  The STD methods are passed directly to the attached TDCArr object
    //
    inline STDMETHOD(getRowCount)(LONG *pcRows);
    inline STDMETHOD(getColumnCount)(LONG *pcColumns);
    inline STDMETHOD(getRWStatus)(LONG iRow, LONG iCol, OSPRW *prwStatus);
    inline STDMETHOD(getVariant)(LONG iRow, LONG iCol,
                                 OSPFORMAT fmt, VARIANT *pVar);
    inline STDMETHOD(setVariant)(LONG iRow, LONG iCol,
                                 OSPFORMAT fmt, VARIANT Var);
    inline STDMETHOD(getLocale)(BSTR *pbstrLocale);
    inline STDMETHOD(deleteRows)(LONG iRow, LONG cRows, LONG *pcRowsDeleted);
    inline STDMETHOD(insertRows)(LONG iRow, LONG cRows, LONG *pcRowsInserted);
// ;begin_internal
    inline STDMETHOD(DeleteColumns)(LONG iColumn, LONG cCols,
                                    LONG *pcColsDeleted);
    inline STDMETHOD(InsertColumns)(LONG iColumn, LONG cCols,
                                    LONG *pcColsInserted);
// ;end_internal
    inline STDMETHOD(find)(LONG iRowStart, LONG iColumn, VARIANT val,
              OSPFIND findFlags, OSPCOMP compType, LONG *piRowFound);
    inline STDMETHOD(addOLEDBSimpleProviderListener)(OLEDBSimpleProviderListener *pospIListener);
    inline STDMETHOD(removeOLEDBSimpleProviderListener)(OLEDBSimpleProviderListener *pospIListener);
    inline STDMETHOD(getEstimatedRows)(LONG *pcRows);
    inline STDMETHOD(isAsync)(BOOL *pbAsync);
    inline STDMETHOD(stopTransfer)();
};

inline STDMETHODIMP
CSimpleTabularData::getRowCount(LONG *pcRows)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->getRowCount(pcRows);
}

inline STDMETHODIMP
CSimpleTabularData::getColumnCount(LONG *pcColumns)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->getColumnCount(pcColumns);
}

inline STDMETHODIMP
CSimpleTabularData::getRWStatus(LONG iRow, LONG iColumn, OSPRW *prwStatus)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->getRWStatus(iRow, iColumn, prwStatus);
}

inline STDMETHODIMP
CSimpleTabularData::getVariant(LONG iRow, LONG iColumn, OSPFORMAT format, VARIANT *pVar)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->getVariant(iRow, iColumn, format, pVar);
}

inline STDMETHODIMP
CSimpleTabularData::setVariant(LONG iRow, LONG iColumn, OSPFORMAT format, VARIANT Var)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->setVariant(iRow, iColumn, format, Var);
}

inline STDMETHODIMP
CSimpleTabularData::getLocale(BSTR *pbstrLocale)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->getLocale(pbstrLocale);
}

inline STDMETHODIMP
CSimpleTabularData::deleteRows(LONG iRow, LONG cRows, LONG *pcRowsDeleted)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->deleteRows(iRow, cRows, pcRowsDeleted);
}

inline STDMETHODIMP
CSimpleTabularData::insertRows(LONG iRow, LONG cRows, LONG *pcRowsInserted)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->insertRows(iRow, cRows, pcRowsInserted);
}

// ;begin_internal
inline STDMETHODIMP
CSimpleTabularData::DeleteColumns(LONG iColumn, LONG cColumns, LONG *pcColumnsDeleted)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->DeleteColumns(iColumn, cColumns, pcColumnsDeleted);
}

inline STDMETHODIMP
CSimpleTabularData::InsertColumns(LONG iColumn, LONG cColumns, LONG *pcColumnsInserted)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->InsertColumns(iColumn, cColumns, pcColumnsInserted);
}
// ;end_internal

inline STDMETHODIMP
CSimpleTabularData::find(LONG iRowStart, LONG iColumn, VARIANT val,
        OSPFIND findFlags, OSPCOMP compType, LONG *piRowFound)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->find(iRowStart, iColumn, val,
                                    findFlags, compType, piRowFound);
}

inline STDMETHODIMP
CSimpleTabularData::addOLEDBSimpleProviderListener(OLEDBSimpleProviderListener *pospIListener)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->addOLEDBSimpleProviderListener(pospIListener);
}

inline STDMETHODIMP
CSimpleTabularData::removeOLEDBSimpleProviderListener(OLEDBSimpleProviderListener *pospIListener)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->removeOLEDBSimpleProviderListener(pospIListener);
}

inline STDMETHODIMP
CSimpleTabularData::getEstimatedRows(LONG *pcRows)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->getEstimatedRows(pcRows);
}

inline STDMETHODIMP
CSimpleTabularData::isAsync(BOOL *pbAsync)
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->isAsync(pbAsync);
}

inline STDMETHODIMP
CSimpleTabularData::stopTransfer()
{
    return m_pTDCArr == NULL ? E_UNEXPECTED :
        m_pTDCArr->stopTransfer();
}

typedef CSimpleTabularData *LPSTD;


STD.IDL

//+------------------------------------------------------------------------
//
//  OLEDBSimpleProvider interface
//
//  Copyright (C) Microsoft Corporation, 1993 - 1997.
//
//  File:       std.idl
//
//-------------------------------------------------------------------------

//-------------------------------------------------------------------------
//
// This file is copied from IE4 build \ie\public\sdk\inc\simpdata.dil
//
//-------------------------------------------------------------------------

//  Usage in MSDEV:
//
//  1. Select Tools/Customize
//  2. Go to Tools tab.
//  3. Click on Add...
//  4. Type  MIDL.EXE  and click OK (or press ENTER)
//  5. Fill in definition of a new tool as follows:
//      Menu Text:      Compile &IDL file
//      Command:        MIDL.EXE
//      Arguments:      /ms_ext /char unsigned /c_ext $(FileName)$(FileExt)
//      Initial Dir:    $(FileDir)
//  6. Click on Close
//
//  Having done this, you can now use MSDEV to compile IDL files (such as
//  this one).  Just open the file and select Tools/Compile IDL file.
//
//  Contact: tedsmith, jerryd, terrylu, or cfranks
//
//-------------------------------------------------------------------------

//+------------------------------------------------------------------------
//
//  OLEDBSimpleProvider interface
//
//  Copyright (C) Microsoft Corporation, 1993 - 1997.
//
//  File:       simpdata.idl
//
//-------------------------------------------------------------------------

cpp_quote("//=--------------------------------------------------------------------------=")
cpp_quote("// simpdata.h")
cpp_quote("//=--------------------------------------------------------------------------=")
cpp_quote("// (C) Copyright 1995-1997 Microsoft Corporation.  All Rights Reserved.")
cpp_quote("//")
cpp_quote("// THIS CODE AND INFORMATION IS PROVIDED \"AS IS\" WITHOUT WARRANTY OF")
cpp_quote("// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO")
cpp_quote("// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A")
cpp_quote("// PARTICULAR PURPOSE.")
cpp_quote("//=--------------------------------------------------------------------------=")
cpp_quote("")
cpp_quote("#pragma comment(lib,\"uuid.lib\")")
cpp_quote("")
cpp_quote("//--------------------------------------------------------------------------")
cpp_quote("// Simple Tabular Data.")
cpp_quote("")
cpp_quote("#ifndef SIMPDATA_H")
cpp_quote("#define SIMPDATA_H")

//import "ocidl.idl";
import "objidl.idl";
import "oleidl.idl";
import "oaidl.idl";

// We use signed integers for row and column numbers, starting with the value 1.
// The value 0 refers to any "label" or "header" information, distinct from the
// data.  The value -1 refers to any an unknown value, or all values, so, for
// example, iRow = -1, iCol = 2 refers to all of column 2

cpp_quote("#define OSP_IndexLabel      (0)")
cpp_quote("#define OSP_IndexAll        (~0ul)")
cpp_quote("#define OSP_IndexUnknown    (~0ul)")
cpp_quote("")

[
        uuid(E0E270C2-C0BE-11d0-8FE4-00A0C90A6341),
        helpstring("OLE-DB Simple Provider Type Library"),
        lcid(0x409),
        version(1.4)
]
library std
{

typedef enum OSPFORMAT
{
    OSPFORMAT_RAW = 0,     // set/get variant as is
    OSPFORMAT_DEFAULT = 0, // default is RAW
    OSPFORMAT_FORMATTED = 1,   // all variants converted to string representation
    OSPFORMAT_HTML = 2      // variant converted to HTML string representation
                            // (providers not required to implement this).
} OSPFORMAT;

typedef enum OSPRW
{
    // All cells are presumed to be readable.
    OSPRW_DEFAULT = 1,
    OSPRW_READONLY = 0,     // readonly
    OSPRW_READWRITE = 1,    // readwrite
    OSPRW_MIXED = 2         // mixed or unknown; most callers will treat
                                     // as READWRITE "Lazy" provider should just
                                     // return OSPRW_MIXED
} OSPRW;

typedef enum OSPFIND
{
    OSPFIND_DEFAULT = 0,
    OSPFIND_UP = 1,             // scan backwards(decreasing row #s)
    OSPFIND_CASESENSITIVE = 2
} OSPFIND;

typedef enum OSPCOMP
{
                            // These values derived from bitmasks
                            // i.e.       GT  LT  EQ
    OSPCOMP_EQ = 1,         // OSPCOMP_EQ          1 Equal
    OSPCOMP_DEFAULT = 1,    // OSPCOMP_EQ is the default
    OSPCOMP_LT = 2,         // OSPCOMP_LT      1   0 Less than
    OSPCOMP_LE = 3,         // OSPCOMP_LE      1   1 Less than or equal
    OSPCOMP_GE = 4,         // OSPCOMP_GT  1   0   0 Greater than or equal
    OSPCOMP_GT = 5,         // OSPCOMP_GE  1   0   1 Greater than
    OSPCOMP_NE = 6,         // OSPCOMP_NE  1   1   0 Not equal
} OSPCOMP;

typedef enum OSPXFER
{
    OSPXFER_COMPLETE = 0,
    OSPXFER_ABORT = 1,
    OSPXFER_ERROR = 2
} OSPXFER;

[
        object,
        uuid(E0E270C1-C0BE-11d0-8FE4-00A0C90A6341),
        pointer_default(unique),
        local,
        version(1.4)
]
interface OLEDBSimpleProviderListener : IUnknown
{
      // As noted above; a -1 (STD_IndexAll) can be used in the row or column
      // (or both).  For example, CellChanged(STD_IndexAll, OSP_IndexAll)
      // implies that all rows and columns changed shape.

      HRESULT aboutToChangeCell([in] long iRow, [in] long iColumn);
      HRESULT cellChanged([in] long iRow, [in] long iColumn);
      HRESULT aboutToDeleteRows([in] long iRow, [in] long iColumn);
      HRESULT deletedRows([in] long iRow, [in] long cRows);
      HRESULT aboutToInsertRows([in] long iRow, [in] long cRows);
      HRESULT insertedRows([in] long iRow, [in] long cRows);
      HRESULT rowsAvailable([in] long iRow, [in] long cRows);
      HRESULT transferComplete([in] OSPXFER xfer);
}

[
        object,
        uuid(E0E270C0-C0BE-11d0-8FE4-00A0C90A6341),
        pointer_default(unique),
        local,
        version(1.4)
]
interface OLEDBSimpleProvider : IUnknown
{
      // Info-retrieving functions:
      // ==========================
      //
      // Note that column names are retrieved using GetString or GetVariant
      // with iRow == OSP_IndexLabel.  RWStatus for a whole row or column
          // can be made with iRow or iColumn == OSP_IndexAll.

      HRESULT getRowCount([out,retval] long *pcRows);
      HRESULT getColumnCount([out,retval] long *pcColumns);

      HRESULT getRWStatus([in] long iRow, [in] long iColumn,
              [out, retval] OSPRW *prwStatus);

      // Variant-oriented Get/Set:
      // =========================
      //
      // Note that use of VARIANT precludes references.

      HRESULT getVariant([in] long iRow, [in] long iColumn,
                         [in] OSPFORMAT format, [out, retval] VARIANT *pVar );
      HRESULT setVariant([in] long iRow, [in] long iColumn,
                         [in] OSPFORMAT format, [in] VARIANT Var );

      // Locale information:
      // ===================
      //

      HRESULT getLocale([out,retval] BSTR *pbstrLocale);

      // Deletion and Insertion:
      // =======================
      //
      // For insertion, iRow or iColumn is index *before* which to insert; the
      // first inserted row or column will end up with that index.
      //
      // In case of error, provider may choose to insert or delete only the
      // first m of n rows or columns which client requested.  OUT parameter
      // indicates how many rows or columns actually inserted or deleted.  In
      // case of success, OUT parameter should be filled in with cRows.

      HRESULT deleteRows([in] long iRow, [in] long cRows,
             [out, retval] long *pcRowsDeleted );
      HRESULT insertRows([in] long iRow, [in] long cRows,
             [out, retval] long *pcRowsInserted );

      // Find
      // Find the first row in column iColumn that contains a value which
      // matches val according to the compType.
      // To continue searching past the found row, the caller should pass
      // *piRowFound+1 as the next iRowStart, or *piRowFound-1 for backward
      // searches (STDFIND_UP).

      HRESULT find([in] long iRowStart, [in] long iColumn,
                   [in] VARIANT val,
                   [in] OSPFIND findFlags,
                   [in] OSPCOMP compType,
                   [out, retval] long *piRowFound );

      // Establish or detach single event sink.

      HRESULT addOLEDBSimpleProviderListener([in] OLEDBSimpleProviderListener *pospIListener);
      HRESULT removeOLEDBSimpleProviderListener([in] OLEDBSimpleProviderListener *pospIListener);

      // Asynch support

      HRESULT isAsync([out, retval] bool *pbAsynch);

      HRESULT getEstimatedRows([out, retval] long *piRows);

      // Stops any asynchronous download that may be in progress.
      HRESULT stopTransfer();
}

cpp_quote("typedef OLEDBSimpleProvider *LPOLEDBSimpleProvider;")

}

cpp_quote("#endif")


STDAFX.H

// stdafx.h : include file for standard system include files,
//      or project specific include files that are used frequently,
//      but are changed infrequently

#define STRICT 1
#define _WIN32_WINNT 0x0400
#define _ATL_APARTMENT_THREADED

#include <atlbase.h>

// turn off ATL debugging, always
#undef _ATL_DEBUG_QI
// AddField messages can be so numerous, we have a separate flag
// for them..
#undef TDC_ATL_DEBUG_ADDFIELD
#ifdef _DEBUG
#define TDC_ATL_DEBUG
#endif

//#define MemAlloc(a) CoTaskMemAlloc((a))
//#define MemFree(a) CoTaskMemFree((a))
//#define MemRealloc(a,b) (((*(a) = CoTaskMemRealloc(*(a),(b))) ? S_OK : E_FAIL))

//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>
#include <atlctl.h>

void ClearInterfaceFn(IUnknown ** ppUnk);

template <class PI>
inline void
ClearInterface(PI * ppI)
{
#ifdef _DEBUG
    IUnknown * pUnk = *ppI;
    _ASSERTE((void *) pUnk == (void *) *ppI);
#endif

    ClearInterfaceFn((IUnknown **) ppI);
}

#ifdef TDC_ATL_DEBUG
#define OutputDebugStringX(X) OutputDebugString(X)
#else
#define OutputDebugStringX(X)
#endif


TARRAY.H

//+-----------------------------------------------------------------------
//
//  Template Array Implementation
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:      tarray.h
//
//  Contents:  Template for resizeable arrays.  Allows for the creation
//             and manipulation of arrays of any type.  Arrays can
//             be dynamically reallocated, and thus can "grow and shrink".
//             Constructors and destructors of array elements is
//             automatically handles, even when the size of the array is
//             changed.
//  Templates: TSTDArray
//
//------------------------------------------------------------------------

#ifndef _TARRAY_H_
#define _TARRAY_H_

//+-----------------------------------------------------------------------
//
//  Class:     TSTDArray
//
//  Synopsis:  Contains an array of "type".  Allows array to grow
//             dynamically.  During debug, can check bounds on indices.
//             Array is indexed 0 to _cArraySize-1.  _cArraySize holds
//               number of elements.
//
//  Methods:   Init          allocate memory for array
//             Passivate
//             []            allows indexing of array
//             GetSize       returns size of array
//             InsertElems   insert elements anywhere in array
//             DeleteElems   delete elements anywhere in array
//
//------------------------------------------------------------------------

template <class TYPE>
class TSTDArray
{
public:
    TSTDArray();
#if DBG == 1
    ~TSTDArray();
#endif
    HRESULT Init(const size_t cSize); // initialize data structures
    void Passivate();

    TYPE& operator[](const size_t iElement);
    const TYPE& operator[](const size_t iElement) const; // constant reference
    size_t GetSize() const { return _cArraySize; }

    HRESULT InsertElems(const size_t iElem, const size_t cElems);
    void DeleteElems(const size_t iElem, const size_t cElems);

private:
#if DBG == 1
    void IsValidObject() const;
#else
    void IsValidObject() const
        {   }
#endif

// All elements are packaged inside a class CElem.  This allows us to
//   overload the new operator so that we can manually invoke the
//   constructors.

    class CElem
    {
        friend TSTDArray;
    private:
        // Now we overload the new operator to allow placement argument
        void *operator new(size_t uSize, void *pv) { return pv; }

        // Internal data:
        TYPE _Element;       // actual element
    };

// Internal data:
    CElem *_paArray;        // pointer to actual data
    size_t _cArraySize;
    size_t _cAllocSize;     // the size of allocated object

#if DBG == 1
    // Ensure we call constructors and destructors right number of times.
    //   Used only as a check.
    size_t _cNumElems;
#endif
};

//+---------------------------------------------------------------------------
//
//  Member:     IsValidObject
//
//  Synopsis:   Validation method.  Checks that array structure is valid.
//              It is usefull to call this member function at the beginning
//              of each member function that uses the internal array to
//              ensure that the array is not corrupt before attempting to
//              modify it.
//

#if DBG == 1
template <class TYPE>
void
TSTDArray<TYPE>::IsValidObject() const
{
    _ASSERT("Must have valid this pointer" &&
           this );

    _ASSERT("Array has no memory" &&
           _paArray );

    _ASSERT("destructors called wrong number of times" &&
           (_cNumElems == _cArraySize) );
}
#endif

//+-----------------------------------------------------------------------
//
//  Constructor for TSTDArray
//
//  Synopsis:  Doesn't do anything.  Must call member function init to
//             actually initialize.  Only call init once.
//
//  Arguments: None.
//
//  Returns:   Nothing.
//

template <class TYPE>
TSTDArray<TYPE>::TSTDArray()
{
// We null the internal data, so that they are not actually used
// until the init member function is called.

    _paArray = 0;
    _cArraySize = 0;
    _cAllocSize = 0;

#if DBG == 1
    _cNumElems = 0;
#endif
}

//+-----------------------------------------------------------------------
//
//  Destructor for TSTDArray
//
//  Synopsis:  Must call member function passivate
//             to actually de-initialize.
//
//  Arguments: None.
//
//  Returns:   Nothing.
//

#if DBG == 1
template <class TYPE>
TSTDArray<TYPE>::~TSTDArray()
{
    _ASSERT("Passivate must be called first" &&
           !_paArray );

    _ASSERT("Destructors called wrong number of times" &&
           (_cNumElems == 0) );
}
#endif

//+-----------------------------------------------------------------------
//
//  Member:    Init
//
//  Synopsis:  Initializes the array abstract data type.  Allocates
//             memory for the array.  Also sets the cArraySize to
//             the number of elements.
//
//  Arguments: cSize    initial size of array (# of elements)
//
//  Returns:   Success if memory can be allocated for table.
//             Returns E_OUTOFMEMORY if can't get memory.
//

template <class TYPE>
HRESULT
TSTDArray<TYPE>::Init(const size_t cSize)
{
    HRESULT hr;

    _ASSERT(this);

    _ASSERT("Only call init once" &&
           !_paArray );

// Get memory:

    // 0 element array is made into 1 element array so that it functions
    //   normally.
    {
        //;begin_internal
        // BUGBUG:
        // MSVC 2.0 has a bug in it.  Evaluating the expression sizeof(CElem)
        //   seems to confuse it.  CElem is a class containing a variable
        //   whose size can only be calculated when the template containing it
        //   is instantiated.  In addition to this, CElem is a member of that
        //   template.  Placing the sizeof(CElem) expression within a more
        //   complicated expression is not possible.
        //;end_internal
        size_t uCElemSize;

        uCElemSize = sizeof(CElem);
        uCElemSize *= (cSize == 0 ? 1 : cSize);
        _paArray = (CElem *) CoTaskMemAlloc(uCElemSize);
    }
    if (!_paArray)
    {
        hr = E_OUTOFMEMORY;
    }
    else
    {
        CElem *pTemp;                   // index used to call constructors

        // We need to call the constructors manually for each element:
        for (pTemp = _paArray; pTemp < _paArray + cSize; pTemp++)
        {
            new (pTemp) CElem;
#if DBG == 1
            _cNumElems++;
#endif
        }

        _cArraySize = cSize;
        _cAllocSize = (cSize == 0 ? 1 : cSize);
        hr = S_OK;
    }

    return hr;
}

//+-----------------------------------------------------------------------
//
//  Member:    Passivate
//
//  Synopsis:  Releases memory held in array.  Should be called before
//             the object is destroyed.  Should only be called once on an
//             object.
//
//  Arguments: None.
//
//  Returns:   Nothing.
//
//------------------------------------------------------------------------

template <class TYPE>
void
TSTDArray<TYPE>::Passivate()
{
    IsValidObject();

    _ASSERT("Only call Passivate once" &&
           _paArray );

    // We need to call the destructors manually for each element:

    {
        CElem *pTemp;                   // index used to call destructors

        for (pTemp = _paArray; pTemp < _paArray + _cArraySize; pTemp++)
        {
            pTemp->CElem::~CElem();
#if DBG == 1
            _cNumElems--;
#endif
        }
    }

    CoTaskMemFree(_paArray);

    _paArray = 0;         // make sure we don't call Passivate again
    _cArraySize = 0;
    _cAllocSize = 0;
}

//+-----------------------------------------------------------------------
//
//  Member:    operator[]
//
//  Synopsis:  Allows indexing of array's elements.  Use this to either
//             store an element in the array or read an element from
//             the array.  It is the user's responsibility to ensure
//             that the index is within the proper range, 0.._cArraySize-1.
//             During debugging, the index range is checked.
//
//  Arguments: iElement       Element index
//
//  Returns:   Reference to element.
//

template <class TYPE>
inline
TYPE&
TSTDArray<TYPE>::operator[](const size_t iElement)
{
    IsValidObject();

    _ASSERT("Index is out of range" &&
           (iElement < _cArraySize) );

    return _paArray[iElement]._Element;
}

//+-----------------------------------------------------------------------
//
//  Member:    operator[] const
//
//  Synopsis:  Same as previous [] operator, but returns a constant
//             reference so that it can't be used as an l-value.
//
//  Arguments: iElement       Element index
//
//  Returns:   Constant reference to element.
//

template <class TYPE>
inline
const TYPE&
TSTDArray<TYPE>::operator[](const size_t iElement) const
{
    IsValidObject(this);

    _ASSERT("Index is out of range" &&
           (iElement < _cArraySize) );

    return _paArray[iElement]._Element;
}

//+-----------------------------------------------------------------------
//
//  Member:    InsertElems
//
//  Synopsis:  Changes the size of the array by using MemRealloc().
//             Inserts a number of elements cElems into the array at
//             iElem.  This can be used to add new elements to the end of
//             the array by specifying iElem equal to _cArraySize.  It is
//             the responsibility of the user to make sure that iElem is
//             within the proper bounds of the array, although this will
//             be checked during debug mode.
//
//  Arguments: iElem       place to insert first element
//             cElems      number of new elements
//
//  Returns:   Returns success if elements can be added.
//             Returns E_OUTOFMEMORY if request cannot be met.
//             Array retains its old size if the request cannot be met.
//

template<class TYPE>
HRESULT
TSTDArray<TYPE>::InsertElems(const size_t iElem, const size_t cElems)
{
    HRESULT hr = S_OK;

    IsValidObject();

    // Note that you can insert past the END of an array (appending to it):
    _ASSERT("iElem is too large" &&
           (iElem <= _cArraySize) );

    if (_cArraySize + cElems > _cAllocSize)
    {
        // Resize the current array we have:
        ULONG cAllocSize = _cAllocSize ? _cAllocSize : 8;
        CElem * paArray;                // new array

        // Double alloc size until it's big enough.  This will, I suppose, loop
        // forever if someone asks to allocate more than 2^31 elements.
        _ASSERT(_cArraySize + cElems < MAXLONG);
        while (_cArraySize + cElems > cAllocSize) cAllocSize <<= 1;

        paArray = (CElem *)CoTaskMemRealloc(_paArray, sizeof(CElem) * cAllocSize);

        if (!paArray)
        {
            hr = E_OUTOFMEMORY;
            goto Error;
        }

        _paArray = paArray;
        _cAllocSize = cAllocSize;
    }

    // Now we have to shift elements to allow space for the new elements:
    memmove(_paArray + iElem + cElems,      // dest
            _paArray + iElem,
            (_cArraySize - iElem) * sizeof(CElem));

    // Call constructors on all new elements:
    {
        CElem *pTemp;   // index used to call constructors

        for (pTemp = _paArray + iElem;
             pTemp < _paArray + iElem + cElems;
             pTemp++)
        {
            new (pTemp) CElem;
#if DBG == 1
            _cNumElems++;
#endif
        }
    }

    _cArraySize += cElems;

Error:
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Member:    DeleteElems
//
//  Synopsis:  Deletes a number of elements cElems from the array at
//             iElem.  It is the responsibility of the user to make sure
//             that the region to be deleted is within the proper bounds
//             of the array, although this will be checked during
//             debug mode.
//
//  Arguments: iElem       place to delete first element
//             cElems      number of elements to delete
//
//  Returns:   Returns success.
//

template<class TYPE>
void
TSTDArray<TYPE>::DeleteElems(const size_t iElem, const size_t cElems)
{
    IsValidObject();

    _ASSERT("Region to delete is too large" &&
           (iElem+cElems-1 < _cArraySize) );

    // First we need to call destructors on elements:
    {
        CElem *pTemp;   // index used to call destructors

        for (pTemp = _paArray + iElem;
             pTemp < _paArray + iElem + cElems;
             pTemp++)
        {
            pTemp->CElem::~CElem();
#if DBG == 1
            _cNumElems--;
#endif
        }
    }

    // Now we need to shift the remaining elements in:
    memmove(_paArray + iElem,                // dest
            _paArray + iElem + cElems,
            (_cArraySize - (iElem + cElems)) * sizeof(CElem));

    _cArraySize -= cElems;
}

#endif  // _TARRAY_H_


TDC.CPP

// TDC.cpp : Implementation of DLL Exports.

// You will need the NT SUR Beta 2 SDK or VC 4.2 or higher in order to build
// this project.  This is because you will need MIDL 3.00.15 or higher and new
// headers and libs.  If you have VC 4.2 installed, then everything should
// already be configured correctly.

// Note: Proxy/Stub Information
//      To build a separate proxy/stub DLL,
//      run nmake -f TDCps.mak in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "std.h"
#include "TDCIds.h"
#include "TDC.h"
#include <MLang.h>

#define IID_DEFINED         // for now avoid a conflict with ATL
#include "TDC_i.c"
#include "STD_i.c"
//#include "mlang_i.c"
#include "Notify.h"
#include "TDCParse.h"
#include "TDCArr.h"
#include "SimpData.h"
#include "TDCCtl.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
    OBJECT_ENTRY(CLSID_CTDCCtl, CTDCCtl)
//MM    OBJECT_ENTRY(CLSID_CSimpleTabularData, CSimpleTabularData)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        _Module.Init(ObjectMap, hInstance);
        DisableThreadLibraryCalls(hInstance);
    }
    else if (dwReason == DLL_PROCESS_DETACH)
        _Module.Term();
    return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
    return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
    // registers object, typelib and all interfaces in typelib
    return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
    _Module.UnregisterServer();
#if _WIN32_WINNT >= 0x0400
    UnRegisterTypeLib(LIBID_TDCLib, 1, 1, NULL, SYS_WIN32);
#endif
    return S_OK;
}


TDC.IDL

#include <olectl.h>
// TDC.idl : IDL source for TDC.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (TDC.tlb) and marshalling code.

#include "TDCIds.h"

[
    uuid(761279e6-2dee-11d1-9544-00c04fb66f5d),
    version(1.1),
    helpstring("Tabular Data Control sample 1.1 Type Library")
]
library TDCLib
{
    importlib("stdole32.tlb");  // don't remove this!

    [ uuid(E0E270C0-C0BE-11d0-8FE4-00A0C90A6341), odl, dual]
    interface OLEDBSimpleProviderX : IDispatch
    {
    };

    [
    object,
        uuid(761279e8-2dee-11d1-9544-00c04fb66f5d),
    dual,
    helpstring("ITDCCtl Interface"),
    pointer_default(unique)
    ]

    interface ITDCCtl : IDispatch
    {

    // Properties

    [propget, id(DISPID_FIELDDELIM)]
        HRESULT FieldDelim([out, retval] BSTR* pbstrFieldDelim);
    [propput, id(DISPID_FIELDDELIM)]
        HRESULT FieldDelim([in] BSTR bstrFieldDelim);

    [propget, id(DISPID_ROWDELIM)]
        HRESULT RowDelim([out, retval] BSTR* pbstrRowDelim);
    [propput, id(DISPID_ROWDELIM)]
        HRESULT RowDelim([in] BSTR bstrRowDelim);

    [propget, id(DISPID_TEXTQUALIFIER)]
        HRESULT TextQualifier([out, retval] BSTR* pbstrTextQualifier);
    [propput, id(DISPID_TEXTQUALIFIER)]
        HRESULT TextQualifier([in] BSTR bstrTextQualifier);

    [propget, id(DISPID_ESCAPECHAR)]
        HRESULT EscapeChar([out, retval] BSTR* pbstrEscapeChar);
    [propput, id(DISPID_ESCAPECHAR)]
        HRESULT EscapeChar([in] BSTR bstrEscapeChar);

    [propget, id(DISPID_USEHEADER)]
        HRESULT UseHeader([out, retval] VARIANT_BOOL* pfUseHeader);
    [propput, id(DISPID_USEHEADER)]
        HRESULT UseHeader([in] VARIANT_BOOL fUseHeader);

    [propget, id(DISPID_SORTCOLUMN), hidden]
        HRESULT SortColumn([out, retval] BSTR* pbstrSortColumn);
    [propput, id(DISPID_SORTCOLUMN), hidden]
        HRESULT SortColumn([in] BSTR bstrSortColumn);

    [propget, id(DISPID_SORTASCENDING), hidden]
        HRESULT SortAscending([out, retval] VARIANT_BOOL *pfSortAscending);
    [propput, id(DISPID_SORTASCENDING), hidden]
        HRESULT SortAscending([in] VARIANT_BOOL fSortAscending);

    [propget, id(DISPID_FILTERVALUE), hidden]
        HRESULT FilterValue([out, retval] BSTR* pbstrFilterValue);
    [propput, id(DISPID_FILTERVALUE), hidden]
        HRESULT FilterValue([in] BSTR bstrFilterValue);

    [propget, id(DISPID_FILTERCRITERION), hidden]
        HRESULT FilterCriterion([out, retval] BSTR* pbstrFilterCriterion);
    [propput, id(DISPID_FILTERCRITERION), hidden]
        HRESULT FilterCriterion([in] BSTR bstrFilterCriterion);

    [propget, id(DISPID_FILTERCOLUMN), hidden]
        HRESULT FilterColumn([out, retval] BSTR* pbstrFilterColumn);
    [propput, id(DISPID_FILTERCOLUMN), hidden]
        HRESULT FilterColumn([in] BSTR bstrFilterColumn);

    [propget, id(DISPID_CHARSET)]
        HRESULT CharSet([out, retval] BSTR *pbstrCharSet);
    [propput, id(DISPID_CHARSET)]
        HRESULT CharSet([in] BSTR bstrCharSet);

    [propget, id(DISPID_LANGUAGE)]
        HRESULT Language([out, retval] BSTR *pbstrLanguage);
    [propput, id(DISPID_LANGUAGE)]
        HRESULT Language([in] BSTR bstrLanguage);

    [propget, id(DISPID_CASESENSITIVE)]
        HRESULT CaseSensitive([out, retval] VARIANT_BOOL *pfCaseSensitive);
    [propput, id(DISPID_CASESENSITIVE)]
        HRESULT CaseSensitive([in] VARIANT_BOOL fCaseSensitive);

    //  This will be tagged differently when we learn more about the HTML
    //  sub-tag "OBJECT"
    [propget, id(DISPID_DATAURL)]
        HRESULT DataURL([out, retval] BSTR* pbstrDataURL);
    [propput, id(DISPID_DATAURL)]
        HRESULT DataURL([in] BSTR bstrDataURL);

    [restricted, id(-3900)]
    HRESULT msDataSourceObject([in] BSTR qualifier,
                   [out, retval] IUnknown **ppUnk);

    [restricted, id(-3901)]
    HRESULT addDataSourceListener([in] IUnknown *pEvent);

    // Methods

    HRESULT Reset();

    // New properties/methods - put here to existing preserve vtable order

// ;begin_internal
#ifdef NEVER
    [propget, id(DISPID_TIMER)]
        HRESULT RefreshInterval([out, retval] LONG *plSecs);
    [propput, id(DISPID_TIMER)]
        HRESULT RefreshInterval([in] LONG lSecs);
#endif
// ;end_internal

    [hidden, restricted]
    HRESULT _OnTimer();

    [propget, id(DISPID_FILTER)]
        HRESULT Filter([out, retval] BSTR* pbstrFilterExpr);
    [propput, id(DISPID_FILTER)]
        HRESULT Filter([in] BSTR bstrFilterExpr);

    [propget, id(DISPID_SORT)]
        HRESULT Sort([out, retval] BSTR* pbstrSortExpr);
    [propput, id(DISPID_SORT)]
        HRESULT Sort([in] BSTR bstrSortExpr);

    [propget, id(DISPID_READYSTATE)]
        HRESULT ReadyState([out, retval] long *state);
    [propput, id(DISPID_READYSTATE)]
        HRESULT ReadyState([in] long state);

    [propget, id(DISPID_APPENDDATA)]
        HRESULT AppendData([out, retval] VARIANT_BOOL *pfAppendData);
    [propput, id(DISPID_APPENDDATA)]
        HRESULT AppendData([in] VARIANT_BOOL fAppendData);

    [propget, id(DISPID_OSP), hidden]
        HRESULT OSP([out, retval] OLEDBSimpleProviderX ** ppISTD);
    };

    [
        object,
        uuid(761279e9-2dee-11d1-9544-00c04fb66f5d),
        dual,
    helpstring("Tabular Data Control Event interface"),
        pointer_default(unique)
    ]
    interface ITDCCtlEvents: IDispatch
    {
        [id(DISPID_READYSTATECHANGE)]
            void onreadystatechange();
    };

    [
        uuid(761279ea-2dee-11d1-9544-00c04fb66f5d),
    helpstring("Tabular Data Control")
    ]
    coclass CTDCCtl
    {
    [default] interface ITDCCtl;
        [default, source] interface ITDCCtlEvents;
    };

};


TDCARR.CPP

//+-----------------------------------------------------------------------
//
//  Tabular Data Control Array
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       TDCArr.cpp
//
//  Contents:   Implementation of the CTDCArr object.
//              This class forms the heart of the Tabular Data Control.
//              It provides the core 2D array of variant values, plus
//              a (possibly filtered/sorted) view of this data for
//              presentation through an ISimpleTabularData interface.
//
//------------------------------------------------------------------------

#include "stdafx.h"
#include "wch.h"
#include "STD.h"
#include "TDC.h"
#include <MLang.h>
#include "Notify.h"
#include "TDCParse.h"
#include "TDCArr.h"
#include "SimpData.h"
#include "TDCIds.h"

//------------------------------------------------------------------------
//
//  Function:  fWCHIsSpace()
//
//  Synopsis:  Indicates whether a WCHAR is considered a space character
//
//  Arguments: wch      Character to test
//
//  Returns:   TRUE/FALSE indicating whether the given character is
//             considered a space.
//
//------------------------------------------------------------------------

inline boolean fWCHIsSpace(WCHAR wch)
{
    return (wch == L' ' || wch == L'\t');
}

//------------------------------------------------------------------------
//
//  Function:  fWCHEatTest()
//
//  Synopsis:  Advances a string pointer over a given test character.
//
//  Arguments: ppwch      Pointer to string to test
//             wch        Match character
//
//  Returns:   TRUE indicating the character matched and the pointer has
//               been advanceed
//             FALSE indicating no match (character pointer left unchanged)
//
//------------------------------------------------------------------------

inline boolean fWCHEatTest(LPWCH *ppwch, WCHAR wch)
{
    if (**ppwch != wch)
        return FALSE;
    (*ppwch)++;
    return TRUE;
}

//------------------------------------------------------------------------
//
//  Function:  fWCHEatSpace()
//
//  Synopsis:  Advances a string pointer over white space.
//
//  Arguments: ppwch      String pointer.
//
//  Returns:   Nothing.
//
//------------------------------------------------------------------------

inline void fWCHEatSpace(LPWCH *ppwch)
{
    while (fWCHIsSpace(**ppwch))
        (*ppwch)++;
}

//------------------------------------------------------------------------
//
//  Method:    CTDCArr()
//
//  Synopsis:  Class constructor.  Due to the COM model, the
//             member function "Create" should be called to actually
//             initialise the STD data structures.
//
//  Arguments: None.
//
//------------------------------------------------------------------------

CTDCArr::CTDCArr() : m_cRef(1)
{
    m_pEventBroker = NULL;
    m_pSortList = NULL;
    m_bstrSortExpr = NULL;
    m_pFilterTree = NULL;
    m_bstrFilterExpr = NULL;
}

//------------------------------------------------------------------------
//
//  Member:    Init()
//
//  Synopsis:  Initialises the internal data.
//
//  Arguments: pEventBroker       Object to delegate notifications to.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::Init(CEventBroker *pEventBroker, IMultiLanguage *pML)
{
    HRESULT hr = S_OK;

    hr = m_arrparrCells.Init(0);
    if (SUCCEEDED(hr))
        hr = m_arrparrFilter.Init(0);
    if (SUCCEEDED(hr))
        hr = m_arrColInfo.Init(0);
    m_iFilterRows = CalcFilterRows();
    m_iDataRows = CalcDataRows();
    m_iCols = CalcCols();
    m_fLastFilter = FALSE;
    m_fSortFilterDisrupted = FALSE;
    m_state = LS_UNINITIALISED;
    m_lcid = GetUserDefaultLCID();
    m_lcidRead = m_lcid;
    _ASSERT(pEventBroker != NULL);

    m_pEventBroker = pEventBroker;
    m_pEventBroker->AddRef();           // better not be called with NULL

    m_pML = pML;
    m_pML->AddRef();

    m_fAsync = FALSE;                   // assume non-asynch for error cases
    return hr;
}

//------------------------------------------------------------------------
//
//  Member:    ~CTDCArr()
//
//  Synopsis:  Destructor for CTDCArr
//
//------------------------------------------------------------------------

CTDCArr::~CTDCArr()
{
    for (LONG iRow = CalcDataRows(); iRow >= 0; iRow--)
    {
        m_arrparrCells[iRow]->Passivate();
        delete m_arrparrCells[iRow];
    }
    m_arrparrCells.Passivate();
    m_arrparrFilter.Passivate();
    m_arrColInfo.Passivate();
    if (m_pSortList != NULL)
        delete m_pSortList;
    SysFreeString(m_bstrSortExpr);
    if (m_pFilterTree != NULL)
        delete m_pFilterTree;
    SysFreeString(m_bstrFilterExpr);

    if (m_pEventBroker)
    {
        m_pEventBroker->Release();
        m_pEventBroker = NULL;
    }

    ClearInterface(&m_pML);
}

LONG CTDCArr::CalcDataRows()
{
    return m_arrparrCells.GetSize() - 1;
}

LONG CTDCArr::CalcFilterRows()
{
    return m_arrparrFilter.GetSize() - 1;
}

LONG CTDCArr::CalcCols()
{
    return m_arrparrCells.GetSize() > 0
                ? m_arrparrCells[0]->GetSize() : 0;
}

//------------------------------------------------------------------------
//
//  Member:    GetRowCount()
//
//  Synopsis:  Retrieves the number of rows in the table.
//
//  Arguments: pcRows          pointer to number of rows    (OUT)
//
//  Returns:   S_OK to indicate success.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::getRowCount(LONG *pcRows)
{
    *pcRows = m_iFilterRows;
    return S_OK;
}

//------------------------------------------------------------------------
//
//  Member:    GetColumnCount()
//
//  Synopsis:  Retrieves the number of column in the table.
//
//  Arguments: pcCols       pointer to number of columns (OUT)
//
//  Returns:   S_OK to indicate success.
//             E_UNEXPECTED if the table has not been loaded yet.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::getColumnCount(LONG *pcCols)
{
    *pcCols = m_iCols;
    return S_OK;
}

//------------------------------------------------------------------------
//
//  Member:    GetRWStatus()
//
//  Synopsis:  Gets the read/write status of a cell, row, column or the
//             entire array.  Since this implementation of STD can never
//             set the read/write status of a cell anywhere, all data
//             cells are presumed to have the default access and all
//             column heading cells are presumed to be read-only.  Therefore,
//             it is not necessary to keep track of this information in
//             individual cells, and this function need only return
//             the value OSPRW_DEFAULT.
//
//  Arguments: iRow            row index (-1 means all rows)
//             iCols           column index (-1 means all columns)
//             prwStatus       pointer to read/write status (OUT)
//
//  Returns:   S_OK if indices are correct (prwStatus set).
//             E_INVALIDARG if indices are out of bounds.
//             E_UNEXPECTED if the table has not been loaded yet.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::getRWStatus(LONG iRow, LONG iCol, OSPRW *prwStatus)
{
    HRESULT hr  = S_OK;

    if ((fValidFilterRow(iRow) || iRow == -1) &&
        (fValidCol(iCol) || iCol == -1))
    {
        if (iRow == -1)
        {
            //  Should return READONLY if there is only a label row,
            //  but frameworks tend to get confused if they want to
            //  later insert data.
            //
//          *prwStatus = m_iDataRows > 0 ? OSPRW_MIXED : OSPRW_READONLY;
            *prwStatus = OSPRW_MIXED;
        }
        else if (iRow == 0)
            *prwStatus = OSPRW_READONLY;
        else
            *prwStatus = OSPRW_DEFAULT;
    }
    else
        hr = E_INVALIDARG;
    return hr;
}

//------------------------------------------------------------------------
//
//  Member:    GetVariant()
//
//  Synopsis:  Retrieves a variant value for a cell.
//
//  Arguments: iRow            row index
//             iCols           column index
//             format          output format
//             pVar            pointer to storage for resulting value
//
//  Returns:   S_OK upon success (contents of pVar set).
//             E_INVALIDARG if indices are out of bounds.
//             E_UNEXPECTED if the table has not been loaded yet.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::getVariant(LONG iRow, LONG iCol, OSPFORMAT format, VARIANT *pVar)
{
    HRESULT hr  = S_OK;

    if (fValidFilterCell(iRow, iCol))
    {
        CTDCCell    *pCell  = GetFilterCell(iRow, iCol);

        if (format == OSPFORMAT_RAW)
        {
            //  Copy the raw variant value
            //
            hr = VariantCopy(pVar, pCell);
        }
        else if (format == OSPFORMAT_FORMATTED || format == OSPFORMAT_HTML)
        {
            //  Construct a BSTR value representing the cell
            //
            if (pCell->vt == VT_BOOL)
            {
                //  For OLE DB spec compliance:
                //    VariantChangeTypeEx converts booleans in BSTR "0", "-1".
                //    This code yields BSTR "False", "True" instead.
                //
                VariantClear(pVar);
                pVar->vt = VT_BSTR;
                hr = VarBstrFromBool(pCell->boolVal, m_lcid, 0, &pVar->bstrVal);
            }
            else
            {
                hr = VariantChangeTypeEx(pVar, pCell, m_lcid, 0, VT_BSTR);
            }
            if (!SUCCEEDED(hr))
            {
                VariantClear(pVar);
                pVar->vt = VT_BSTR;
                pVar->bstrVal = SysAllocString(L"#Error");
            }
        }
        else
            hr = E_INVALIDARG;
    }
    else
        hr = E_INVALIDARG;

    return hr;
}

//------------------------------------------------------------------------
//
//  Member:    SetVariant()
//
//  Synopsis:  Sets a cell's variant value from a given variant value.
//             The given variant type is coerced into the column's
//             underlying type.
//
//  Arguments: iRow            row index
//             iCols           column index
//             format          output format
//             Var             value to be stored in the cell.
//
//  Returns:   S_OK upon success (contents of pVar set).
//             E_INVALIDARG if indices are out of bounds.
//             E_UNEXPECTED if the table has not been loaded yet.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::setVariant(LONG iRow, LONG iCol, OSPFORMAT format, VARIANT Var)
{
    HRESULT hr;

    if (fValidFilterCell(iRow, iCol))
    {
        CTDCCell    *pCell  = GetFilterCell(iRow, iCol);
        CTDCColInfo *pColInfo   = GetColInfo(iCol);

        if (format == OSPFORMAT_RAW ||
            format == OSPFORMAT_FORMATTED || format == OSPFORMAT_HTML)
        {
            if (m_pEventBroker != NULL)
            {
                hr = m_pEventBroker->aboutToChangeCell(iRow, iCol);
                if (!SUCCEEDED(hr))
                    goto Cleanup;
            }

            if (Var.vt == pColInfo->vtType)
                hr = VariantCopy(pCell, &Var);
            else
            {
                //  For OLE DB spec compliance:
                //    VariantChangeTypeEx converts booleans in BSTR "0", "-1".
                //    This code yields BSTR "False", "True" instead.
                //
                if (Var.vt == VT_BOOL && pColInfo->vtType==VT_BSTR)
                {
                    VariantClear(pCell);
                    pCell->vt = VT_BSTR;
                    hr = VarBstrFromBool(Var.boolVal, m_lcid, 0, &pCell->bstrVal);
                }
                else
                    hr = VariantChangeTypeEx(pCell, &Var, m_lcid,
                                             0, pColInfo->vtType);
            }
            if (SUCCEEDED(hr) && m_pEventBroker != NULL)
                hr = m_pEventBroker->cellChanged(iRow, iCol);
            m_fSortFilterDisrupted = TRUE;
        }
        else
            hr = E_INVALIDARG;
    }
    else
        hr = E_INVALIDARG;

Cleanup:
    return hr;
}

//------------------------------------------------------------------------
//
//  Member:    GetLocale()
//
//  Synopsis:  Returns the locale of our data.
//
//  Arguments: Returns a BSTR, representing an RFC1766 form string for our
//             locale.  Note this may not necessarily match our LANGUAGE
//             param, if we had one, because teh string is canoncialized
//             by using MLang to convert it to an LCID and back to a string
//             again.
//
//  Returns:   S_OK to indicate success.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::getLocale(BSTR *pbstrLocale)
{
    return m_pML->GetRfc1766FromLcid(m_lcid, pbstrLocale);
}

//+-----------------------------------------------------------------------
//
//  Member:    DeleteRows
//
//  Synopsis:  Used to delete rows from the table.  Bounds are checked
//             to make sure that the rows can all be deleted.  Label row
//             cannot be deleted.
//
//  Arguments: iRow            first row to delete
//             cRows           number of rows to delete
//             pcRowsDeleted   actual number of rows deleted (OUT)
//
//  Returns:   S_OK upon success, i.e. all rows could be deleted
//             E_INVALIDARG if cRows < 0 or any rows to be deleted
//               are out of bounds
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::deleteRows(LONG iRow, LONG cRows, LONG *pcRowsDeleted)
{
    HRESULT hr;
    *pcRowsDeleted = 0;

    if (fValidFilterRow(iRow) && iRow > 0 && cRows >= 0 &&
        fValidFilterRow(iRow + cRows - 1))
    {
        if (m_pEventBroker != NULL)
        {
            hr = m_pEventBroker->aboutToDeleteRows(iRow, cRows);
            if (!SUCCEEDED(hr))
                goto Cleanup;
        }

        *pcRowsDeleted = cRows;
        hr = S_OK;
        if (cRows > 0)
        {
            //  Delete the rows from the array
            //
            m_arrparrFilter.DeleteElems(iRow, cRows);
            m_iFilterRows = CalcFilterRows();

            m_fSortFilterDisrupted = TRUE;
            //  Notify the event-handler of the deletion
            //
            if (m_pEventBroker != NULL)
                hr = m_pEventBroker->deletedRows(iRow, cRows);
        }
    }
    else
    {
        hr = E_INVALIDARG;
    }
Cleanup:
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Member:    InsertRows()
//
//  Synopsis:  Allows for the insertion of new rows.  This can either be
//             used to insert new rows between existing rows, or to
//             append new rows to the end of the table.  Thus, to
//             insert new rows at the end of the table, a user would
//             specify the initial row as 1 greater than the current
//             row dimension.
//             Note that iRow is checked to ensure that it is within the
//             proper bounds (1..<current # of rows>+1).
//             User cannot delete column heading row.
//
//  Arguments: iRow            rows will be inserted *before* row 'iRow'
//             cRows           how many rows to insert
//             pcRowsInserted  actual number of rows inserted (OUT)
//
//  Returns:   S_OK upon success, i.e. all rows could be inserted.
//             E_INVALIDARG if row is out of allowed bounds.
//             It is possible that fewer than the requested rows were
//             inserted.  In this case, E_OUTOFMEMORY would be returned,
//             and the actual number of rows inserted would be set.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::insertRows(LONG iRow, LONG cRows, LONG *pcRowsInserted)
{
    HRESULT hr  = S_OK;
    TSTDArray<CTDCCell> **pRows = NULL;
    LONG    iTmpRow;

    //  Verify that the insertion row is within range
    //
    if (iRow < 1 || iRow > m_iFilterRows + 1)
    {
        hr = E_INVALIDARG;
        goto Cleanup;
    }

    if (cRows <= 0)
    {
        if (cRows < 0)
            hr = E_INVALIDARG;
        goto Cleanup;
    }

    //  Unless success is complete, assume 0 rows inserted.
    //
    *pcRowsInserted = 0;

    //  Allocate a temporary array of rows
    //
    pRows = new TSTDArray<CTDCCell>* [cRows];
    if (pRows == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto Cleanup;
    }

    memset(pRows, '\0', sizeof(pRows[0]) * cRows);

    for (iTmpRow = 0; iTmpRow < cRows; iTmpRow++)
    {
        if ((pRows[iTmpRow] = new TSTDArray<CTDCCell>) == NULL)
        {
            hr = E_OUTOFMEMORY;
            goto CleanupTmpRows;
        }
        hr = pRows[iTmpRow]->InsertElems(0, m_iCols);
        if (!SUCCEEDED(hr))
            goto CleanupTmpRows;
    }

    //  Expand the Cell-Rows and Filter-Rows arrays to cope with the new rows.
    //
    _ASSERT(m_iFilterRows <= m_iDataRows);
    hr = m_arrparrCells.InsertElems(iRow, cRows);
    if (!SUCCEEDED(hr))
        goto CleanupTmpRows;
    hr = m_arrparrFilter.InsertElems(iRow, cRows);
    if (!SUCCEEDED(hr))
    {
        //  Undo the previous allocation
        //
        m_arrparrCells.DeleteElems(iRow, cRows);
        goto CleanupTmpRows;
    }

    if (m_pEventBroker != NULL)
    {
        hr = m_pEventBroker->aboutToInsertRows(iRow, cRows);
        if (FAILED(hr))
            goto CleanupTmpRows;
    }

    //  Copy across the row pointers
    //
    for (iTmpRow = 0; iTmpRow < cRows; iTmpRow++)
    {
        m_arrparrCells[iRow + iTmpRow] = pRows[iTmpRow];
        m_arrparrFilter[iRow + iTmpRow] = pRows[iTmpRow];
    }

    //  Return indicating success
    //
    *pcRowsInserted = cRows;
    m_iFilterRows = CalcFilterRows();;
    m_iDataRows = CalcDataRows();

    // Fire events:
    if (*pcRowsInserted != 0)
    {
        m_fSortFilterDisrupted = TRUE;
        if (m_pEventBroker != NULL)
            hr = m_pEventBroker->insertedRows(iRow, cRows);
    }
    goto Cleanup;

CleanupTmpRows:
    //  Free the memory associated with the tmp rows.
    //
    for (iTmpRow = 0; iTmpRow < cRows; iTmpRow++)
        if (pRows[iTmpRow] != NULL)
            delete pRows[iTmpRow];

Cleanup:
    if (pRows != NULL)
        delete pRows;
    return hr;
}

// ;begin_internal
//+-----------------------------------------------------------------------
//
//  Member:    DeleteColumns()
//
//  Synopsis:  Used to delete columns from the table.  Bounds are checked
//             to make sure that the columns can all be deleted.  Label
//             column cannot be deleted.
//
//  Arguments: iCol               first column to delete
//             cCols              number of columns to delete
//             pcColsDeleted      actual number of rows deleted (OUT)
//
//  Returns:   S_OK upon succes, i.e. all columns could be deleted
//             E_INVALIDARG if column is out of allowed bounds.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::DeleteColumns(LONG iCol, LONG cCols, LONG *pcColsDeleted)
{
    HRESULT hr;

    if (fValidCol(iCol) && iCol > 0 && cCols >= 0 &&
        fValidCol(iCol + cCols - 1))
    {
        *pcColsDeleted = cCols;
        hr = S_OK;
        if (cCols > 0)
        {
            for (LONG iRow = 0; iRow < m_iFilterRows; iRow++)
            {
                TSTDArray<CTDCCell> *pRow;

                pRow = m_arrparrCells[iRow];
                pRow->DeleteElems(iCol - 1, cCols);
            }
            m_arrColInfo.DeleteElems(iCol - 1, cCols);
            m_iCols = CalcCols();

            if (!m_fUseHeader)
                RenumberColumnHeadings();

            m_fSortFilterDisrupted = TRUE;

            //  Notify the event-handler of the deletion
            //
#ifdef NEVER
            if (m_pEventBroker != NULL)
                hr = m_pEventBroker->DeletedCols(iCol, cCols);
#endif
        }
        _ASSERT(m_arrColInfo.GetSize() == (ULONG) m_iCols);
    }
    else
    {
        hr = E_INVALIDARG;
        *pcColsDeleted = 0;
    }
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Member:    InsertColumns()
//
//  Synopsis:  Allows for the insertion of new columns.  This can either be
//             used to insert new columns between existing columns, or to
//             append new columns to the end of the table.  Thus, to
//             insert new columns at the end of the table, a user would
//             specify the initial columns as 1 greater than the current
//             columns dimension.
//             Note that iColumn is checked to ensure that it is within the
//             proper bounds (1..<current # of cols>+1).
//
//  Arguments: iCol            columns will be inserted *before* row 'iCol'
//             cCols           how many columns to insert
//             pcColsInserted  actual number of columns inserted (OUT)
//
//  Returns:   S_OK upon success, i.e. all columns could be inserted.
//             E_INVALIDARG if column is out of allowed bounds.
//             It is possible that fewer than the requested columns were
//             inserted.  In this case, E_OUTOFMEMORY would be returned,
//             and the actual number of columns inserted would be set.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::InsertColumns(LONG iCol, LONG cCols, LONG *pcColsInserted)
{
    HRESULT hr  = S_OK;
    LONG iTmpRow;

    //  Verify that the insertion column is within range
    //
    if (iCol < 1 || iCol > m_iCols + 1)
    {
        hr = E_INVALIDARG;
        goto Cleanup;
    }

    if (cCols <= 0)
    {
        if (cCols < 0)
            hr = E_INVALIDARG;
        goto Cleanup;
    }

    //  Unless success is complete, assume 0 columns inserted.
    //
    *pcColsInserted = 0;

    for (iTmpRow = 0; iTmpRow <= m_iDataRows; iTmpRow++)
    {
        hr = m_arrparrCells[iTmpRow]->InsertElems(iCol, cCols);
        if (!SUCCEEDED(hr))
        {
            //  Undo the changes we've done
            //
            while (--iTmpRow >= 0)
                m_arrparrCells[iTmpRow]->DeleteElems(iCol, cCols);
            goto Cleanup;
        }
    }

    //  Return indicating success
    //
    *pcColsInserted = cCols;
    m_iCols = CalcCols();

    // Fire events:
    if (*pcColsInserted != 0)
    {
        m_fSortFilterDisrupted = TRUE;
#ifdef NEVER
        if (m_pEventBroker != NULL)
            hr = m_pEventBroker->InsertedCols(iCol, cCols);
#endif
    }

Cleanup:
    //  If we're using automatically numbered column headings and some
    //  columns were inserted, then renumber the columns.
    //
    if (*pcColsInserted > 0 && !m_fUseHeader)
        RenumberColumnHeadings();

    return hr;
}
// ;end_internal

//+-----------------------------------------------------------------------
//
//  Member:    Find()
//
//  Synopsis:  Searches for a row matching the specified criteria
//
//  Arguments: iRowStart       The starting row for the search
//             iCol            The column being tested
//             vTest           The value against which cells in the
//                               test column are tested
//             findFlags       Flags indicating whether to search up/down
//                               and whether comparisons are case sensitive.
//             compType        The comparison operator for matching (find a
//                             cell =, >=, <=, >, <, <> the test value)
//             piRowFound      The row with a matching cell [OUT]
//
//  Returns:   S_OK upon success, i.e. a row was found (piRowFound set).
//             E_FAIL upon failure, i.e. a row was not found.
//             E_INVALIDARG if starting row 'iRowStart' or test column 'iCol'
//               are out of bounds.
//             DISP_E_TYPEMISMATCH if the test value's type does not match
//               the test column's type.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::find(LONG iRowStart, LONG iCol, VARIANT vTest,
        OSPFIND findFlags, OSPCOMP compType, LONG *piRowFound)
{
    HRESULT hr = S_OK;
    boolean fUp = FALSE;
    boolean fCaseSensitive  = FALSE;
    LONG    iRow;

    *piRowFound = -1;

    //  Validate arguments
    //
    if (iRowStart < 1 || !fValidFilterRow(iRowStart) || !fValidCol(iCol))
    {
        hr = E_INVALIDARG;
        goto Cleanup;
    }

    if (GetColInfo(iCol)->vtType != vTest.vt)
    {
        //  Search-value type does not match the underlying column type
        //  Fail as per spec.
        //
        hr = DISP_E_TYPEMISMATCH;
        goto Cleanup;
    }

    if ((findFlags & OSPFIND_UP) != 0)
        fUp = TRUE;
    if ((findFlags & OSPFIND_CASESENSITIVE) != 0)
        fCaseSensitive = TRUE;

    for (iRow = iRowStart;
         fUp ? iRow > 0 : iRow <= m_iFilterRows;
         fUp ? iRow-- : iRow++)
    {
        int         iCmp = VariantComp(GetFilterCell(iRow, iCol), &vTest, vTest.vt, fCaseSensitive);
        boolean     fFound  = FALSE;

        switch (compType)
        {
        case OSPCOMP_LT:    fFound = iCmp <  0; break;
        case OSPCOMP_LE:    fFound = iCmp <= 0; break;
        case OSPCOMP_GT:    fFound = iCmp >  0; break;
        case OSPCOMP_GE:    fFound = iCmp >= 0; break;
        case OSPCOMP_EQ:    fFound = iCmp == 0; break;
        case OSPCOMP_NE:    fFound = iCmp != 0; break;
        default:
            hr = E_INVALIDARG;
            goto Cleanup;
        }
        if (fFound)
        {
            *piRowFound = iRow;
            hr = S_OK;
            goto Cleanup;
        }
    }

    hr = E_FAIL;

Cleanup:
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Member:    addOLEDBSimpleProviderListener()
//
//  Synopsis:  Sets or clears a reference to the COM object which receives
//             notifications of cell changes, row/column insert/deletes etc.
//
//  Arguments: pEvent          Pointer to the COM object to receive
//                             notifications, or NULL if no notifications
//                             are to be sent.
//
//  Returns:   S_OK upon success.
//             Error code upon success.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCArr::addOLEDBSimpleProviderListener(OLEDBSimpleProviderListener *pSTDEvents)
{
    HRESULT hr = S_OK;

    if (m_pEventBroker == NULL)
        hr = E_FAIL;
    else
    {
        hr = m_pEventBroker->SetSTDEvents(pSTDEvents);
        // If the event sink has been added, and we're already loaded,
        // then fire transferComplete, because we probably couldn't before.
        if (LS_LOADED==m_state)
            m_pEventBroker->STDLoadCompleted();
    }
    return hr;
}

STDMETHODIMP
CTDCArr::removeOLEDBSimpleProviderListener(OLEDBSimpleProviderListener *pSTDEvents)
{
    HRESULT hr = S_OK;

    if (m_pEventBroker && pSTDEvents==m_pEventBroker->GetSTDEvents())
        hr = m_pEventBroker->SetSTDEvents(NULL);
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Member:    FindCol()
//
//  Synopsis:  Locate a column by name
//
//  Arguments: pwchColName        Name of column to locate
//
//  Returns:   +ve Column number upon success (column name matched)
//             0 upon failure (no column name matched)
//
//------------------------------------------------------------------------

LONG
CTDCArr::FindCol(LPWCH pwchColName)
{
    LONG iCol = 0;

    if (pwchColName != NULL)
    {
        for (iCol = m_iCols; iCol > 0; iCol--)

{
            CTDCCell    *pCell  = GetDataCell(0, iCol);

            _ASSERT(pCell->vt == VT_BSTR);
            if (wch_icmp(pwchColName, pCell->bstrVal) == 0)
                break;
        }
    }
    return iCol;
}

class SortElt
{
public:
    CTDCArr *pInstance;
    int     iRow;
    TSTDArray<CTDCCell> *   parrRow;
};

//+-----------------------------------------------------------------------
//
//  Function:  CompareSort()
//
//  Synopsis:  Called by qsort() to order the rows of a table.
//
//  Arguments: pElt1, pElt1       pointers to elements to be compared
//
//  Returns:   -1 if the first element is less than the second element
//              0 if the first element equals the second element
//             +1 if the first element is greater than the second element
//
//------------------------------------------------------------------------

static int
CompareSort(const void *pElt1, const void *pElt2)
{
    SortElt *pse1   = (SortElt *) pElt1;
    SortElt *pse2   = (SortElt *) pElt2;

    return pse1->pInstance->SortComp(pse1->iRow, pse2->iRow);
}

//+-----------------------------------------------------------------------
//
//  Function:  extract_num()
//
//  Synopsis:  Extracts the first non-negative number from the character
//             stream referenced by 'ppwch'.  Updates 'ppwch' to point
//             to the character following the digits found.
//
//  Arguments: ppwch      Pointer to null-terminated WCHAR string
//
//  Returns:   Non-negative number extracted upon success (pointer updated)
//             -1 upon failure (no digits found; pointer moved to end-of-string
//
//+-----------------------------------------------------------------------

static int
extract_num(WCHAR **ppwch)
{
    int retval  = 0;
    boolean fFoundDigits    = FALSE;

    if (*ppwch != NULL)
    {
        //  Skip over leading non-digits
        //
        while ((**ppwch) != 0 && ((**ppwch) < L'0' || (**ppwch) > L'9'))
            (*ppwch)++;

        //  Accumulate digits
        //
        fFoundDigits = *ppwch != 0;
        while ((**ppwch) >= L'0' && (**ppwch) <= L'9')
            retval = 10 * retval + *(*ppwch)++ - L'0';
    }

    return fFoundDigits ? retval : -1;
}

//+-----------------------------------------------------------------------
//
//  Member:    CreateNumberedColumnHeadings()
//
//  Synopsis:  Allocates cells for numbered column headings.
//
//  Arguments: None.
//
//  Returns:   S_OK upon success.
//             E_OUTOFMEMORY if there was insufficient memory to complete
//             the operation.
//
//------------------------------------------------------------------------

HRESULT CTDCArr::CreateNumberedColumnHeadings()
{
    HRESULT hr  = S_OK;
    LONG    iCol;

    iCol = m_iCols;

    //  Allocate a new row entry
    //
    hr = m_arrparrCells.InsertElems(0, 1);
    if (!SUCCEEDED(hr))
        goto Cleanup;

    //  Allocate a new row of cells
    //
    m_arrparrCells[0] = new TSTDArray<CTDCCell>;
    if (m_arrparrCells[0] == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto Cleanup;
    }
    hr = m_arrparrCells[0]->InsertElems(0, iCol);

Cleanup:
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Member:    RenumberColumnHeadings()
//
//  Synopsis:  Set the automatic name "Column<column-number>" for each column
//
//  Arguments: None.
//
//  Returns:   Nothing.
//
//------------------------------------------------------------------------

void CTDCArr::RenumberColumnHeadings()
{
    for (LONG iCol = m_iCols; iCol > 0; iCol--)
    {
        CTDCCell    *pCell  = GetDataCell(0, iCol);
        WCHAR       awchLabel[20];

        wch_cpy(awchLabel, L"Column");
        _ltow(iCol, &awchLabel[6], 10);

        pCell->clear();
        pCell->vt = VT_BSTR;
        pCell->bstrVal = SysAllocString(awchLabel);
    }
}

//+-----------------------------------------------------------------------
//
//  Member:    ParseColumnHeadings()
//
//  Synopsis:  Extracts type information (if present) from column
//             headings, removes leadning
//
//  Arguments: None.
//
//  Returns:   S_OK upon success.
//             E_OUTOFMEMORY if there was insufficient memory to complete
//             the operation.
//
//------------------------------------------------------------------------

HRESULT CTDCArr::ParseColumnHeadings()
{
    LPWCH   pwchIntType     = L"int";
    LPWCH   pwchFloatType   = L"float";
    LPWCH   pwchStringType  = L"string";
    LPWCH   pwchBooleanType = L"boolean";
    LPWCH   pwchDateType    = L"date";

    HRESULT hr  = S_OK;
    LONG    iCol;

    iCol = m_iCols;

    //  Allocate space for column type info
    //
    hr = m_arrColInfo.InsertElems(0, iCol);
    if (!SUCCEEDED(hr))
        goto Cleanup;

    for (; iCol > 0; iCol--)
    {
        //  Column headings have the format:
        //      <column-name>[:<typename>[,<format>]]
        //
        CTDCColInfo *pColInfo   = GetColInfo(iCol);
        CTDCCell    *pCell      = GetDataCell(0, iCol);

        _ASSERT(pCell->vt == VT_BSTR);

        BSTR        bstr;
        LPWCH       pColon;

        bstr = pCell->bstrVal;
        pColInfo->vtType = VT_BSTR;     //  Default type for a column is BSTR
        pColon = wch_chr(bstr, L':');
        if (pColon != NULL)
        {
            WCHAR   *pwchFormat = NULL;
            LPWCH   pSpace;

            *pColon++ = 0;
            pSpace = wch_chr(pColon, L' ');

            if (pSpace != NULL)
            {
                *pSpace++ = '\0';
                pwchFormat = pSpace;
            }
            if (wch_icmp(pColon, pwchIntType) == 0)
                pColInfo->vtType = VT_I4;
            else if (wch_icmp(pColon, pwchFloatType) == 0)
                pColInfo->vtType = VT_R8;
            else if (wch_icmp(pColon, pwchStringType) == 0)
                pColInfo->vtType = VT_BSTR;
            else if (wch_icmp(pColon, pwchBooleanType) == 0)
                pColInfo->vtType = VT_BOOL;
            else if (wch_icmp(pColon, pwchDateType) == 0)
            {
                pColInfo->vtType = VT_DATE;

                TDCDateFmt  fmt = TDCDF_NULL;

                if (pwchFormat != NULL)
                {
                    int nPos    = 0;
                    int nDayPos = 0;
                    int nMonPos = 0;
                    int nYearPos= 0;

                    //  Convert the format string into an internal enum type
                    //  Find the relative positions of the letters 'D' 'M' 'Y'
                    //
                    for (; *pwchFormat != 0; nPos++, pwchFormat++)
                    {
                        switch (*pwchFormat)
                        {
                        case L'D':
                        case L'd':
                            nDayPos = nPos;
                            break;
                        case L'M':
                        case L'm':
                            nMonPos = nPos;
                            break;
                        case L'Y':
                        case L'y':
                            nYearPos = nPos;
                            break;
                        }
                    }
                    //  Compare the relative positions to work out the format
                    //
                    if (nDayPos < nMonPos && nMonPos < nYearPos)
                        fmt = TDCDF_DMY;
                    else if (nMonPos < nDayPos && nDayPos < nYearPos)
                        fmt = TDCDF_MDY;
                    else if (nDayPos < nYearPos && nYearPos < nMonPos)
                        fmt = TDCDF_DYM;
                    else if (nMonPos < nYearPos && nYearPos < nDayPos)
                        fmt = TDCDF_MYD;
                    else if (nYearPos < nMonPos && nMonPos < nDayPos)
                        fmt = TDCDF_YMD;
                    else if (nYearPos < nDayPos && nDayPos < nMonPos)
                        fmt = TDCDF_YDM;
                }
                pColInfo->datefmt = fmt;
            }
        }

        if (bstr != NULL)
        {
            //  Remove leading/trailing spaces from column name
            //
            LPWCH       pwch;
            LPWCH       pwchDest = NULL;
            LPWCH       pLastNonSpace = NULL;

            for (pwch = bstr; *pwch != 0; pwch++)
            {
                if (!fWCHIsSpace(*pwch))
                {
                    if (pwchDest == NULL)
                        pwchDest = bstr;
                    pLastNonSpace = pwchDest;
                }
                if (pwchDest != NULL)
                    *pwchDest++ = *pwch;
            }
            if (pLastNonSpace == NULL)
                bstr[0] = 0;        // all spaces!  Make it null string.
            else
                pLastNonSpace[1] = 0;
        }

        //  Copy the modified column header and free the original
        //
        pCell->bstrVal = SysAllocString(bstr);
        SysFreeString(bstr);

        if (pCell->bstrVal == NULL)
        {
            hr = E_OUTOFMEMORY;
            goto Cleanup;
        }
    }
Cleanup:
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Member:    VariantFromBSTR()
//
//  Synopsis:  Convert a BSTR value into a variant compatible with a
//             given column type.
//
//  Arguments: pVar       Pointer to resultant variant value
//             bstr       Source BSTR
//             pColInfo   Column information (type, format options etc).
//             lcid       Locale # for locale-specific conversion.
//
//  Returns:   S_OK upon success (pVar set)
//             OLE_E_CANTCONVERT if the given BSTR is badly formatted
//               (e.g. contains an invalid date value for a date conversion).
//             E_OUTOFMEMORY if insufficient memory is available for
//               a conversion.
//
//------------------------------------------------------------------------

HRESULT CTDCArr::VariantFromBSTR(VARIANT *pVar, BSTR bstr, CTDCColInfo *pColInfo, LCID lcid)
{
    HRESULT hr  = E_FAIL;

    VariantInit(pVar);
    switch (pColInfo->vtType)
    {
    case VT_DATE:
        if (pColInfo->datefmt != TDCDF_NULL)
        {
            //  Parse the date string according to specified format.
            //  First, find the three numeric components in the date.
            //
            int  n1;
            int  n2;
            int  n3;
            WCHAR   *pwch   = bstr;
            SYSTEMTIME  st;

            n1 = extract_num(&pwch);
            n2 = extract_num(&pwch);
            n3 = extract_num(&pwch);

            memset(&st, '\0', sizeof(st));
            switch (pColInfo->datefmt)
            {
            case TDCDF_DMY:
                st.wDay = n1;
                st.wMonth = n2;
                st.wYear = n3;
                break;
            case TDCDF_MDY:
                st.wDay = n2;
                st.wMonth = n1;
                st.wYear = n3;
                break;
            case TDCDF_DYM:
                st.wDay = n1;
                st.wMonth = n3;
                st.wYear = n2;
                break;
            case TDCDF_MYD:
                st.wDay = n3;
                st.wMonth = n1;
                st.wYear = n2;
                break;
            case TDCDF_YMD:
                st.wDay = n3;
                st.wMonth = n2;
                st.wYear = n1;
                break;
            case TDCDF_YDM:
                st.wDay = n2;
                st.wMonth = n3;
                st.wYear = n1;
                break;
            }

            VariantClear(pVar);
            if (n1 >= 0 && n2 >= 0 && n3 >= 0 &&
                SystemTimeToVariantTime(&st, &pVar->date))
            {
                pVar->vt = VT_DATE;
                hr = S_OK;
            }
            else
                hr = OLE_E_CANTCONVERT;
        }
        else
        {
            //  No date format specified - just use the default conversion
            //
            VARIANT vSrc;

            VariantInit(&vSrc);
            vSrc.vt = VT_BSTR;
            vSrc.bstrVal = bstr;
            hr = VariantChangeTypeEx(pVar, &vSrc, lcid, 0, pColInfo->vtType);
        }
        break;
    case VT_BOOL:
    case VT_I4:
    case VT_R8:
    default:
        //
        //  Perform a standard conversion.
        //
        {
            VARIANT vSrc;

            VariantInit(&vSrc);
            vSrc.vt = VT_BSTR;
            vSrc.bstrVal = bstr;
            hr = VariantChangeTypeEx(pVar, &vSrc, lcid, 0, pColInfo->vtType);
        }
        break;
    case VT_BSTR:
        //
        //  Duplicate the BSTR
        //
        pVar->bstrVal = SysAllocString(bstr);
        if (bstr != NULL && pVar->bstrVal == NULL)
            hr = E_OUTOFMEMORY;
        else
        {
            pVar->vt = VT_BSTR;
            hr = S_OK;
        }
        break;
    }
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Member:    VariantComp()
//
//  Synopsis:  Compares two variant values.
//
//  Arguments: pVar1            First variant value
//             pVar2            Second variant value
//             fCaseSensitive   TRUE if string comparisons should be
//                              case-sensitive, false if string comparisons
//                              should be case-insensitive.  Ignored if
//                              non-string data types are being compared.
//
//  Returns:   -1 if
//             OLE_E_CANTCONVERT if the given BSTR is badly formatted
//               (e.g. contains an invalid date value for a date conversion).
//             E_OUTOFMEMORY if insufficient memory is available for
//               a conversion.
//
//------------------------------------------------------------------------

int CTDCArr::VariantComp(VARIANT *pVar1, VARIANT *pVar2, VARTYPE type,
    boolean fCaseSensitive)
{
    int retval = 0;

    //  NULLs are lexically less than anything else
    //
    if (pVar1->vt == VT_NULL)
        retval = pVar2->vt == VT_NULL ? 0 : -1;
    else if (pVar2->vt == VT_NULL)
        retval = 1;
    else if (pVar1->vt != type)
    {
        //  Type-mismatches are lexically greater than anything else
        //
        retval = pVar2->vt == type ? 1 : 0;
    }
    else if (pVar2->vt != type)
    {
        //  Type-mismatches are lexically greater than anything else
        //
        retval = -1;
    }
    else
    {
        switch (type)
        {
        case VT_I4:
            retval = pVar1->lVal < pVar2->lVal
                ? -1
                : pVar1->lVal > pVar2->lVal
                    ? 1
                    : 0;
            break;
        case VT_R8:
            retval = pVar1->dblVal < pVar2->dblVal
                ? -1
                : pVar1->dblVal > pVar2->dblVal
                    ? 1
                    : 0;
            break;
        case VT_BSTR:
            retval = fCaseSensitive
                ? wch_cmp(pVar1->bstrVal, pVar2->bstrVal)
                : wch_icmp(pVar1->bstrVal, pVar2->bstrVal);
            break;
        case VT_BOOL:
            retval = pVar1->boolVal
                ? (pVar2->boolVal ? 0 : 1)
                : (pVar2->boolVal ? -1 : 0);
            break;
        case VT_DATE:
            retval = pVar1->date < pVar2->date
                ? -1
                : pVar1->date > pVar2->date
                    ? 1
                    : 0;
            break;
        default:
            retval = 0;     //  Unrecognised types are all lexically equal
            break;
        }
    }

    return retval;
}

//+-----------------------------------------------------------------------
//
//  Method:    CreateSortList()
//
//  Synopsis:  Creates a list of sort criteria from a text description.
//
//  Arguments: bstrSortCols          ';'-separted list of column names,
//                                   optionally prefixed with '+' (default)
//                                   or '-' indicating ascending or descending
//                                   sort order respectively for that column.
//
//  Returns:   S_OK upon success.
//             E_OUTOFMEMORY if insufficient memory is available for
//               the construction of sort criteria.
//
//  Side Effect:   Saves created list in m_pSortList
//
//+-----------------------------------------------------------------------

HRESULT CTDCArr::CreateSortList(BSTR bstrSortCols)
{
    HRESULT     hr = S_OK;

    if (m_pSortList != NULL)
    {
        delete m_pSortList;
        m_pSortList = NULL;
    }
    if (bstrSortCols != NULL)
    {
        WCHAR   *pwchEnd  = bstrSortCols;
        CTDCSortCriterion   **pLast = &m_pSortList;

        while (*pwchEnd != 0)
        {
            WCHAR   *pwchStart  = pwchEnd;
            boolean fSortAscending = TRUE;

            //  Discard leading white space and field-separators
            //
            while (*pwchStart == L';' || fWCHIsSpace(*pwchStart))
                pwchStart++;

            //  Strip off optional direction indicator + white space
            //
            if (*pwchStart == L'+' || *pwchStart == '-')
            {
                fSortAscending = *pwchStart++ == L'+';
                while (fWCHIsSpace(*pwchStart))
                    pwchStart++;
            }

            //  Find the field terminator.
            //  Strip out trailing white spaces.
            //
            for (pwchEnd = pwchStart; *pwchEnd != 0 && *pwchEnd != L';';)
                pwchEnd++;

            while (pwchStart < pwchEnd && fWCHIsSpace(pwchEnd[-1]))
                pwchEnd--;

            //  Ignore blank column names - this could be the result of
            //  a leading or trailing ';'.
            //
            if (pwchStart >= pwchEnd)
                continue;

            //  Find the column number from the column name
            //
            BSTR bstrColName = SysAllocStringLen(pwchStart, pwchEnd - pwchStart);

            if (bstrColName == NULL)
            {
                hr = E_OUTOFMEMORY;
                goto Cleanup;
            }
            LONG iCol = FindCol(bstrColName);
            SysFreeString(bstrColName);

            if (iCol > 0)
            {
                //  Allocate a node for this criterion
                //
                _ASSERT(*pLast == NULL);
                *pLast = new CTDCSortCriterion;
                if (*pLast == NULL)
                {
                    hr = E_OUTOFMEMORY;
                    goto Cleanup;
                }
                (*pLast)->m_fSortAscending = fSortAscending;
                (*pLast)->m_iSortCol = iCol;
                pLast = &(*pLast)->m_pNext;
            }
        }
    }

Cleanup:
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Method:    SortComp()
//
//  Synopsis:  Compares two rows using the elememts in the columns specified
//             by the current sort criteria.
//
//  Arguments: iRow1      Index of first row being compared
//             iRow2      Index of second row being compared
//
//  Returns:   -1 if first row should be sorted before the second row
//             0 if rows are equal
//             1 if first row should be sorted after the second row
//
//+-----------------------------------------------------------------------

int CTDCArr::SortComp(LONG iRow1, LONG iRow2)
{
    CTDCSortCriterion   *pCriterion;
    int     cmp = 0;

    for (pCriterion = m_pSortList;
         pCriterion != NULL && cmp == 0;
         pCriterion = pCriterion->m_pNext)
    {
        CTDCCell    *pCell1 = GetFilterCell(iRow1, pCriterion->m_iSortCol);
        CTDCCell    *pCell2 = GetFilterCell(iRow2, pCriterion->m_iSortCol);

        cmp = VariantComp(pCell1, pCell2, GetColInfo(pCriterion->m_iSortCol)->vtType,
                         m_fCaseSensitive);
        if (!pCriterion->m_fSortAscending)
            cmp = -cmp;
    }
    return cmp;
}

//+-----------------------------------------------------------------------
//
//  Method:    FilterParseComplex()
//
//  Synopsis:  Takes the text of a filter query, parses it and creates
//             a tree of CTDCFilterNode representing the query.
//
//  Arguments: phr: pointer to HRESULT value, set to indicate success/failure.
//
//             ppwchQuery:  This is a textual representation of a query.  The
//                          query language syntax is:
//
//               Query ::== Complex
//
//               Complex ::== Simple
//                       ::== Simple '&' Simple ( '&' Simple ... )
//                       ::== Simple '|' Simple ( '|' Simple ... )
//
//               Simple ::== '(' Complex ')'
//                      ::== Atom Relop Atom
//
//               Relop ::== '=' | '>' | '>=' | '<' | '<=' | '<>'
//
//               Atom ::== Bunch of characters up to a (, ), >, <, =, & or |
//                         If it's recognisable as field name, then it's
//                         treated as a field name.  Otherwise it's treated
//                         as a value.  Quotes (") are processed, and force
//                         the atom to be treated as a value.  Escape
//                         characters (\) are processed and allow the
//                         use of special characters within a field name.
//
//               Notes:
//               -----
//                  * The definition of 'Complex' expressly forbids mixing
//                    logical ANDs and ORs ('&' and '|') unless parentheses
//                    are used to clarify the query.  Something like:
//                          field1 > 2 & field3 = "lime" | field4 < 5
//                    is illegal, but:
//                          (field1 > 2 & field3 = "lime") | field4 < 5
//                    is fine.
//
//                  * It is illegal to attempt a comparison of two columns
//                    with different types.
//
//
//  Returns:   Pointer to parsed Filter Node upon success (*phr set to S_OK)
//             NULL upon failure (*phr set to an appropriate error code)
//
//+-----------------------------------------------------------------------

CTDCFilterNode *CTDCArr::FilterParseComplex(LPWCH *ppwchQuery, HRESULT *phr)
{
    *phr = S_OK;
    CTDCFilterNode  *retval;
    WCHAR   wchBoolOp   = 0;

    retval = FilterParseSimple(ppwchQuery, phr);

    //  Stop if there's an error, or we encounter a terminating ')' or '\0'
    //
    while (retval != NULL && **ppwchQuery != L')' && **ppwchQuery != 0)
    {
        //  Next character should be a matching logical connector ...
        //
        if (**ppwchQuery != L'&' && **ppwchQuery != L'|')
        {
            *phr = E_FAIL;
            break;
        }
        if (wchBoolOp == 0)
            wchBoolOp = **ppwchQuery;
        else if (wchBoolOp != **ppwchQuery)
        {
            *phr = E_FAIL;
            break;
        }
        (*ppwchQuery)++;
        CTDCFilterNode *pTmp = new CTDCFilterNode;
        if (pTmp == NULL)
        {
            *phr = E_OUTOFMEMORY;
            break;
        }
        pTmp->m_type = (wchBoolOp == L'&')
            ? CTDCFilterNode::NT_AND
            : CTDCFilterNode::NT_OR;
        pTmp->m_pLeft = retval;
        retval = pTmp;
        retval->m_pRight = FilterParseSimple(ppwchQuery, phr);
        if (retval->m_pRight == NULL)
            break;
    }
    if (!SUCCEEDED(*phr) && retval != NULL)
    {
        delete retval;
        retval = NULL;
    }
    return retval;
}

CTDCFilterNode *CTDCArr::FilterParseSimple(LPWCH *ppwch, HRESULT *phr)
{
    *phr = S_OK;
    CTDCFilterNode  *retval = NULL;

    fWCHEatSpace(ppwch);    //  Eat up white space

    if (fWCHEatTest(ppwch, L'('))
    {
        retval = FilterParseComplex(ppwch, phr);
        if (retval != NULL)
        {
            if (fWCHEatTest(ppwch, L')'))
                fWCHEatSpace(ppwch);    //  Eat up white space
            else
                *phr = E_FAIL;
        }
        goto Cleanup;
    }

    retval = FilterParseAtom(ppwch, phr);
    if (retval == NULL)
        goto Cleanup;

    {
        CTDCFilterNode *pTmp = new CTDCFilterNode;
        if (pTmp == NULL)
        {
            *phr = E_OUTOFMEMORY;
            goto Cleanup;
        }
        pTmp->m_pLeft = retval;
        retval = pTmp;
    }

    retval->m_vt = retval->m_pLeft->m_vt;

    //  Get the relational operator
    //
    if (fWCHEatTest(ppwch, L'='))
        retval->m_type = CTDCFilterNode::NT_EQ;
    else if (fWCHEatTest(ppwch, L'>'))
        retval->m_type = fWCHEatTest(ppwch, L'=')
            ? CTDCFilterNode::NT_GE
            : CTDCFilterNode::NT_GT;
    else if (fWCHEatTest(ppwch, L'<'))
        retval->m_type = fWCHEatTest(ppwch, L'=')
                ? CTDCFilterNode::NT_LE
                : fWCHEatTest(ppwch, L'>')
                    ? CTDCFilterNode::NT_NE
                    : CTDCFilterNode::NT_LT;
    else
    {
        *phr = E_FAIL;
        goto Cleanup;
    }

    retval->m_pRight = FilterParseAtom(ppwch, phr);
    if (retval->m_pRight == NULL)
        goto Cleanup;

    if (retval->m_pLeft->m_iCol <= 0 && retval->m_pRight->m_iCol <= 0)
    {
        //  At least one of the atoms being compared must be a column
        //
        //  This condition means we don't have to test for comparison
        //  of two wildcard values.
        //
        *phr = E_FAIL;
        goto Cleanup;
    }

    //  Check type compatibility of atoms
    //
    if (retval->m_pRight->m_vt != retval->m_vt)
    {
        CTDCFilterNode  *pSrc = retval->m_pRight;
        CTDCFilterNode  *pTarg= retval->m_pLeft;

        if (retval->m_pLeft->m_iCol > 0)
        {
            if (retval->m_pRight->m_iCol > 0)
            {
                //  Two columns of incompatible type - can't resolve
                //
                *phr = E_FAIL;
                goto Cleanup;
            }
            pSrc = retval->m_pLeft;
            pTarg = retval->m_pRight;
        }
        _ASSERT(pTarg->m_vt == VT_BSTR);
        _ASSERT(pTarg->m_iCol == 0);
        _ASSERT(pSrc->m_iCol > 0);
        CTDCColInfo *pColInfo = GetColInfo(pSrc->m_iCol);
        _ASSERT(pColInfo->vtType == pSrc->m_vt);
        VARIANT vtmp;
        VariantInit(&vtmp);
        *phr = VariantFromBSTR(&vtmp, pTarg->m_value.bstrVal, pColInfo, m_lcid);
        if (!SUCCEEDED(*phr))
            goto Cleanup;
        VariantClear(&pTarg->m_value);
        pTarg->m_value = vtmp;
        pTarg->m_vt = pSrc->m_vt;
        retval->m_vt = pSrc->m_vt;
    }

Cleanup:
    if (!SUCCEEDED(*phr) && retval != NULL)
    {
        delete retval;
        retval = NULL;
    }
    return retval;
}

CTDCFilterNode *CTDCArr::FilterParseAtom(LPWCH *ppwch, HRESULT *phr)
{
    *phr = S_OK;
    CTDCFilterNode  *retval = NULL;
    int nQuote  = 0;
    boolean fDone = FALSE;
    LPWCH   pwchDest;
    LPWCH   pwchLastStrip;

    fWCHEatSpace(ppwch);    //  Eat up white space

    WCHAR   *pwchTmpBuf = new WCHAR[wch_len(*ppwch) + 1];
    if (pwchTmpBuf == NULL)
    {
        *phr = E_OUTOFMEMORY;
        goto Cleanup;
    }

    pwchDest = pwchTmpBuf;
    pwchLastStrip = pwchTmpBuf;

    while (**ppwch != 0 && !fDone)
        switch (**ppwch)
        {
        case L'\\':
            //  Handle escape characters
            //
            if ((*pwchDest++ = *++(*ppwch)) != 0)
                pwchLastStrip = (*ppwch)++;
            break;
        case L'"':
            //  Quotes
            //
            pwchLastStrip = (*ppwch)++;
            nQuote++;
            break;
        case L'>':
        case L'<':
        case L'=':
        case L'(':
        case L')':
        case L'&':
        case L'|':
            if (fDone = ((nQuote & 1) == 0))
                break;
        default:
            *pwchDest++ = *(*ppwch)++;
        }

    //  Strip off trailing white space
    //
    while (pwchDest > pwchLastStrip && fWCHIsSpace(pwchDest[-1]))
        pwchDest--;
    *pwchDest = 0;

    if ((pwchDest == pwchTmpBuf && nQuote == 0) || (nQuote & 1 != 0))
    {
        //  Empty string or mismatched quote

//
        *phr = E_FAIL;
        goto Cleanup;
    }

    retval = new CTDCFilterNode;
    if (retval == NULL)
    {
        *phr = E_OUTOFMEMORY;
        goto Cleanup;
    }

    retval->m_type = CTDCFilterNode::NT_ATOM;
    retval->m_iCol = nQuote > 0 ? 0 : FindCol(pwchTmpBuf);
    if (retval->m_iCol == 0)
    {
        retval->m_vt = VT_BSTR;
        retval->m_value.vt = VT_BSTR;
        retval->m_value.bstrVal = SysAllocString(pwchTmpBuf);
        if (retval->m_value.bstrVal == NULL)
        {
            *phr = E_OUTOFMEMORY;
            goto Cleanup;
        }
        retval->m_fWildcard = wch_chr(retval->m_value.bstrVal, L'*') != NULL;
    }
    else
    {
        retval->m_vt = GetColInfo(retval->m_iCol)->vtType;
        retval->m_fWildcard = FALSE;
    }

Cleanup:
    if (pwchTmpBuf != NULL)
        delete pwchTmpBuf;
    if (!SUCCEEDED(*phr) && retval != NULL)
    {
        delete retval;
        retval = NULL;
    }
    return retval;
}

//+-----------------------------------------------------------------------
//
//  Method:    EvalDataRow()
//
//  Synopsis:  Evaluates the given data row # against the filter query
//             represented by 'pNode'.
//
//  Arguments: iRow      The number of the row to evaluate.
//             pNode     A filter query to the row against.
//
//  Returns:   TRUE if the given row satisfies the filter query.
//             FALSE otherwise.
//
//+-----------------------------------------------------------------------

boolean CTDCArr::EvalDataRow(LONG iRow, CTDCFilterNode *pNode)
{
    boolean retval  = TRUE;
    VARIANT *pVar1;
    VARIANT *pVar2;

    _ASSERT(pNode != NULL);
    switch (pNode->m_type)
    {
    case CTDCFilterNode::NT_AND:
        retval = EvalDataRow(iRow, pNode->m_pLeft) &&
                 EvalDataRow(iRow, pNode->m_pRight);
        break;
    case CTDCFilterNode::NT_OR:
        retval = EvalDataRow(iRow, pNode->m_pLeft) ||
                 EvalDataRow(iRow, pNode->m_pRight);
        break;
    case CTDCFilterNode::NT_EQ:
    case CTDCFilterNode::NT_NE:
    case CTDCFilterNode::NT_LT:
    case CTDCFilterNode::NT_GT:
    case CTDCFilterNode::NT_LE:
    case CTDCFilterNode::NT_GE:
        pVar1 = &pNode->m_pLeft->m_value;
        pVar2 = &pNode->m_pRight->m_value;

        if (pNode->m_pLeft->m_iCol > 0)
            pVar1 = GetDataCell(iRow, pNode->m_pLeft->m_iCol);
        if (pNode->m_pRight->m_iCol > 0)
            pVar2 = GetDataCell(iRow, pNode->m_pRight->m_iCol);

        if ((pNode->m_pLeft->m_fWildcard || pNode->m_pRight->m_fWildcard) &&
            (pNode->m_type == CTDCFilterNode::NT_EQ ||
             pNode->m_type == CTDCFilterNode::NT_NE) &&
             pVar1->vt == VT_BSTR && pVar2->vt == VT_BSTR)
        {
            //  Wildcards are only meaningful in comparing strings
            //  for equlaity / inequality
            //
            VARIANT *pText;
            VARIANT *pPattern;

            if (pNode->m_pLeft->m_fWildcard)
            {
                pPattern = pVar1;
                pText = pVar2;
            }
            else
            {
                pText = pVar1;
                pPattern = pVar2;
            }

            retval = wch_wildcardMatch(pText->bstrVal, pPattern->bstrVal,
                                       m_fCaseSensitive)
                ? (pNode->m_type == CTDCFilterNode::NT_EQ)
                : (pNode->m_type == CTDCFilterNode::NT_NE);
        }
        else
        {
            int     cmp;

            cmp = VariantComp(pVar1, pVar2, pNode->m_vt, m_fCaseSensitive);

            switch (pNode->m_type)
            {
            case CTDCFilterNode::NT_LT:    retval = cmp <  0;  break;
            case CTDCFilterNode::NT_LE:    retval = cmp <= 0;  break;
            case CTDCFilterNode::NT_GT:    retval = cmp >  0;  break;
            case CTDCFilterNode::NT_GE:    retval = cmp >= 0;  break;
            case CTDCFilterNode::NT_EQ:    retval = cmp == 0;  break;
            case CTDCFilterNode::NT_NE:    retval = cmp != 0;  break;
            }
        }
        break;

    default:
        _ASSERT(FALSE);
    }
    return retval;
}

//+-----------------------------------------------------------------------
//
//  Method:    ApplySortFilterCriteria()
//
//  Synopsis:  Resets any filter and sorting criterion for the control to
//             the values specified.  Initiates sort/filter operations
//             if appropriate.
//
//  Arguments: None.
//
//  Returns:   S_OK upon success.
//             E_OUTOFMEMORY if there was insufficient memory to complete
//               the operation.
//
//+-----------------------------------------------------------------------

STDMETHODIMP
CTDCArr::ApplySortFilterCriteria()
{
    HRESULT hr  = S_OK;
    LONG iRow;

    if (!m_fSortFilterDisrupted ||
        m_state == LS_UNINITIALISED ||
        m_state == LS_LOADING_HEADER_UNAVAILABLE)
    {
        //  No change, or can't do anything yet.
        //
        goto Cleanup;
    }

    //  Discard the old parse trees
    //
    if (m_pSortList != NULL)
        delete m_pSortList;
    if (m_pFilterTree != NULL)
        delete m_pFilterTree;

    m_pSortList = NULL;
    m_pFilterTree = NULL;

    //  Discard old filtered rows
    //
    if (m_arrparrFilter.GetSize() > 0)
        m_arrparrFilter.DeleteElems(0, m_arrparrFilter.GetSize());

    //  Create an array of filter rows from the data rows
    //
    hr = m_arrparrFilter.InsertElems(0, m_iDataRows + 1);
    if (!SUCCEEDED(hr))
        goto Cleanup;
    for (iRow = 0; iRow <= m_iDataRows; iRow++)
        m_arrparrFilter[iRow] = m_arrparrCells[iRow];
    m_iFilterRows = CalcFilterRows();

    //  Create the filter parse tree
    //
    if (m_bstrFilterExpr != NULL)
    {
        LPWCH pwchQuery = m_bstrFilterExpr;

        m_pFilterTree = FilterParseComplex(&pwchQuery, &hr);
        if (hr == E_FAIL || (m_pFilterTree != NULL && *pwchQuery != 0))
        {
            //  Parse failed or there were unparsed characters left over.
            //  This gets treated as an 'include everything' filter.
            //
            if (m_pFilterTree != NULL)
            {
                delete m_pFilterTree;
                m_pFilterTree = NULL;
            }
            hr = S_OK;
        }
    }

    //  Filter the rows
    //
    if (m_pFilterTree != NULL)
    {
        LONG    iRowDest    = 1;

        for (iRow = 1; iRow <= m_iFilterRows; iRow++)
            if (EvalDataRow(iRow, m_pFilterTree))
                m_arrparrFilter[iRowDest++] = m_arrparrFilter[iRow];
        if (iRowDest < iRow)
            m_arrparrFilter.DeleteElems(iRowDest, iRow - iRowDest);
        m_iFilterRows = CalcFilterRows();
    }

    //  Create the sort list
    //
    hr = CreateSortList(m_bstrSortExpr);
    if (!SUCCEEDED(hr))
        goto Cleanup;

    //  Sort the filtered rows
    //
    if (m_pSortList != NULL && m_iFilterRows > 0)
    {
        SortElt *pSortArr   = new SortElt[m_iFilterRows + 1];
        if (pSortArr == NULL)
        {
            hr = E_OUTOFMEMORY;
            goto Cleanup;
        }
        for (iRow = 0; iRow <= m_iFilterRows; iRow++)
        {
            pSortArr[iRow].pInstance = this;
            pSortArr[iRow].iRow = iRow;
            pSortArr[iRow].parrRow = m_arrparrFilter[iRow];
        }

        qsort((void *)&pSortArr[1], m_iFilterRows, sizeof(pSortArr[0]), CompareSort);

        for (iRow = 0; iRow <= m_iFilterRows; iRow++)
            m_arrparrFilter[iRow] = pSortArr[iRow].parrRow;

        delete pSortArr;

    }

    m_fSortFilterDisrupted = FALSE;

    if (m_state == LS_LOADING_HEADER_AVAILABLE && m_iDataRows > 0)
    {
        //  We've just parsed the sort/filter expressions - there
        //  was no data to sort/filter, so dont register a change.
    }
    else
    {
        //  Notify the event-broker of the changes
        //
        if (m_pEventBroker != NULL)
            hr = m_pEventBroker->STDDataSetChanged();
    }

Cleanup:
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Method:    SetSortFilterCriteria()
//
//  Synopsis:  Resets any filter and sorting criterion for the control to
//             the values specified.  Initiates sort/filter operations
//             if any the changes invalidate existing criteria.
//
//  Arguments: bstrSortExpr    List of columns for sorting ("" = no sorting)
//             bstrFilterExpr  Expression for filtering ("" = no filtering)
//
//  Returns:   S_OK upon success.
//             E_OUTOFMEMORY if there was insufficient memory to complete
//               the operation.
//
//+-----------------------------------------------------------------------

STDMETHODIMP
CTDCArr::SetSortFilterCriteria(BSTR bstrSortExpr, BSTR bstrFilterExpr,
                              boolean fCaseSensitive)
{
    HRESULT hr  = S_OK;

    //  Check if we need to reparse the sort/filter criteria
    //

    if (wch_cmp(bstrSortExpr, m_bstrSortExpr) != 0 ||
        wch_cmp(bstrFilterExpr, m_bstrFilterExpr) != 0 ||
        fCaseSensitive != m_fCaseSensitive)
    {
        m_fSortFilterDisrupted = TRUE;
    }
    m_bstrSortExpr = bstrSortExpr;
    m_bstrFilterExpr = bstrFilterExpr;
    m_fCaseSensitive = fCaseSensitive;

    //  If not loaded, leave it to the load process to apply any changes
    //
    if (m_state == LS_LOADED)
        hr = ApplySortFilterCriteria();
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Method:    CTDCCStartDataLoad()
//
//  Synopsis:  Preparation for a load operation via FieldSink routines below
//
//  Arguments: fUseHeader      TRUE if the first line of fields should
//                               be interpreted as column name/type info.
//             bstrSortExpr    Sort expression for ordering rows
//             bstrFilterExpr  Filter expression for including/excluding rows
//             lcidRead        Locale ID to use for interpreting locale-
//                               dependent data formats (date, number etc).
//             pBSC            COM object performing the data transfer
//             fAppend         Flag indicating whether the data should be
//                             appended to any existing data.
//
//  Returns:   S_OK indicating success.
//
//+-----------------------------------------------------------------------

STDMETHODIMP
CTDCArr::StartDataLoad(boolean fUseHeader, BSTR bstrSortExpr,
    BSTR bstrFilterExpr, LCID lcidRead,
    CComObject<CMyBindStatusCallback<CTDCCtl> > *pBSC,
    boolean fAppend, boolean fCaseSensitive)
{
    HRESULT hr  = S_OK;

    //  If we're asked to append to existing data AND
    //     - there isn't any OR
    //     - the previous load didn't load a header row
    //  then treat it as an initial load.
    //
    if (fAppend && m_state == LS_UNINITIALISED)
        fAppend = FALSE;

    if (fAppend)
    {
        if (m_state != LS_LOADED || m_iDataRows < 0)
        {
            hr = E_FAIL;
            goto Cleanup;
        }
        m_state = LS_LOADING_HEADER_AVAILABLE;
    }
    else
    {
        if (m_state != LS_UNINITIALISED ||
            m_iDataRows != -1 ||
            m_iFilterRows != -1 ||
            m_iCols != 0)
        {
            hr = E_FAIL;
            goto Cleanup;
        }
        m_state = LS_LOADING_HEADER_UNAVAILABLE;
        m_fSortFilterDisrupted = TRUE;
    }

    SetSortFilterCriteria(bstrSortExpr, bstrFilterExpr,
                         fCaseSensitive);

    m_fUseHeader = fUseHeader;
    m_fSkipRow = fAppend && fUseHeader;
    _ASSERT(m_iFilterRows == CalcFilterRows());
    _ASSERT(m_iDataRows == CalcDataRows());
    _ASSERT(m_iCols == CalcCols());
    m_iCurrRow = m_iDataRows + 1;
    m_iCurrCol = 1;
    m_lcidRead = lcidRead;
    if (m_pEventBroker != NULL)
    hr = m_pEventBroker->STDLoadStarted(pBSC, fAppend);

Cleanup:
    return hr;
}

//////////////////////////////////////////////////////////////////////////
//
//        CTDCFieldSink Methods - see comments in file TDCParse.h
//        ---------------------
//
//////////////////////////////////////////////////////////////////////////

//+-----------------------------------------------------------------------
//
//  Method:    AddField()
//
//  Synopsis:  Adds a data cell to the growing cell grid.
//
//  Arguments: pwch         Wide-char string holding data for the cell
//             dwSize       # of significant bytes in 'pwch'
//
//  Returns:   S_OK indicating success.
//             E_OUTOFMEMORY if there was not enough memory to add the cell.
//
//+-----------------------------------------------------------------------

STDMETHODIMP
CTDCArr::AddField(LPWCH pwch, DWORD dwSize)
{
    _ASSERT(m_state == LS_LOADING_HEADER_UNAVAILABLE ||
            m_state == LS_LOADING_HEADER_AVAILABLE);

    HRESULT hr          = S_OK;
    LONG    nCols       = 0;
    BSTR    bstr        = NULL;

    if (m_fSkipRow)
        goto Cleanup;
#ifdef TDC_ATL_DEBUG_ADDFIELD
    ATLTRACE( _T("CTDCArr::AddField called: %d, %d\n"), m_iCurrRow, m_iCurrCol);
#endif

    if (m_iCurrRow > m_iDataRows && m_iCurrCol == 1)
    {
        TSTDArray<CTDCCell> *pRow;

        //  Need to insert a new row
        //
        _ASSERT(m_iCurrRow == m_iDataRows + 1);
        hr = m_arrparrCells.InsertElems(m_iCurrRow, 1);
        if (!SUCCEEDED(hr))
            goto Cleanup;
        pRow = new TSTDArray<CTDCCell>;
        if (pRow == NULL)
        {
            hr = E_OUTOFMEMORY;
            goto Cleanup;
        }
        m_arrparrCells[m_iCurrRow] = pRow;
        if (m_iCurrRow > 0)
        {
            //  We've already read at least one row, so we know how
            //  many columns to insert for this row
            //
            hr = m_arrparrCells[m_iCurrRow]->InsertElems(0, m_iCols);
            if (!SUCCEEDED(hr))
                goto Cleanup;
        }
    }
    if (m_iCurrRow == 0)
    {
        //  This is the first row - we don't know how many columns there
        //  will be, so just insert a single cell for this new element.
        //
        _ASSERT(m_iCurrCol == m_iCols + 1);
        hr = m_arrparrCells[m_iCurrRow]->InsertElems(m_iCurrCol - 1, 1);
        if (!SUCCEEDED(hr))
            goto Cleanup;
        m_iCols++;
    }

    if (m_iCurrCol <= m_iCols)
    {
        CTDCCell    *pCell  = GetDataCell(m_iCurrRow, m_iCurrCol);

        pCell->clear();
        pCell->vt = VT_BSTR;

        if (dwSize <= 0)
            pCell->bstrVal = NULL;
        else
        {
            pCell->bstrVal = SysAllocStringLen(pwch, dwSize);
            if (pCell->bstrVal == NULL)
            {
                hr = E_OUTOFMEMORY;
                goto Cleanup;
            }
        }

        if (m_iCurrRow > 0)
        {
            CTDCColInfo *pColInfo = GetColInfo(m_iCurrCol);

            if (pColInfo->vtType != VT_BSTR)
            {
                VARIANT     v;
                HRESULT     hr;

                hr = VariantFromBSTR(&v, pCell->bstrVal, pColInfo, m_lcidRead);
                if (SUCCEEDED(hr))
                {
                    hr = VariantCopy(pCell, &v);
                    VariantClear(&v);
                }
                else
                {
                    //  Leave it as a BSTR
                    //
                    hr = S_OK;
                }
            }
        }
    }

    m_iCurrCol++;

Cleanup:
    return hr;
}

// InsertionSortHelper -
//  returns -1 if the candidate row < current filter array row
//           0 if the candidate row = current filter array row
//           1 if the candidate row > current filter array row

int
CTDCArr::InsertionSortHelper(int iRow)
{
    CTDCSortCriterion *pCriterion;
    int cmp = 0;

    for (pCriterion = m_pSortList; pCriterion != NULL;
         pCriterion = pCriterion->m_pNext)
    {
        CTDCCell    *pCell1 = GetDataCell(m_iDataRows, pCriterion->m_iSortCol);
        CTDCCell    *pCell2 = GetFilterCell(iRow, pCriterion->m_iSortCol);

        cmp = VariantComp(pCell1, pCell2,
                          GetColInfo(pCriterion->m_iSortCol)->vtType, m_fCaseSensitive);

        if (!pCriterion->m_fSortAscending)
            cmp = -cmp;

        // if < or >, we don't have to look at any further criterion
        if (cmp)
            break;
    }
    return cmp;
}

//+-----------------------------------------------------------------------
//
//  Method:    EOLN()
//
//  Synopsis:  Closes of the current row in the growing cell grid,
//               handling column headings if it's the first row.
//
//  Arguments: None.
//
//  Returns:   S_OK indicating success.
//             E_OUTOFMEMORY if insufficient memory is available for
//               a conversion.
//
//+-----------------------------------------------------------------------

STDMETHODIMP
CTDCArr::EOLN()
{
    ATLTRACE(_T("CTDCArr::EOLN called, row: %d\n"), m_iCurrRow);

    HRESULT hr  = S_OK;

    _ASSERT(m_state == LS_LOADING_HEADER_UNAVAILABLE ||
            m_state == LS_LOADING_HEADER_AVAILABLE);

    if (m_fSkipRow)
    {
        //  Appending to existing data; skip over the first (header) line
        m_fSkipRow = FALSE;
        goto Cleanup;
    }

    if (m_iCurrRow == 0)
    {
        //  The first row has been inserted - if m_fUseHeader indicates
        //  that the first row contains header information then parse
        //  it; otherwise, create some numbered column headings.
        //
        if (!m_fUseHeader)
        {
            hr = CreateNumberedColumnHeadings();
            if (!SUCCEEDED(hr))
                goto Cleanup;

            //  An extra row has been inserted - update the insertion
            //  row index for later insertion of new elements
            //
            m_iCurrRow++;

            //  Initialise each column heading as "Column<column#>"
            //
            RenumberColumnHeadings();
        }

        m_iDataRows++;
        m_iFilterRows++;
        _ASSERT(m_iDataRows == 0);
        _ASSERT(m_iFilterRows == 0);

        ParseColumnHeadings();

        m_state = LS_LOADING_HEADER_AVAILABLE;

        //  Insert the hedaer row into the list of filtered rows
        //
        hr = m_arrparrFilter.InsertElems(0, 1);
        if (!SUCCEEDED(hr))
            goto Cleanup;
        m_arrparrFilter[0] = m_arrparrCells[0];

        //  Notify the event handler that the headers have been loaded
        //
        if (m_pEventBroker != NULL)
        {
            hr = m_pEventBroker->STDLoadedHeader();
            OutputDebugStringX(_T("TDCCtl: header loaded\n"));
            if (!SUCCEEDED(hr))
                goto Cleanup;
        }
    }

    if (m_iCurrRow > 0)
    {
        //  Convert uninitialised cells into their column's type.
        //
        LONG    iCol;

        for (iCol = m_iCurrCol; iCol < m_iCols; iCol++)
        {
            CTDCCell    *pCell    = GetDataCell(m_iCurrRow, iCol);

            //  This uninitialised VARIANT is assumed to be the result
            //  of specifying too few cells in a row.
            //
            _ASSERT(pCell->vt == VT_EMPTY);
            pCell->vt = VT_BSTR;
            pCell->bstrVal = NULL;

            CTDCColInfo *pColInfo = GetColInfo(iCol);

            if (pColInfo->vtType != VT_BSTR)
            {
                VARIANT     v;
                HRESULT     tmp_hr;

                tmp_hr = VariantFromBSTR(&v, pCell->bstrVal, pColInfo, m_lcidRead);
                if (SUCCEEDED(tmp_hr))
                {
                    hr = VariantCopy(pCell, &v);
                    VariantClear(&v);
                    if (!SUCCEEDED(hr))
                        goto Cleanup;
                }
                else
                {
                    //  Leave the cell as a BSTR
                    //
                }
            }
        }
        m_iDataRows++;
    }

    m_iCurrCol = 1;
    m_iCurrRow++;

    if (m_fSortFilterDisrupted)
    {
        //  This will have the side-effect of incorporating any new data rows
        //
        hr = ApplySortFilterCriteria();
        if (!SUCCEEDED(hr))
            goto Cleanup;
    }
    else if (m_iDataRows > 0 &&
        (m_pFilterTree == NULL || EvalDataRow(m_iDataRows, m_pFilterTree)))
    {
        //  The new row passed the filter criteria.
        //  Insert the new row into the filtered list
        //
        LONG iRowInsertedAt = m_iFilterRows + 1;

        //  at the correct insertion point according to the current
        //  sort criteria, if there is one.  We only need to do the search
        //  if this is not the first row, and if the candidate row is less
        //  than the last row.
        if (m_pSortList != NULL && m_iFilterRows != 0
            && InsertionSortHelper(m_iFilterRows) < 0)
        {
            // not at end, do traditional binary search.
            LONG lLow = 1;          // we don't use element zero!
            LONG lHigh = m_iFilterRows + 1;
            LONG lMid;

            while (lLow < lHigh)
            {
                lMid = (lLow + lHigh) / 2;
                // Note that InsertionSortHelper automatically flips the comparison
                // if m_fAscending flag is off.
                if (InsertionSortHelper(lMid) <= 0)
                {
                    lHigh = lMid;
                }
                else
                {
                    lLow = lMid + 1;
                }
            }
            iRowInsertedAt = lLow;
        }

        hr = m_arrparrFilter.InsertElems(iRowInsertedAt, 1);
        if (!SUCCEEDED(hr))
            goto Cleanup;
        m_arrparrFilter[iRowInsertedAt] = m_arrparrCells[m_iDataRows];
        ++m_iFilterRows;

        //  Notify event handler of row insertion
        //
        if (m_pEventBroker != NULL)
            hr = m_pEventBroker->rowsAvailable(iRowInsertedAt, 1);

    }

Cleanup:
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Method:    EOF()
//
//  Synopsis:  Indicates no more cells will be added to the cell grid.
//             The column-heading cells are added unless it was indicated
//             that cell headings should be taken from the data read.
//             The cells in each column are converted to that column's
//             specified data type.
//
//  Arguments: None.
//
//  Returns:   S_OK indicating success.
//
//+-----------------------------------------------------------------------

STDMETHODIMP
CTDCArr::EOF()
{
    OutputDebugStringX(_T("CTDArr::EOF() called\n"));
    _ASSERT(m_state == LS_LOADING_HEADER_UNAVAILABLE ||
            m_state == LS_LOADING_HEADER_AVAILABLE);
    HRESULT hr = S_OK;

    if (m_iCurrCol > 1)
        EOLN();
    m_state = LS_LOADED;
    m_iFilterRows = CalcFilterRows();
    _ASSERT(m_iDataRows == CalcDataRows());
    _ASSERT(m_iCols == CalcCols());

    if (m_fSortFilterDisrupted)
    {
        hr = ApplySortFilterCriteria();
        if (!SUCCEEDED(hr))
            goto Cleanup;
    }
    if (m_pEventBroker != NULL)
        hr = m_pEventBroker->STDLoadCompleted();

Cleanup:
    return hr;
}

// GetEstimatedRows..
// We should really see if URLMon has a means of giving a byte count on the file
// we're downloading.  For now though..
STDMETHODIMP
CTDCArr::getEstimatedRows(LONG *pcRows)
{
    *pcRows = m_iFilterRows;
    if (m_state<LS_LOADED)
    {
        // Return twice number of rows, but be careful not to return 2 * 0.
        *pcRows = m_iFilterRows ? m_iFilterRows * 2 : -1;
    }
    return S_OK;
}

STDMETHODIMP
CTDCArr::isAsync(BOOL *pbAsync)
{
//    *pbAsync = m_fAsync;
    // The TDC always behaves as if it's Async.  Specifically, we always fire
    // TransferComplete, even if we have to buffer the notification until our
    // addOLEDBSimplerProviderListener is actually called.
    *pbAsync = TRUE;
    return S_OK;
}

STDMETHODIMP
CTDCArr::stopTransfer()
{
    HRESULT hr = S_OK;

    //  Force the load state into UNINITIALISED or LOADED ...
    //
    switch (m_state)
    {
    case LS_UNINITIALISED:
    case LS_LOADED:
        break;

    case LS_LOADING_HEADER_UNAVAILABLE:
        //  Free any allocated cell memory
        //
        if (m_arrparrFilter.GetSize() > 0)
            m_arrparrFilter.DeleteElems(0, m_arrparrFilter.GetSize());
        if (m_arrparrCells.GetSize() > 0)
            m_arrparrCells.DeleteElems(0, m_arrparrCells.GetSize());
        m_state = LS_UNINITIALISED;
        m_iFilterRows = CalcFilterRows();
        m_iDataRows = CalcDataRows();
        m_iCols = CalcCols();

        // If we stop the load before the header was parsed, we won't
        // have a dataset, but we still need to fire datasetchanged,
        // to let our customer know the query failed.
        if (m_pEventBroker != NULL)
            hr = m_pEventBroker->STDDataSetChanged();

        //
        // fall through to LOADING_HEADER_AVAILABLE!
        //

    case LS_LOADING_HEADER_AVAILABLE:
        m_state = LS_LOADED;            // mark us as finished now

        // LoadStopped will abort any transfer in progress, and fire
        // transferComplete with the OSPXFER_ABORT flag.
        if (m_pEventBroker != NULL)
            hr = m_pEventBroker->STDLoadStopped();
        break;
    }

    return hr;
}

//////////////////////////////////////////////////////////////////////////
//
//        Implementation of IUnknown COM interface.
//        -----------------------------------------
//
//////////////////////////////////////////////////////////////////////////

//+-----------------------------------------------------------------------
//
//  Method:    QueryInterface()
//
//  Synopsis:  Implements part of the standard IUnknown COM interface.
//               (Returns a pointer to this COM object)
//
//  Arguments: riid          GUID to recognise
//             ppv           Pointer to this COM object [OUT]
//
//  Returns:   S_OK upon success.
//             E_NOINTERFACE if queried for an unrecognised interface.
//
//+-----------------------------------------------------------------------

STDMETHODIMP
CTDCArr::QueryInterface (REFIID riid, LPVOID * ppv)
{
    HRESULT hr;

    _ASSERTE(ppv != NULL);

    // This is the non-delegating IUnknown implementation
    if (riid == IID_IUnknown || riid == IID_OLEDBSimpleProvider)
    {
        *ppv = this;
        ((LPUNKNOWN)*ppv)->AddRef();
        hr = S_OK;
    }
    else
    {
        *ppv = NULL;
        hr = E_NOINTERFACE;
    }

#ifdef _ATL_DEBUG_QI
    AtlDumpIID(riid, _T("CTDCArr"), hr);
#endif
    return hr;
}

//+-----------------------------------------------------------------------
//
//  Method:    AddRef()
//
//  Synopsis:  Implements part of the standard IUnknown COM interface.
//               (Adds a reference to this COM object)
//
//  Arguments: None
//
//  Returns:   Number of references to this COM object.
//
//+-----------------------------------------------------------------------

STDMETHODIMP_(ULONG)
CTDCArr::AddRef ()
{
    return ++m_cRef;
}

//+-----------------------------------------------------------------------
//
//  Method:    Release()
//
//  Synopsis:  Implements part of the standard IUnknown COM interface.
//               (Removes a reference to this COM object)
//
//  Arguments: None
//
//  Returns:   Number of remaining references to this COM object.
//             0 if the COM object is no longer referenced.
//
//+-----------------------------------------------------------------------

STDMETHODIMP_(ULONG)
CTDCArr::Release ()
{
    ULONG retval;

    m_cRef -= 1;
    retval = m_cRef;
    if (!m_cRef)
    {
        m_cRef = 0xffff;    //MM: Use this 'flag' for debug?
        delete this;
    }

    return retval;
}


TDCARR.H

//+-----------------------------------------------------------------------
//
//  TDC Array
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       TDCArr.h
//
//  Contents:   Declaration of the CTDCArr class.
//              This class forms the heart of the Tabular Data Control.
//              It provides the core 2D array of variant values, plus
//              a (possibly filtered/sorted) view of this data for
//              presentation through an ISimpleTabularData interface.
//
//------------------------------------------------------------------------

// ;begin_internal
#ifndef TDC_SKEL
// ;end_internal
#include "tarray.h"

//------------------------------------------------------------------------
//
//  CTDCCell
//
//  This class represents a cell value within the 2D TDC control
//
//------------------------------------------------------------------------

class CTDCCell : public VARIANT
{
public:
    CTDCCell()
    {
        VariantInit(this);
    }
    ~CTDCCell()
    {
        clear();
    }
    void clear()
    {
        VariantClear(this);
    }
};

//------------------------------------------------------------------------
//
//  TDCDateFmt
//
//  This enum represents the 6 meaningful ways to format dates
//
//------------------------------------------------------------------------
typedef enum
{
    TDCDF_NULL,
    TDCDF_DMY,
    TDCDF_DYM,
    TDCDF_MDY,
    TDCDF_MYD,
    TDCDF_YMD,
    TDCDF_YDM,
}
    TDCDateFmt;

//------------------------------------------------------------------------
//
//  CTDCColInfo
//
//  This class represents type/formatting information for a column
//
//------------------------------------------------------------------------

class CTDCColInfo
{
public:
    VARTYPE vtType;
    TDCDateFmt  datefmt;        //  Format string for dates

    CTDCColInfo()
    {
        vtType = VT_EMPTY;
    }
};
// ;begin_internal
#endif  // TDC_SKEL
// ;end_internal

//------------------------------------------------------------------------
//
//  CTDCSortCriterion
//
//  This class represents a sorting criterion (sort column and direction)
//
//------------------------------------------------------------------------

class CTDCSortCriterion
{
public:
    LONG    m_iSortCol;
    boolean m_fSortAscending;
    CTDCSortCriterion *m_pNext;

    CTDCSortCriterion()
    {
        m_pNext = NULL;
    }
    ~CTDCSortCriterion()
    {
        if (m_pNext != NULL)
            delete m_pNext;
    }
};

//------------------------------------------------------------------------
//
//  CTDCFilterNode
//
//  This class represents a tree node in a filter query.
//
//------------------------------------------------------------------------

class CTDCFilterNode
{
public:
    enum NODE_OP
    {
        NT_AND,
        NT_OR,
        NT_EQ,
        NT_NE,
        NT_LT,
        NT_GT,
        NT_LE,
        NT_GE,
        NT_ATOM,
        NT_NULL,
    };
    NODE_OP        m_type;
    CTDCFilterNode *m_pLeft;    // NT_AND ... NT_GE
    CTDCFilterNode *m_pRight;   // NT_AND ... NT_GE
    LONG           m_iCol;      // NT_ATOM, +ve column #, 0 means fixed value
    VARIANT        m_value;     // NT_ATOM, m_iCol == 0: optional fixed value
    VARTYPE        m_vt;        // NT_EQ ... NT_ATOM - type of comparison/atom
    boolean        m_fWildcard; // True for string literals with '*' wildcard

    CTDCFilterNode()
    {
        m_type = NT_NULL;
        m_pLeft = NULL;
        m_pRight = NULL;
        m_iCol = 0;
        m_vt = VT_EMPTY;
        VariantInit(&m_value);
    }
    ~CTDCFilterNode()
    {
        if (m_pLeft != NULL)
            delete m_pLeft;
        if (m_pRight != NULL)
            delete m_pRight;
        VariantClear(&m_value);
    }
};

class CEventBroker;

//------------------------------------------------------------------------
//
//  CTDCArr
//
//------------------------------------------------------------------------

class CTDCArr : public OLEDBSimpleProvider,
                public CTDCFieldSink
{
public:
    STDMETHOD(QueryInterface)   (REFIID, LPVOID FAR*);
    STDMETHOD_(ULONG,AddRef)    (THIS);
    STDMETHOD_(ULONG,Release)   (THIS);

    CTDCArr();
    STDMETHOD(Init)(CEventBroker *pEventBroker, IMultiLanguage *pML);

    //  CTDCFieldSink methods
    //
    STDMETHOD(AddField)(LPWCH pwch, DWORD dwSize);
    STDMETHOD(EOLN)();
    STDMETHOD(EOF)();

    //  TDC control methods
    //
    STDMETHOD(StartDataLoad)(boolean fUseHeader,
                             BSTR bstrSortExpr, BSTR bstrFilterExpr, LCID lcid,
                             CComObject<CMyBindStatusCallback<CTDCCtl> > *pBSC,
                             boolean fAppend, boolean fCaseSensitive);
    STDMETHOD(SetSortFilterCriteria)(BSTR bstrSortExpr, BSTR bstrFilterExpr,
                                    boolean fCaseSensitive);

    //  OLEDBSimpleProvider methods
    //
    STDMETHOD(getRowCount)(LONG *pcRows);
    STDMETHOD(getColumnCount)(LONG *pcCols);
    STDMETHOD(getRWStatus)(LONG iRow, LONG iCol, OSPRW *prwStatus);
    STDMETHOD(getVariant)(LONG iRow, LONG iCol, OSPFORMAT format, VARIANT *pVar);
    STDMETHOD(setVariant)(LONG iRow, LONG iCol, OSPFORMAT format, VARIANT Var);
    STDMETHOD(getLocale)(BSTR *pbstrLocale);
    STDMETHOD(deleteRows)(LONG iRow, LONG cRows, LONG *pcRowsDeleted);
    STDMETHOD(insertRows)(LONG iRow, LONG cRows, LONG *pcRowsInserted);
    STDMETHOD(find) (LONG iRowStart, LONG iCol, VARIANT val,
            OSPFIND findFlags, OSPCOMP compType, LONG *piRowFound);
    STDMETHOD(addOLEDBSimpleProviderListener)(OLEDBSimpleProviderListener *pospIListener);
    STDMETHOD(removeOLEDBSimpleProviderListener)(OLEDBSimpleProviderListener *pospIListener);
    STDMETHOD(getEstimatedRows)(LONG *pcRows);
    STDMETHOD(isAsync)(BOOL *pbAsync);
    STDMETHOD(stopTransfer)();
// ;begin_internal
    STDMETHOD(DeleteColumns)(LONG iCol, LONG cCols, LONG *pcColsDeleted);
    STDMETHOD(InsertColumns)(LONG iCol, LONG cCols, LONG *pcColsInserted);
// ;end_internal

    //  This member is used during a sort operation
    //
    int SortComp(LONG iRow1, LONG iRow2);

    enum LOAD_STATE
    {
        LS_UNINITIALISED,
        LS_LOADING_HEADER_UNAVAILABLE,
        LS_LOADING_HEADER_AVAILABLE,
        LS_LOADED,
    };
    LOAD_STATE  GetLoadState()  { return m_state; }
    void SetIsAsync(BOOL fAsync) { m_fAsync = fAsync; }
    CEventBroker    *m_pEventBroker;
    IMultiLanguage  *m_pML;

private:

    ULONG       m_cRef;         // interface reference count
    LOAD_STATE  m_state;
    LCID        m_lcid;         // Default user LCID
    LCID        m_lcidRead;     // User LCID corresponding to LANGUAGE property

    ~CTDCArr();

// ;begin_internal
#ifndef TDC_SKEL
// ;end_internal
    boolean     m_fSortFilterDisrupted;
    STDMETHOD(ApplySortFilterCriteria)();

    //  These members are used during a sort operation
    //
    CTDCSortCriterion   *m_pSortList;
    BSTR                m_bstrSortExpr;
    HRESULT CreateSortList(BSTR bstrSortCols);

    //  These members are used during a filter operation
    //
    CTDCFilterNode  *m_pFilterTree;
    BSTR            m_bstrFilterExpr;
    boolean EvalDataRow(LONG iRow, CTDCFilterNode *pNode);
    CTDCFilterNode *FilterParseComplex(LPWCH *ppwch, HRESULT *phr);
    CTDCFilterNode *FilterParseSimple(LPWCH *ppwch, HRESULT *phr);
    CTDCFilterNode *FilterParseAtom(LPWCH *ppwch, HRESULT *phr);
    LONG    m_fLastFilter;
// ;begin_internal
#endif // TDC_SKEL
// ;end_internal

    //  These members are used during a load
    //
    boolean m_fUseHeader;
    boolean m_fSkipRow;
    LONG    m_iCurrRow;
    LONG    m_iCurrCol;

    LONG    m_iDataRows;
    LONG    m_iFilterRows;
    LONG    m_iCols;
    boolean m_fCaseSensitive;

    BOOL    m_fAsync;                   // TRUE iff Async

    //  These methods and members form the internal array implementation
    //
    inline boolean fValidDataRow(LONG iRow);
    inline boolean fValidFilterRow(LONG iRow);
    inline boolean fValidCol(LONG iCol);
    inline boolean fValidDataCell(LONG iRow, LONG iCol);
    inline boolean fValidFilterCell(LONG iRow, LONG iCol);
    inline CTDCCell *GetDataCell(LONG iRow, LONG iCol);
    inline CTDCCell *GetFilterCell(LONG iRow, LONG iCol);
    inline CTDCColInfo *GetColInfo(LONG iCol);
    LONG CalcDataRows();
    LONG CalcFilterRows();
    LONG CalcCols();

    TSTDArray<TSTDArray<CTDCCell> *>   m_arrparrCells;
    TSTDArray<TSTDArray<CTDCCell> *>   m_arrparrFilter;
    TSTDArray<CTDCColInfo>             m_arrColInfo;

    //  Misc internal methods
    //
    LONG    FindCol(BSTR bstrColName);
    HRESULT GetVariantBSTR(VARIANT *pv, BSTR *pbstr, boolean *pfAllocated);
    void    RenumberColumnHeadings();
    HRESULT CreateNumberedColumnHeadings();
    HRESULT ParseColumnHeadings();
    HRESULT VariantFromBSTR(VARIANT *pv, BSTR bstr, CTDCColInfo *pColInfo, LCID);
    int VariantComp(VARIANT *pVar1, VARIANT *pVar2, VARTYPE type,
                    boolean fCaseSensitive);
    int InsertionSortHelper(int iRow);
};

inline boolean CTDCArr::fValidDataRow(LONG iRow)
{
    return iRow >= 0 && iRow <= m_iDataRows;
}

inline boolean CTDCArr::fValidFilterRow(LONG iRow)
{
    return iRow >= 0 && iRow <= m_iFilterRows;
}

inline boolean CTDCArr::fValidCol(LONG iCol)
{
    return iCol >= 1 && iCol <= m_iCols;
}

inline boolean CTDCArr::fValidDataCell(LONG iRow, LONG iCol)
{
    return fValidDataRow(iRow) && fValidCol(iCol);
}

inline boolean CTDCArr::fValidFilterCell(LONG iRow, LONG iCol)
{
    return fValidFilterRow(iRow) && fValidCol(iCol);
}

// ;begin_internal
#ifndef TDC_SKEL
// ;end_internal
inline CTDCCell *CTDCArr::GetDataCell(LONG iRow, LONG iCol)
{
    return &((*m_arrparrCells[iRow])[iCol - 1]);
}

inline CTDCColInfo *CTDCArr::GetColInfo(LONG iCol)
{
    return &m_arrColInfo[iCol - 1];
}

inline CTDCCell *CTDCArr::GetFilterCell(LONG iRow, LONG iCol)
{
    return &((*m_arrparrFilter[iRow])[iCol - 1]);
}
// ;begin_internal
#endif  // TDC_SKEL
// ;end_internal


TDCCTL.CPP

//------------------------------------------------------------------------
//
//  Tabular Data Control
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       TDCCtl.cpp
//
//  Contents:   Implementation of the CTDCCtl ActiveX control.
//
//------------------------------------------------------------------------

#include "stdafx.h"
#include "STD.h"
#include "TDCIds.h"
#include "TDC.h"
#include <MLang.h>
#include "Notify.h"
#include "TDCParse.h"
#include "TDCArr.h"
#include "SimpData.h"
#include "TDCCtl.h"
#include "locale.h"
#include "langcode.h"

//------------------------------------------------------------------------
//
//  Function:   EmptyBSTR()
//
//  Synopsis:   Indicates whether the given BSTR object represents an
//              empty string.
//
//  Arguments:  bstr     String to test
//
//  Returns:    TRUE if 'bstr' represents an empty string
//              FALSE otherwise.
//
//------------------------------------------------------------------------

inline boolean EmptyBSTR(BSTR bstr)
{
    return bstr == NULL || bstr[0] == 0;
}

void
ClearInterfaceFn(IUnknown ** ppUnk)
{
    IUnknown * pUnk;

    pUnk = *ppUnk;
    *ppUnk = NULL;
    if (pUnk)
        pUnk->Release();
}

//------------------------------------------------------------------------
//
//  Method:     CTDCCtl()
//
//  Synopsis:   Class constructor
//
//  Arguments:  None
//
//------------------------------------------------------------------------

CTDCCtl::CTDCCtl()
{
    m_cbstrFieldDelim = DEFAULT_FIELD_DELIM;
    m_cbstrRowDelim = DEFAULT_ROW_DELIM;
    m_cbstrQuoteChar = DEFAULT_QUOTE_CHAR;
    m_fUseHeader = VARIANT_FALSE;
    m_fSortAscending = VARIANT_TRUE;
    m_fAppendData = VARIANT_FALSE;
    m_pSTD = NULL;
    m_pArr = NULL;
    m_pUnify = NULL;
    m_pEventBroker = new CEventBroker(this);
    m_pDataSourceListener = NULL;
// ;begin_internal
    m_pDATASRCListener = NULL;
// ;end_internal
    m_pBSC = NULL;
    m_enumFilterCriterion = (OSPCOMP) 0;
    m_fDataURLChanged = FALSE;
    m_lTimer = 0;
    m_fCaseSensitive = VARIANT_TRUE;
    m_hrDownloadStatus = S_OK;
    m_fInReset = FALSE;

    //  Create an MLANG object
    //
    m_nCodePage = 0;                    // use default from host
    {
        HRESULT hr;

        m_pML = NULL;
        hr = CoCreateInstance(CLSID_CMultiLanguage, NULL,
                              CLSCTX_INPROC_SERVER, IID_IMultiLanguage,
                              (void**) &m_pML);
        // Don't set the default Charset here.  Leave m_nCodepage set
        // to 0 to indicate default charset.  Later we'll try to query
        // our host's default charset, and failing that we'll use CP_ACP.
        _ASSERTE(SUCCEEDED(hr) && m_pML != NULL);
    }

    m_lcidRead = 0x0000;                // use default from host
}

//------------------------------------------------------------------------
//
//  Method:     ~CTDCCtl()
//
//  Synopsis:   Class destructor
//
//------------------------------------------------------------------------

CTDCCtl::~CTDCCtl()
{
    ULONG cRef = _ThreadModel::Decrement(&m_dwRef);

    ClearInterface(&m_pSTD);

    if (cRef ==0)
    {
        TimerOff();
        ReleaseTDCArr(FALSE);

        if (m_pEventBroker)
        {
            m_pEventBroker->Release();
            m_pEventBroker = NULL;
        }
        ClearInterface(&m_pDataSourceListener);
// ;begin_internal
        ClearInterface(&m_pDATASRCListener);
// ;end_internal
        ClearInterface(&m_pML);
    }
}

//------------------------------------------------------------------------
//
//  These set/get methods implement the control's properties,
//  copying values to and from class members.  They perform no
//  other processing apart from argument validation.
//
//------------------------------------------------------------------------

STDMETHODIMP CTDCCtl::get_ReadyState(LONG *plReadyState)
{
    HRESULT hr;

    if (m_pEventBroker == NULL)
    {
        // We must provide a ReadyState whether we want to or not, or our
        // host can never go COMPLETE.
        *plReadyState = READYSTATE_COMPLETE;
        hr = S_OK;
    }
    else
        hr = m_pEventBroker->GetReadyState(plReadyState);
    return hr;
}

STDMETHODIMP CTDCCtl::put_ReadyState(LONG lReadyState)
{
    // We don't allow setting of Ready State, but take advantage of a little
    // kludge here to update our container's impression of our readystate
    FireOnChanged(DISPID_READYSTATE);
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_FieldDelim(BSTR* pbstrFieldDelim)
{
    *pbstrFieldDelim = m_cbstrFieldDelim.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_FieldDelim(BSTR bstrFieldDelim)
{
    HRESULT hr = S_OK;

    if (bstrFieldDelim == NULL || bstrFieldDelim[0] == 0)
    {
        m_cbstrFieldDelim = DEFAULT_FIELD_DELIM;
        if (m_cbstrFieldDelim == NULL)
            hr = E_OUTOFMEMORY;
    }
    else
        m_cbstrFieldDelim = bstrFieldDelim;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_RowDelim(BSTR* pbstrRowDelim)
{
    *pbstrRowDelim = m_cbstrRowDelim.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_RowDelim(BSTR bstrRowDelim)
{
    HRESULT hr = S_OK;

    if (bstrRowDelim == NULL || bstrRowDelim[0] == 0)
    {
        m_cbstrRowDelim = DEFAULT_ROW_DELIM;
        if (m_cbstrRowDelim == NULL)
            hr = E_OUTOFMEMORY;
    }
    else
        m_cbstrRowDelim = bstrRowDelim;
    return hr;
}

STDMETHODIMP CTDCCtl::get_TextQualifier(BSTR* pbstrTextQualifier)
{
    *pbstrTextQualifier = m_cbstrQuoteChar.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_TextQualifier(BSTR bstrTextQualifier)
{
    m_cbstrQuoteChar = bstrTextQualifier;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_EscapeChar(BSTR* pbstrEscapeChar)
{
    *pbstrEscapeChar = m_cbstrEscapeChar.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_EscapeChar(BSTR bstrEscapeChar)
{
    m_cbstrEscapeChar = bstrEscapeChar;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_UseHeader(VARIANT_BOOL* pfUseHeader)
{
    *pfUseHeader = m_fUseHeader;
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_UseHeader(VARIANT_BOOL fUseHeader)
{
    m_fUseHeader = fUseHeader;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_SortColumn(BSTR* pbstrSortColumn)
{
    *pbstrSortColumn = m_cbstrSortColumn.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_SortColumn(BSTR bstrSortColumn)
{
    m_cbstrSortColumn = bstrSortColumn;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_SortAscending(VARIANT_BOOL* pfSortAscending)
{
    *pfSortAscending = m_fSortAscending;
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_SortAscending(VARIANT_BOOL fSortAscending)
{
    m_fSortAscending = fSortAscending;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_FilterValue(BSTR* pbstrFilterValue)
{
    *pbstrFilterValue = m_cbstrFilterValue.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_FilterValue(BSTR bstrFilterValue)
{
    m_cbstrFilterValue = bstrFilterValue;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_FilterCriterion(BSTR* pbstrFilterCriterion)
{
    HRESULT hr;
    WCHAR   *pwchCriterion;

    switch (m_enumFilterCriterion)
    {
    case OSPCOMP_EQ:    pwchCriterion = L"=";   break;
    case OSPCOMP_LT:    pwchCriterion = L"<";   break;
    case OSPCOMP_LE:    pwchCriterion = L"<=";  break;
    case OSPCOMP_GE:    pwchCriterion = L">=";  break;
    case OSPCOMP_GT:    pwchCriterion = L">";   break;
    case OSPCOMP_NE:    pwchCriterion = L"<>";  break;
    default:            pwchCriterion = L"??";  break;
    }
    *pbstrFilterCriterion = SysAllocString(pwchCriterion);
    hr = (*pbstrFilterCriterion == NULL) ? E_OUTOFMEMORY : S_OK;

    return hr;
}

STDMETHODIMP CTDCCtl::put_FilterCriterion(BSTR bstrFilterCriterion)
{
    m_enumFilterCriterion = (OSPCOMP) 0;
    if (bstrFilterCriterion != NULL)
    {
        switch (bstrFilterCriterion[0])
        {
        case L'<':
            if (bstrFilterCriterion[1] == 0)
                m_enumFilterCriterion = OSPCOMP_LT;
            else if (bstrFilterCriterion[2] == 0)
            {
                if (bstrFilterCriterion[1] == L'>')
                    m_enumFilterCriterion = OSPCOMP_NE;
                else if (bstrFilterCriterion[1] == L'=')
                    m_enumFilterCriterion = OSPCOMP_LE;
            }
            break;
        case L'>':
            if (bstrFilterCriterion[1] == 0)
                m_enumFilterCriterion = OSPCOMP_GT;
            else if (bstrFilterCriterion[1] == L'=' && bstrFilterCriterion[2] == 0)
                m_enumFilterCriterion = OSPCOMP_GE;
            break;
        case L'=':
            if (bstrFilterCriterion[1] == 0)
                m_enumFilterCriterion = OSPCOMP_EQ;
            break;
        }
    }

    //  Return SUCCESS, even on an invalid value; otherwise the
    //  frameworks using the control will panic and abandon all hope.
    //
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_FilterColumn(BSTR* pbstrFilterColumn)
{
    *pbstrFilterColumn = m_cbstrFilterColumn.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_FilterColumn(BSTR bstrFilterColumn)
{
    m_cbstrFilterColumn = bstrFilterColumn;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_CharSet(BSTR* pbstrCharSet)
{
    HRESULT hr = E_FAIL;

    if (m_pML != NULL)
    {
        MIMECPINFO  info;

        hr = m_pML->GetCodePageInfo(m_nCodePage, &info);
        if (SUCCEEDED(hr))
        {
            *pbstrCharSet = SysAllocString(info.wszWebCharset);
            if (*pbstrCharSet == NULL)
                hr = E_OUTOFMEMORY;
        }
    }
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_CharSet(BSTR bstrCharSet)
{
    HRESULT hr = E_FAIL;

    if (m_pML != NULL)
    {
        MIMECSETINFO    info;

        hr = m_pML->GetCharsetInfo(bstrCharSet, &info);
        if (SUCCEEDED(hr))
        {
            m_nCodePage = info.uiCodePage;
            if (m_nCodePage == 0)
                m_nCodePage = info.uiInternetEncoding;
        }
    }
    return hr;
}

STDMETHODIMP CTDCCtl::get_Language(BSTR* pbstrLanguage)
{
    if (m_pArr)
    {
        return m_pArr->getLocale(pbstrLanguage);
    }

    *pbstrLanguage = m_cbstrLanguage.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_Language_(LPWCH pwchLanguage)
{
    HRESULT hr  = S_OK;
    LCID    lcid;

    hr = m_pML->GetLcidFromRfc1766(&lcid, pwchLanguage);
    if (SUCCEEDED(hr))
    {
        m_cbstrLanguage = pwchLanguage;
        m_lcidRead = lcid;
    }
    return hr;
}

STDMETHODIMP CTDCCtl::put_Language(BSTR bstrLanguage)
{
    return put_Language_(bstrLanguage);
}

STDMETHODIMP CTDCCtl::get_DataURL(BSTR* pbstrDataURL)
{
    *pbstrDataURL = m_cbstrDataURL.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_DataURL(BSTR bstrDataURL)
{
    HRESULT hr = S_OK;

    m_cbstrDataURL = bstrDataURL;
    m_fDataURLChanged = TRUE;
    return hr;
}

// ;begin_internal
#ifdef NEVER
STDMETHODIMP CTDCCtl::get_RefreshInterval(LONG* plTimer)
{
    *plTimer = m_lTimer;
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_RefreshInterval(LONG lTimer)
{
    m_lTimer = lTimer;
    if (m_lTimer > 0)
        TimerOn(m_lTimer * 1000);
    else
        TimerOff();
    return S_OK;
}
#endif
// ;end_internal

STDMETHODIMP CTDCCtl::get_Filter(BSTR* pbstrFilterExpr)
{
    *pbstrFilterExpr = m_cbstrFilterExpr.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_Filter(BSTR bstrFilterExpr)
{
    m_cbstrFilterExpr = bstrFilterExpr;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_Sort(BSTR* pbstrSortExpr)
{
    *pbstrSortExpr = m_cbstrSortExpr.Copy();
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_Sort(BSTR bstrSortExpr)
{
    m_cbstrSortExpr = bstrSortExpr;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_AppendData(VARIANT_BOOL* pfAppendData)
{
    *pfAppendData = m_fAppendData;
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_AppendData(VARIANT_BOOL fAppendData)
{
    m_fAppendData = fAppendData;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_CaseSensitive(VARIANT_BOOL* pfCaseSensitive)
{
    *pfCaseSensitive = m_fCaseSensitive;
    return S_OK;
}

STDMETHODIMP CTDCCtl::put_CaseSensitive(VARIANT_BOOL fCaseSensitive)
{
    m_fCaseSensitive = fCaseSensitive;
    return S_OK;
}

STDMETHODIMP CTDCCtl::get_OSP(OLEDBSimpleProviderX ** ppISTD)
{
    // Return an OSP if we have one, but don't create one on demand!
    // (Otherwise property bag load stuff will cause us to create an
    // OSP prematurely).
    *ppISTD = NULL;
    if (m_pSTD)
    {
        *ppISTD = (OLEDBSimpleProviderX *)m_pSTD;
    }
    return S_OK;
}

//------------------------------------------------------------------------
//
//  Method:    UpdateReadyState
//
//  Synopsis:  Vectors to the event brokers ReadyState, if there is one.
// ;begin_internal
//             Note, we have to be able to set our readystate and fire change
//             events on it, whether or not creation of the broker succeeded,
//             or we prevent our host container from reaching
//             READYSTATE_COMPLETE, which is not acceptable.  We therefore
//             have to duplicate some of the broker's work here.  This makes
//             me wonder whether the broker architecture was a good idea.
// ;end_internal
//
//  Arguments: None.
//
//  Returns:   S_OK upon success.
//             Error codes as per Reset() upon error.
//
//------------------------------------------------------------------------
void
CTDCCtl::UpdateReadyState(LONG lReadyState)
{
    if (m_pEventBroker)
        m_pEventBroker->UpdateReadyState(lReadyState);
    else
    {
        // We have no broker, but our host is still waiting for us to
        // go READYSTATE_COMPLETE.  We fire the OnChange here noting that
        // get_ReadyState with no broker will return COMPLETE.
        FireOnChanged(DISPID_READYSTATE);
        FireOnReadyStateChanged();
    }
}

//------------------------------------------------------------------------
//
//  Method:    _OnTimer()
//
//  Synopsis:  Handles an internal timer event by refreshing the control.
//
//  Arguments: None.
//
//  Returns:   S_OK upon success.
//             Error codes as per Reset() upon error.
//
//------------------------------------------------------------------------

STDMETHODIMP CTDCCtl::_OnTimer()
{
    HRESULT hr = S_OK;

    if (m_pArr != NULL && m_pArr->GetLoadState() == CTDCArr::LS_LOADED)
    {
        m_fDataURLChanged = TRUE;
        hr = Reset();
    }

    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    msDataSourceObject()
//
//  Synopsis:  Yields an ISimpleTabularData interface for this control.
//             If this is the first call, a load operation is initiated
//             reading data from the control's specified DataURL property.
//             An STD object is created to point to the control's embedded
//             TDCArr object.
//
//  Arguments: qualifier     Ignored - must be an empty BSTR.
//             ppUnk         Pointer to returned interface  [OUT]
//
//  Returns:   S_OK upon success.
//             E_INVALIDARG if 'qualifier' isn't an empty BSTR.
//             E_OUTOFMEMORY if non enough memory could be allocated to
//               complete the construction of the interface.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCCtl::msDataSourceObject(BSTR qualifier, IUnknown **ppUnk)
{
    HRESULT hr  = S_OK;

    *ppUnk = NULL;                      // NULL in case of failure

    if (!EmptyBSTR(qualifier))
    {
        hr = E_INVALIDARG;
        goto error;
    }

    // Was there a previous attempt to load this page that failed?
    // (Probably due to security or file not found or something).
    if (m_hrDownloadStatus)
    {
        hr = m_hrDownloadStatus;
        goto error;
    }

    if (m_pArr == NULL)
    {
        // We don't have a valid TDC to give back, probably have to try
        // downloading one.
        UpdateReadyState(READYSTATE_LOADED);
        hr = CreateTDCArr(FALSE);
        if (hr)
            goto error;
    }

    _ASSERTE(m_pArr != NULL);

    if (m_pSTD == NULL)
    {
        OutputDebugStringX(_T("Creating an STD COM object\n"));

        // fetch ISimpleTabularData interface pointer
        m_pArr->QueryInterface(IID_OLEDBSimpleProvider, (void**)&m_pSTD);
        _ASSERTE(m_pSTD != NULL);
    }

    // Return the STD if we have one, otherwise it stays NULL
    if (m_pSTD && m_pArr->GetLoadState() >= CTDCArr::LS_LOADING_HEADER_AVAILABLE)
    {
        *ppUnk = (OLEDBSimpleProviderX *) m_pSTD;
        m_pSTD->AddRef();           // We must AddRef the STD we return!
    }

cleanup:
    return hr;

error:
    UpdateReadyState(READYSTATE_COMPLETE);
    goto cleanup;
}

// Override IPersistPropertyBagImpl::Load
STDMETHODIMP
CTDCCtl::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
{
    HRESULT hr;
    IUnknown *pSTD;

    // Do normal load
    // IPersistPropertyBagImpl<CTDCCtl>
    hr = IPersistPropertyBagImpl<CTDCCtl>::Load(pPropBag, pErrorLog);

    // and then start download, if we can
    (void)msDataSourceObject(NULL, &pSTD);

    // If we actually got an STD, we should release it.  This won't really
    // make it go away, since we still have the ref from the QI.  This is
    // a bit of a kludge that we should clean up later.
    ClearInterface(&pSTD);

    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    CreateTDCArr()
//
//  Synopsis:  Creates the control's embedded TDCArr object.
//             Initiates a data download from the DataURL property.
//
//  Arguments: fAppend         Flag indicating whether data should be
//                             appended to an existing TDC object.
//
//  Returns:   S_OK upon success.
//             E_OUTOFMEMORY if non enough memory could be allocated to
//               complete the construction of the TDCArr object.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCCtl::CreateTDCArr(boolean fAppend)
{
    HRESULT hr  = S_OK;

    if (m_pEventBroker == NULL)
    {
        hr = E_FAIL;
        goto Error;
    }

    // Iff we're appending is m_pArr allowed to be non-null here.
    _ASSERT ((m_pArr != NULL) == fAppend);

    if (m_pArr == NULL)
    {
        m_pArr = new CTDCArr();
        if (m_pArr == NULL)
        {
            hr = E_OUTOFMEMORY;
            goto Error;
        }

        hr = m_pArr->Init(m_pEventBroker, m_pML);
        if (FAILED(hr))
            goto Error;
    }

    hr = InitiateDataLoad(fAppend);
    if (hr)
        goto Error;

    // We decide something is not async if it finished loading during
    // the InitiateDataLoad call.
    m_pArr->SetIsAsync(!(m_pArr->GetLoadState()==CTDCArr::LS_LOADED));

Cleanup:
    return hr;

Error:
    if (!fAppend)
    {
        ClearInterface(&m_pArr);
    }
    goto Cleanup;
}

//------------------------------------------------------------------------
//
//  Method:    ReleaseTDCArr()
//
//  Synopsis:  Releases the control's embedded TDCArr object.
//             Releases the control's CTDCUnify and CTDCTokenise objects.
//             Releases the old event broker and re-creates it if replacing.
//
//  Arguments: fReplacingTDCArr   Flag indicating whether a new TDCArr object
//                                will be created.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//             E_OUTOFMEMORY if non enough memory could be allocated to
//               complete the construction of the new CEventBroker object.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCCtl::ReleaseTDCArr(boolean fReplacingTDCArr)
{
    HRESULT hr = S_OK;

    TerminateDataLoad();

    //  Release the reference to the current TDCArr object
    //
    if (m_pArr != NULL)
    {
        m_pArr->Release();
        m_pArr = NULL;

        // Since we've shut down the CTDCArr object, we should release
        // it's OLEDBSimplerProviderListener sink.
        if (m_pEventBroker)
        {
            m_pEventBroker->SetSTDEvents(NULL);
        }

        if (fReplacingTDCArr)
        {
            // Release our previous Event Broker.
            if (m_pEventBroker)
            {
                m_pEventBroker->Release();
                m_pEventBroker = NULL;
            }

            //  Create a new event broker.
            m_pEventBroker = new CEventBroker(this);
            if (m_pEventBroker == NULL)
            {
                hr = E_OUTOFMEMORY;
                goto Cleanup;
            }

            // Set the DataSourceListener for the new event broker.
            m_pEventBroker->SetDataSourceListener(m_pDataSourceListener);

// ;begin_internal
            m_pEventBroker->SetDATASRCListener(m_pDATASRCListener);
// ;end_internal
        }
    }

Cleanup:
    return hr;
}

const IID IID_IDATASRCListener = {0x3050f380,0x98b5,0x11cf,{0xbb,0x82,0x00,0xaa,0x00,0xbd,0xce,0x0b}};
const IID IID_DataSourceListener = {0x7c0ffab2,0xcd84,0x11d0,{0x94,0x9a,0x00,0xa0,0xc9,0x11,0x10,0xed}};

//------------------------------------------------------------------------
//
//  Method:    addDataSourceListener()
//
//  Synopsis:  Sets the COM object which should receive notification
//             events.
//
//  Arguments: pEvent        Pointer to COM object to receive notification
//                           events, or NULL if no notifications to be sent.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP
CTDCCtl::addDataSourceListener(IUnknown *pListener)
{

    if (m_pEventBroker != NULL)
    {
        HRESULT hr = S_OK;
        IUnknown * pDatasrcListener;

        // Make sure this is the interface we expect
        hr = pListener->QueryInterface(IID_DataSourceListener,
                                       (void **)&pDatasrcListener);
        if (SUCCEEDED(hr))
        {
            m_pEventBroker->
                    SetDataSourceListener((DataSourceListener *)pDatasrcListener);

            // Clear any previous
            ClearInterface (&m_pDataSourceListener);
            // and remember the new.
            m_pDataSourceListener = (DataSourceListener *)pDatasrcListener;
        }
// ;begin_internal
        else
        {
            // The definition of this interface was changed from IDATASRCListener to
            // DataSourceListener.  To make sure we don't cause crashes, we QI to
            // determine which one we were handed.
            hr = pListener->QueryInterface(IID_IDATASRCListener,
                                           (void **)&pDatasrcListener);
            if (SUCCEEDED(hr))
            {
                m_pEventBroker->
                        SetDATASRCListener((DATASRCListener *) pDatasrcListener);

                // Clear any previous
                ClearInterface (&m_pDATASRCListener);
                // and remember the new.
                m_pDATASRCListener = (DATASRCListener *)pDatasrcListener;
            }
        }
// ;end_internal
        return hr;
    }
    else
        return E_FAIL;
}

//------------------------------------------------------------------------
//
//  Method:    Reset()
//
//  Synopsis:  Reset the control's filter/sort criteria.
//
//  Arguments: None.
//
//  Returns:   S_OK upon success.
//             Error code upon failure.
//
//------------------------------------------------------------------------

STDMETHODIMP CTDCCtl::Reset()
{
    HRESULT hr  = S_OK;

    // The next query to msDataSourceObject should get a new STD
    ClearInterface(&m_pSTD);

    // Infinite recursive calls to Reset can occur if script code calls reset
    // from within the datasetchanged event.  This isn't a good idea.
    if (m_fInReset)
    {
        hr = E_FAIL;
        goto Cleanup;
    }

    m_fInReset = TRUE;

    // Clear any previous error
    m_hrDownloadStatus = S_OK;

    if (m_fDataURLChanged)
    {
        if (!m_fAppendData)
        {
            // Release previous TDC array with "replacing" flag.
            hr = ReleaseTDCArr(TRUE);
            if (!SUCCEEDED(hr))         // possible memory failure
                goto Cleanup;
        }

        // Read the new data into a TDC arry, appending if specified.
        hr = CreateTDCArr((BOOL)m_fAppendData);
    }
    else if (m_pArr != NULL)
    {
        // Re-apply the sort and filter criteria
        hr = m_pArr->SetSortFilterCriteria(bstrConstructSortExpr(),
                                           bstrConstructFilterExpr(),
                                           m_fCaseSensitive ? 1 : 0);
    }

    m_fInReset = FALSE;

Cleanup:
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    bstrConstructSortExpr()
//
//  Synopsis:  Constructs a sort expression from the Sort property or
//             (for backward compatibility) from the SortColumn/SortAscending
//             properties.
//
//             This method only exists to isolate backward-compatibility
//             with the old-fashioned sort properties.
//
//  Arguments: None.
//
//  Returns:   The constructed sort expression.
//
//  NB!  It is the caller's responsibility to free the string returned.
//
//------------------------------------------------------------------------

BSTR
CTDCCtl::bstrConstructSortExpr()
{
    BSTR    bstr = NULL;

    if (!EmptyBSTR(m_cbstrSortExpr))
        bstr = SysAllocString(m_cbstrSortExpr);
    else if (!EmptyBSTR(m_cbstrSortColumn))
    {
        //  Use the old-fashioned sort properties
        //  Construct a sort expression of the form:
        //     <SortColumn>  or
        //    -<SortColumn>
        //
        if (m_fSortAscending)
            bstr = SysAllocString(m_cbstrSortColumn);
        else
        {
            bstr = SysAllocStringLen(NULL, SysStringLen(m_cbstrSortColumn) + 1);
            if (bstr != NULL)
            {
                bstr[0] = L'-';
                wch_cpy(&bstr[1], m_cbstrSortColumn);
            }
        }
    }

    return bstr;
}

//------------------------------------------------------------------------
//
//  Method:    bstrConstructFilterExpr()
//
//  Synopsis:  Constructs a filter expression from the Filter property or
//             (for backward compatibility) from the FilterColumn/FilterValue/
//             FilterCriterion properties.
//
//             This method only exists to isolate backward-compatibility
//             with the old-fashioned filter properties.
//
//  Arguments: None.
//
//  Returns:   The constructed filter expression
//
//  NB!  It is the caller's responsibility to free the string returned.
//
//------------------------------------------------------------------------

BSTR
CTDCCtl::bstrConstructFilterExpr()
{
    BSTR    bstr = NULL;

    if (!EmptyBSTR(m_cbstrFilterExpr))
        bstr = SysAllocString(m_cbstrFilterExpr);
    else if (!EmptyBSTR(m_cbstrFilterColumn))
    {
        //  Use the old-fashioned filter properties
        //  Construct a sort expression of the form:
        //     <FilterColumn> <FilterCriterion> "<FilterValue>"
        //
        BSTR bstrFilterOp;
        HRESULT hr;

        hr = get_FilterCriterion(&bstrFilterOp);
        if (!SUCCEEDED(hr))
            goto Cleanup;
        bstr = SysAllocStringLen(NULL,
                    SysStringLen(m_cbstrFilterColumn) +
                    SysStringLen(bstrFilterOp) +
                    1 +
                    SysStringLen(m_cbstrFilterValue) +
                    1);
        if (bstr != NULL)
        {
            DWORD pos = 0;

            wch_cpy(&bstr[pos], m_cbstrFilterColumn);
            pos = wch_len(bstr);
            wch_cpy(&bstr[pos], bstrFilterOp);
            pos = wch_len(bstr);
            bstr[pos++] = L'"';
            wch_cpy(&bstr[pos], m_cbstrFilterValue);
            pos = wch_len(bstr);
            bstr[pos++] = L'"';
            bstr[pos] = 0;
        }
        SysFreeString(bstrFilterOp);
    }
Cleanup:
    return bstr;
}

//------------------------------------------------------------------------
//
//  Method:    TerminateDataLoad()
//
//  Synopsis:  Stop the current data load operation.
//
//  Returns:   S_OK upon success.
//
//------------------------------------------------------------------------

STDMETHODIMP CTDCCtl::TerminateDataLoad()
{
    HRESULT hr  = S_OK;

    // Make sure if we call Reset() right away now, we don't re-download
    // the data.
    m_fDataURLChanged = FALSE;

    m_pBSC = NULL;      //  Block any outstanding OnData calls

    if (m_pEventBroker)
        m_pEventBroker->m_pBSC = NULL;  // kill all

    if (m_pUnify != NULL)
        delete m_pUnify;

    m_pUnify = NULL;

    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    InitiateDataLoad()
//
//  Synopsis:  Start loading data from the control's DataURL property.
//
//  Arguments: fAppend        Flag to indicate whether data should be
//                            appended to an existing TDCArr object.
//
//  Returns:   S_OK upon success.
//             E_OUTOFMEMORY if not enough memory could be allocated to
//               complete the download.
//
//------------------------------------------------------------------------

STDMETHODIMP CTDCCtl::InitiateDataLoad(boolean fAppend)
{
    HRESULT hr  = S_OK;

    WCHAR   wchFieldDelim = (!m_cbstrFieldDelim) ? 0 : m_cbstrFieldDelim[0];
    WCHAR   wchRowDelim   = (!m_cbstrRowDelim)   ? 0 : m_cbstrRowDelim[0];
    // Default quote char to double-quote, not NULL
    WCHAR   wchQuoteChar  = (!m_cbstrQuoteChar)  ? 0 : m_cbstrQuoteChar[0];
    WCHAR   wchEscapeChar = (!m_cbstrEscapeChar) ? 0 : m_cbstrEscapeChar[0];

    //
    // Figure out what code page to use.
    //
    if (0==m_nCodePage)
    {
        // 0 means user didn't set one, so ask our container.
        VARIANT varCodepage;
        VariantInit(&varCodepage);
        GetAmbientProperty(DISPID_AMBIENT_CODEPAGE, varCodepage);

        // If container didn't provide one, use the default code page
        // of the client system.
        m_nCodePage = (varCodepage.vt == VT_UI4)
                        ? (ULONG)varCodepage.lVal
                        : 1252;         // ultimate default is Latin-1
    }

    //
    // Default LCID the same way as Codepage
    //
    if (0==m_lcidRead)
    {
        hr = GetAmbientLocaleID(m_lcidRead);
        if (FAILED(hr))
        {
            // Ultimate default is US locale -- sort of Web global
            // language default.
            put_Language_(L"en-us");
        }
    }

    if (EmptyBSTR(m_cbstrDataURL))
    {
        hr = S_FALSE;                   // quiet failure
        goto Error;
    }

    OutputDebugStringX(_T("Initiating Data Download\n"));

    //  No data load should currently be in progress -
    //  This data load has been initiated on the construction of a new
    //  TDCArr object, or appending to an existing loaded TDCArr object.
    //  Any currently running data load would have been
    //  terminated by the call to ReleaseTDCArr().
    //

    _ASSERT(m_pUnify == NULL);
    _ASSERT(m_pBSC == NULL);

    m_hrDownloadStatus = S_OK;

    //  Create a pipeline of objects to process the URL data
    //
    //    CMyBindStatusCallback -> CTDCUnify -> CTDCTokenise -> CTDCArr
    //

    CComObject<CMyBindStatusCallback<CTDCCtl> >::CreateInstance(&m_pBSC);

    if (m_pBSC == NULL)
    {
        hr = E_FAIL;
        goto Error;
    }
    hr = m_pArr->StartDataLoad(m_fUseHeader ? TRUE : FALSE,
                               bstrConstructSortExpr(),
                               bstrConstructFilterExpr(),
                               m_lcidRead,
                               m_pBSC,
                               fAppend,
                               m_fCaseSensitive ? 1 : 0);
    if (!SUCCEEDED(hr))
        goto Error;

    m_pUnify = new CTDCUnify();
    if (m_pUnify == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    m_pUnify->Create(m_nCodePage, m_pML);

    // Init tokenizer
    m_pUnify->InitTokenizer(m_pArr, wchFieldDelim, wchRowDelim,
                            wchQuoteChar, wchEscapeChar);

    m_fSecurityChecked = FALSE;

    // Start (and maybe perform) actual download.
    // If we're within a Reset() call, always force a "reload" of the data
    // from the server -- i.e. turn on BINDF_GETNEWESTVERSION to make sure
    // sure the cache data isn't stale.
    hr = m_pBSC->StartAsyncDownload(this, OnData, m_cbstrDataURL, m_spClientSite, TRUE,
                                    m_fInReset == TRUE);
    if (FAILED(hr))
        goto Error;

    // m_hrDownloadStatus remembers the first (if any) error that occured during
    // the OnData callbacks.  Unlike an error returning from StartAsyncDownload,
    // this doesn't necessarily cause us to throw away the TDC array.
    hr = m_hrDownloadStatus;
    if (!SUCCEEDED(hr))
        m_pBSC = NULL;

Cleanup:
    return hr;

Error:
    TerminateDataLoad();
    if (m_pEventBroker)
    {
        // Fire data set changed to indicate query failed,
        m_pEventBroker->STDDataSetChanged();
        // and go complete.
        UpdateReadyState(READYSTATE_COMPLETE);
    }
    goto Cleanup;
}

// ;begin_internal

#define ZERO '0'
#define NINE '9'
#define DOT '.'

// Scans a string and returns TRUE if it looks like a numeric
// part of an IP address
static BOOL ScanNumber (LPOLESTR& psz)
{
    // first char
    if (*psz < ZERO || *psz > NINE)
        return FALSE;
    psz++;

    // second char
    if (*psz < ZERO || *psz > NINE)
        return FALSE;
    psz++;

    // third char
    if (*psz < ZERO || *psz > NINE)
        return FALSE;

    return TRUE;
}

// ;end_internal

//------------------------------------------------------------------------
//
//  Method:    SecurityCheckDataURL(pszURL)
//
//  Synopsis:  Check that the data URL is within the same security zone
//             as the document that loaded the control.
//
//  Arguments: URL to check
//
//  Returns:   S_OK upon success.
//             E_INVALID if the security check failed or we failed to get
//               an interface that we needed
//
//------------------------------------------------------------------------

// ;begin_internal
// Wendy Richards(v-wendri) 6/6/97
// Copied this here because I couldn't link without it. The version
// of URLMON.LIB I have does not have this symbol exported
// ;end_internal

const IID IID_IInternetHostSecurityManager = {0x3af280b6,0xcb3f,0x11d0,{0x89,0x1e,0x00,0xc0,0x4f,0xb6,0xbf,0xc4}};

#define MAX_SEC_ID 256

STDMETHODIMP CTDCCtl::SecurityCheckDataURL(LPOLESTR pszURL)
{
    CComQIPtr<IServiceProvider, &IID_IServiceProvider> pSP(m_spClientSite);
    CComPtr<IInternetSecurityManager> pSM;
    CComPtr<IInternetHostSecurityManager> pHSM;
    CComPtr<IMoniker> pMoniker;

    BYTE     bSecIDHost[MAX_SEC_ID], bSecIDURL[MAX_SEC_ID];
    DWORD    cbSecIDHost = MAX_SEC_ID, cbSecIDURL = MAX_SEC_ID;
    HRESULT  hr = E_FAIL;

    USES_CONVERSION;

    // If we're running under the timer, it's quite possible our ClientSite will
    // disappear out from under us.  We'll obviously fail the security check,
    // but things are shutting down anyway..
    if (pSP==NULL)
        goto Cleanup;

    hr = CoInternetCreateSecurityManager(pSP, &pSM, 0L);
    if (!SUCCEEDED(hr))
        goto Cleanup;

    hr = pSP->QueryService(IID_IInternetHostSecurityManager,
                           IID_IInternetHostSecurityManager,
                           (LPVOID *)&pHSM);
    if (!SUCCEEDED(hr))
        goto Cleanup;

    hr = pHSM->GetSecurityId(bSecIDHost, &cbSecIDHost, 0L);
    if (!SUCCEEDED(hr))
        goto Cleanup;

    hr = pSM->GetSecurityId(OLE2W(pszURL), bSecIDURL, &cbSecIDURL, 0L);
    if (!SUCCEEDED(hr))
        goto Cleanup;

    if (cbSecIDHost != cbSecIDURL)
    {
        hr = E_FAIL;
        goto Cleanup;
    }

    if (memcmp(bSecIDHost, bSecIDURL, cbSecIDHost) != 0)
    {
        hr = E_FAIL;
        goto Cleanup;
    }

Cleanup:
#ifdef ATLTRACE
    LPOLESTR pszHostName = NULL;
    TCHAR *pszFailPass = hr ? _T("Failed") : _T("Passed");
    GetHostURL(m_spClientSite, &pszHostName);
    ATLTRACE(_T("CTDCCtl: %s security check on %S referencing %S\n"), pszFailPass,
             pszHostName, pszURL);
    bSecIDHost[cbSecIDHost] = 0;
    bSecIDURL[cbSecIDURL] = 0;
    ATLTRACE(_T("CTDCCtl: Security ID Host %d bytes: %s\n"), cbSecIDHost, bSecIDHost);
    ATLTRACE(_T("CTDCCtl: Security ID URL %d bytes: %s\n"), cbSecIDURL, bSecIDURL);
    CoTaskMemFree(pszHostName);
#endif
    return hr;
}

//------------------------------------------------------------------------
//
//  Method:    OnData()
//
//  Synopsis:  Accepts a chunk of data loaded from a URL and parses it.
//
//  Arguments: pBSC       The invoking data transfer object.
//             pBytes     Character buffer containing data.
//             dwSize     Count of the number of bytes in 'pBytes'.
//
//  Returns:   Nothing.
//
//------------------------------------------------------------------------

void CTDCCtl::OnData(CMyBindStatusCallback<CTDCCtl> *pBSC, BYTE *pBytes, DWORD dwSize)
{
    HRESULT hr;
    CTDCUnify::ALLOWDOMAINLIST nAllowDomainList;

    if (pBSC != m_pBSC)
    {
        OutputDebugStringX(_T("OnData called from invalid callback object\n"));
        hr = S_OK;
        goto Cleanup;
    }

    if (pBytes != NULL && dwSize != 0)
    {
        OutputDebugStringX(_T("OnData called with data buffer\n"));
        //  Process this chunk of data
        //
        hr = m_pUnify->ConvertByteBuffer(pBytes, dwSize);

        if (!m_fSecurityChecked)
        {
            // Note that we MUST check for the allow domain list at the
            // front of every file, even if it's on the same host.  This
            // is to make sure if we always strip off the @!allow_domain line.
            nAllowDomainList = m_pUnify->CheckForAllowDomainList();

            switch (nAllowDomainList)
            {
                // Don't have enough chars to tell yet.
                case CTDCUnify::ALLOW_DOMAINLIST_DONTKNOW:
                    // Return without errors or arborting.
                    // Presumably the next data packet will bring more info.
                    return;

                case CTDCUnify::ALLOW_DOMAINLIST_NO:
                    hr = E_FAIL;
                    // It's not on the exception list, still try to see if
                    // it's in the same domain.
                    break;

                case CTDCUnify::ALLOW_DOMAINLIST_YES:
                    // The file is decorated.  Now check the domain list
                    // against our host domain name.
                    hr = SecurityMatchAllowDomainList();
#ifdef ATLTRACE
                    if (!hr) ATLTRACE(_T("CTDCCtl: @!allow_domain list matched."));
                    else ATLTRACE(_T("CTDCCtl: @!allow_domain list did not match"));
#endif
                    break;
            }

            // Unless we passed the previous security check, we still have to
            // do the next one.
            if (FAILED(hr))
            {
                if (FAILED(hr = SecurityCheckDataURL(m_pBSC->m_pszURL)))
                    goto Error;
            }
            else
            {
                hr = SecurityMatchProtocols(m_pBSC->m_pszURL);
                if (FAILED(hr))
                    goto Error;
            }

            // Set m_fSecurityChecked only if it passes security.  This is in case for some
            // reason we get more callbacks before the StopTransfer takes affect.
            m_fSecurityChecked = TRUE;
        }

        // Normal case, we can process data!
        hr = m_pUnify->AddWcharBuffer(FALSE);

    }
    else if (pBytes == NULL || dwSize == 0)
    {
        OutputDebugStringX(_T("OnData called with empty (terminating) buffer\n"));

        // We might not have gotten enough to check security yet.
        if (!m_fSecurityChecked)
        {
            if (FAILED(hr = SecurityCheckDataURL(m_pBSC->m_pszURL)))
                goto Error;
        }

        //  No more data - trigger an EOF
        //
        hr = m_pUnify->AddWcharBuffer(TRUE); // last chance to parse any stragglers

        if (m_pArr!=NULL)
            hr = m_pArr->EOF();

        TerminateDataLoad();
    }

Cleanup:
    //  Void fn - can't return an error code ...
    //
    if (SUCCEEDED(m_hrDownloadStatus))
        m_hrDownloadStatus = hr;
    return;

Error:
    // Security failure.
    // Abort the current download
    if (m_pBSC && m_pBSC->m_spBinding)
    {
        (void) m_pBSC->m_spBinding->Abort();
    }

    m_hrDownloadStatus = hr;

    // Notify data set changed for the abort
    if (m_pEventBroker != NULL)
        hr = m_pEventBroker->STDDataSetChanged();
    goto Cleanup;
}

//
// Utility routine to get our
//
HRESULT
GetHostURL(IOleClientSite *pSite, LPOLESTR *ppszHostName)
{
    HRESULT hr;
    CComPtr<IMoniker> spMoniker;
    CComPtr<IBindCtx> spBindCtx;

    if (!pSite)
    {
        hr = E_FAIL;
        goto Cleanup;
    }

    hr = pSite->GetMoniker(OLEGETMONIKER_ONLYIFTHERE, OLEWHICHMK_CONTAINER,
                           &spMoniker);
    if (FAILED(hr))
        goto Cleanup;

    hr = CreateBindCtx(0, &spBindCtx);
    if (FAILED(hr))
        goto Cleanup;

    hr = spMoniker->GetDisplayName(spBindCtx, NULL, ppszHostName);
    if (FAILED(hr))
        goto Cleanup;

Cleanup:
    return hr;
}

HRESULT
CTDCCtl::SecurityMatchProtocols(LPOLESTR pszURL)
{
    HRESULT hr = E_FAIL;

    LPOLESTR pszHostURL = NULL;
    LPWCH pszPostHostProtocol;
    LPWCH pszPostProtocol;

    if (FAILED(GetHostURL(m_spClientSite, &pszHostURL)))
        goto Cleanup;

    pszPostHostProtocol = wch_chr(pszHostURL, _T(':'));
    pszPostProtocol     = wch_chr(pszURL, _T(':'));
    if (!pszPostHostProtocol || !pszPostProtocol)
        goto Cleanup;
    else
    {
        int ccChars1 = pszPostHostProtocol - pszHostURL;
        int ccChars2 = pszPostProtocol - pszURL;
        if (ccChars1 != ccChars2)
            goto Cleanup;
        else if (wch_ncmp(pszHostURL, pszURL, ccChars1) != 0)
            goto Cleanup;
    }
    hr = S_OK;

Cleanup:
    if (pszHostURL)
        CoTaskMemFree(pszHostURL);

    return hr;
}

HRESULT
CTDCCtl::SecurityMatchAllowDomainList()
{
    HRESULT hr;
    WCHAR swzHostDomain[INTERNET_MAX_HOST_NAME_LENGTH];
    DWORD cchHostDomain = INTERNET_MAX_HOST_NAME_LENGTH;
    LPOLESTR pszHostName = NULL;

    hr = GetHostURL(m_spClientSite, &pszHostName);
    if (FAILED(hr))
        goto Cleanup;

    hr = CoInternetParseUrl(pszHostName, PARSE_DOMAIN, 0, swzHostDomain, cchHostDomain,
                            &cchHostDomain, 0);
    if (FAILED(hr))
        goto Cleanup;

    hr = m_pUnify->MatchAllowDomainList(swzHostDomain);

Cleanup:
    CoTaskMemFree(pszHostName);
    return hr;
}


TDCCTL.H

//+-----------------------------------------------------------------------
//
//  Tabular Data Control
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       TDCCtl.h
//
//  Contents:   Declaration of the CTDCCtl ActiveX Control.
//
//------------------------------------------------------------------------

#include "resource.h"       // main symbols
#include "std.h"
#include "wch.h"
#include <wininet.h>        // for INTERNET_MAX_URL_LENGTH

#pragma comment(lib, "wininet.lib")

#ifndef DISPID_AMBIENT_CODEPAGE
#define DISPID_AMBIENT_CODEPAGE (-725)
#endif

// Declare helper needed in IHttpNegotiateImpl
HRESULT
GetHostURL(IOleClientSite *pSite, LPOLESTR *ppszHostName);

//------------------------------------------------------------------------
//
//  Template:  CMyBindStatusCallback
//
//  Synopsis:  This is a temporary kludge to get around an ATL feature
//             while we're waiting for it to become official code.
//
//------------------------------------------------------------------------

template <class T>
class ATL_NO_VTABLE IServiceProviderImpl
{
    public:
        // IUnknown
        //
        STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
        _ATL_DEBUG_ADDREF_RELEASE_IMPL(IServiceProviderImpl)

        STDMETHOD(QueryService) (REFGUID guidService,
                                 REFIID riid,
                                 void **ppvObject)
        {
            return S_OK;
        }

};

template <class T>
class ATL_NO_VTABLE IHttpNegotiateImpl
{
    public:
        // IUnknown
        //
        STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
        _ATL_DEBUG_ADDREF_RELEASE_IMPL(IHttpNegotiateImpl)

        STDMETHOD(BeginningTransaction) (LPCWSTR szURL,
                                         LPCWSTR szHeaders,
                                         DWORD dwReserved,
                                         LPWSTR *pszAdditionalHeaders)
        {
            return S_OK;
        }

        STDMETHOD(OnResponse) (DWORD dwResponseCode,
                               LPCWSTR szResponseHeaders,
                               LPCWSTR szRequestHeaders,
                               LPWSTR *pszAdditionalRequestHeaders)
        {
            return S_OK;
        }

};

template <class T>
class ATL_NO_VTABLE CMyBindStatusCallback :
    public CComObjectRootEx<T::_ThreadModel::ThreadModelNoCS>,
    public IBindStatusCallbackImpl<T>, public IHttpNegotiateImpl<T>, public IServiceProviderImpl<T>
{
    typedef void (T::*ATL_PDATAAVAILABLE)(CMyBindStatusCallback<T>* pbsc, BYTE* pBytes, DWORD dwSize);

    public:

        BEGIN_COM_MAP(CMyBindStatusCallback<T>)
                COM_INTERFACE_ENTRY_IID(IID_IBindStatusCallback, IBindStatusCallbackImpl<T>)
                COM_INTERFACE_ENTRY_IID(IID_IHttpNegotiate, IHttpNegotiateImpl<T>)
                COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProviderImpl<T>)
        END_COM_MAP()

        CMyBindStatusCallback()
        {
            m_pT = NULL;
            m_pFunc = NULL;
            m_fReload = FALSE;
        }
        ~CMyBindStatusCallback()
        {
            ATLTRACE(_T("~CMyBindStatusCallback\n"));
        }

        // IServiceProvider methods

        STDMETHOD(QueryService) (REFGUID guidService,
                                 REFIID riid,
                                 void **ppvObject)
        {
            // As it turns out, the service ID for IHttpNegotiate is the same
            // as it's IID (confusing).  This is the only service we support.
            if (IsEqualGUID(IID_IHttpNegotiate, guidService))
            {
                return ((IHttpNegotiate *)this)->QueryInterface(riid, ppvObject);
            }
            else return E_NOTIMPL;
        }

        //
        // IHttpNegotiate methods
        //

        STDMETHOD(BeginningTransaction) (LPCWSTR szURL,
                                         LPCWSTR szHeaders,
                                         DWORD dwReserved,
                                         LPWSTR *pszAdditionalHeaders)
        {
            HRESULT hr = S_OK;
            WCHAR swzHostScheme[INTERNET_MAX_URL_LENGTH];
            DWORD cchHostScheme = INTERNET_MAX_URL_LENGTH;
            WCHAR swzFileScheme[INTERNET_MAX_URL_LENGTH];
            DWORD cchFileScheme = INTERNET_MAX_URL_LENGTH;

            LPOLESTR pszHostName;

            *pszAdditionalHeaders = NULL;

            hr = GetHostURL(m_spClientSite, &pszHostName);
            if (FAILED(hr))
                goto Cleanup;

            // PARSE_SCHEMA didn't work, so we'll just CANONICALIZE and then use the first N
            // characters of the URL
            hr = CoInternetParseUrl(pszHostName, PARSE_CANONICALIZE, 0, swzHostScheme, cchHostScheme,
                                    &cchHostScheme, 0);
            if (FAILED(hr))
                goto Cleanup;

            // Don't send a referer which isn't http: or https:, it's none
            // of the servers' business.  Further, don't send an https:
            // referer when requesting an http: file.
            if (0 != wch_incmp(swzHostScheme, L"https:", 6) &&
                0 != wch_incmp(swzHostScheme, L"http:", 5))
                goto Cleanup;

            if (0 == wch_incmp(swzHostScheme, L"https:", 6))
            {
                hr = CoInternetParseUrl(szURL, PARSE_CANONICALIZE, 0, swzFileScheme, cchFileScheme,
                                        &cchFileScheme, 0);
                if (0 == wch_incmp(swzFileScheme, L"http:", 65)) // don't send https: referer
                    goto Cleanup;                                // to an http: file.
            }

            // 3*sizeof(WCHAR) is for CR, LF, & '\0'
            *pszAdditionalHeaders = (WCHAR *)CoTaskMemAlloc(sizeof(L"Referer: ") +
                                                            ocslen(pszHostName)*sizeof(WCHAR) +
                                                            3*sizeof(WCHAR));
            if (NULL != *pszAdditionalHeaders)
            {
                ocscpy(*pszAdditionalHeaders, L"Referer: ");
                ocscpy(&((*pszAdditionalHeaders)[9]), pszHostName);
                ocscpy(&((*pszAdditionalHeaders)[9+ocslen(pszHostName)]), L"\r\n");
            }

Cleanup:
            CoTaskMemFree(pszHostName);
            return hr;
        }

        STDMETHOD(OnResponse) (DWORD dwResponseCode,
                               LPCWSTR szResponseHeaders,
                               LPCWSTR szRequestHeaders,
                               LPWSTR *pszAdditionalRequestHeaders)
        {
            return S_OK;
        }

        //
        // IBindStatusCallback methods
        //

        STDMETHOD(OnStartBinding)(DWORD dwReserved, IBinding *pBinding)
        {
            ATLTRACE(_T("CMyBindStatusCallback::OnStartBinding\n"));
            m_spBinding = pBinding;
            return S_OK;
        }

        STDMETHOD(GetPriority)(LONG *pnPriority)
        {
            ATLTRACENOTIMPL(_T("CMyBindStatusCallback::GetPriority"));
        }

        STDMETHOD(OnLowResource)(DWORD reserved)
        {
            ATLTRACENOTIMPL(_T("CMyBindStatusCallback::OnLowResource"));
        }

        STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
        {
            if (BINDSTATUS_REDIRECTING == ulStatusCode && szStatusText != NULL)
            {
                ocscpy(m_pszURL, szStatusText);
            }
            return S_OK;
        }

        STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR szError)
        {
            //      ATLTRACE(_T("CMyBindStatusCallback::OnStopBinding\n"));
            (m_pT->*m_pFunc)(this, NULL, 0);
            m_spBinding.Release();
            m_spBindCtx.Release();
            m_spMoniker.Release();
            return S_OK;
        }

        STDMETHOD(GetBindInfo)(DWORD *pgrfBINDF, BINDINFO *pbindInfo)
        {
            ATLTRACE(_T("CMyBindStatusCallback::GetBindInfo\n"));

            if (!pbindInfo || !pbindInfo->cbSize || !pgrfBINDF)
                return E_INVALIDARG;

            *pgrfBINDF = BINDF_ASYNCHRONOUS
                         | BINDF_ASYNCSTORAGE
                         ;
// ;begin_internal
#ifdef NEVER
            // I want DEBUG mode to NOT cache things!! -cfranks
            *pgrfBINDF |= BINDF_GETNEWESTVERSION
                          | BINDF_NOWRITECACHE
                          | BINDF_RESYNCHRONIZE
                          ;
#endif
// ;end_internal

#ifndef DISPID_AMBIENT_OFFLINE
#define DISPID_AMBIENT_OFFLINE          (-5501)
#endif
            // Get our offline property from container
            VARIANT var;
            VariantInit(&var);
            DWORD dwConnectedStateFlags;
            m_pT->GetAmbientProperty(DISPID_AMBIENT_OFFLINE, var);
            if (var.vt==VT_BOOL && var.boolVal)
            {
                if (!(InternetGetConnectedState(&dwConnectedStateFlags, 0)) &&
                    (0 == (dwConnectedStateFlags & INTERNET_CONNECTION_MODEM_BUSY)))
                {
                    ATLTRACE(_T("CMyBindStatusCallback::GetBindInfo OFFLINE\n"));
                   // We're not even dialed out to another connectoid
                    *pgrfBINDF |= BINDF_OFFLINEOPERATION;
                }
                else
                {
                    ATLTRACE(_T("CMyBindStatusCallback::GetBindInfo OFFLINE\n"));
                    *pgrfBINDF &= ~BINDF_OFFLINEOPERATION;
                }
            }

            // See if we should force a reload, iff we're not offline.
            if (!(*pgrfBINDF & BINDF_OFFLINEOPERATION) && m_fReload)
            {
                *pgrfBINDF |= BINDF_RESYNCHRONIZE|BINDF_PRAGMA_NO_CACHE;
            }

            ULONG cbSize = pbindInfo->cbSize;
            memset(pbindInfo, 0, cbSize);

            pbindInfo->cbSize = cbSize;
            pbindInfo->dwBindVerb = BINDVERB_GET;

            return S_OK;
        }

        STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
        {
            ATLTRACE(_T("CMyBindStatusCallback::OnDataAvailable\n"));
            HRESULT hr = S_OK;

            // Get the Stream passed
            if (BSCF_FIRSTDATANOTIFICATION & grfBSCF)
            {
                if (!m_spStream && pstgmed->tymed == TYMED_ISTREAM)
                {
                    m_spStream = pstgmed->pstm;
                }
            }

            DWORD dwRead = dwSize - m_dwTotalRead; // Minimum amount available that hasn't been read
            DWORD dwActuallyRead = 0;            // Placeholder for amount read during this pull

            // If there is some data to be read then go ahead and read them
            if (m_spStream)
            {
                if (dwRead > 0)
                {
                    BYTE* pBytes = NULL;
                    ATLTRY(pBytes = new BYTE[dwRead + 1]);
                    if (pBytes == NULL)
                        return S_FALSE;
                    hr = m_spStream->Read(pBytes, dwRead, &dwActuallyRead);
                    if (SUCCEEDED(hr))
                    {
                        pBytes[dwActuallyRead] = 0;
                        if (dwActuallyRead>0)
                        {
                            (m_pT->*m_pFunc)(this, pBytes, dwActuallyRead);
                            m_dwTotalRead += dwActuallyRead;
                        }
                    }
                    delete[] pBytes;
                }
            }

            if (BSCF_LASTDATANOTIFICATION & grfBSCF)
                m_spStream.Release();
            return hr;
        }

        STDMETHOD(OnObjectAvailable)(REFIID riid, IUnknown *punk)
        {
            ATLTRACENOTIMPL(_T("CMyBindStatusCallback::OnObjectAvailable"));
        }

        HRESULT _StartAsyncDownload(BSTR bstrURL, IUnknown* pUnkContainer, BOOL bRelative)
        {
            m_dwTotalRead = 0;
            m_dwAvailableToRead = 0;
            HRESULT hr = S_OK;
            CComQIPtr<IServiceProvider, &IID_IServiceProvider> spServiceProvider(pUnkContainer);
            CComPtr<IBindHost> spBindHost;
            CComPtr<IStream> spStream;
            if (spServiceProvider)
                spServiceProvider->QueryService(SID_IBindHost, IID_IBindHost, (void**)&spBindHost);

            // We don't bother checking this QI, because the only failure mode is that our
            // BeginningNegotitation method won't be able able to properly add the referer string.
            (void)pUnkContainer->QueryInterface(IID_IOleClientSite, (void **)&m_spClientSite);

            if (spBindHost == NULL)
            {
                if (bRelative)
                    return E_NOINTERFACE;  // relative asked for, but no IBindHost
                hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker);
                if (SUCCEEDED(hr))
                    hr = CreateBindCtx(0, &m_spBindCtx);

                if (SUCCEEDED(hr))
                    hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L);
                else
                    m_spMoniker.Release();

                if (SUCCEEDED(hr))
                {
                    LPOLESTR pszTemp = NULL;
                    hr = m_spMoniker->GetDisplayName(m_spBindCtx, NULL, &pszTemp);
                    if (!hr && pszTemp != NULL)
                        ocscpy(m_pszURL, pszTemp);
                    CoTaskMemFree(pszTemp);

                    hr = m_spMoniker->BindToStorage(m_spBindCtx, 0, IID_IStream, (void**)&spStream);
                }
            }
            else
            {
                hr = CreateBindCtx(0, &m_spBindCtx);
                if (SUCCEEDED(hr))
                    hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L);

                if (SUCCEEDED(hr))
                {
                    if (bRelative)
                        hr = spBindHost->CreateMoniker(bstrURL, m_spBindCtx, &m_spMoniker, 0);
                    else
                        hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker);
                }

                if (SUCCEEDED(hr))
                {
                    LPOLESTR pszTemp = NULL;
                    hr = m_spMoniker->GetDisplayName(m_spBindCtx, NULL, &pszTemp);
                    if (!hr && pszTemp != NULL)
                        ocscpy(m_pszURL, pszTemp);
                    CoTaskMemFree(pszTemp);
                    hr = spBindHost->MonikerBindToStorage(m_spMoniker, NULL, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), IID_IStream, (void**)&spStream);
                    ATLTRACE(_T("Bound"));
                }
            }
            return hr;
        }

        HRESULT StartAsyncDownload(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer, BOOL bRelative,
                                   BOOL fReload)
        {
            m_pT = pT;
            m_pFunc = pFunc;
            m_fReload = fReload;        // force reload if TRUE
            return  _StartAsyncDownload(bstrURL, pUnkContainer, bRelative);
        }

        static HRESULT Download(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE)
        {
            CComObject<CMyBindStatusCallback<T> > *pbsc;
            HRESULT hRes = CComObject<CMyBindStatusCallback<T> >::CreateInstance(&pbsc);
            if (FAILED(hRes))
                return hRes;
            return pbsc->StartAsyncDownload(pT, pFunc, bstrURL, pUnkContainer, bRelative, FALSE);
        }
        CComPtr<IMoniker> m_spMoniker;
        CComPtr<IBindCtx> m_spBindCtx;
        CComPtr<IBinding> m_spBinding;
        CComPtr<IStream> m_spStream;
        CComPtr<IOleClientSite> m_spClientSite;
        BOOL m_fReload;
        OLECHAR m_pszURL[INTERNET_MAX_URL_LENGTH];
        T* m_pT;
        ATL_PDATAAVAILABLE m_pFunc;
        DWORD m_dwTotalRead;
        DWORD m_dwAvailableToRead;
};

//////////////////////////////////////////////////////////////////////////////////////////////////
// CTimer
template <class Derived, class T, const IID* piid>
class CTimer
{
public:

    CTimer()
    {
        m_bTimerOn = FALSE;
    }

    HRESULT TimerOn(DWORD dwTimerInterval)
    {
        Derived* pDerived = ((Derived*)this);

        m_dwTimerInterval = dwTimerInterval;
        if (m_bTimerOn) // already on, just change interval
            return S_OK;

        m_bTimerOn = TRUE;
        m_dwTimerInterval = dwTimerInterval;
        m_pStream = NULL;

        HRESULT hRes;

        hRes = CoMarshalInterThreadInterfaceInStream(*piid, (T*)pDerived, &m_pStream);

        // Create thread and pass the thread proc the this ptr
        m_hThread = CreateThread(NULL, 0, &_Apartment, (void*)this, 0, &m_dwThreadID);

        return S_OK;
    }

    void TimerOff()
    {
        if (m_bTimerOn)
        {
            m_bTimerOn = FALSE;
            AtlWaitWithMessageLoop(m_hThread);
        }
    }

// Implementation
private:
    static DWORD WINAPI _Apartment(void* pv)
    {
        CTimer<Derived, T, piid>* pThis = (CTimer<Derived, T, piid>*) pv;
        pThis->Apartment();
        return 0;
    }

    DWORD Apartment()
    {
        CoInitialize(NULL);
        HRESULT hRes;

        m_spT.Release();

        if (m_pStream)
        {
            hRes = CoGetInterfaceAndReleaseStream(m_pStream, *piid, (void**)&m_spT);
        }

        while(m_bTimerOn)
        {
            Sleep(m_dwTimerInterval);
            if (!m_bTimerOn)
                break;

            m_spT->_OnTimer();
        }
        m_spT.Release();

        CoUninitialize();
        return 0;
    }

// Attributes
public:
    DWORD m_dwTimerInterval;

// Implementation
private:
    HANDLE m_hThread;
    DWORD m_dwThreadID;
    LPSTREAM m_pStream;
    CComPtr<T> m_spT;
    BOOL m_bTimerOn;
};

class CEventBroker;

//////////////////////////////////////////////////////////////////////////////
// CProxyITDCCtlEvents
template <class T>
class CProxyITDCCtlEvents : public IConnectionPointImpl<T, &IID_ITDCCtlEvents, CComDynamicUnkArray>
{
//ITDCCtlEvents : IDispatch
public:
    void FireOnReadyStateChanged()
    {
        T* pT = (T*)this;
        pT->Lock();
        IUnknown** pp = m_vec.begin();
        while (pp < m_vec.end())
        {
            if (*pp != NULL)
            {
                DISPPARAMS dispParams;
                dispParams.cArgs = 0;
                dispParams.cNamedArgs = 0;
                dispParams.rgvarg = NULL;
                dispParams.rgdispidNamedArgs = NULL;
                ITDCCtlEvents* pITDCCtlEvents = reinterpret_cast<ITDCCtlEvents*>(*pp);
                pITDCCtlEvents->Invoke(DISPID_READYSTATECHANGE, IID_NULL, CP_ACP, DISPATCH_METHOD, &dispParams,
                                       NULL, NULL, NULL);
            }
            pp++;
        }
        pT->Unlock();
        return;
    }

};

//------------------------------------------------------------------------
//
//  Class:     CTDCCtl
//
//  Synopsis:  This is the TabularDataControl COM object.
//             It creates a CTDCArr object to manage the control's data.
//
//------------------------------------------------------------------------

class CTDCCtl :
    public CComObjectRoot,
    public CComCoClass<CTDCCtl, &CLSID_CTDCCtl>,
    public CComControl<CTDCCtl>,
    public CStockPropImpl<CTDCCtl, ITDCCtl, &IID_ITDCCtl, &LIBID_TDCLib>,
    public IProvideClassInfo2Impl<&CLSID_CTDCCtl, &IID_ITDCCtlEvents, &LIBID_TDCLib>,
    public IPersistStreamInitImpl<CTDCCtl>,
    public IOleControlImpl<CTDCCtl>,
    public IOleObjectImpl<CTDCCtl>,
    public IOleInPlaceActiveObjectImpl<CTDCCtl>,
    public IViewObjectExImpl<CTDCCtl>,
    public IOleInPlaceObjectWindowlessImpl<CTDCCtl>,
    public IPersistPropertyBagImpl<CTDCCtl>,
    public CTimer<CTDCCtl, ITDCCtl, &IID_ITDCCtl>,
    public IRunnableObjectImpl<CTDCCtl>,
    public IConnectionPointContainerImpl<CTDCCtl>,
    public IPropertyNotifySinkCP<CTDCCtl>,
    public CProxyITDCCtlEvents<CTDCCtl>
{
public:
    CTDCCtl();
    ~CTDCCtl();

DECLARE_REGISTRY_RESOURCEID(IDR_TDCCtl)

DECLARE_NOT_AGGREGATABLE(CTDCCtl)

BEGIN_COM_MAP(CTDCCtl)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ITDCCtl)
    COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx)
    COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx)
    COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
    COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless)
    COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless)
    COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
    COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
    COM_INTERFACE_ENTRY_IMPL(IOleControl)
    COM_INTERFACE_ENTRY_IMPL(IOleObject)
    COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
    COM_INTERFACE_ENTRY(IProvideClassInfo)
    COM_INTERFACE_ENTRY(IProvideClassInfo2)
    COM_INTERFACE_ENTRY_IMPL(IPersistPropertyBag)
    COM_INTERFACE_ENTRY_IMPL(IRunnableObject)
    COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
END_COM_MAP()

BEGIN_PROPERTY_MAP(CTDCCtl)
    PROP_ENTRY("RowDelim",      DISPID_ROWDELIM,    CLSID_CTDCCtl)
    PROP_ENTRY("FieldDelim",    DISPID_FIELDDELIM,  CLSID_CTDCCtl)
    PROP_ENTRY("TextQualifier", DISPID_TEXTQUALIFIER,   CLSID_CTDCCtl)
    PROP_ENTRY("EscapeChar",    DISPID_ESCAPECHAR,  CLSID_CTDCCtl)
    PROP_ENTRY("UseHeader",     DISPID_USEHEADER,   CLSID_CTDCCtl)
    PROP_ENTRY("SortAscending", DISPID_SORTASCENDING,   CLSID_CTDCCtl)
    PROP_ENTRY("SortColumn",    DISPID_SORTCOLUMN,  CLSID_CTDCCtl)
    PROP_ENTRY("FilterValue",   DISPID_FILTERVALUE, CLSID_CTDCCtl)
    PROP_ENTRY("FilterCriterion",   DISPID_FILTERCRITERION, CLSID_CTDCCtl)
    PROP_ENTRY("FilterColumn",  DISPID_FILTERCOLUMN,CLSID_CTDCCtl)
    PROP_ENTRY("CharSet",       DISPID_CHARSET,     CLSID_CTDCCtl)
    PROP_ENTRY("Language",      DISPID_LANGUAGE,    CLSID_CTDCCtl)
    PROP_ENTRY("CaseSensitive", DISPID_CASESENSITIVE, CLSID_CTDCCtl)
    PROP_ENTRY("Sort",          DISPID_SORT,        CLSID_CTDCCtl)
// ;begin_internal
//  Doesn't work right yet.
//    PROP_ENTRY("RefreshInterval",   DISPID_TIMER,      CLSID_CTDCCtl)
// ;end_internal
    PROP_ENTRY("Filter",        DISPID_FILTER,      CLSID_CTDCCtl)
    PROP_ENTRY("AppendData",    DISPID_APPENDDATA,  CLSID_CTDCCtl)
// ;begin_internal
//  Trying to save this property causes OLEAUT to GP Fault trying
//  to conver the IDispatch * to a BSTR!
//    PROP_ENTRY("OSP",           DISPID_OSP,         CLSID_CTDCCtl)
// ;end_internal
    //  This will be removed when we learn more about the HTML
    //  sub-tag "OBJECT"
    PROP_ENTRY("DataURL",       DISPID_DATAURL,     CLSID_CTDCCtl)
    PROP_ENTRY("ReadyState",    DISPID_READYSTATE,  CLSID_CTDCCtl)
END_PROPERTY_MAP()

BEGIN_CONNECTION_POINT_MAP(CTDCCtl)
CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink)
CONNECTION_POINT_ENTRY(IID_ITDCCtlEvents)
END_CONNECTION_POINT_MAP()

BEGIN_MSG_MAP(CTDCCtl)
    MESSAGE_HANDLER(WM_PAINT, OnPaint)
    MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
    MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
    MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
END_MSG_MAP()

private:
    CComBSTR     m_cbstrFieldDelim;
    CComBSTR     m_cbstrRowDelim;
    CComBSTR     m_cbstrQuoteChar;
    CComBSTR     m_cbstrEscapeChar;
    VARIANT_BOOL m_fUseHeader;
    CComBSTR     m_cbstrSortColumn;
    VARIANT_BOOL m_fSortAscending;
    CComBSTR     m_cbstrFilterValue;
    OSPCOMP      m_enumFilterCriterion;
    CComBSTR     m_cbstrFilterColumn;
    UINT         m_nCodePage;
    CComBSTR     m_cbstrLanguage;
    CComBSTR     m_cbstrDataURL;
    LCID         m_lcidRead;
    boolean      m_fDataURLChanged;
    HRESULT      m_hrDownloadStatus;
    LONG         m_lTimer;
    CComBSTR     m_cbstrFilterExpr;
    CComBSTR     m_cbstrSortExpr;
    VARIANT_BOOL m_fAppendData;
    VARIANT_BOOL m_fCaseSensitive;
    boolean      m_fInReset;

    OLEDBSimpleProvider *m_pSTD;
    CTDCArr      *m_pArr;
    IMultiLanguage  *m_pML;
    BOOL         m_fSecurityChecked;

// ;begin_internal
    DATASRCListener *m_pDATASRCListener;
// ;end_internal
    DataSourceListener *m_pDataSourceListener;
    CEventBroker *m_pEventBroker;

    //  These member objects are used while parsing the input stream
    //
    CTDCUnify       *m_pUnify;
    CComObject<CMyBindStatusCallback<CTDCCtl> > *m_pBSC;

// These members and methods expose the ITDCCtl interface
//
public:

    //  Control Properties
    //
    STDMETHOD(get_FieldDelim)(BSTR* pbstrFieldDelim);
    STDMETHOD(put_FieldDelim)(BSTR bstrFieldDelim);
    STDMETHOD(get_RowDelim)(BSTR* pbstrRowDelim);
    STDMETHOD(put_RowDelim)(BSTR bstrRowDelim);
    STDMETHOD(get_TextQualifier)(BSTR* pbstrTextQualifier);
    STDMETHOD(put_TextQualifier)(BSTR bstrTextQualifier);
    STDMETHOD(get_EscapeChar)(BSTR* pbstrEscapeChar);
    STDMETHOD(put_EscapeChar)(BSTR bstrEscapeChar);
    STDMETHOD(get_UseHeader)(VARIANT_BOOL* pfUseHeader);
    STDMETHOD(put_UseHeader)(VARIANT_BOOL fUseHeader);
    STDMETHOD(get_SortColumn)(BSTR* pbstrSortColumn);
    STDMETHOD(put_SortColumn)(BSTR bstrSortColumn);
    STDMETHOD(get_SortAscending)(VARIANT_BOOL* pfSortAscending);
    STDMETHOD(put_SortAscending)(VARIANT_BOOL fSortAscending);
    STDMETHOD(get_FilterValue)(BSTR* pbstrFilterValue);
    STDMETHOD(put_FilterValue)(BSTR bstrFilterValue);
    STDMETHOD(get_FilterCriterion)(BSTR* pbstrFilterCriterion);
    STDMETHOD(put_FilterCriterion)(BSTR bstrFilterCriterion);
    STDMETHOD(get_FilterColumn)(BSTR* pbstrFilterColumn);
    STDMETHOD(put_FilterColumn)(BSTR bstrFilterColumn);
    STDMETHOD(get_CharSet)(BSTR *pbstrCharSet);
    STDMETHOD(put_CharSet)(BSTR bstrCharSet);
    STDMETHOD(get_Language)(BSTR* pbstrLanguage);
    STDMETHOD(put_Language_)(LPWCH pwchLanguage);
    STDMETHOD(put_Language)(BSTR bstrLanguage);
    STDMETHOD(get_CaseSensitive)(VARIANT_BOOL *pfCaseSensitive);
    STDMETHOD(put_CaseSensitive)(VARIANT_BOOL fCaseSensitive);
    STDMETHOD(get_DataURL)(BSTR* pbstrDataURL); //
    STDMETHOD(put_DataURL)(BSTR bstrDataURL);
// ;begin_internal
//    STDMETHOD(get_RefreshInterval)(LONG* plTimer);
//    STDMETHOD(put_RefreshInterval)(LONG lTimer);
// ;end_internal
    STDMETHOD(get_Filter)(BSTR* pbstrFilterExpr);
    STDMETHOD(put_Filter)(BSTR bstrFilterExpr);
    STDMETHOD(get_Sort)(BSTR* pbstrSortExpr);
    STDMETHOD(put_Sort)(BSTR bstrSortExpr);
    STDMETHOD(get_AppendData)(VARIANT_BOOL* pfAppendData);
    STDMETHOD(put_AppendData)(VARIANT_BOOL fAppendData);
    STDMETHOD(get_OSP)(OLEDBSimpleProviderX ** ppISTD);

    STDMETHOD(get_ReadyState)(LONG *lReadyState);
    STDMETHOD(put_ReadyState)(LONG lReadyState);

    // Override IPersistPropertyBagImpl::Load
    STDMETHOD(Load)(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog);

    void UpdateReadyState(LONG lReadyState);
    //  Data source notification methods
    STDMETHOD(msDataSourceObject)(BSTR qualifier, IUnknown **ppUnk);
    STDMETHOD(addDataSourceListener)(IUnknown *pEvent);

    //  Control Methods
    //
    STDMETHOD(Reset)();
    STDMETHOD(_OnTimer)(void);

private:
    STDMETHOD(CreateTDCArr)(boolean fAppend);
    STDMETHOD(ReleaseTDCArr)(boolean fReplacing);
    void LockBSC();
    void UnlockBSC();
    STDMETHOD(InitiateDataLoad)(boolean fAppend);
    STDMETHOD(SecurityCheckDataURL)(LPOLESTR pszURL);
    STDMETHOD(SecurityMatchAllowDomainList)();
    STDMETHOD(SecurityMatchProtocols)(LPOLESTR pszURL);
    STDMETHOD(TerminateDataLoad)();
    BSTR bstrConstructSortExpr();
    BSTR bstrConstructFilterExpr();

protected:
    void OnData(CMyBindStatusCallback<CTDCCtl> *pbsc, BYTE *pBytes, DWORD dwSize);
};


TDCIDS.H

#define DISPID_FIELDDELIM       1
#define DISPID_ROWDELIM         2
#define DISPID_TEXTQUALIFIER    3
#define DISPID_ESCAPECHAR       4
#define DISPID_USEHEADER        5
#define DISPID_SORTCOLUMN       6
#define DISPID_SORTASCENDING    7
#define DISPID_FILTERVALUE      8
#define DISPID_FILTERCRITERION  9
#define DISPID_FILTERCOLUMN     10
#define DISPID_CHARSET          11
#define DISPID_DATAURL          12
#define DISPID_SIMPLEPROVIDER   13
#define DISPID_LANGUAGE         14
#define DISPID_TIMER            15
#define DISPID_FILTER           16
#define DISPID_SORT             17
#define DISPID_APPENDDATA       18
#define DISPID_OSP              19
#define DISPID_CASESENSITIVE    20


TDCPARSE.CPP

//------------------------------------------------------------------------
//
//  Tabular Data Control Parsing Module
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       TDCParse.cpp
//
//  Contents:   Implementation of CTDCParse classes.
//
//------------------------------------------------------------------------

#include "stdafx.h"
#include "STD.h"
#include "TDC.h"
#include <MLang.h>
#include "Notify.h"
#include "TDCParse.h"
#include "TDCArr.h"
#include "locale.h"
#include "wch.h"

#define BYTE_ORDER_MARK 0xFEFF
#define REVERSE_BYTE_ORDER_MARK 0xFFFE

//------------------------------------------------------------------------
//
//  Function:   IsSpace()
//
//  Synopsis:   Returns TRUE if the given character is a space or tab character.
//
//  Arguments:  ch            Character to test.
//
//  Returns:    TRUE if 'ch' is a space or tab character.
//              FALSE otherwise.
//
//------------------------------------------------------------------------

inline boolean IsSpace(WCHAR ch)
{
    return (ch == L' ' || ch == L'\t');
}

//////////////////////////////////////////////////////////////////////////
//
//        CTDCTokenise Class - see comments in file TDCParse.h
//        ------------------
//////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------
//
//  Method:     CTDCTokenise::Create()
//
//  Synopsis:   Initialise the CTDCTokenise object
//
//  Arguments:  pFieldSink         Object to send parsed fields to.
//              wchDelimField      \
//              wchDelimRow         |  Set of characters that control
//              wchQuote            |  the parsing of fields
//              wchEscape          /
//
//  Returns:    S_OK indicating success.
//
//------------------------------------------------------------------------

HRESULT CTDCUnify::InitTokenizer(CTDCFieldSink *pFieldSink, WCHAR wchDelimField,
                                 WCHAR wchDelimRow, WCHAR wchQuote, WCHAR wchEscape)
{
    _ASSERT(pFieldSink != NULL);
    m_pFieldSink = pFieldSink;
    m_wchDelimField = wchDelimField;
    m_wchDelimRow = wchDelimRow;
    m_wchQuote = wchQuote;
    m_wchEscape = wchEscape;
    m_ucParsed = 0;

    m_fIgnoreNextLF = FALSE;
    m_fIgnoreNextCR = FALSE;
    m_fIgnoreNextWhiteSpace = FALSE;
    m_fEscapeActive = FALSE;
    m_fQuoteActive = FALSE;
    m_fFoldWhiteSpace = FALSE;

    //  Ensure that the field and row delimiters are set.
    //
    if (m_wchDelimRow == 0)
        m_wchDelimRow = DEFAULT_ROW_DELIM[0];

    //  Remove conflicting delimiter values
    //
    if (m_wchDelimRow == m_wchDelimField)
        m_wchDelimRow = 0;
    if (m_wchQuote != 0)
    {
        if (m_wchQuote == m_wchDelimField || m_wchQuote == m_wchDelimRow)
            m_wchQuote = 0;
    }
    if (m_wchEscape != 0)
    {
        if (m_wchEscape == m_wchDelimField ||
            m_wchEscape == m_wchDelimRow ||
            m_wchEscape == m_wchQuote)
            m_wchEscape = 0;
    }

    m_fFoldCRLF = (m_wchDelimRow == L'\r' || m_wchDelimRow == L'\n');

    return S_OK;
}

//------------------------------------------------------------------------
//
//  Method:     CTDCTokenise::AddWcharBuffer()
//
//  Synopsis:   Takes a buffer of characters, breaks it up into fields
//              and passes them to the embedded CTDCFieldSink object
//              as fields.
//
//  Arguments:  pwch               Buffer containing characters to be parsed.
//              dwSize             Number of significant characters in 'pwch'
//                                  dwSize == 0 means "End-of-stream"
//
//  Returns:    S_OK upon success.
//              E_OUTOFMEMORY indicating insufficient memory to carry
//                out the parse operation.
//              Other misc error code upon failure.
//
//------------------------------------------------------------------------

HRESULT CTDCUnify::AddWcharBuffer(BOOL fLastData)
{

    OutputDebugStringX(_T("CTDCTokenise::AddWcharBuffer called\n"));

    _ASSERT(m_pFieldSink != NULL);

    HRESULT hr = S_OK;

    LPWCH   pwchCurr;   //  Next character to process
    LPWCH   pwchEnd;    //  End-of-buffer marker
    LPWCH   pwchDest;   //  Where to write next char processed
    LPWCH   pwchStart;  //  Beginning of current token

    pwchStart = &m_psWcharBuf[0];
    pwchCurr = pwchStart + m_ucParsed;
    pwchDest = pwchCurr;
    pwchEnd = &m_psWcharBuf[m_ucWcharBufCount];

    //  Read up to the next field boundary (field or row delimiter)
    //
    while (pwchCurr < pwchEnd)
    {
        if (m_fIgnoreNextLF)
        {
            //  We're expecting a LF to terminate a CR-LF sequence.
            //
            m_fIgnoreNextLF = FALSE;
            if (*pwchCurr == L'\n')
            {
                //  Found a LF - ignore it
                //
                pwchCurr++;
                continue;
            }

            //  Found something else - carry on ...
            //
        }

        if (m_fIgnoreNextCR)
        {
            //  We're expecting a CR to terminate a LF-CR sequence.
            //
            m_fIgnoreNextCR = FALSE;
            if (*pwchCurr == L'\r')
            {
                //  Found a CR - ignore it
                //
                pwchCurr++;
                continue;
            }

            //  Found something else - carry on ...
            //
        }

        if (m_fIgnoreNextWhiteSpace)
        {
            //  We're expecting the rest of a white-space sequence
            //
            if (IsSpace(*pwchCurr))
            {
                //  Found white-space - ignore it
                //
                pwchCurr++;
                continue;
            }
            m_fIgnoreNextWhiteSpace = FALSE;
        }

        //  Escape characters work, even in quoted strings
        //
        if (m_fEscapeActive)
        {
            *pwchDest++ = *pwchCurr++;
            m_fEscapeActive = FALSE;
            continue;
        }
        if (*pwchCurr == m_wchEscape)
        {
            pwchCurr++;
            m_fEscapeActive = TRUE;
            continue;
        }

        //  Quotes activate/deactivate Field/Row delimiters
        //
        if (*pwchCurr == m_wchQuote)
        {
            pwchCurr++;
            m_fQuoteActive = !m_fQuoteActive;
            continue;
        }

        if (m_fQuoteActive)
        {
            *pwchDest++ = *pwchCurr++;
            continue;
        }

        if (*pwchCurr == m_wchDelimField ||
            (m_fFoldWhiteSpace && IsSpace(*pwchCurr)))
        {
            hr = m_pFieldSink->AddField(pwchStart, pwchDest - pwchStart);
            if (!SUCCEEDED(hr))
                goto Cleanup;
            pwchCurr++;
            if (m_fFoldWhiteSpace && IsSpace(*pwchCurr))
                m_fIgnoreNextWhiteSpace = TRUE;
            pwchStart = &m_psWcharBuf[0];
            pwchDest = pwchStart;
            continue;
        }

        if (*pwchCurr == m_wchDelimRow ||
            (m_fFoldCRLF && (*pwchCurr == L'\r' || *pwchCurr == L'\n')))
        {
            hr = m_pFieldSink->AddField(pwchStart, pwchDest - pwchStart);
            if (!SUCCEEDED(hr))
                goto Cleanup;
            hr = m_pFieldSink->EOLN();
            if (!SUCCEEDED(hr))
                goto Cleanup;
            if (m_fFoldCRLF)
            {
                m_fIgnoreNextLF = (*pwchCurr == L'\r');
                m_fIgnoreNextCR = (*pwchCurr == L'\n');
            }
            pwchCurr++;
            pwchStart = &m_psWcharBuf[0];
            pwchDest = pwchStart;
            continue;
        }

        *pwchDest++ = *pwchCurr++;
    }

    m_ucWcharBufCount = pwchDest - pwchStart;
    m_ucParsed = pwchDest - pwchStart;  // amount we've already parsed

    // If this is the last data packet, and there's a fragment left,
    // parse it.
    if (m_ucWcharBufCount && fLastData)
    {
        hr = m_pFieldSink->AddField(pwchStart, m_ucParsed);
        if (!SUCCEEDED(hr))
            goto Cleanup;
        m_ucParsed = 0;
        hr = m_pFieldSink->EOLN();
        return hr;
    }

Cleanup:
    return hr;
}

//////////////////////////////////////////////////////////////////////////
//
//        CTDCUnify Class - see comments in file TDCParse.h
//        ---------------
//////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------
//
//  Method:     CTDCUnify::CTDCUnify()
//
//  Synopsis:   Constuctor
//
//------------------------------------------------------------------------

CTDCUnify::CTDCUnify()
{
    m_pML = NULL;
}

//------------------------------------------------------------------------
//
//  Method:     CTDCUnify::~CTDCUnify()
//
//  Synopsis:   Destructor
//
//------------------------------------------------------------------------

CTDCUnify::~CTDCUnify()
{
    delete [] m_psByteBuf;
    delete [] m_psWcharBuf;

    if (m_pML != NULL)
        m_pML->Release();
}

//------------------------------------------------------------------------
//
//  Method:     CTDCUnify::Create()
//
//  Synopsis:   Initialise the CTDCUnify object
//
//  Arguments:  pTokenise         Object to send converted buffers to.
//              nCodePage         Code page for ASCII->Unicode conversions
//              pML               MLANG COM object (used for conversions)
//
//  Returns:    S_OK to indicate success.
//
//------------------------------------------------------------------------

HRESULT CTDCUnify::Create(UINT nCodePage, IMultiLanguage *pML)
{
    m_pML = pML;
    m_pML->AddRef();
    m_nCodePage = nCodePage;
    m_fDataMarkedUnicode = FALSE;
    m_fDataIsUnicode = FALSE;
    m_dwBytesProcessed = 0;
    m_fCanConvertToUnicode = 0;
    m_nUnicode = 0;

    m_dwConvertMode = 0;
    m_ucByteBufSize = 0;
    m_ucByteBufCount = 0;
    m_psByteBuf = NULL;

    m_ucWcharBufSize = 0;
    m_ucWcharBufCount = 0;
    m_psWcharBuf = NULL;

    if (m_pML != NULL)
    {
        HRESULT hr;

        hr = m_pML->IsConvertible(m_nCodePage, UNICODE_CP);
        m_fCanConvertToUnicode = SUCCEEDED(hr);
    }
    return S_OK;
}

//------------------------------------------------------------------------
//
//  Method:     CTDCUnify::IsUnicode
//
//  Synopsis:   Determines if our text buffer is Unicode or not.  Should
//              only be called once on the FIRST text buffer.
//
//              Assume if the data is marked as Unicode, that it's correct.
//
//              The determination this routine makes will override any
//              single byte codepage the user may have specified.
//
//
//  Arguments:  pBytes            Buffer containing characters to be converted.
//              dwSize            Number of significant characters in 'pBytes'
//
//  Returns:    Code page of text, or zero if not Unicode (UNICODE_CP,
//              UNICODE_REVERSE_CP, or 0)
//
//
//------------------------------------------------------------------------
int
CTDCUnify::IsUnicode(BYTE * pBytes, DWORD dwSize)
{
// Document.Write can cause our hosting page to be Unicode, when the TDC
// data page might not be.  This can cause some real problems..
    if (UNICODE_CP == m_nCodePage ||
        UNICODE_REVERSE_CP == m_nCodePage)
    {
        return m_nCodePage;
    }

    if (BYTE_ORDER_MARK == *(WCHAR *)pBytes)
        return UNICODE_CP;

    if (UNICODE_REVERSE_CP == *(WCHAR *)pBytes)
        return UNICODE_REVERSE_CP;

    else return 0;
}

//------------------------------------------------------------------------
//
//  Method:     CTDCUnify::ConvertByteBuffer()
//
//  Synopsis:   Converts a byte-buffer into a wide-character stream
//              (applying unicode conversions if necessary) and passes
//              it to the embedded TDCTokenise object to be broken into
//              fields.
//
//  Arguments:  pBytes            Buffer containing characters to be converted.
//              dwSize            Number of significant characters in 'pBytes'
//                                  dwSize == 0 means "End-of-stream"
//
//  Returns:    S_OK upon success.
//              OLE_E_CANTCONVERT if a non-unicode buffer can't be
//                converted into unicode.
//              E_OUTOFMEMORY if there isn't enough memory to perform
//                a data conversion.
//
//------------------------------------------------------------------------

HRESULT CTDCUnify::ConvertByteBuffer(BYTE *pBytes, DWORD dwSize)
{
    OutputDebugStringX(_T("CTDCUnify::AddByteBuffer called\n"));

    _ASSERT(pBytes != NULL || dwSize == 0);

    //  Examine the data to determine if it's in UNICODE format or not.
    //  Convert to UNICODE if neccessary.
    //
    HRESULT     hr = S_OK;
    UINT        ucBytes;
    UINT        ucWchars;

    _ASSERT(pBytes != NULL);
    _ASSERT(dwSize > 0);

    if (!m_fCanConvertToUnicode)
        hr = OLE_E_CANTCONVERT;

    // Is there enough space in Byte buffer for this packet?
    if (dwSize > (m_ucByteBufSize - m_ucByteBufCount))
    {
        // No, the current buffer is too small, make a new one.
        BYTE * psTemp = new BYTE[m_ucByteBufCount + dwSize];
        if (psTemp==NULL)
        {
            hr = E_OUTOFMEMORY;

            goto Done;
        }

        if (m_psByteBuf != NULL)        // if not first time
        {
            memmove(psTemp, m_psByteBuf, m_ucByteBufCount);
            delete [] m_psByteBuf;
        }
        m_ucByteBufSize = m_ucByteBufCount + dwSize;
        m_psByteBuf = psTemp;
    }

    // Append the new data to the old data.
    memmove(m_psByteBuf + m_ucByteBufCount, pBytes, dwSize);
    m_ucByteBufCount += dwSize;

    // Is there enough space in the Wchar buffer for the converted data?
    // We make a very conservative assumption here that N source buffer bytes
    // convert to N Wchar buffer chars (or 2*N bytes).  This will ensure that
    // our call to ConvertToUnicode will never not finish because there wasn't
    // enough room in the output buffer.
    if (dwSize > (m_ucWcharBufSize - m_ucWcharBufCount))
    {
        // The current buffer is too small, make a new one.
        WCHAR * psTemp = new WCHAR[m_ucWcharBufCount + dwSize];
        if (psTemp==NULL)
        {
            hr = E_OUTOFMEMORY;
            goto Done;
        }

        if (m_psWcharBuf != NULL)       // if not first time
        {
            memmove(psTemp, m_psWcharBuf,
                    m_ucWcharBufCount*sizeof(WCHAR));
            delete [] m_psWcharBuf;
        }
        m_psWcharBuf = psTemp;
        m_ucWcharBufSize = m_ucWcharBufCount + dwSize;
    }

    if (0 == m_dwBytesProcessed)
    {
        // Need at least 2 chars for Unicode signature (0xFFFE or 0xFEFF)
        if (m_ucByteBufCount > 1)
        {
            // If we detect Unicode, it overrides any user specified code page.
            m_nUnicode = IsUnicode(m_psByteBuf, m_ucByteBufCount);
            m_nCodePage = m_nUnicode ? m_nUnicode : m_nCodePage;
        }
        else
            goto Done;                  // too little to detect anything yet
    }

    // Convert as many source bytes as we can to Unicode chars
    ucBytes = m_ucByteBufCount;
    ucWchars = m_ucWcharBufSize - m_ucWcharBufCount;

    // ConvertStringToUnicode won't convert Unicode to Unicode for us.
    // So we'll do it ourselves.
    if (m_nUnicode)
    {
        _ASSERT( ucWchars * sizeof(WCHAR) >= ucBytes);

        // This might copy an odd extra byte
        memmove((BYTE *)(m_psWcharBuf + m_ucWcharBufCount), m_psByteBuf,
                ucBytes);

        // But we only count the number of complete WCHAR's we copied.
        ucWchars = ucBytes / sizeof(WCHAR);
        ucBytes = ucWchars * sizeof(WCHAR);

        if (UNICODE_REVERSE_CP == m_nUnicode)
        {
            // need to byte swap
            BYTE *pByteSwap = (BYTE *)(m_psWcharBuf + m_ucWcharBufCount);
            BYTE bTemp;
            for (ULONG i = ucWchars; i != 0; i--)
            {
                // Well, OK, we've kind of hardwired WCHAR == 2 here, but ..
                bTemp = pByteSwap[0];
                pByteSwap[0] = pByteSwap[1];
                pByteSwap[1] = bTemp;
                pByteSwap += 2;
            }
        }

        // On first packet, need to remove Unicode signature.
        // Only need to look for 0xFFFE -- we already swapped bytes.
        if (0 == m_dwBytesProcessed && m_psWcharBuf[0] == BYTE_ORDER_MARK)
        {
            ucWchars--;
            memmove((BYTE *)m_psWcharBuf, (BYTE *)m_psWcharBuf+2,
                   ucWchars*sizeof(ucWchars));
        }
    }
    else
    {
        hr = m_pML->ConvertStringToUnicode(&m_dwConvertMode, m_nCodePage,
                                           (char *)m_psByteBuf, &ucBytes,
                                           m_psWcharBuf +m_ucWcharBufCount,
                                           &ucWchars);
    }
    // Move any leftover source characters to the start of the buffer.
    // These are probably split Unicode chars, lead bytes without trail
    // bytes, etc.
    m_ucByteBufCount -= ucBytes;
    memmove(m_psByteBuf, m_psByteBuf + ucBytes,
            m_ucByteBufCount);

    // The number of useful chars in the output buf is increased by the
    // number we managed to convert.
    m_ucWcharBufCount += ucWchars;
    m_dwBytesProcessed += ucWchars;

Done:
    return hr;
}

LPWCH SkipSpace(LPWCH pwchCurr)
{
    while (IsSpace(*pwchCurr)) pwchCurr++;
    return pwchCurr;
}

static
boolean IsEnd(WCHAR ch)
{
    return (ch == 0 || ch == L'\r' || ch == L'\n');
}

static
boolean IsBreak(WCHAR ch)
{
    return (ch == L';' || IsEnd(ch));
}

// Returns FALSE if names didn't match.
// Returns TRUE if they did.
// Sets *ppwchAdvance to terminator of the match name
BOOL
MatchName(LPWCH pwchMatchName, LPCWCH pwzHostName, LPWCH *ppwchAdvance)
{
    // match from right to left
    LPWCH pwchMatchRight = &pwchMatchName[0];
    LPCWCH pwchHostRight = &pwzHostName[0] + ocslen(pwzHostName) -1;

    // handle empty match name
    if (IsBreak(*pwchMatchRight))
    {
        if (!IsEnd(*pwchMatchRight))    // be sure to advance (unless at end)
            ++ pwchMatchRight;
        *ppwchAdvance = pwchMatchRight;
        return FALSE;
    }

    // Find end of Match name.
    while (!IsBreak(*pwchMatchRight)) pwchMatchRight++;

    *ppwchAdvance = pwchMatchRight;     // return pointer to terminator

    pwchMatchRight--;

    while (IsSpace(*pwchMatchRight) && pwchMatchRight >= pwchMatchName)
        -- pwchMatchRight;              // ignore trailing whitespace

    // match full wildcard the easy way
    if (pwchMatchRight == pwchMatchName && pwchMatchRight[0] == '*')
        return TRUE;

    // match right-to-left, stop at mismatch or beginning of either string
    for (; pwchMatchRight>=pwchMatchName && pwchHostRight>=pwzHostName;
            --pwchMatchRight, --pwchHostRight)
    {
        if (*pwchMatchRight != *pwchHostRight || *pwchMatchRight == '*')
            break;
    }

    // it's a match if strings matched completely
    if (pwchMatchRight+1 == pwchMatchName  &&  pwchHostRight+1 == pwzHostName)
        return TRUE;

    // or if match name started with "*." and the rest matched a suffix of host name
    if (pwchMatchRight == pwchMatchName  &&  pwchMatchRight[0] == '*'  &&
        pwchMatchRight[1] == '.')
        return TRUE;

    // otherwise it's not a match
    return FALSE;
}

HRESULT
CTDCUnify::MatchAllowDomainList(LPCWSTR pwzURL)
{
    HRESULT hr = E_FAIL;                // assume failure
    LPWCH pwchCurr = &m_psWcharBuf[0];
    LPWCH pwchCurr2;
    int cchHostDoman = ocslen(pwzURL);

    // skip over white space
    pwchCurr = SkipSpace(pwchCurr);
    if (IsEnd(*pwchCurr))
        goto Cleanup;

    // must have the equal sign
    if (*pwchCurr++ != '=' || *pwchCurr == '\0')
        goto Cleanup;

    while (TRUE)
    {
        // skip over white space
        pwchCurr = SkipSpace(pwchCurr);

        if (IsEnd(*pwchCurr))           // terminate on \r, \n, \0
            break;

        if (IsBreak(*pwchCurr))         // Must be ';',
            pwchCurr++;                 // skip it.

        // skip over white space
        pwchCurr = SkipSpace(pwchCurr);

        if (MatchName(pwchCurr, pwzURL, &pwchCurr2))
        {
            hr = S_OK;
            break;
        }
        pwchCurr = pwchCurr2;
    }

Cleanup:
    while (!IsEnd(*pwchCurr))
        pwchCurr++;

    // Skip CRLF combos
    if (*pwchCurr == '\r' && pwchCurr[1] == '\n') pwchCurr++;

    // Eat the AllowDomain line so it doesn't screw up the data.
    m_ucWcharBufCount -= (pwchCurr+1 - m_psWcharBuf);
    memmove(m_psWcharBuf, pwchCurr+1, m_ucWcharBufCount*sizeof(WCHAR));

    return hr;
}

//------------------------------------------------------------------------
//
//  Method:     CTDCUnify::CheckForAllowDomainList
//
//  Synopsis:   Checks the beggining of the Wide Char buffer to see if it
//              contains the string "@!allow.domains".  This is used to
//              determine if this file has a list of domain names which are
//              allowed to access this file, even though the access may be
//              coming from another internet host.
//
//  Arguments:  uses CTDCUnify state variables for the Wide Char buffer:
//              m_psWcharBUf            the Wide char buffer
//              m_ucWcharBufCount       the # of chars in the wide char buf
//
//  Returns:    ALLOW_DOMAINLIST_NO             signature not found
//              ALLOW_DOMAINLIST_YES            signature was found
//              ALLOW_DOMAINLIST_DONTKNOW       don't have enough characters
//                                              to know for sure yet.
//
//------------------------------------------------------------------------

CTDCUnify::ALLOWDOMAINLIST
CTDCUnify::CheckForAllowDomainList()
{
    ULONG cAllowDomainLen = ocslen(ALLOW_DOMAIN_STRING);

    // Make sure we have a while line.
    LPWCH pwchCurr = m_psWcharBuf;
    while (!IsEnd(*pwchCurr)) pwchCurr++;
    if (*pwchCurr == '\0')              // if buffer ended before line did
        return ALLOW_DOMAINLIST_DONTKNOW;

    if (0 == wch_incmp(m_psWcharBuf, ALLOW_DOMAIN_STRING, cAllowDomainLen))
    {
        // We matched equal and have the whole string.
        // Take the "@!allow.domains" out of the buffer..
        m_ucWcharBufCount -= cAllowDomainLen;
        memmove(m_psWcharBuf, &m_psWcharBuf[cAllowDomainLen],
                m_ucWcharBufCount*sizeof(WCHAR));
        return ALLOW_DOMAINLIST_YES;
    }

    // We didn't match equal, no point in looking any more.
    return ALLOW_DOMAINLIST_NO;
}


TDCPARSE.H

//------------------------------------------------------------------------
//
//  Tabular Data Control Parse Module
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       TDCParse.h
//
//  Contents:   Declaration of the TDC parser classes.
//
//  The intent of these classes once was to create a pipeline.
//
//
//          |
//          |       Wide-character stream
//          |       ~~~~~~~~~~~~~~~~~~~~~
//         \|/
//  ------------------------
//  | CTDCTokenise object  | Created with field & row delimiters, quote &
//  |   AddWcharBuffer()   |     escape characters
//  ------------------------
//          |
//          |       Stream of <field>, <eoln> and <eof> tokens
//          |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//         \|/
//  ------------------------
//  | CTDCFieldSink object | Abstract class, e.g. STD object created with
//  |   AddField()         |     sort/filter criteria & fUseHeader flag
//  |   EOLN()             |     to interpret the sequence of fields.
//  |   EOF()              |
//  ------------------------
//
//------------------------------------------------------------------------

#define DEFAULT_FIELD_DELIM L","
#define DEFAULT_ROW_DELIM   L"\n"
#define DEFAULT_QUOTE_CHAR  L"\""

#define UNICODE_CP          1200        // Win32's Unicode codepage
#define UNICODE_REVERSE_CP  1201        // Byte-swapped Unicode codepage

#define ALLOW_DOMAIN_STRING L"@!allow_domains"

//------------------------------------------------------------------------
//
//  Class:  CTDCFieldSink
//
//  This class accumulates a sequence of <fields> and <eoln> tokens
//  into a 2-D array.
//
//  An admissible calling sequence on this object is:
//   * 0 or more calls to AddField() or EOLN()
//   * 1 call to EOF()
//
//------------------------------------------------------------------------

class CTDCFieldSink
{
public:
    STDMETHOD(AddField)(LPWCH pwch, DWORD dwSize) PURE;
    STDMETHOD(EOLN)() PURE;
    STDMETHOD(EOF)() PURE;
};

//------------------------------------------------------------------------
//
//  Class: CTDCUnify
//
//  This class takes a series of byte buffers and breaks them up into
//  UNICODE buffers.
//  The resulting buffers are passed to a CTDCTokenise object.
//
//  An admissible calling sequence on this object is:
//   * Exactly 1 call to Create()
//   * 0 or more calls to AddByteBuffer() with a non-zero-sized buffer
//   * Exactly 1 call to AddByteBuffer() with a zero-sized buffer
//
//  Calls to query the characteristics of the parsed data are allowed
//  after the call to Create(), but are only meaningful after a
//  reasonable amount of data has been collected.
//
//
//  Caveats:
//  ~~~~~~~
//  The class characterises the input stream as ASCII/UNICODE/COMPOSITE
//  based on the buffer passed in the initial call to AddByteBuffer().
//  If this buffer is too small, the class may make an incorrect
//  characterisation.
//
//------------------------------------------------------------------------

class CTDCUnify
{
public:
    CTDCUnify();
    ~CTDCUnify();
    HRESULT Create(UINT nCodePage, IMultiLanguage *pML);
    HRESULT ConvertByteBuffer(BYTE *pBytes, DWORD dwSize);
    HRESULT InitTokenizer(CTDCFieldSink *pFieldSink,
                          WCHAR wchDelimField,
                          WCHAR wchDelimRow,
                          WCHAR wchQuote,
                          WCHAR wchEscape);
    HRESULT AddWcharBuffer(BOOL fAtEnd);
    int IsUnicode(BYTE * pBytes, DWORD dwSize);
    enum ALLOWDOMAINLIST
    {
        ALLOW_DOMAINLIST_YES,
        ALLOW_DOMAINLIST_NO,
        ALLOW_DOMAINLIST_DONTKNOW
    };

    ALLOWDOMAINLIST CheckForAllowDomainList();
    HRESULT MatchAllowDomainList(LPCWSTR pwzURL);

private:
    CTDCFieldSink *m_pFieldSink;
    WCHAR m_wchDelimField;
    WCHAR m_wchDelimRow;
    WCHAR m_wchQuote;
    WCHAR m_wchEscape;
    WCHAR m_ucParsed;

    boolean m_fEscapeActive;
    boolean m_fQuoteActive;
    boolean m_fIgnoreNextLF;
    boolean m_fIgnoreNextCR;
    boolean m_fIgnoreNextWhiteSpace;
    boolean m_fFoldCRLF;
    boolean m_fFoldWhiteSpace;

    UINT            m_nCodePage;
    UINT            m_nUnicode;
    boolean         m_fDataMarkedUnicode;
    boolean         m_fDataIsUnicode;
    boolean         m_fCanConvertToUnicode;
    DWORD           m_dwBytesProcessed;
    DWORD           m_dwConvertMode;

    BYTE            *m_psByteBuf;
    ULONG           m_ucByteBufSize;
    ULONG           m_ucByteBufCount;

    WCHAR           *m_psWcharBuf;
    ULONG           m_ucWcharBufSize;
    ULONG           m_ucWcharBufCount;

    IMultiLanguage *m_pML;
};


WCH.CPP

//+-----------------------------------------------------------------------
//
//  Wide Character Routines
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       wch.cpp
//
//  Contents:   Implementation of wide characters routines.
//              These routines are being used to avoid dragging in
//              the initialization chunk of the C run-time library
//              that would be required by library routines such as
//              wcsicmp() etc.
//
//------------------------------------------------------------------------

#include "stdafx.h"
#include "shlwapi.h"            // Wrapper routines for non-Win95 calls

#pragma comment(lib, "shlwapi.lib")

//------------------------------------------------------------------------
//
//  Function:  wch_icmp()
//
//  Synopsis:  Perform a case-insensitive comparison of two strings.
//
//  Arguments: pwch1     First string to compare
//             pwch2     Second string to compare
//             Treats NULLs as empty strings.
//
//  Returns:   0 if the strings are lexically equal (allowing for
//               case insensitivity)
//             -1 if pwch1 lexically less than pwch2
//             +1 if pwch1 lexically greater than pwch2
//
//------------------------------------------------------------------------

int wch_icmp(LPWCH pwch1, LPWCH pwch2)
{
    USES_CONVERSION;

    if (pwch1 == NULL)
        pwch1 = L"";
    if (pwch2 == NULL)
        pwch2 = L"";

    return StrCmpIW(pwch1, pwch2);
}

//------------------------------------------------------------------------
//
//  Function:  wch_incmp()
//
//  Synopsis:  Perform a case-insensitive comparison of two strings,
//             up to a specified maximum number of characters.
//
//  Arguments: pwch1     First string to compare
//             pwch2     Second string to compare
//             dwMaxLen  Maximum number of characters to compare.
//
//             Treats NULLs as empty strings.
//
//  Returns:   0 if the strings are lexically equal (allowing for
//               case insensitivity)
//             -1 if pwch1 lexically less than pwch2
//             +1 if pwch1 lexically greater than pwch2
//
//------------------------------------------------------------------------

int wch_incmp(LPWCH pwch1, LPWCH pwch2, DWORD dwMaxLen)
{

    if (pwch1 == NULL)
        pwch1 = L"";
    if (pwch2 == NULL)
        pwch2 = L"";

    return StrCmpNIW(pwch1, pwch2, dwMaxLen);
}

//------------------------------------------------------------------------
//
//  Function:  wch_len()
//
//  Synopsis:  Calculate the length of a string.
//             Treats NULL as an empty string.
//
//  Arguments: pwch      String to measure
//
//  Returns:   Length of given string.
//
//------------------------------------------------------------------------

int wch_len(LPWCH pwch)
{
    LPWCH   pwchOrig = pwch;

    if (pwch == NULL)
        return 0;
    while (*pwch++ != 0)
        ;
    return pwch - pwchOrig - 1;
}

//------------------------------------------------------------------------
//
//  Function:  wch_cmp()
//
//  Synopsis:  Perform a case-sensitive comparison of two strings.
//             Treats NULLs as empty strings.
//
//  Arguments: pwch1     First string to compare
//             pwch2     Second string to compare
//
//  Returns:   0 if the strings are lexically equal
//             -1 if pwch1 lexically less than pwch2
//             +1 if pwch1 lexically greater than pwch2
//
//------------------------------------------------------------------------

int wch_cmp(LPWCH pwch1, LPWCH pwch2)
{
    if (pwch1 == NULL)
        pwch1 = L"";
    if (pwch2 == NULL)
        pwch2 = L"";
    for (; *pwch1 != 0 && *pwch1 == *pwch2; pwch1++, pwch2++)
        ;
    return *pwch1 - *pwch2;
}

//------------------------------------------------------------------------
//
//  Function:  wch_ncmp()
//
//  Synopsis:  Perform a case-sensitive comparison of two strings,
//             up to a specified maximum number of characters.
//
//  Arguments: pwch1     First string to compare
//             pwch2     Second string to compare
//             dwMaxLen  Maximum number of characters to compare.
//
//             Treats NULLs as empty strings.
//
//  Returns:   0 if the strings are lexically equal (allowing for
//               case insensitivity)
//             -1 if pwch1 lexically less than pwch2
//             +1 if pwch1 lexically greater than pwch2
//
//------------------------------------------------------------------------

int wch_ncmp(LPWCH pwch1, LPWCH pwch2, DWORD dwMaxLen)
{
    int cmp;

    if (pwch1 == NULL)
        pwch1 = L"";
    if (pwch2 == NULL)
        pwch2 = L"";

    for (cmp = 0; cmp == 0 && dwMaxLen-- > 0; pwch1++, pwch2++)
        if (*pwch1 == 0)
        {
            cmp = (*pwch2 == 0) ? 0 : -1;
            break;
        }
        else
            cmp = (*pwch2 == 0) ? 1 : (*pwch2 - *pwch1);

    return cmp;
}

//------------------------------------------------------------------------
//
//  Function:  wch_cpy()
//
//  Synopsis:  Copy a wide-character null-terminated string.
//             Treats NULL source as an empty string.
//
//  Arguments: pwchDesc  Destination buffer.
//             pwchSrc   Source string.
//
//  Returns:   Nothing.
//
//------------------------------------------------------------------------

void wch_cpy(LPWCH pwchDest, LPWCH pwchSrc)
{
    if (pwchSrc == NULL)
        *pwchDest = 0;
    else
        while ((*pwchDest++ = *pwchSrc++) != 0)
            ;
}

//------------------------------------------------------------------------
//
//  Function:  wch_chr()
//
//  Synopsis:  Searches for a character in a null-terminated wide-character
//             string.
//             Treats NULL pwch as an empty string.
//
//  Arguments: pwch      Search string.
//             wch       Character to search for.copy.
//
//  Returns:   Pointer to first occurrence of 'wch' in 'pwch' if found.
//             NULL if 'wch' does not occur in 'pwch'.
//
//------------------------------------------------------------------------

LPWCH wch_chr(LPWCH pwch, WCHAR wch)
{
    if (pwch != NULL)
        for (; *pwch != 0; pwch++)
            if (*pwch == wch)
                return pwch;
    return NULL;
}

//------------------------------------------------------------------------
//
//  Function:  wch_wildcardMatch()
//
//  Synopsis:  Determines whether the given text matches the given
//             pattern, which interprets the character '*' as a match
//             for 0-or-more characters.
//             Treats NULL pwchText as an empty string.
//             Treats NULL pwchPattern as an empty string.
//
//  Arguments: pwchText         Text to match.
//             pwchPattern      Pattern to match against.
//             fCaseSensitive   Flag to indicate whether match should be
//                                case sensitive.
//
//  Returns:   TRUE if the text matches the given pattern.
//             FALSE otherwise.
//
//------------------------------------------------------------------------

// ;begin_internal
// compiler bug (VC5 with optimise?)
// ;end_internal
boolean wch_wildcardMatch(LPWCH pwchText, LPWCH pwchPattern,
                          boolean fCaseSensitive)
{
    boolean fMatched;
    LPWCH pwchStar;
    DWORD   dwPatternLen;

    if (pwchText == NULL || pwchText[0] == 0)
    {
        //  Empty/NULL text.  This matches:
        //     - Empty/NULL patterns
        //     - Patterns consisting of a string of '*'s
        //
        //  Equivalently, the text FAILS to match if there
        //  is at least one non-* character in the pattern.
        //
        fMatched = TRUE;
        if (pwchPattern != NULL)
            while (fMatched && *pwchPattern != 0)
                fMatched = *pwchPattern++ == L'*';
        goto Done;
    }
    if (pwchPattern == NULL || pwchPattern[0] == 0)
    {
        //  NULL pattern can only match empty text.
        //  Since we've already dealt with the case of empty text above,
        //  the match must fail
        //
        fMatched = FALSE;
        goto Done;
    }

    //  Find the occurrence of the first '*' in the pattern ...
    //
    pwchStar = wch_chr(pwchPattern, L'*');

    if (pwchStar == NULL)
    {
        //  No '*'s in the pattern - compute an exact match
        //
        fMatched = fCaseSensitive
            ? wch_cmp(pwchText, pwchPattern) == 0
            : wch_icmp(pwchText, pwchPattern) == 0;
        goto Done;
    }

    int (*pfnBufCmp)(LPWCH pwch1, LPWCH pwch2, DWORD dwMaxCmp);

    pfnBufCmp = fCaseSensitive ? wch_ncmp : wch_incmp;

    //  Ensure an exact match for characters preceding the first '*', if any
    //
    dwPatternLen = pwchStar - pwchPattern;
    fMatched = (*pfnBufCmp)(pwchText, pwchPattern, dwPatternLen) == 0;
    if (!fMatched)
        goto Done;
    pwchText += dwPatternLen;

    for (;;)
    {
        DWORD dwTextLen = wch_len(pwchText);

        //  Skip over leading '*'s in the pattern
        //
        _ASSERT(*pwchStar == L'*');
        while (*pwchStar == L'*')
            pwchStar++;

        pwchPattern = pwchStar;

        //  Find the next occurrence of a '*' in the pattern
        //
        if (*pwchPattern == 0)
        {
            //  This must be have been a trailing '*' in the pattern.
            //  It automatically matches what remains of the text.
            //
            fMatched = TRUE;
            goto Done;
        }
        pwchStar = wch_chr(pwchPattern, L'*');
        if (pwchStar == NULL)
        {
            //  No more '*'s - require an exact match of remaining
            //  pattern text with the end of the text.
            //
            dwPatternLen = wch_len(pwchPattern);
            fMatched = (dwTextLen >= dwPatternLen) &&
                        (*pfnBufCmp)(pwchText + dwTextLen - dwPatternLen,
                                     pwchPattern, dwPatternLen) == 0;
            goto Done;
        }

        //  Locate an exact match for the pattern-up-to-next-*
        //  within the text buffer
        //
        dwPatternLen = pwchStar - pwchPattern;
        fMatched = FALSE;
        while (dwTextLen >= dwPatternLen)
        {
            fMatched = (*pfnBufCmp)(pwchText, pwchPattern, dwPatternLen) == 0;
            if (fMatched)
                break;
             dwTextLen--;
             pwchText++;
        }
        if (!fMatched)
            goto Done;
        pwchText += dwPatternLen;
    }

Done:
    return fMatched;
}


WCH.H

//+-----------------------------------------------------------------------
//
//  Wide Character Routines
//  Copyright (C) Microsoft Corporation, 1996, 1997
//
//  File:       wch.h
//
//  Contents:   Declaration of wide characters routines.
//              These routines are being used to avoid dragging in
//              the initialisation chunk of the C run-time library
//              that would be required by library routines such as
//              wcsicmp() etc.
//
//------------------------------------------------------------------------

extern int wch_icmp(LPWCH pwch1, LPWCH pwch2);
extern int wch_incmp(LPWCH pwch1, LPWCH pwch2, DWORD dwMaxCmp);
extern int wch_cmp(LPWCH pwch1, LPWCH pwch2);
extern int wch_ncmp(LPWCH pwch1, LPWCH pwch2, DWORD dwMaxCmp);
extern int wch_len(LPWCH pwch);
extern void wch_cpy(LPWCH pwch1, LPWCH pwch2);
extern LPWCH wch_chr(LPWCH pwch, WCHAR wch);
extern boolean wch_wildcardMatch(LPWCH pwchText, LPWCH pwchPattern,
                                 boolean fCaseSensitive);

//------------------------------------------------------------------------
//
//  Function:  wch_ncpy()
//
//  Synopsis:  Perform an n-character wide-string copy.
//             Copies 'dwSize' characters from 'pwchSrc' to 'pwchDest'.
//
//  Arguments: pwchDesc  Destination buffer.
//             pwchSrc   Source string.
//             dwSize    Number of characters to copy.
//
//  Returns:   Nothing.
//
//------------------------------------------------------------------------

inline void wch_ncpy(LPWCH pwchDest, LPWCH pwchSrc, DWORD dwSize)
{
    memcpy(pwchDest, pwchSrc, dwSize * sizeof(WCHAR));
}


README.TXT

The Progress sample illustrates the use of the URL Moniker to
download data across the network.

To build the Progress sample from the command line, first run
setenv.bat to be  sure the Internet SDK include, bin, and lib directories
are included in  your environment. Type  "nmake" at the command line
to compile the  sample (using the default makefile supplied).


PROGRESS.CPP

// ===========================================================================
// File: P R O G R E S S . 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 sample demonstrates using a URL moniker to download information.
// The key routines include the implementation of IBindStatusCallback
// and the CDownload::DoDownload routine, which creates and binds to the
// URL moniker.
//
// Instructions:
//
//  To use this sample:
//   * build it using the NMAKE command. NMAKE will create PROGRESS.EXE.
//   * run PROGRESS.EXE. specify the resource to download by passing an
//     URL on the command-line. use no command-line argument to default to
//     downloading "http://www.msn.com".
//  * The program displays a dialog box containing information about the
//    download:
//     - a status message, describing the current status of the download
//     - a progress message, describing the amount of information that
//       has been downloaded.
//     - a text box, which displays chunks of the download information as
//       it arrives.
//  * Press the "GO" button to begin the download.
//
// Sample update:-
//   * New feature include Progress Bar to indicated progress of the download
// * Edit box replaces the old text box. Also the whole file can be viewed.
//     If the file exceeds 32KB then only the first 32 KB from the last data
//   pull will be displayed.  If the pull exceeds 32 KB, then only the first
//     32 KB of the last Read will be displayed.
//
///
// File updated by Jason Strayer 27-Aug-1997
// File updated by Jobi George 19-June-1996
// File updated by Ramesha Gopalakrishna 28-June-1996
// File updated by Oliver Wallace 9-July-1996
// Copyright 1995 - 1998 Microsoft Corporation.  All Rights Reserved.
// ===========================================================================
#include "urlmon.h"
#include "wininet.h"
#include "resource.h"
#include "commctrl.h"

#define EDIT_BOX_LIMIT 0x7FFF    //  The Edit box limit

// %%Classes: ----------------------------------------------------------------
class CDownload {
  public:
    CDownload(LPCWSTR szURL);
    ~CDownload();
    HRESULT DoDownload(HWND,HWND,HWND,HWND);
    LPCWSTR              m_url;

  private:
    IMoniker*            m_pmk;
    IBindCtx*            m_pbc;
    IBindStatusCallback* m_pbsc;
};

class CBindStatusCallback : public IBindStatusCallback {
  public:
    // IUnknown methods
    STDMETHODIMP    QueryInterface(REFIID riid,void ** ppv);
    STDMETHODIMP_(ULONG)    AddRef()    { return m_cRef++; }
    STDMETHODIMP_(ULONG)    Release()   { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; }

    // IBindStatusCallback methods
    STDMETHODIMP    OnStartBinding(DWORD dwReserved, IBinding* pbinding);
    STDMETHODIMP    GetPriority(LONG* pnPriority);
    STDMETHODIMP    OnLowResource(DWORD dwReserved);
    STDMETHODIMP    OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode,
                        LPCWSTR pwzStatusText);
    STDMETHODIMP    OnStopBinding(HRESULT hrResult, LPCWSTR szError);
    STDMETHODIMP    GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo);
    STDMETHODIMP    OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pfmtetc,
                        STGMEDIUM* pstgmed);
    STDMETHODIMP    OnObjectAvailable(REFIID riid, IUnknown* punk);

    // constructors/destructors
    CBindStatusCallback(HWND hwndStatus, HWND hwndProgress, HWND hwndText, HWND hwndProgressBar);
    ~CBindStatusCallback();

    inline void SetStatus(LPCWSTR szStatus)     { SetWndText(m_hwndStatus, szStatus); }
    void SetProgress(LPCWSTR szProgress)        { SetWndText(m_hwndProgress, szProgress); }
void SetProgressBar(ULONG cProgress, ULONG maxProgress)
{
// Set the range and increment of the progress bar.
SendMessage(m_hwndProgressBar, PBM_SETRANGE, 0, 100);
        SendMessage(m_hwndProgressBar, PBM_SETPOS, (WPARAM) (maxProgress ? cProgress * 100 / maxProgress : 0), 0);
}
    void SetWndText(HWND hwnd, LPCWSTR szText);

    // data members
    DWORD           m_cRef;
    IBinding*       m_pbinding;
    IStream*        m_pstm;
    HWND            m_hwndStatus;
    HWND            m_hwndProgress;
    HWND            m_hwndText;
HWNDm_hwndProgressBar;
    DWORD           m_cbOld;
};

// ===========================================================================
//                     CBindStatusCallback Implementation
// ===========================================================================

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::CBindStatusCallback
// ---------------------------------------------------------------------------
CBindStatusCallback::CBindStatusCallback(HWND hwndStatus, HWND hwndProgress, HWND hwndText, HWND hwndProgressBar)
{
    m_hwndStatus = hwndStatus;
    m_hwndProgress = hwndProgress;
    m_hwndText = hwndText;
m_hwndProgressBar = hwndProgressBar;
    m_pbinding = NULL;
    m_pstm = NULL;
    m_cRef = 1;
    m_cbOld = 0;
}  // CBindStatusCallback

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::~CBindStatusCallback
// ---------------------------------------------------------------------------
CBindStatusCallback::~CBindStatusCallback()
{
}  // ~CBindStatusCallback

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::SetWndText
// ---------------------------------------------------------------------------
 inline void
CBindStatusCallback::SetWndText(HWND hwnd,LPCWSTR szText)
{
    if (IsWindow(hwnd))
        {
        char    rgchBuf[INTERNET_MAX_PATH_LENGTH];
        WideCharToMultiByte(CP_ACP, 0, szText, -1, rgchBuf, INTERNET_MAX_PATH_LENGTH, 0, 0);
SetWindowText(hwnd, rgchBuf);
}
}  // CBindStatusCallback::SetWndText

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::QueryInterface
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::QueryInterface(REFIID riid, void** ppv)
{
    *ppv = NULL;

    if (riid==IID_IUnknown || riid==IID_IBindStatusCallback)
        {
        *ppv = this;
        AddRef();
        return S_OK;
        }
    return E_NOINTERFACE;
}  // CBindStatusCallback::QueryInterface

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStartBinding
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnStartBinding(DWORD dwReserved, IBinding* pbinding)
{
    if (m_pbinding != NULL)
        m_pbinding->Release();
    m_pbinding = pbinding;
    if (m_pbinding != NULL)
        {
        m_pbinding->AddRef();
        SetStatus(L"Status: Starting to bind...");
        }
    return S_OK;
}  // CBindStatusCallback::OnStartBinding

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetPriority
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::GetPriority(LONG* pnPriority)
{
return E_NOTIMPL;
}  // CBindStatusCallback::GetPriority

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnLowResource
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnLowResource(DWORD dwReserved)
{
    return E_NOTIMPL;
}  // CBindStatusCallback::OnLowResource

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnProgress
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
    char    sz[255];

if(szStatusText!=NULL)
    WideCharToMultiByte(CP_ACP, 0, szStatusText,-1, sz, 255, 0,0);

    char    msg[100];
    WCHAR   out[200];

    wsprintf(msg,"Progress: %s %d of %d ", sz, ulProgress, (ulProgress>ulProgressMax)?ulProgress:ulProgressMax);
    MultiByteToWideChar(CP_ACP, 0, msg, -1, out, sizeof(out));

    SetProgress(out);
SetProgressBar(ulProgress, ulProgressMax);
    return(NOERROR);
}  // CBindStatusCallback::OnProgress

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStopBinding
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnStopBinding(HRESULT hrStatus, LPCWSTR pszError)
{
    if (hrStatus)
        SetStatus(L"Status: File download Failed.");

if (m_pbinding)
{
m_pbinding->Release();
m_pbinding = NULL;
}

    return S_OK;
}  // CBindStatusCallback::OnStopBinding

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetBindInfo
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindInfo)
{
if (!pbindInfo || !pbindInfo->cbSize || !pgrfBINDF)
return E_INVALIDARG;

*pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA |
BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE;

// remember incoming cbSize
ULONG cbSize = pbindInfo->cbSize;
// zero out structure
memset(pbindInfo, 0, cbSize);

// restore cbSize
pbindInfo->cbSize = cbSize;
pbindInfo->dwBindVerb = BINDVERB_GET;

return S_OK;
}  // CBindStatusCallback::GetBindInfo

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnDataAvailable
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC* pfmtetc, STGMEDIUM* pstgmed)
{
 HRESULT hr=S_OK;
 DWORD dStrlength=0;

// Get the Stream passed
    if (BSCF_FIRSTDATANOTIFICATION & grfBSCF)
    {
        if (!m_pstm && pstgmed->tymed == TYMED_ISTREAM)
    {
    m_pstm = pstgmed->pstm;
            if (m_pstm)
                m_pstm->AddRef();
    }
    }

    // If there is some data to be read then go ahead and read them
    if (m_pstm && dwSize > m_cbOld)
{
        DWORD dwRead = dwSize - m_cbOld; // Minimum amount available that hasn't been read
        DWORD dwActuallyRead = 0;            // Placeholder for amount read during this pull

if (dwRead > 0)
do
{
        char * pNewstr = new char[dwRead + 1];
if (pNewstr==NULL)
return S_FALSE;
hr=m_pstm->Read(pNewstr,dwRead,&dwActuallyRead);
    pNewstr[dwActuallyRead] = 0;
// If we really read something then lets add it to the Edit box
if (dwActuallyRead>0)
{
dStrlength=GetWindowTextLength(m_hwndText);

char *pOldstr=NULL;
if (dwActuallyRead>=EDIT_BOX_LIMIT)  // Read exceeded Edit box size
{
pOldstr= new char[EDIT_BOX_LIMIT];
if (pOldstr==NULL)
{
delete[] pNewstr;
return S_FALSE;
}
lstrcpyn(pOldstr,pNewstr,EDIT_BOX_LIMIT-1);
}
                else if (dStrlength + dwActuallyRead >= EDIT_BOX_LIMIT)
                {
                    // This pull exceeded the edit box size.  Only copy the last read.
                    pOldstr = new char[dwActuallyRead+1];
                    if (pOldstr==NULL)
                    {
                        delete[] pNewstr;
                        return S_FALSE;
                    }
                    lstrcpyn(pOldstr,pNewstr,dwActuallyRead);
                }
  else
{
pOldstr= new char[dStrlength+dwActuallyRead+1];
if (pOldstr==NULL)
{
delete[] pNewstr;
return S_FALSE;
 }
GetWindowText(m_hwndText,pOldstr,(dStrlength+dwActuallyRead+1));
lstrcat(pOldstr,pNewstr);
}
        SetWindowText(m_hwndText,pOldstr);
m_cbOld += dwActuallyRead;
delete[] pOldstr;
}
delete[] pNewstr;

                } while (!(hr == E_PENDING || hr == S_FALSE) && SUCCEEDED(hr));
}//     if (m_pstm && dwSize > m_cbOld)

if (BSCF_LASTDATANOTIFICATION & grfBSCF)
{
        if (m_pstm)
            m_pstm->Release();
hr=S_OK;  // If it was the last data then we should return S_OK as we just finished reading everything
        SetStatus(L"Status: File downloaded.");
}

    return hr;
}  // CBindStatusCallback::OnDataAvailable

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnObjectAvailable
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnObjectAvailable(REFIID riid, IUnknown* punk)
{
    return E_NOTIMPL;
}  // CBindStatusCallback::OnObjectAvailable

// ===========================================================================
//                           CDownload Implementation
// ===========================================================================

// ---------------------------------------------------------------------------
// %%Function: CDownload::CDownload
// ---------------------------------------------------------------------------
CDownload::CDownload(LPCWSTR szURL)
{
    m_url = szURL;
    m_pmk = 0;
    m_pbc = 0;
    m_pbsc = 0;
}  // CDownload

// ---------------------------------------------------------------------------
// %%Function: CDownload::~CDownload
// ---------------------------------------------------------------------------
CDownload::~CDownload()
{
    if (m_pmk)
        m_pmk->Release();
    if (m_pbc)
        m_pbc->Release();
    if (m_pbsc)
        m_pbsc->Release();
}  // ~CDownload

// ---------------------------------------------------------------------------
// %%Function: CDownload::DoDownload
// ---------------------------------------------------------------------------
 HRESULT
CDownload::DoDownload(HWND hwndStatus, HWND hwndProgress, HWND hwndText, HWND hwndProgressBar)
{
    IStream* pstm = NULL;
    HRESULT hr;

    hr = CreateURLMoniker(NULL, m_url, &m_pmk);
    if (FAILED(hr))
        goto LErrExit;

    m_pbsc = new CBindStatusCallback(hwndStatus, hwndProgress, hwndText, hwndProgressBar);
    if (m_pbsc == NULL)
        {
        hr = E_OUTOFMEMORY;
        goto LErrExit;
        }

    hr = CreateBindCtx(0, &m_pbc);
    if (FAILED(hr))
        goto LErrExit;

    hr = RegisterBindStatusCallback(m_pbc,
            m_pbsc,
            0,
            0L);
    if (FAILED(hr))
        goto LErrExit;

    hr = m_pmk->BindToStorage(m_pbc, 0, IID_IStream, (void**)&pstm);
    if (FAILED(hr))
        goto LErrExit;

    return hr;

LErrExit:
    if (m_pbc != NULL)
        {
        m_pbc->Release();
        m_pbc = NULL;
        }
    if (m_pbsc != NULL)
        {
        m_pbsc->Release();
        m_pbsc = NULL;
        }
    if (m_pmk != NULL)
        {
        m_pmk->Release();
        m_pmk = NULL;
        }
if (pstm)
{
pstm->Release();
pstm = NULL;
}
    return hr;
}  // CDownload::DoDownload

// ===========================================================================
//                  User Interface and Initialization Routines
// ===========================================================================

// ---------------------------------------------------------------------------
// %%Function: DialogProc
// ---------------------------------------------------------------------------
 BOOL CALLBACK
DialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static CDownload* pcdl = NULL;

    switch(message)
        {
        case WM_INITDIALOG:
            EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
            EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), TRUE);
            pcdl = (CDownload*)(LPVOID)(lParam);
            break;

        case WM_COMMAND:
            {
            switch (LOWORD(wParam))
                {
                case IDOK:
                    {
                    char rgchBuf[INTERNET_MAX_PATH_LENGTH];
                    HWND hwndStatus = GetDlgItem(hwndDlg, IDC_DISPLAY);
                    HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESS);
                    HWND hwndText = GetDlgItem(hwndDlg, IDC_EDIT);
HWND hwndProgressBar = GetDlgItem(hwndDlg, IDC_PROGRESSBAR);

                    EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
                    EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE);

                    WideCharToMultiByte(CP_ACP, 0, pcdl->m_url, -1, rgchBuf, MAX_PATH, 0, 0);
            SetWindowText(hwndDlg, rgchBuf);

                    SetWindowText(hwndStatus, TEXT("Status: Initiating Bind..."));
                    pcdl->DoDownload(hwndStatus, hwndProgress, hwndText, hwndProgressBar);
                    EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), TRUE);
                    break;
                    }

                case IDCANCEL:
                    EndDialog(hwndDlg,0);
                    return 1;
                }
            break;
            }
        }

    return 0;
}  // DialogProc

// ---------------------------------------------------------------------------
// %%Function: WinMain
// ---------------------------------------------------------------------------
 int WINAPI
WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR szCmdLine, int nCmdShow)
{
    static WCHAR    szDefaultURL[] = L"http://www.msn.com";
    WCHAR           rgwchCmdLine[MAX_PATH];
    HRESULT         hr;

InitCommonControls();

    hr = CoInitialize(NULL);
    if (FAILED(hr))
        return hr;

    MultiByteToWideChar(CP_ACP, 0, szCmdLine, -1, rgwchCmdLine, MAX_PATH);

    CDownload download(rgwchCmdLine[0] ? rgwchCmdLine : szDefaultURL);

    int x = DialogBoxParam(hinst, MAKEINTRESOURCE(IDD_DIALOG1), HWND_DESKTOP,
        DialogProc, (LPARAM)(LPVOID)&download);

    CoUninitialize();

    return 0;
}  // WinMain

// EOF =======================================================================


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Progress.rc
//
#define IDD_DIALOG1                     101
#define IDC_DISPLAY                     1001
#define IDC_PROGRESS                    1002
#define IDC_EDIT                        1011
#define IDC_PROGRESSBAR                 1013

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1014
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


ABOUTBOX.CPP

//=------------------------------------------------------------------------=
// AboutBox.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// CAboutBox implementation
//

#include "stdafx.h"
#include "resource.h"
#include "aboutbox.h"
#include <dos.h>
#include <direct.h>

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutBox dialog

BEGIN_MESSAGE_MAP(CAboutBox, CDialog)
//{{AFX_MSG_MAP(CAboutBox)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CAboutBox::CAboutBox(CWnd* pParent /*=NULL*/)
: CDialog(CAboutBox::IDD, pParent)
{
//{{AFX_DATA_INIT(CAboutBox)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}

void CAboutBox::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutBox)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}

/////////////////////////////////////////////////////////////////////////////
// CAboutBox message handlers

BOOL CAboutBox::OnInitDialog()
{
CDialog::OnInitDialog();

// initialize the big icon control
m_icon.SubclassDlgItem(IDC_BIGICON, this);
m_icon.SizeToContent();

#ifndef _MAC
// fill available memory
CString str, strFmt;
strFmt.LoadString(IDS_PHYSICAL_MEM);

MEMORYSTATUS MemStat;
MemStat.dwLength = sizeof(MEMORYSTATUS);
GlobalMemoryStatus(&MemStat);
wsprintf(str.GetBuffer(80), strFmt, MemStat.dwTotalPhys / 1024L);
str.ReleaseBuffer();
SetDlgItemText(IDC_PHYSICAL_MEM, str);

// fill disk free information
struct _diskfree_t diskfree;
if (_getdiskfree(_getdrive(), &diskfree) == 0)
{
strFmt.LoadString(IDS_DISK_SPACE);
wsprintf(str.GetBuffer(80), strFmt,
(DWORD)diskfree.avail_clusters *
(DWORD)diskfree.sectors_per_cluster *
(DWORD)diskfree.bytes_per_sector / (DWORD)1024L);
str.ReleaseBuffer();
}
else
str.LoadString(IDS_DISK_SPACE_UNAVAIL);
SetDlgItemText(IDC_DISK_SPACE, str);
#endif //_MAC

return TRUE;  // return TRUE  unless you set the focus to a control
}

/////////////////////////////////////////////////////////////////////////////
// CSplashWnd dialog

BEGIN_MESSAGE_MAP(CSplashWnd, CDialog)
//{{AFX_MSG_MAP(CSplashWnd)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CSplashWnd::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSplashWnd)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}

BOOL CSplashWnd::Create(CWnd* pParent)
{
//{{AFX_DATA_INIT(CSplashWnd)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT

if (!CDialog::Create(CSplashWnd::IDD, pParent))
{
TRACE0("Warning: creation of CSplashWnd dialog failed\n");
return FALSE;
}

return TRUE;
}

BOOL CSplashWnd::OnInitDialog()
{
CDialog::OnInitDialog();
CenterWindow();

// initialize the big icon control
m_icon.SubclassDlgItem(IDC_BIGICON, this);
m_icon.SizeToContent();

return TRUE;  // return TRUE  unless you set the focus to a control
}

/////////////////////////////////////////////////////////////////////////////
// CSplashWnd message handlers

/////////////////////////////////////////////////////////////////////////////
// CBigIcon

BEGIN_MESSAGE_MAP(CBigIcon, CButton)
//{{AFX_MSG_MAP(CBigIcon)
ON_WM_DRAWITEM()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CBigIcon message handlers

#define CY_SHADOW   4
#define CX_SHADOW   4

void CBigIcon::SizeToContent()
{
// get system icon size
int cxIcon = ::GetSystemMetrics(SM_CXICON);
int cyIcon = ::GetSystemMetrics(SM_CYICON);

// a big icon should be twice the size of an icon + shadows
SetWindowPos(NULL, 0, 0, cxIcon*2 + CX_SHADOW + 4, cyIcon*2 + CY_SHADOW + 4,
SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
}

void CBigIcon::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
ASSERT(pDC != NULL);

CRect rect;
GetClientRect(rect);
int cxClient = rect.Width();
int cyClient = rect.Height();

// load icon
HICON hicon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
if (hicon == NULL)
return;

// draw icon into off-screen bitmap
int cxIcon = ::GetSystemMetrics(SM_CXICON);
int cyIcon = ::GetSystemMetrics(SM_CYICON);

CBitmap bitmap;
if (!bitmap.CreateCompatibleBitmap(pDC, cxIcon, cyIcon))
return;
CDC dcMem;
if (!dcMem.CreateCompatibleDC(pDC))
return;
CBitmap* pBitmapOld = dcMem.SelectObject(&bitmap);
if (pBitmapOld == NULL)
return;

// blt the bits already on the window onto the off-screen bitmap
dcMem.StretchBlt(0, 0, cxIcon, cyIcon, pDC,
2, 2, cxClient-CX_SHADOW-4, cyClient-CY_SHADOW-4, SRCCOPY);

// draw the icon on the background
dcMem.DrawIcon(0, 0, hicon);

// draw border around icon
CPen pen;
pen.CreateStockObject(BLACK_PEN);
CPen* pPenOld = pDC->SelectObject(&pen);
pDC->Rectangle(0, 0, cxClient-CX_SHADOW, cyClient-CY_SHADOW);
if (pPenOld)
pDC->SelectObject(pPenOld);

// draw shadows around icon
CBrush br;
br.CreateStockObject(DKGRAY_BRUSH);
rect.SetRect(cxClient-CX_SHADOW, CY_SHADOW, cxClient, cyClient);
pDC->FillRect(rect, &br);
rect.SetRect(CX_SHADOW, cyClient-CY_SHADOW, cxClient, cyClient);
pDC->FillRect(rect, &br);

// draw the icon contents
pDC->StretchBlt(2, 2, cxClient-CX_SHADOW-4, cyClient-CY_SHADOW-4,
&dcMem, 0, 0, cxIcon, cyIcon, SRCCOPY);
}

BOOL CBigIcon::OnEraseBkgnd(CDC*)
{
return TRUE;    // we don't do any erasing...
}

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


ABOUTBOX.H

//=------------------------------------------------------------------------=
// AboutBox.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CAboutBox, CBigIcon & CSplashWnd classes
//

/////////////////////////////////////////////////////////////////////////////
// CBigIcon window

class CBigIcon : public CButton
{
// Attributes
public:

// Operations
public:
void SizeToContent();

// Implementation
protected:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);

//{{AFX_MSG(CBigIcon)
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////
// CAboutBox dialog

class CAboutBox : public CDialog
{
// Construction
public:
CAboutBox(CWnd* pParent = NULL);    // standard constructor

// Dialog Data
//{{AFX_DATA(CAboutBox)
enum { IDD = IDD_ABOUTBOX };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA

// Implementation
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

CBigIcon m_icon; // self-draw button

// Generated message map functions
//{{AFX_MSG(CAboutBox)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////
// CSplashWnd dialog

class CSplashWnd : public CDialog
{
// Construction
public:
BOOL Create(CWnd* pParent);

// Dialog Data
//{{AFX_DATA(CSplashWnd)
enum { IDD = IDD_SPLASH };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA

// Implementation
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

CBigIcon m_icon; // self-draw button
CFont m_font;   // light version of dialog font

// Generated message map functions
//{{AFX_MSG(CSplashWnd)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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


DATAPATH.H

// WARNING: Datapath.h is a dead file, you should not use it in your code.
// we are including it hear and stubbing it to get around a problem with
// the VC++ 4.2 MFC include files.  You should use urlmon.h and oaidl.h in
// your code rather than datapath.h

#include <urlmon.h>
#include <oaidl.h>


DOWNLOAD.CPP

// ===========================================================================
// File: DOWNLOAD.Cpp
// Defines the class behaviour for CBindStatusCallback.

#include "stdafx.h"
#include "Mainfrm.h"
#include "download.h"
#include "winnls.h"
// #include "Util.H"

// ===========================================================================
//                     CBindStatusCallback Implementation
// ===========================================================================

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::CBindStatusCallback
// ---------------------------------------------------------------------------
CBindStatusCallback::CBindStatusCallback(CMainFrame * pMDIframewnd)
{
    m_pbinding = NULL;
    m_pstm = NULL;
    m_cRef = 1;
    m_cbOld = 0;
m_pMDIframwnd=pMDIframewnd;
}  // CBindStatusCallback

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::~CBindStatusCallback
// ---------------------------------------------------------------------------
CBindStatusCallback::~CBindStatusCallback()
{
    if (m_pstm)
        m_pstm->Release();
    if (m_pbinding)
        m_pbinding->Release();
}  // ~CBindStatusCallback

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::QueryInterface
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::QueryInterface(REFIID riid, void** ppv)
{
    *ppv = NULL;

    if (riid==IID_IUnknown || riid==IID_IBindStatusCallback)
        {
        *ppv = this;
        AddRef();
        return S_OK;
        }
    return E_NOINTERFACE;
}  // CBindStatusCallback::QueryInterface

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStartBinding
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnStartBinding(DWORD grfBSCOption, IBinding* pbinding)
{
m_pMDIframwnd->MyShowPane("Starting to Download File ");
return(NOERROR);
}  // CBindStatusCallback::OnStartBinding

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetPriority
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::GetPriority(LONG* pnPriority)
{
 return(NOERROR);
}  // CBindStatusCallback::GetPriority

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnLowResource
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnLowResource(DWORD dwReserved)
{
  return(NOERROR);
}  // CBindStatusCallback::OnLowResource

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStopBinding
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnStopBinding(HRESULT hrStatus, LPCWSTR pszError)
{
if (hrStatus)
m_pMDIframwnd->MyShowPane("File download Failed.");
else
m_pMDIframwnd->MyShowPane("File Downloaded");
return(NOERROR);
}  // CBindStatusCallback::OnStopBinding

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetBindInfo
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindInfo)
{
 return (NOERROR);
}  // CBindStatusCallback::GetBindInfo

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnDataAvailable
// This function is called whenever data starts arriving. When the file download is
// complete then the BSCF_LASTDATANOTIFICATION comes and you can get the local cached
// File Name.
// ---------------------------------------------------------------------------
 STDMETHODIMP
 CBindStatusCallback::OnDataAvailable
 (
 DWORD grfBSCF,
 DWORD dwSize,
 FORMATETC* pfmtetc,
 STGMEDIUM* pstgmed
 )
 {
  return(NOERROR);
 }  // CBindStatusCallback::OnDataAvailable

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnObjectAvailable
// ---------------------------------------------------------------------------
 STDMETHODIMP
CBindStatusCallback::OnObjectAvailable(REFIID riid, IUnknown* punk)
{
 return(NOERROR);
}  // CBindStatusCallback::OnObjectAvailable

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnProgress
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnProgress
(
     ULONG ulProgress,
     ULONG ulProgressMax,
     ULONG ulStatusCode,
     LPCWSTR pwzStatusText
)
{
    char    msg[100];
    wsprintf(msg,"Downloaded : %d bytes of %d ", ulProgress, (ulProgress>ulProgressMax)?ulProgress:ulProgressMax);
m_pMDIframwnd->MyShowPane(msg);

 return(NOERROR);
}// CBindStatusCallback::OnProgress


DOWNLOAD.H

//=------------------------------------------------------------------------=
// Download.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CBindStatusCallback class
//

#ifndef _DOWNLOAD_H_
#define _DOWNLOAD_H_

#include <urlmon.h>

class CBindStatusCallback : public IBindStatusCallback
{

  public:
    // IUnknown methods
    STDMETHODIMP    QueryInterface(REFIID riid,void ** ppv);
    STDMETHODIMP_(ULONG)    AddRef()    { return m_cRef++; }
    STDMETHODIMP_(ULONG)    Release()   { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; }

    // IBindStatusCallback methods
    STDMETHODIMP    OnStartBinding(DWORD grfBSCOption, IBinding* pbinding);
    STDMETHODIMP    GetPriority(LONG* pnPriority);
    STDMETHODIMP    OnLowResource(DWORD dwReserved);
STDMETHOD(OnProgress)(
            /* [in] */ ULONG ulProgress,
            /* [in] */ ULONG ulProgressMax,
            /* [in] */ ULONG ulStatusCode,
            /* [in] */ LPCWSTR szStatusText);
    STDMETHODIMP    OnStopBinding(HRESULT hrResult, LPCWSTR szError);
    STDMETHODIMP    GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo);
    STDMETHODIMP    OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pfmtetc,
                        STGMEDIUM* pstgmed);
    STDMETHODIMP    OnObjectAvailable(REFIID riid, IUnknown* punk);

    // constructors/destructors
    CBindStatusCallback(CMainFrame* pMDIframewnd);
    ~CBindStatusCallback();

    // data members
 DWORD           m_cRef;
    IBinding*       m_pbinding;
    IStream*        m_pstm;
    DWORD           m_cbOld;
CMainFrame*m_pMDIframwnd;

};

#endif // _DOWNLOAD_H_


FILEOPEN.CPP

//=------------------------------------------------------------------------=
// FileOpen.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// CModFileDialog implementation
//

#include "stdafx.h"
#include "resource.h"
#include "Fileopen.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CModFileDialog

IMPLEMENT_DYNAMIC(CModFileDialog, CFileDialog)

CModFileDialog::CModFileDialog(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName,
DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) :
CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd)
{
m_urlflag=FALSE;
}

BEGIN_MESSAGE_MAP(CModFileDialog, CFileDialog)
//{{AFX_MSG_MAP(CModFileDialog)
ON_BN_CLICKED(url, Onurl)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// OnUrl
// This function enables and disables the various controls on the FileOpen
// Dialog box depending on the type of file to be opened.

void CModFileDialog::Onurl()
{
m_urlflag=!m_urlflag;

// URL FILE
if (m_urlflag)
{
SetDlgItemText(stc3,"URL");
SetDlgItemText(edt1,"http://www.microsoft.com/default.htm");
GetDlgItem(lst1)->EnableWindow(FALSE);
GetDlgItem(lst2)->EnableWindow(FALSE);
GetDlgItem(cmb1)->EnableWindow(FALSE);
GetDlgItem(cmb2)->EnableWindow(FALSE);
GetDlgItem(psh14)->EnableWindow(FALSE);
}
// Ordinary File
else
{
SetDlgItemText(stc3,"FileName");
SetDlgItemText(edt1,"*.*");
GetDlgItem(lst1)->EnableWindow(TRUE);
GetDlgItem(lst2)->EnableWindow(TRUE);
GetDlgItem(cmb1)->EnableWindow(TRUE);
GetDlgItem(cmb2)->EnableWindow(TRUE);
GetDlgItem(psh14)->EnableWindow(TRUE);
}
}


FILEOPEN.H

//=------------------------------------------------------------------------=
// FileOpen.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CModFileDialog class
//

/////////////////////////////////////////////////////////////////////////////
// CModFileDialog dialog

class CModFileDialog : public CFileDialog
{
DECLARE_DYNAMIC(CModFileDialog)

public:
CModFileDialog(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd* pParentWnd = NULL);

protected:
//{{AFX_MSG(CModFileDialog)
afx_msg void Onurl();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

public:
BOOL m_urlflag;
};


IPFRAME.CPP

//=------------------------------------------------------------------------=
// IPFrame.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// implementation of the CInPlaceFrame class
//

#include "stdafx.h"
#include "superpad.h"
#include "ipframe.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CInPlaceFrame

IMPLEMENT_DYNCREATE(CInPlaceFrame, COleIPFrameWnd)

BEGIN_MESSAGE_MAP(CInPlaceFrame, COleIPFrameWnd)
//{{AFX_MSG_MAP(CInPlaceFrame)
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// arrays of IDs used to initialize control bars

// toolbar buttons - IDs are command buttons
static UINT BASED_CODE buttons[] =
{
// same order as in the bitmap 'toolbar.bmp'
ID_EDIT_CUT,
ID_EDIT_COPY,
ID_EDIT_PASTE,
ID_SEPARATOR,
ID_APP_ABOUT,
};

/////////////////////////////////////////////////////////////////////////////
// CInPlaceFrame construction/destruction

CInPlaceFrame::CInPlaceFrame()
{
}

CInPlaceFrame::~CInPlaceFrame()
{
}

int CInPlaceFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleIPFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;

if (!m_wndResizeBar.Create(this))
{
TRACE0("Failed to create resize bar\n");
return -1;      // fail to create
}
return 0;
}

// OnCreateControlBars is called by the framework to create control
//  bars on the client applications windows.
BOOL CInPlaceFrame::OnCreateControlBars(CWnd* pWndFrame, CWnd* /*pWndDoc*/)
{
// create toolbar on client's frame window
if (!m_wndToolBar.Create(pWndFrame) ||
!m_wndToolBar.LoadBitmap(IDR_TEXTTYPE_INPLACE) ||
!m_wndToolBar.SetButtons(buttons,
  sizeof(buttons)/sizeof(UINT)))
{
TRACE0("Failed to create toolbar\n");
return FALSE;
}

// set owner to this window, so messages are delivered to correct app
m_wndToolBar.SetOwner(this);
return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CInPlaceFrame diagnostics

#ifdef _DEBUG
void CInPlaceFrame::AssertValid() const
{
COleIPFrameWnd::AssertValid();
}

void CInPlaceFrame::Dump(CDumpContext& dc) const
{
COleIPFrameWnd::Dump(dc);
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CInPlaceFrame commands


IPFRAME.H

//=------------------------------------------------------------------------=
// IPFrame.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CInPlaceFrame class
//

class CInPlaceFrame : public COleIPFrameWnd
{
DECLARE_DYNCREATE(CInPlaceFrame)
public:
CInPlaceFrame();

// Attributes
public:

// Operations
public:

// Implementation
public:
virtual ~CInPlaceFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
BOOL OnCreateControlBars(CWnd* pWndFrame, CWnd* pWndDoc);

protected:  // control bar embedded members
CToolBar    m_wndToolBar;
COleResizeBar   m_wndResizeBar;

// Generated message map functions
protected:
//{{AFX_MSG(CInPlaceFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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


LINKITEM.CPP

//=------------------------------------------------------------------------=
// LinkItem.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// implementation of the CPadLinkItem class
//

#include "stdafx.h"
#include "padview.h"
#include "paddoc.h"
#include "paditem.h"
#include "linkitem.h"
#include <limits.h>

IMPLEMENT_DYNAMIC(CPadLinkItem, CEmbeddedItem)

CPadLinkItem::CPadLinkItem(CPadDoc* pContainerDoc, LPCTSTR pszItemName)
: CEmbeddedItem(pContainerDoc)
{
CString strItemName(pszItemName);
SetItemName(strItemName);
_stscanf(strItemName, _T("%d %d"), &m_nBeg, &m_nEnd);
}

CPadLinkItem::CPadLinkItem(CPadDoc* pContainerDoc, int nFrom, int nTo)
: CEmbeddedItem(pContainerDoc)
{
m_nBeg = nFrom;
m_nEnd = nTo;
TCHAR buf[30];
wsprintf(buf, _T("%d %d"), m_nBeg, m_nEnd);
SetItemName(buf);
}

void CPadLinkItem::OnShow()
{
CPadDoc* pDoc = (CPadDoc*)GetDocument();
ASSERT(pDoc != NULL);
ASSERT_VALID(pDoc);
ASSERT_KINDOF(CPadDoc, pDoc);
pDoc->SetSelection(m_nBeg, m_nEnd);

CEmbeddedItem::OnShow();
}

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


LINKITEM.H

//=------------------------------------------------------------------------=
// LinkItem.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CEmbeddedItem class
//

#ifndef __LINKITEM_H__
#define __LINKITEM_H__

class CPadDoc;
class CPadView;

class CPadLinkItem : public CEmbeddedItem
{
DECLARE_DYNAMIC(CPadLinkItem)

// Constructors
public:
CPadLinkItem(CPadDoc* pContainerDoc, LPCTSTR pszItemName);
CPadLinkItem(CPadDoc* pContainerDoc, int nFrom, int nTo);

protected:
virtual void OnShow();
};

#endif  // __LINKITEM_H__


MAINFRM.CPP

//=------------------------------------------------------------------------=
// MainFrm.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// implementation of the CMainFrame class
//

#include "stdafx.h"
#include "superpad.h"
#include "mainfrm.h"

IMPLEMENT_DYNCREATE(CMainFrame, CMDIFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

static UINT BASED_CODE buttons[] =
{
// same order as in the bitmap 'toolbar.bmp'
ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVE, 0,
ID_EDIT_CUT, ID_EDIT_COPY, ID_EDIT_PASTE, 0,
ID_FILE_PRINT, ID_APP_ABOUT,
};

static UINT BASED_CODE indicators[] =
{
0, ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL,
};

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.Create(this) ||
!m_wndToolBar.LoadBitmap(IDR_MAINFRAME) ||
!m_wndToolBar.SetButtons(buttons, sizeof(buttons)/sizeof(UINT)))
{
return -1;      // fail to create
}
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
return -1;      // fail to create
}
return 0;
}

/////////////////////////////////////////////////////////////////////////////
// Helpers for saving/restoring window state

static TCHAR BASED_CODE szSection[] = _T("Settings");
static TCHAR BASED_CODE szWindowPos[] = _T("WindowPos");
static TCHAR szFormat[] = _T("%u,%u,%d,%d,%d,%d,%d,%d,%d,%d");

static BOOL PASCAL NEAR ReadWindowPlacement(LPWINDOWPLACEMENT pwp)
{
CString strBuffer = AfxGetApp()->GetProfileString(szSection, szWindowPos);
if (strBuffer.IsEmpty())
return FALSE;

WINDOWPLACEMENT wp;
int nRead = _stscanf(strBuffer, szFormat,
&wp.flags, &wp.showCmd,
&wp.ptMinPosition.x, &wp.ptMinPosition.y,
&wp.ptMaxPosition.x, &wp.ptMaxPosition.y,
&wp.rcNormalPosition.left, &wp.rcNormalPosition.top,
&wp.rcNormalPosition.right, &wp.rcNormalPosition.bottom);

if (nRead != 10)
return FALSE;

wp.length = sizeof wp;
*pwp = wp;
return TRUE;
}

static void PASCAL NEAR WriteWindowPlacement(LPWINDOWPLACEMENT pwp)
// write a window placement to settings section of app's ini file
{
TCHAR szBuffer[sizeof("-32767")*8 + sizeof("65535")*2];

wsprintf(szBuffer, szFormat,
pwp->flags, pwp->showCmd,
pwp->ptMinPosition.x, pwp->ptMinPosition.y,
pwp->ptMaxPosition.x, pwp->ptMaxPosition.y,
pwp->rcNormalPosition.left, pwp->rcNormalPosition.top,
pwp->rcNormalPosition.right, pwp->rcNormalPosition.bottom);
AfxGetApp()->WriteProfileString(szSection, szWindowPos, szBuffer);
}

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

void CMainFrame::InitialShowWindow(UINT nCmdShow)
{
WINDOWPLACEMENT wp;
if (!ReadWindowPlacement(&wp))
{
ShowWindow(nCmdShow);
return;
}
if (nCmdShow != SW_SHOWNORMAL)
wp.showCmd = nCmdShow;
SetWindowPlacement(&wp);
ShowWindow(wp.showCmd);
}

void CMainFrame::OnClose()
{
// before it is destroyed, save the position of the window
WINDOWPLACEMENT wp;
wp.length = sizeof wp;
if (GetWindowPlacement(&wp))
{
wp.flags = 0;
if (IsZoomed())
wp.flags |= WPF_RESTORETOMAXIMIZED;
// and write it to the .INI file
WriteWindowPlacement(&wp);
}

CMDIFrameWnd::OnClose();
}

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

/////////////////////////////////////////////////////////////////////////////
// MyShowPane( LPCSTR text )
// This displays the progress notification on the status bar
void CMainFrame::MyShowPane( LPCSTR text )
{
    m_wndStatusBar.SetPaneText(0,text,TRUE) ;
}


MAINFRM.H

//=------------------------------------------------------------------------=
// MainFrm.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CMainFrm class
//

#ifndef __AFXEXT_H__
#include <afxext.h>         // for access to CToolBar and CStatusBar
#endif

class CMainFrame : public CMDIFrameWnd
{
DECLARE_DYNCREATE(CMainFrame)
public:
void InitialShowWindow(UINT nCmdShow);
    void MyShowPane( LPCSTR text ) ;

protected:
CStatusBar  m_wndStatusBar;
CToolBar    m_wndToolBar;

protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnClose();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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


PADDOC.CPP

//=------------------------------------------------------------------------=
// PadDoc.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// implementation of the CPadDoc class
//

#include "stdafx.h"
#include "superpad.h"
#include "paddoc.h"
#include "paditem.h"
#include "linkitem.h"

#include <wpapi.h>

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPadDoc

IMPLEMENT_DYNCREATE(CPadDoc, COleServerDoc)

BEGIN_MESSAGE_MAP(CPadDoc, COleServerDoc)
//{{AFX_MSG_MAP(CPadDoc)
ON_COMMAND(ID_VIEW_UPDATENOW, OnViewUpdatenow)
ON_COMMAND(ID_CANCEL_INPLACE, OnCancelInplace)
ON_COMMAND(ID_FILE_SAVE, OnFileSave)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPadDoc delegation to CEditView

CPadDoc::CPadDoc()
{
m_urlflag=FALSE;
}

CPadDoc::~CPadDoc()
{
if (m_urlflag!=FALSE)
DeleteFile(GetPathName());
}

void CPadDoc::DeleteContents()
{
COleServerDoc::DeleteContents();
if (m_viewList.IsEmpty())
return;
CEditView* pView = (CEditView*)m_viewList.GetHead();
ASSERT_KINDOF(CEditView, pView);
pView->DeleteContents();
}

void CPadDoc::Serialize(CArchive& ar)
{
CEditView* pView = (CEditView*)m_viewList.GetHead();
ASSERT_KINDOF(CEditView, pView);
pView->SerializeRaw(ar);
}

COleServerItem* CPadDoc::OnGetEmbeddedItem()
{
CEmbeddedItem* pItem = new CEmbeddedItem(this);
ASSERT_VALID(pItem);
return pItem;
}

COleServerItem* CPadDoc::OnGetLinkedItem(LPCTSTR lpszItemName)
{
CPadLinkItem *pItem =
(CPadLinkItem*)COleServerDoc::OnGetLinkedItem(lpszItemName);
if (pItem == NULL)
pItem = new CPadLinkItem(this, lpszItemName);

ASSERT_VALID(pItem);
return pItem;
}

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

void CPadDoc::OnViewUpdatenow()
{
UpdateAllItems(NULL);
}

// Note: both the server and the container should have a keyboard method
//  of deactivating an active in-place item.

void CPadDoc::OnCancelInplace()
{
if (IsInPlaceActive())
OnDeactivateUI(FALSE);
}

void CPadDoc::SetSelection(int nBeg, int nEnd)
{
CEditView* pView = (CEditView*)m_viewList.GetHead();
ASSERT_KINDOF(CEditView, pView);
pView->GetEditCtrl().SetSel(nBeg, nEnd);
}

void CPadDoc::OnFileSave()
{
// If the file being saved is not URL file then call standard save
if (m_urlflag==FALSE)
{
CDocument::OnFileSave();
return;
}

// Make a local copy of the file so that you can post by the same name
// as that of URL and not the temporary file name.
LONG lRet;
LPTSTR lpszFileName=m_urlfile.GetBuffer(MAX_PATH);
LPTSTR tempPath= new CHAR[MAX_PATH];
::GetTempPath(MAX_PATH,tempPath);
strcat(tempPath,lpszFileName);
CDocument::OnSaveDocument(tempPath);

// Call WebPost Wizard
lRet = WpPost(NULL,// hWin
1,// count of files
&tempPath,// file list
NULL,// lpcbSiteName
NULL,// lpszSiteName
NULL,// lpcbURL
NULL,// lpszURL
0 );// flags
DeleteFile(tempPath);  // Delete the temporary file which holds the HTM file
return;
}


PADDOC.H

//=------------------------------------------------------------------------=
// PadDoc.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CPadDoc class
//

#ifndef __PADDOC_H__
#define __PADDOC_H__

class CEmbeddedItem;

class CPadDoc : public COleServerDoc
{
DECLARE_DYNCREATE(CPadDoc)

// Constructors
public:
CPadDoc();

//Operations
public:
void SetSelection(int nBeg, int nEnd);

// Implementation
public:
virtual COleServerItem* OnGetEmbeddedItem();
virtual COleServerItem* OnGetLinkedItem(LPCTSTR lpszItemName);

protected:
virtual void Serialize(CArchive& ar);   // overridden for document i/o
virtual void DeleteContents();
virtual ~CPadDoc();

//{{AFX_MSG(CPadDoc)
afx_msg void OnViewUpdatenow();
afx_msg void OnCancelInplace();
afx_msg void OnFileSave();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

friend class CEmbeddedItem;

public:
BOOL m_urlflag;
CString m_urlfile;
};

#endif  // __PADDOC_H__

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


PADFRAME.CPP

//=------------------------------------------------------------------------=
// PadFrame.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// implementation of the CPadFrame class
//

#include "stdafx.h"
#include "superpad.h"
#include "padframe.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPadFrame

IMPLEMENT_DYNCREATE(CPadFrame, CMDIChildWnd)

int CPadFrame::m_nDefCmdShow = SW_SHOWMAXIMIZED;
int CPadFrame::m_nDefCmdShowOld = SW_SHOWMAXIMIZED;

static TCHAR BASED_CODE szSec[] = _T("Settings");
static TCHAR BASED_CODE szShowCmd[] = _T("ShowCmd");

void CPadFrame::ActivateFrame(int nCmdShow)
{
if (nCmdShow == -1)
nCmdShow = m_nDefCmdShow;   // use our default
CMDIChildWnd::ActivateFrame(nCmdShow);
}

void CPadFrame::Initialize()
{
m_nDefCmdShow = AfxGetApp()->GetProfileInt(szSec, szShowCmd, m_nDefCmdShow);
m_nDefCmdShowOld = m_nDefCmdShow;
}

void CPadFrame::Terminate()
{
if (m_nDefCmdShow != m_nDefCmdShowOld)
{
AfxGetApp()->WriteProfileInt(szSec, szShowCmd, m_nDefCmdShow);
m_nDefCmdShowOld = m_nDefCmdShow;
}
}

BEGIN_MESSAGE_MAP(CPadFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CPadFrame)
ON_WM_SIZE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPadFrame message handlers

void CPadFrame::OnSize(UINT nType, int cx, int cy)
{
CMDIChildWnd::OnSize(nType, cx, cy);
if (!IsWindowVisible())
return;

switch (nType)
{
case SIZE_MAXIMIZED:
m_nDefCmdShow = SW_SHOWMAXIMIZED;
break;
case SIZE_RESTORED:
m_nDefCmdShow = SW_SHOWNORMAL;
break;
}
}


PADFRAME.H

//=------------------------------------------------------------------------=
// PadFrame.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CPadFrame class
//

/////////////////////////////////////////////////////////////////////////////
// CPadFrame frame

class CPadFrame : public CMDIChildWnd
{
DECLARE_DYNCREATE(CPadFrame)
protected:

// Attributes
public:

// Operations
public:
static void Initialize();
static void Terminate();

// Implementation
protected:
void ActivateFrame(int nCmdShow);

// Generated message map functions
//{{AFX_MSG(CPadFrame)
afx_msg void OnSize(UINT nType, int cx, int cy);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

static int m_nDefCmdShow;
static int m_nDefCmdShowOld;
};

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


PADITEM.CPP

//=------------------------------------------------------------------------=
// PadItem.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// implementation of the CEmbeddedItem class
//

#include "stdafx.h"
#include "padview.h"
#include "paddoc.h"
#include "paditem.h"
#include <limits.h>
#include <winnls.h>

IMPLEMENT_DYNAMIC(CEmbeddedItem, COleServerItem)

CEmbeddedItem::CEmbeddedItem(CPadDoc* pContainerDoc)
: COleServerItem(pContainerDoc, TRUE)
{
m_nBeg = 0;
m_nEnd = UINT_MAX;

// support CF_TEXT format
GetDataSource()->DelayRenderFileData(CF_TEXT);
}

BOOL CEmbeddedItem::OnRenderFileData(LPFORMATETC lpFormatEtc, CFile* pFile)
{
ASSERT(lpFormatEtc != NULL);
if (lpFormatEtc->cfFormat != CF_TEXT)
return COleServerItem::OnRenderFileData(lpFormatEtc, pFile);

BOOL bResult = FALSE;
TRY
{
// save contents of this item to the file
SaveToFile(pFile);

// CF_TEXT has NUL termination
char chZero = '\0';
pFile->Write(&chZero, sizeof(char));

bResult = TRUE; // success
}
END_TRY

return bResult;
}

CPadView* CEmbeddedItem::GetView() const
{
CDocument* pDoc = GetDocument();
ASSERT_VALID(pDoc);
POSITION pos = pDoc->GetFirstViewPosition();
if (pos == NULL)
return NULL;

CPadView* pView = (CPadView*)pDoc->GetNextView(pos);
ASSERT_VALID(pView);
ASSERT_KINDOF(CPadView, pView);
return pView;
}

void CEmbeddedItem::SaveToFile(CFile* pFile)
{
CPadView* pView = GetView();
LPCTSTR lpszText = NULL;

TRY
{
// get access to the edit buffer
lpszText = pView->LockBuffer();

// get range (make sure inside of text length)
UINT nEnd = pView->GetWindowTextLength();
UINT nBeg = min(m_nBeg, nEnd);
nEnd = min(m_nEnd, nEnd);
#ifdef _UNICODE
// copy to temp buffer, convert, then write to file
int nLen = WideCharToMultiByte(CP_ACP, 0, lpszText+nBeg, nEnd-nBeg,
NULL, 0, NULL, NULL);
char* pszTemp = new char[nLen];
WideCharToMultiByte(CP_ACP, 0, lpszText+nBeg, nEnd-nBeg,
pszTemp, nLen, NULL, NULL);
pFile->Write(pszTemp, nLen);
delete[] pszTemp;
#else
// write it out to the file
pFile->Write(lpszText+nBeg, (nEnd-nBeg) * sizeof(char));
#endif
}
END_TRY

// release access to edit buffer
if (lpszText != NULL)
pView->UnlockBuffer();
}

void CEmbeddedItem::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// save just the portion this item refers to
ar.Flush();
CFile* pFile = ar.GetFile();
SaveToFile(pFile);
}
else
{
// otherwise, read in the entire file
GetDocument()->Serialize(ar);
}
}

BOOL CEmbeddedItem::OnGetExtent(DVASPECT dwDrawAspect, CSize& rSize)
{
if (dwDrawAspect != DVASPECT_CONTENT)
return COleServerItem::OnGetExtent(dwDrawAspect, rSize);

// no drawing will happen if cliprect is NULL
CClientDC dc(NULL);
dc.IntersectClipRect(0, 0, 0, 0);
return OnDraw(&dc, rSize);
}

BOOL CEmbeddedItem::OnDraw(CDC* pDC, CSize& rSize)
{
// get view attached to the item
CPadView* pView = GetView();

// In some situations, OLE1 servers will ask for the presentation data
//  during shutdown, even though it is not necessary (since the picture
//  has not changed).  This will happen when closing a frame window
//  for example.  By this time all the views are gone and there is no
//  way to produce the metafile data, since the actual text is
//  stored by the edit control (the view).  In this case, we simply
//  fail the call.
if (pView == NULL)
return FALSE;

// edit controls have a border around them
CRect rectClient;
CRect margin;

{ // Calculate correct ClientRect
pView->GetClientRect(&rectClient);
rectClient.InflateRect(-1,-1);

if (pView->GetStyle() & WS_HSCROLL)
rectClient.bottom++;
if (pView->GetStyle() & WS_VSCROLL)
rectClient.right++;
}

{ // Calculate margins
CRect rectEdit;
pView->GetEditCtrl().GetRect(&rectEdit);

if (rectEdit.IsRectEmpty())
rectEdit.SetRect(4,4,4,4);

int HorzMargin = rectEdit.left - rectClient.left;
int VertMargin = rectEdit.top - rectClient.top;

margin.SetRect(HorzMargin,VertMargin,HorzMargin,VertMargin);

if (pView->GetStyle() & WS_HSCROLL)
margin.bottom++;
if (pView->GetStyle() & WS_VSCROLL)
margin.right++;
}

// get the font from the CEditView
CFont* pFont = pView->GetFont();
CFont* pOldFont = NULL;
if (pFont != NULL)
pOldFont = pDC->SelectObject(pFont);

// get formatting rectangle
CRect rect(rectClient);
rect.left += margin.left;
rect.top += margin.top;
rect.right -= margin.right;
rect.bottom = INT_MAX;

pDC->SetBkMode(TRANSPARENT);

// first just determine the correct extents of the text
pDC->SaveDC();
pDC->IntersectClipRect(0, 0, 0, 0); // no drawing with NULL clipping
if (pView->PrintInsideRect(pDC, rect, m_nBeg, m_nEnd) == 0)
{
TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
rect.bottom = rect.top + tm.tmHeight + tm.tmExternalLeading;
}
pDC->RestoreDC(-1);

// then, really output the text
pDC->SetWindowOrg(rect.left-margin.left,rect.top-margin.top);
pDC->SetWindowExt(margin.left + rect.Width() + margin.right,
margin.top + rect.Height() + margin.bottom);
pView->PrintInsideRect(pDC, rect, m_nBeg, m_nEnd);

// adjust for border (rect.left is already adjusted)
rect.left -= margin.left;
rect.top -= margin.top;
rect.right += margin.right;
rect.bottom += margin.bottom;

// select previous font
if (pOldFont != NULL)
pDC->SelectObject(pOldFont);

// return HIMETRIC size
rSize = rect.Size();
pDC->LPtoHIMETRIC(&rSize);
return TRUE;
}

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


PADITEM.H

//=------------------------------------------------------------------------=
// PadItem.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CEmbeddedItem class
//

#ifndef __PADITEM_H__
#define __PADITEM_H__

class CPadDoc;
class CPadView;

class CEmbeddedItem : public COleServerItem
{
DECLARE_DYNAMIC(CEmbeddedItem)

// Constructors
public:
CEmbeddedItem(CPadDoc* pContainerDoc);

// Attributes
UINT m_nBeg, m_nEnd;
CPadDoc* GetDocument() const
{ return (CPadDoc*) COleServerItem::GetDocument(); }
CPadView* GetView() const;

// Implementation
public:
virtual BOOL OnDraw(CDC* pDC, CSize& rSize);
virtual BOOL OnGetExtent(DVASPECT dwDrawAspect, CSize& rSize);

protected:
virtual BOOL OnRenderFileData(LPFORMATETC lpFormatEtc, CFile* pFile);
virtual void Serialize(CArchive& ar);   // overridden for document i/o

void SaveToFile(CFile* pFile);
};

#endif  // __PADITEM_H__


PADVIEW.CPP

//=------------------------------------------------------------------------=
// PadView.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// implementation of the CPadView class
//

#include "stdafx.h"
#include "superpad.h"
#include "padview.h"
#include "paditem.h"
#include "linkitem.h"
#include "tabstop.h"
#include <afxpriv.h>
#include <stdlib.h>

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPadView

IMPLEMENT_DYNCREATE(CPadView, CEditView)

BEGIN_MESSAGE_MAP(CPadView, CEditView)
//{{AFX_MSG_MAP(CPadView)
ON_WM_CREATE()
ON_COMMAND(ID_CHOOSE_FONT, OnChooseFont)
ON_COMMAND(ID_WORD_WRAP, OnWordWrap)
ON_UPDATE_COMMAND_UI(ID_WORD_WRAP, OnUpdateWordWrap)
ON_WM_RBUTTONDOWN()
ON_COMMAND(ID_CHOOSE_PRINT_FONT, OnChoosePrintFont)
ON_COMMAND(ID_MIRROR_DISPLAY_FONT, OnMirrorDisplayFont)
ON_UPDATE_COMMAND_UI(ID_MIRROR_DISPLAY_FONT, OnUpdateMirrorDisplayFont)
ON_UPDATE_COMMAND_UI(ID_CHOOSE_PRINT_FONT, OnUpdateChoosePrintFont)
ON_WM_SIZE()
ON_CONTROL_REFLECT(EN_CHANGE, OnEditChange)
ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
ON_COMMAND(ID_EDIT_CUT, OnEditCut)
ON_WM_TIMER()
//}}AFX_MSG_MAP
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)

#ifndef _MAC
ON_COMMAND(ID_SET_TABSTOPS, OnSetTabStops)
#endif

END_MESSAGE_MAP()

UINT CPadView::m_nDefTabStops;
UINT CPadView::m_nDefTabStopsOld;
BOOL CPadView::m_bDefWordWrap;
BOOL CPadView::m_bDefWordWrapOld;
LOGFONT NEAR CPadView::m_lfDefFont;
LOGFONT NEAR CPadView::m_lfDefFontOld;
LOGFONT NEAR CPadView::m_lfDefPrintFont;
LOGFONT NEAR CPadView::m_lfDefPrintFontOld;

/////////////////////////////////////////////////////////////////////////////
// Static initialization/termination

static TCHAR BASED_CODE szSettings[] = _T("Settings");
static TCHAR BASED_CODE szTabStops[] = _T("TabStops");
static TCHAR BASED_CODE szFont[] = _T("Font");
static TCHAR BASED_CODE szPrintFont[] = _T("PrintFont");
static TCHAR BASED_CODE szHeight[] = _T("Height");
static TCHAR BASED_CODE szWeight[] = _T("Weight");
static TCHAR BASED_CODE szItalic[] = _T("Italic");
static TCHAR BASED_CODE szUnderline[] = _T("Underline");
static TCHAR BASED_CODE szPitchAndFamily[] = _T("PitchAndFamily");
static TCHAR BASED_CODE szCharSet[] = _T("CharSet");
static TCHAR BASED_CODE szFaceName[] = _T("FaceName");
static TCHAR BASED_CODE szSystem[] = _T("System");
static TCHAR BASED_CODE szWordWrap[] = _T("WordWrap");

static void GetProfileFont(LPCTSTR szSec, LOGFONT* plf)
{
CWinApp* pApp = AfxGetApp();
plf->lfHeight = pApp->GetProfileInt(szSec, szHeight, 0);
if (plf->lfHeight != 0)
{
plf->lfWeight = pApp->GetProfileInt(szSec, szWeight, 0);
plf->lfItalic = (BYTE)pApp->GetProfileInt(szSec, szItalic, 0);
plf->lfUnderline = (BYTE)pApp->GetProfileInt(szSec, szUnderline, 0);
plf->lfPitchAndFamily = (BYTE)pApp->GetProfileInt(szSec, szPitchAndFamily, 0);
plf->lfCharSet = (BYTE)pApp->GetProfileInt(szSec, szCharSet, DEFAULT_CHARSET);
CString strFont = pApp->GetProfileString(szSec, szFaceName, szSystem);
lstrcpyn((TCHAR*)plf->lfFaceName, strFont, sizeof plf->lfFaceName);
plf->lfFaceName[sizeof plf->lfFaceName-1] = 0;
}
}

static void WriteProfileFont(LPCTSTR szSec, const LOGFONT* plf, LOGFONT* plfOld)
{
CWinApp* pApp = AfxGetApp();

if (plf->lfHeight != plfOld->lfHeight)
pApp->WriteProfileInt(szSec, szHeight, plf->lfHeight);
if (plf->lfHeight != 0)
{
if (plf->lfHeight != plfOld->lfHeight)
pApp->WriteProfileInt(szSec, szHeight, plf->lfHeight);
if (plf->lfWeight != plfOld->lfWeight)
pApp->WriteProfileInt(szSec, szWeight, plf->lfWeight);
if (plf->lfItalic != plfOld->lfItalic)
pApp->WriteProfileInt(szSec, szItalic, plf->lfItalic);
if (plf->lfUnderline != plfOld->lfUnderline)
pApp->WriteProfileInt(szSec, szUnderline, plf->lfUnderline);
if (plf->lfPitchAndFamily != plfOld->lfPitchAndFamily)
pApp->WriteProfileInt(szSec, szPitchAndFamily, plf->lfPitchAndFamily);
if (plf->lfCharSet != plfOld->lfCharSet)
pApp->WriteProfileInt(szSec, szCharSet, plf->lfCharSet);
if (_tcscmp(plf->lfFaceName, plfOld->lfFaceName) != 0)
pApp->WriteProfileString(szSec, szFaceName, (LPCTSTR)plf->lfFaceName);
}
*plfOld = *plf;
}

void CPadView::Initialize()
{
CWinApp* pApp = AfxGetApp();
m_bDefWordWrap = pApp->GetProfileInt(szSettings, szWordWrap, 0);
m_bDefWordWrapOld = m_bDefWordWrap;
m_nDefTabStops = pApp->GetProfileInt(szSettings, szTabStops, 8*4);
m_nDefTabStopsOld = m_nDefTabStops;
GetProfileFont(szFont, &m_lfDefFont);
m_lfDefFontOld = m_lfDefFont;
GetProfileFont(szPrintFont, &m_lfDefPrintFont);
m_lfDefPrintFontOld = m_lfDefPrintFont;
}

void CPadView::Terminate()
{
CWinApp* pApp = AfxGetApp();
if (m_nDefTabStops != m_nDefTabStopsOld)
pApp->WriteProfileInt(szSettings, szTabStops, m_nDefTabStops);
if (m_bDefWordWrap != m_bDefWordWrapOld)
pApp->WriteProfileInt(szSettings, szWordWrap, m_bDefWordWrap);
WriteProfileFont(szFont, &m_lfDefFont, &m_lfDefFontOld);
WriteProfileFont(szPrintFont, &m_lfDefPrintFont, &m_lfDefPrintFontOld);
}

/////////////////////////////////////////////////////////////////////////////
// CPadView construction/destruction

CPadView::CPadView()
{
#ifndef _MAC
m_nTabStops = m_nDefTabStops;
#endif

m_uTimerID = 0;
}

BOOL CPadView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CEditView::PreCreateWindow(cs))
return FALSE;

if (m_bDefWordWrap)
cs.style &= ~(WS_HSCROLL|ES_AUTOHSCROLL);

return TRUE;
}

int CPadView::OnCreate(LPCREATESTRUCT lpcs)
{
if (CEditView::OnCreate(lpcs) != 0)
return -1;
if (m_lfDefFont.lfHeight != 0)
{
m_font.CreateFontIndirect(&m_lfDefFont);
SetFont(&m_font);
}
if (m_lfDefPrintFont.lfHeight != 0)
{
m_fontPrint.CreateFontIndirect(&m_lfDefPrintFont);
SetPrinterFont(&m_fontPrint);
}
return 0;
}

/////////////////////////////////////////////////////////////////////////////
// CPadView Word Wrap support

BOOL CPadView::IsWordWrap() const
{
return (GetStyle() & ES_AUTOHSCROLL) == 0;
}

BOOL CPadView::SetWordWrap(BOOL bWordWrap)
{
bWordWrap = !!bWordWrap;    // make sure ==TRUE || ==FALSE
if (IsWordWrap() == bWordWrap)
return FALSE;

// preserve original control's state.
CFont* pFont = GetFont();
int nLen = GetBufferLength();
TCHAR* pSaveText = new TCHAR[GetBufferLength()+1];
GetWindowText(pSaveText, nLen+1);

// create new edit control with appropriate style and size.
DWORD dwStyle = dwStyleDefault & ~(ES_AUTOHSCROLL|WS_HSCROLL|WS_VISIBLE);
if (!bWordWrap)
dwStyle |= ES_AUTOHSCROLL|WS_HSCROLL;

CWnd* pParent = GetParent();
CRect rect;
GetWindowRect(rect);
pParent->ScreenToClient(rect);
CWnd* pFocus = GetFocus();

UINT nID = GetDlgCtrlID();

HWND hWnd = ::CreateWindow(_T("edit"), NULL, dwStyle,
rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
pParent->m_hWnd, (HMENU)nID, AfxGetInstanceHandle(), NULL);

if (hWnd == NULL)
{
delete[] pSaveText;
return FALSE;
}

// set the window text to nothing to make sure following set doesn't fail
SetWindowText(NULL);

// restore visual state
::SetWindowText(hWnd, pSaveText);
delete[] pSaveText;
if (pFont != NULL)
{
ASSERT(pFont->m_hObject != NULL);
::SendMessage(hWnd, WM_SETFONT, (WPARAM)pFont->m_hObject, 0);
}

// detach old window, attach new
SetDlgCtrlID(nID+1);
HWND hWndOld = Detach();
::SetWindowLong(hWndOld, GWL_WNDPROC, (LONG)*GetSuperWndProcAddr());
ASSERT(m_hWnd == NULL);
SubclassWindow(hWnd);
ASSERT(m_hWnd == hWnd);
GetParentFrame()->SendMessage(WM_RECALCPARENT);

#ifndef _MAC
UINT nTabStops = m_nTabStops;
GetEditCtrl().SetTabStops(nTabStops);
#endif

    GetClientRect(&rect);
SetWindowPos(NULL, 0, 0, 0, 0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW);
SetWindowPos(NULL, 0, 0, 0, 0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_DRAWFRAME);
SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
UpdateWindow();

// destroy old
::SetWindowPos(hWndOld, NULL, 0, 0, 0, 0,
SWP_HIDEWINDOW|SWP_NOREDRAW|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|
SWP_NOZORDER);
::DestroyWindow(hWndOld);

// restore rest of state...
GetEditCtrl().LimitText(nMaxSize);
if (pFocus == this)
SetFocus();

// notify container that doc changed
GetDocument()->UpdateAllItems(NULL);

ASSERT_VALID(this);
return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CPadView Printing support

void CPadView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
CEditView::OnBeginPrinting(pDC, pInfo);

LPCTSTR pszFileName = GetDocument()->GetPathName();
BOOL bForceSysTime = _tcschr(pszFileName, '.') == NULL;
CTime timeSys = CTime::GetCurrentTime();
CFileStatus status;
CFile::GetStatus(pszFileName, status);

if (dlgPageSetup.m_iHeaderTime != 0 || bForceSysTime)
m_timeHeader = timeSys;
else
m_timeHeader = status.m_mtime;

if (dlgPageSetup.m_iFooterTime != 0 || bForceSysTime)
m_timeFooter = timeSys;
else
m_timeFooter = status.m_mtime;

if (!pInfo->m_bPreview)
return;

CWaitCursor wait;
pInfo->m_nCurPage = 0xFFFF;
OnPrepareDC(pDC, pInfo);

UINT nIndex = LOWORD(GetEditCtrl().GetSel());
UINT nCurPage = 1;
while (nCurPage < (UINT)m_aPageStart.GetSize())
{
if (nIndex < m_aPageStart[nCurPage])
break;
nCurPage++;
}
pInfo->m_nCurPage = nCurPage;
pInfo->SetMaxPage(m_aPageStart.GetSize());
m_nPreviewPage = nCurPage;
}

void CPadView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
// get string to show as "filename" in header/footer
LPCTSTR pszFileName = GetDocument()->GetPathName();
if (pszFileName[0] == 0)
pszFileName = GetDocument()->GetTitle();

// go thru global CPageSetupDlg to format the header and footer
CString strHeader;
dlgPageSetup.FormatHeader(strHeader, m_timeHeader, pszFileName,
pInfo->m_nCurPage);
CString strFooter;
dlgPageSetup.FormatFooter(strFooter, m_timeFooter, pszFileName,
pInfo->m_nCurPage);

TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
int cyChar = tm.tmHeight;
CRect rectPage = pInfo->m_rectDraw;

// draw and exclude space for header
if (!strHeader.IsEmpty())
{
pDC->TextOut(rectPage.left, rectPage.top, strHeader);
rectPage.top += cyChar + cyChar / 4;
pDC->MoveTo(rectPage.left, rectPage.top);
pDC->LineTo(rectPage.right, rectPage.top);
rectPage.top += cyChar / 4;
}

// allow space for footer
pInfo->m_rectDraw = rectPage;
if (!strFooter.IsEmpty())
pInfo->m_rectDraw.bottom -= cyChar + cyChar/4 + cyChar/4;

// draw body text
CEditView::OnPrint(pDC, pInfo);

// draw footer
if (!strFooter.IsEmpty())
{
rectPage.bottom -= cyChar;
pDC->TextOut(rectPage.left, rectPage.bottom, strFooter);
rectPage.bottom -= cyChar / 4;
pDC->MoveTo(rectPage.left, rectPage.bottom);
pDC->LineTo(rectPage.right, rectPage.bottom);
rectPage.bottom -= cyChar / 4;
}
}

void CPadView::OnScrollTo(CDC*, CPrintInfo* pInfo, POINT)
{
UINT nPage = pInfo->m_nCurPage;
ASSERT(nPage < (UINT)m_aPageStart.GetSize());
if (nPage != m_nPreviewPage)
{
UINT nIndex = m_aPageStart[nPage];
GetEditCtrl().SetSel((int)nIndex, (int)nIndex);
}
}

/////////////////////////////////////////////////////////////////////////////
// CPadView Font Handling

void CPadView::OnChooseFont()
{
   // get current font description
   CFont* pFont = GetFont();
   LOGFONT lf;
   if (pFont != NULL)
   pFont->GetObject(sizeof(LOGFONT), &lf);
   else
   ::GetObject(GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &lf);

CFontDialog dlg(&lf, CF_SCREENFONTS|CF_INITTOLOGFONTSTRUCT);
if (dlg.DoModal() == IDOK)
{
// switch to new font.
m_font.DeleteObject();
if (m_font.CreateFontIndirect(&lf))
{
CWaitCursor wait;
SetFont(&m_font);
m_lfDefFont = lf;

if (GetPrinterFont() == NULL)
{
// notify container that content has changed
GetDocument()->UpdateAllItems(NULL);
}
}
}
}

static void ScaleLogFont(LPLOGFONT plf, const CDC& dcFrom, const CDC& dcTo)
// helper to scale log font member from one DC to another!
{
plf->lfHeight = MulDiv(plf->lfHeight,
dcTo.GetDeviceCaps(LOGPIXELSY), dcFrom.GetDeviceCaps(LOGPIXELSY));
plf->lfWidth = MulDiv(plf->lfWidth,
dcTo.GetDeviceCaps(LOGPIXELSX), dcFrom.GetDeviceCaps(LOGPIXELSX));
}

void CPadView::OnChoosePrintFont()
{
CWaitCursor wait;
CFont* pFont = GetPrinterFont();
LOGFONT lf;
LPLOGFONT plf = NULL;
if (pFont != NULL)
{
pFont->GetObject(sizeof(LOGFONT), &lf);
plf = &lf;
}

// magic to get printer dialog that would be used if we were printing!
CPrintDialog dlgPrint(FALSE);
if (!AfxGetApp()->GetPrinterDeviceDefaults(&dlgPrint.m_pd))
{
AfxMessageBox(IDP_ERR_GET_DEVICE_DEFAULTS);
return;
}
wait.Restore();
HDC hdcPrint = dlgPrint.CreatePrinterDC();
if (hdcPrint == NULL)
{
AfxMessageBox(IDP_ERR_GET_PRINTER_DC);
return;
}

CDC dcScreen;
dcScreen.Attach(::GetDC(NULL));
CDC dcPrint;
dcPrint.Attach(hdcPrint);

if (plf != NULL)
{
// need to map initial logfont to screen metrics.
::ScaleLogFont(plf, dcPrint, dcScreen);
}

// now bring up the dialog since we know the printer DC
CFontDialog dlg(plf, CF_PRINTERFONTS, &dcPrint);
if (dlg.DoModal() == IDOK)
{
// map the resulting logfont back to printer metrics.
lf = dlg.m_lf;
::ScaleLogFont(&lf, dcScreen, dcPrint);

m_fontPrint.DeleteObject();
if (m_fontPrint.CreateFontIndirect(&lf))
{
SetPrinterFont(&m_fontPrint);
m_lfDefPrintFont = lf;

// notify container that content has changed
GetDocument()->UpdateAllItems(NULL);
}
}
//NOTE: destructor will call dcPrint.DeleteDC

::ReleaseDC(NULL, dcScreen.Detach());
}

void CPadView::OnMirrorDisplayFont()
{
if (GetPrinterFont() != NULL)
{
SetPrinterFont(NULL);
m_lfDefPrintFont.lfHeight = 0;

// notify container that content changed
GetDocument()->UpdateAllItems(NULL);
}
}

void CPadView::OnUpdateChoosePrintFont(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(GetPrinterFont() != NULL);
}

void CPadView::OnUpdateMirrorDisplayFont(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(GetPrinterFont() == NULL);
}

/////////////////////////////////////////////////////////////////////////////
// CPadView Tab Stops

#ifndef _MAC // CEditView::SetTabStops is not supported on the MAC
void CPadView::OnSetTabStops()
{
CSetTabStops dlg;
dlg.m_nTabStops = m_nTabStops/4;
if (dlg.DoModal() == IDOK)
{
CWaitCursor wait;
SetTabStops(dlg.m_nTabStops*4);
m_nDefTabStops = m_nTabStops;

// notify container that content changed
GetDocument()->UpdateAllItems(NULL);
}
}
#endif

/////////////////////////////////////////////////////////////////////////////
// CPadView Word Wrap

void CPadView::OnUpdateWordWrap(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(IsWordWrap());
}

void CPadView::OnWordWrap()
{
CWaitCursor wait;
SetWordWrap(!IsWordWrap());
m_bDefWordWrap = IsWordWrap();
}

/////////////////////////////////////////////////////////////////////////////
// CPadView commands

void CPadView::OnRButtonDown(UINT, CPoint)
{
GetParentFrame()->BringWindowToTop();
}

void CPadView::OnSize(UINT nType, int cx, int cy)
{
CWaitCursor wait;
CEditView::OnSize(nType, cx, cy);

CFrameWnd* pFrameWnd = GetParentFrame();
ASSERT_VALID(pFrameWnd);

if ((pFrameWnd->GetStyle() & WS_VISIBLE) &&
pFrameWnd->IsKindOf(RUNTIME_CLASS(COleIPFrameWnd)))
{
// update the cx part of the extent to the width of the control
COleServerItem* pItem = GetDocument()->GetEmbeddedItem();

// only update if it has actually changed
if ((int)pItem->m_sizeExtent.cx != cx)
{
pItem->m_sizeExtent.cx = cx;
OnEditChange();
}
}
}

/////////////////////////////////////////////////////////////////////////////
// CPadView OLE support

void CPadView::OnEditChange()
{
CEditView::OnEditChange();

if (m_uTimerID != 0) // if outstanding timer kill it
KillTimer(m_uTimerID);
m_uTimerID = SetTimer(1, 200, NULL); //set a timer for 200 milliseconds
if (m_uTimerID == 0) // no timer available so force update now
GetDocument()->UpdateAllItems(NULL);
}

void CPadView::OnEditCopy()
{
CWaitCursor wait;

// get the current selection
UINT nFrom, nTo;
GetEditCtrl().GetSel((int&)nFrom, (int&)nTo);

// what gets copied depends on partial vs. full selection
if ((nFrom == 0 && nTo == (UINT)GetWindowTextLength()))
{
// copy entire document to the clipboard
GetDocument()->GetEmbeddedItem()->CopyToClipboard(TRUE);
}
else
{
// copy linked item to clipboard
CPadLinkItem item(GetDocument(), nFrom, nTo);
item.CopyToClipboard(TRUE);
}
}

void CPadView::OnEditCut()
{
OnEditCopy();
CEditView::OnEditCut();
}

void CPadView::OnTimer(UINT nIDEvent)
{
if (m_uTimerID != nIDEvent) // not our timer
{
CEditView::OnTimer(nIDEvent);
return;
}

KillTimer(m_uTimerID); // kill one-shot timer
m_uTimerID = 0;
GetDocument()->UpdateAllItems(NULL);
}

/////////////////////////////////////////////////////////////////////////////
// CPadView diagnostics

#ifdef _DEBUG
void CPadView::AssertValid() const
{
CEditView::AssertValid();
}

void CPadView::Dump(CDumpContext& dc) const
{
CEditView::Dump(dc);
}
#endif //_DEBUG

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


PADVIEW.H

//=------------------------------------------------------------------------=
// PadView.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CPadView class
//

#ifndef __PADVIEW_H__
#define __PADVIEW_H__

#include <afxdlgs.h>
#include <afxext.h>
#include "paddoc.h"

class CPadView : public CEditView
{
DECLARE_DYNCREATE(CPadView)

// Construction
CPadView();
BOOL PreCreateWindow(CREATESTRUCT& cs);

// Attributes
public:
// static init/term...
static void Initialize();
static void Terminate();

CPadDoc* GetDocument()
{ return (CPadDoc*)m_pDocument; }

// Operations
public:
// Word wrap...
BOOL IsWordWrap() const;
BOOL SetWordWrap(BOOL bWordWrap);

// Printing...
virtual void OnPrint(CDC* pDC, CPrintInfo *pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnScrollTo(CDC* pDC, CPrintInfo* pInfo, POINT point);

// Buffer access...
LPCTSTR LockBuffer() const
{ return CEditView::LockBuffer(); }
void UnlockBuffer() const
{ CEditView::UnlockBuffer(); }

// Implementation
public:
BOOL IsUpdatePending() {return (m_uTimerID != NULL);}

#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:
UINT m_uTimerID; // ==0 when no outstanding

static LOGFONT NEAR m_lfDefFont;
static LOGFONT NEAR m_lfDefFontOld;
CFont m_font;

static LOGFONT NEAR m_lfDefPrintFont;
static LOGFONT NEAR m_lfDefPrintFontOld;
CFont m_fontPrint;

static UINT m_nDefTabStops;
static UINT m_nDefTabStopsOld;
static BOOL m_bDefWordWrap;
static BOOL m_bDefWordWrapOld;

UINT m_nPreviewPage;
CTime m_timeHeader;
CTime m_timeFooter;

//{{AFX_MSG(CPadView)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnChooseFont();
afx_msg void OnWordWrap();
afx_msg void OnUpdateWordWrap(CCmdUI* pCmdUI);
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnChoosePrintFont();
afx_msg void OnMirrorDisplayFont();
afx_msg void OnUpdateMirrorDisplayFont(CCmdUI* pCmdUI);
afx_msg void OnUpdateChoosePrintFont(CCmdUI* pCmdUI);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnEditChange();
afx_msg void OnEditCopy();
afx_msg void OnEditCut();
afx_msg void OnTimer(UINT nIDEvent);
//}}AFX_MSG

    #ifndef _MAC
afx_msg void OnSetTabStops();
#endif

DECLARE_MESSAGE_MAP()
};

#endif  // __PADVIEW_H__

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


PAGESET.CPP

//=------------------------------------------------------------------------=
// PageSet.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// implementation of the CPageSetup class
//

#include "stdafx.h"
#include "superpad.h"
#include <malloc.h>

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPageSetupDlg dialog

IMPLEMENT_DYNAMIC(CPageSetupDlg, CDialog)

CPageSetupDlg::CPageSetupDlg(CWnd* pParent /*=NULL*/)
: CDialog(CPageSetupDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CPageSetupDlg)
m_iFooterTime = -1;
m_iHeaderTime = -1;
//}}AFX_DATA_INIT
}

void CPageSetupDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CPageSetupDlg)
DDX_Text(pDX, IDC_FOOTER, m_strFooter);
DDX_Text(pDX, IDC_HEADER, m_strHeader);
DDX_Radio(pDX, IDC_FOOTER_FILE, m_iFooterTime);
DDX_Radio(pDX, IDC_HEADER_FILE, m_iHeaderTime);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CPageSetupDlg, CDialog)
//{{AFX_MSG_MAP(CPageSetupDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPageSetup member functions

static void Replace(CString& strModify, LPCTSTR pszWhat, LPCTSTR pszWith)
// replace pszWhat with pwzWith in string strModify
{
int i;
if ((i = strModify.Find(pszWhat)) >= 0)
{
strModify = strModify.Left(i) + pszWith +
strModify.Mid(i+lstrlen(pszWhat));
}
}

void CPageSetupDlg::FormatFilePage(CString& strFormat,
LPCTSTR pszFileName, UINT nPage)
{
TCHAR sz[32];
wsprintf(sz, _T("%d"), nPage);
::Replace(strFormat, _T("&p"), sz);
if (pszFileName == NULL)
{
pszFileName = sz;
sz[0] = 0;
}
::Replace(strFormat, _T("&f"), pszFileName);
}

void CPageSetupDlg::FormatHeader(CString& strHeader, CTime& time,
LPCTSTR pszFileName, UINT nPage)
{
CString strFormat = m_strHeader;
FormatFilePage(strFormat, pszFileName, nPage);
#ifdef _UNICODE
int nLen = strFormat.GetLength();
char *pszFormat = (char*)_alloca(nLen+1);
wcstombs(pszFormat, strFormat, nLen+1);
strHeader = time.Format(pszFormat);
#else
strHeader = time.Format(strFormat);
#endif
}

void CPageSetupDlg::FormatFooter(CString& strFooter, CTime& time,
LPCTSTR pszFileName, UINT nPage)
{
CString strFormat = m_strFooter;
FormatFilePage(strFormat, pszFileName, nPage);
#ifdef _UNICODE
int nLen = strFormat.GetLength();
char *pszFormat = (char*)_alloca(nLen+1);
wcstombs(pszFormat, strFormat, nLen+1);
strFooter = time.Format(pszFormat);
#else
strFooter = time.Format(strFormat);
#endif
}

static TCHAR BASED_CODE szPageSetup[] = _T("PageSetup");
static TCHAR BASED_CODE szHeader[] = _T("Header");
static TCHAR BASED_CODE szFooter[] = _T("Footer");
static TCHAR BASED_CODE szHeaderTime[] = _T("HeaderTime");
static TCHAR BASED_CODE szFooterTime[] = _T("FooterTime");

void CPageSetupDlg::Initialize()
{
m_strHeader = AfxGetApp()->GetProfileString(szPageSetup, szHeader,
_T("File: &f"));
m_strFooter = AfxGetApp()->GetProfileString(szPageSetup, szFooter,
_T("Page: &p"));
m_iHeaderTime = AfxGetApp()->GetProfileInt(szPageSetup, szHeaderTime, 0);
m_iFooterTime = AfxGetApp()->GetProfileInt(szPageSetup, szFooterTime, 0);
m_strHeaderOld = m_strHeader;
m_strFooterOld = m_strFooter;
m_iHeaderTimeOld = m_iHeaderTime;
m_iFooterTimeOld = m_iFooterTime;
}

void CPageSetupDlg::Terminate()
{
if (m_strHeader != m_strHeaderOld)
AfxGetApp()->WriteProfileString(szPageSetup, szHeader, m_strHeader);
if (m_strFooter != m_strFooterOld)
AfxGetApp()->WriteProfileString(szPageSetup, szFooter, m_strFooter);
if (m_iHeaderTime != m_iHeaderTimeOld)
AfxGetApp()->WriteProfileInt(szPageSetup, szHeaderTime, m_iHeaderTime);
if (m_iFooterTime != m_iFooterTimeOld)
AfxGetApp()->WriteProfileInt(szPageSetup, szFooterTime, m_iFooterTime);
}

/////////////////////////////////////////////////////////////////////////////
// CPageSetupDlg message handlers


PAGESET.H

//=------------------------------------------------------------------------=
// PageSet.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CPageSetupDlg class
//

#ifndef _PAGESET_H_
#define _PAGESET_H_

/////////////////////////////////////////////////////////////////////////////
// CPageSetupDlg dialog

class CPageSetupDlg : public CDialog
{
DECLARE_DYNAMIC(CPageSetupDlg)
// Construction
public:
CPageSetupDlg(CWnd* pParent = NULL);    // standard constructor
void Initialize();
void Terminate();

// Dialog Data
//{{AFX_DATA(CPageSetupDlg)
enum { IDD = IDD_PAGE_SETUP };
CString m_strFooter;
CString m_strHeader;
int     m_iFooterTime;
int     m_iHeaderTime;
//}}AFX_DATA

CString m_strFooterOld;
CString m_strHeaderOld;
int     m_iFooterTimeOld;
int     m_iHeaderTimeOld;

// Operations
void FormatHeader(CString& strHeader, CTime& time,
LPCTSTR pszFileName, UINT nPage);
void FormatFooter(CString& strFooter, CTime& time,
LPCTSTR pszFileName, UINT nPage);

// Implementation
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

static void FormatFilePage(
CString& strFormat, LPCTSTR pszFileName, UINT nPage);

// Generated message map functions
//{{AFX_MSG(CPageSetupDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

#endif // _PAGESET_H_

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


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by urlpad.rc
//
#include <dlgs.h>
#define IDR_MAINFRAME                   1
#define IDR_TEXTTYPE                    2
#define IDR_TEXTTYPE_INPLACE            3
#define IDR_TEXTTYPE_EMBEDDED           4
#define IDP_OLE_INIT_FAILED             100
#define IDD_PAGE_SETUP                  101
#define IDC_PHYSICAL_MEM                101
#define ID_MIRROR_DISPLAY_FONT          104
#define IDD_SPLASH                      105
#define IDC_DISK_SPACE                  107
#define IDD_ABOUTBOX                    512
#define IDC_HEADER                      1002
#define IDC_FOOTER                      1003
#define ID_CHOOSE_FONT                  1004
#define IDC_EDIT_TABS                   1004
#define IDC_HEADER_FILE                 1004
#define ID_SET_TABSTOPS                 1005
#define IDC_HEADER_SYSTEM               1005
#define ID_WORD_WRAP                    1006
#define IDC_FOOTER_FILE                 1006
#define IDC_BUTTON1                     1006
#define IDC_FOOTER_SYSTEM               1007
#define IDS_DISK_SPACE_UNAVAIL          1008
#define IDC_BIGICON                     1008
#define url                             1008
#define IDS_DISK_SPACE                  1009
#define IDD_SET_TABSTOPS                1010
#define IDS_MATH_COPR_NOTPRESENT        1010
#define IDS_MATH_COPR_PRESENT           1011
#define IDS_PHYSICAL_MEM                1012
#define MYFILEOPEN                      1536
#define IDP_ERR_GET_PRINTER_DC          4000
#define IDP_ERR_GET_DEVICE_DEFAULTS     4001
#define ID_PAGE_SETUP                   32769
#define ID_CHOOSE_PRINT_FONT            32770
#define ID_VIEW_UPDATENOW               32771
#define ID_CANCEL_INPLACE               32772

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         32772
#define _APS_NEXT_CONTROL_VALUE         1010
#define _APS_NEXT_SYMED_VALUE           106
#endif
#endif


STDAFX.CPP

//=------------------------------------------------------------------------=
// StdAfx.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// pre-compiled header and types file
//

#include "stdafx.h"


STDAFX.H

//=------------------------------------------------------------------------=
// StdAfx.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// include file for standard system include files,
//  or project specific include files that are used frequently, but
//are changed infrequently

// #define VC_EXTRALEAN

#include <afxwin.h>
#include <afxext.h>
#include <afxole.h>


SUPERPAD.CPP

//=------------------------------------------------------------------------=
// SuperPad.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "superpad.h"
#include "mainfrm.h"
#include "padview.h"
#include "paddoc.h"
#include "padframe.h"
#include "ipframe.h"

#include <locale.h>

#include "download.h"
#include "urlmon.h"
#include "Fileopen.h"

BEGIN_MESSAGE_MAP(CTheApp, CWinApp)
//{{AFX_MSG_MAP(CTheApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
ON_COMMAND(ID_PAGE_SETUP, OnPageSetup)
ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
//}}AFX_MSG_MAP
// Standard file based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// Superpad global data

CTheApp NEAR theApp;

// this is the GUID for OCLIENT documents
static const GUID BASED_CODE clsid =
{ 0x00021844, 0, 0, { 0xC0, 0, 0, 0, 0, 0, 0, 0x46 } };

/////////////////////////////////////////////////////////////////////////////
// CTheApp initialization

CTheApp::CTheApp()
{
// initialize locale to system default
_tsetlocale(LC_ALL, _T(""));
}

BOOL CTheApp::InitInstance()
{
// initialized OLE 2.0 libraries
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}

// initialization that must be done before splash dialog is shown
Enable3dControls();
CPadFrame::Initialize();

// add doc template
CDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_TEXTTYPE,
RUNTIME_CLASS(CPadDoc),
RUNTIME_CLASS(CPadFrame),
RUNTIME_CLASS(CPadView));
pDocTemplate->SetServerInfo(
IDR_TEXTTYPE_EMBEDDED, IDR_TEXTTYPE_INPLACE,
RUNTIME_CLASS(CInPlaceFrame));
AddDocTemplate(pDocTemplate);

// connect the server to the document template
m_server.ConnectTemplate(clsid, pDocTemplate, FALSE);

// create main window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
int nCmdShow = m_nCmdShow;

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

// setup main window
nCmdShow = !cmdInfo.m_bRunEmbedded ? m_nCmdShow : SW_HIDE;
m_nCmdShow = SW_HIDE;
((CMainFrame*)m_pMainWnd)->InitialShowWindow(nCmdShow);

if (!cmdInfo.m_bRunEmbedded)
{
m_pMainWnd->UpdateWindow();

if (!m_pMainWnd->IsIconic() && cmdInfo.m_bShowSplash &&
m_splash.Create(m_pMainWnd))
{
m_splash.ShowWindow(SW_SHOW);
m_splash.UpdateWindow();
m_splash.SetTimer(1, 500, NULL);
}
m_dwSplashTime = ::GetCurrentTime();

// update the registry now that the splash dialog is visible
m_server.UpdateRegistry();
RegisterShellFileTypes(TRUE);
}

// do other initialization after (possibly) creating the splash window
EnableShellOpen();
m_pMainWnd->DragAcceptFiles();

LoadStdProfileSettings();
CPadView::Initialize();
dlgPageSetup.Initialize();

// register any class objects we may have (for automation & OLE)
COleTemplateServer::RegisterAll();

// open default file if command line doesn't have /Embedding
if (!cmdInfo.m_bRunEmbedded)
{
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;

m_nCmdShow = nCmdShow;
m_pMainWnd->UpdateWindow();
}

return TRUE;
}

BOOL CTheApp::OnIdle(LONG lCount)
{
// call base class idle first
BOOL bResult = CWinApp::OnIdle(lCount);

// then do our work
if (m_splash.m_hWnd != NULL)
{
if (::GetCurrentTime() - m_dwSplashTime > 2500)
{
// timeout expired, destroy the splash window
m_splash.DestroyWindow();
m_pMainWnd->UpdateWindow();

// NOTE: don't set bResult to FALSE,
//  CWinApp::OnIdle may have returned TRUE
}
else
{
// check again later...
bResult = TRUE;
}
}

return bResult;
}

BOOL CTheApp::PreTranslateMessage(MSG* pMsg)
{
BOOL bResult = CWinApp::PreTranslateMessage(pMsg);

if (m_splash.m_hWnd != NULL &&
(pMsg->message == WM_KEYDOWN ||
 pMsg->message == WM_SYSKEYDOWN ||
 pMsg->message == WM_LBUTTONDOWN ||
 pMsg->message == WM_RBUTTONDOWN ||
 pMsg->message == WM_MBUTTONDOWN ||
 pMsg->message == WM_NCLBUTTONDOWN ||
 pMsg->message == WM_NCRBUTTONDOWN ||
 pMsg->message == WM_NCMBUTTONDOWN))
{
m_splash.DestroyWindow();
m_pMainWnd->UpdateWindow();
}

return bResult;
}

int CTheApp::ExitInstance()
{
dlgPageSetup.Terminate();
CPadView::Terminate();
CPadFrame::Terminate();

return CWinApp::ExitInstance();
}

CTheApp::~CTheApp()
{
}

/////////////////////////////////////////////////////////////////////////////
// CTheApp message handlers

void CTheApp::OnAppAbout()
{
CAboutBox about;
about.DoModal();
}

CPageSetupDlg NEAR dlgPageSetup;
void CTheApp::OnPageSetup()
{
dlgPageSetup.DoModal();
}

void CTheApp::OnFileOpen()
{
// Prompt the user (with all document templates)
CString newName;
UINT retval;

retval=PromptForFileName(newName, AFX_IDS_OPENFILE,
                   OFN_HIDEREADONLY|OFN_ENABLETEMPLATE|OFN_NOVALIDATE,
   TRUE);

if (retval==0)
return; // open cancelled
else
if (retval==1)  // If this an ordinary file then just open
{
OpenDocumentFile(newName);
return;
}

CWinApp* pApp = AfxGetApp() ;
CMainFrame* pFrame = (CMainFrame*)pApp->m_pMainWnd;
CBindStatusCallback *dtfcb= new CBindStatusCallback(pFrame);
if( !dtfcb )
{
// Error to be handled
return;
}

// File to be downloaded to temporary directory
static TCHAR count = '1';
CString localfile;

LPTSTR tempPath= new CHAR[MAX_PATH];
if (tempPath)
{
::GetTempPath(MAX_PATH,tempPath);
localfile+=tempPath;
delete [] tempPath;
}

// If we couldn't allocate memory for tempPath then lets create the file locally.
// Lets go ahead with downloading of file.
// The file is downloaded to [temporary directory]urltmpX.htm [where X is the no of view]
localfile+="urltmp";
localfile+=count;
localfile+=".htm";
count++;

// Do file downlaod
 HRESULT hr = URLDownloadToFile(0,newName,localfile,0,dtfcb);

    // Open the local file.
CPadDoc *PPadDoc = (CPadDoc *)OpenDocumentFile(localfile);
if (PPadDoc)
{
PPadDoc->m_urlflag=TRUE;

CHAR seps[]   = "/\t\n";
CHAR *tokens = strtok((LPTSTR)newName.GetBuffer(_MAX_PATH), seps);
CHAR oldtoken[_MAX_PATH];
while (( tokens != NULL ))
{
lstrcpy(oldtoken,tokens);
tokens = strtok(NULL, seps);
}
PPadDoc->m_urlfile=oldtoken;
PPadDoc->SetTitle(oldtoken); // Sort of fooling but better than some cryptic temporary file name.
}
if (dtfcb)
delete dtfcb;
}

//////////////////////////////////////////////////////////////////////////////
// %%Function: CTheApp::PromptForFileName
// Puts custom Template and calls the FileOpen Dialog
// Return Type specifies whether
// 0 : IDCANCEL pressed
// 1 : Local File
// 2 : URL file
// ---------------------------------------------------------------------------

UINT
CTheApp::PromptForFileName
(
CString& fileName,//Buffer to return the filename
UINT nIDSTitle,//Title String
DWORD dwFlags,//Flags for FileOpen Dialog
BOOL bOpenFileDialog// Flag specifying whether File Open or File Save
)
{

CModFileDialog dlgFile(bOpenFileDialog);
CString title;
VERIFY(title.LoadString(nIDSTitle));
dlgFile.m_ofn.Flags |= dwFlags;
dlgFile.m_ofn.lpstrTitle = title;
dlgFile.m_ofn.lpstrFile = fileName.GetBuffer(_MAX_PATH);
dlgFile.m_ofn.lpTemplateName = MAKEINTRESOURCE(MYFILEOPEN);
    dlgFile.m_ofn.Flags &= ~OFN_EXPLORER;
dlgFile.m_ofn.hInstance=AfxGetInstanceHandle();
dlgFile.m_ofn.lpstrFilter = "*.*";
dlgFile.m_ofn.lpstrDefExt = "*.*";

UINT bRet =(dlgFile.DoModal() == IDOK) ? 1 : 0;
if ((dlgFile.m_urlflag==TRUE)&&(bRet!=0))
bRet=2;
fileName.ReleaseBuffer();
return bRet;
}


SUPERPAD.H

//=------------------------------------------------------------------------=
// SuperPad.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// main header file for the SuperPad application
//

#include "resource.h"
#include "pageset.h"
#include "aboutbox.h"

/////////////////////////////////////////////////////////////////////////////
// CTheApp

class CTheApp : public CWinApp
{
// Constructors
public:
CTheApp();
~CTheApp();
UINT PromptForFileName(CString& fileName, UINT nIDSTitle, DWORD dwFlags,BOOL bOpenFileDialog);

// Implementation
private:
virtual BOOL InitInstance();
virtual int ExitInstance();
virtual BOOL OnIdle(LONG lCount);
virtual BOOL PreTranslateMessage(MSG* pMsg);

DWORD m_dwSplashTime;
CSplashWnd m_splash;

COleTemplateServer m_server;

//{{AFX_MSG(CTheApp)
afx_msg void OnAppAbout();
afx_msg void OnPageSetup();
afx_msg void OnFileOpen();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

extern CPageSetupDlg NEAR dlgPageSetup;
extern CTheApp NEAR theApp;

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


TABSTOP.CPP

//=------------------------------------------------------------------------=
// TabStop.Cpp
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// implementation of the CTabStops class
//

#include "stdafx.h"
#include "superpad.h"
#include "tabstop.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CSetTabStops dialog

IMPLEMENT_DYNAMIC(CSetTabStops, CDialog)

CSetTabStops::CSetTabStops(CWnd* pParent /* = NULL */)
: CDialog(CSetTabStops::IDD, pParent)
{
//{{AFX_DATA_INIT(CSetTabStops)
//}}AFX_DATA_INIT
}

void CSetTabStops::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSetTabStops)
DDX_Text(pDX, IDC_EDIT_TABS, m_nTabStops);
DDV_MinMaxUInt(pDX, m_nTabStops, 1, 16);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CSetTabStops, CDialog)
//{{AFX_MSG_MAP(CSetTabStops)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSetTabStops message handlers


TABSTOP.H

//=------------------------------------------------------------------------=
// TabStop.h
//=------------------------------------------------------------------------=
// Copyright 1992-1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Definition of the CSetTabStops class
//

/////////////////////////////////////////////////////////////////////////////
// CSetTabStops dialog

class CSetTabStops : public CDialog
{
DECLARE_DYNAMIC(CSetTabStops)
// Construction
public:
CSetTabStops(CWnd* pParent = NULL); // standard constructor

// Dialog Data
//{{AFX_DATA(CSetTabStops)
enum { IDD = IDD_SET_TABSTOPS };
UINT    m_nTabStops;
//}}AFX_DATA

// Implementation
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

//{{AFX_MSG(CSetTabStops)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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


LINKVIEW.FRM

VERSION 5.00
Object = "{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}#1.1#0"; "SHDOCVW.DLL"
Begin VB.Form LinkView
   Caption         =   "Form1"
   ClientHeight    =   6480
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   7590
   LinkTopic       =   "Form1"
   ScaleHeight     =   6480
   ScaleWidth      =   7590
   StartUpPosition =   3  'Windows Default
   Begin SHDocVwCtl.WebBrowser WebBrowser1
      Height          =   3495
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   7215
      ExtentX         =   6165
      ExtentY         =   12726
      ViewMode        =   1
      Offline         =   0
      Silent          =   0
      RegisterAsBrowser=   0
      RegisterAsDropTarget=   1
      AutoArrange     =   -1  'True
      NoClientEdge    =   0   'False
      AlignLeft       =   0   'False
      ViewID          =   "{0057D0E0-3573-11CF-AE69-08002B2E1262}"
      Location        =   ""
   End
   Begin VB.TextBox editNavURL
      Height          =   375
      Left            =   1800
      TabIndex        =   1
      Top             =   5940
      Width           =   5655
   End
   Begin VB.CommandButton btnNavigate
      Caption         =   "Navigate"
      Default         =   -1  'True
      Height          =   375
      Left            =   120
      TabIndex        =   2
      Top             =   5940
      Width           =   1455
   End
   Begin VB.TextBox editID
      Height          =   375
      Left            =   3480
      TabIndex        =   10
      TabStop         =   0   'False
      Top             =   5400
      Width           =   3975
   End
   Begin VB.TextBox editElement
      Height          =   375
      Left            =   1920
      TabIndex        =   8
      TabStop         =   0   'False
      Top             =   5400
      Width           =   855
   End
   Begin VB.ListBox listLinks
      Height          =   1425
      Left            =   1800
      TabIndex        =   6
      TabStop         =   0   'False
      Top             =   3840
      Width           =   5655
   End
   Begin VB.CommandButton btnGoBack
      Caption         =   "Go Back"
      Enabled         =   0   'False
      Height          =   375
      Left            =   120
      TabIndex        =   4
      Top             =   4320
      Width           =   1455
   End
   Begin VB.CommandButton btnGoForward
      Caption         =   "Go Forward"
      Enabled         =   0   'False
      Height          =   375
      Left            =   120
      TabIndex        =   5
      Top             =   3840
      Width           =   1455
   End
   Begin VB.CommandButton btnGetLinks
      Caption         =   "Get Links"
      Height          =   375
      Left            =   120
      TabIndex        =   3
      Top             =   4950
      Width           =   1455
   End
   Begin VB.Label Label2
      Caption         =   "href:"
      Height          =   285
      Left            =   3000
      TabIndex        =   9
      Top             =   5490
      Width           =   375
   End
   Begin VB.Label Label1
      Caption         =   "onMouseOver Element:"
      Height          =   285
      Left            =   120
      TabIndex        =   7
      Top             =   5490
      Width           =   1815
   End
End
Attribute VB_Name = "LinkView"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Dim WithEvents DocEvents As HTMLDocument
Attribute DocEvents.VB_VarHelpID = -1

Private Sub Form_Load()
    WebBrowser1.navigate ("http://www.microsoft.com")
End Sub

Private Sub WebBrowser1_BeforeNavigate2(ByVal pDisp As Object, URL As Variant, Flags As Variant, TargetFrameName As Variant, PostData As Variant, Headers As Variant, Cancel As Boolean)
    Set DocEvents = Nothing
    listLinks.Clear
    editElement = ""
    editID = ""
End Sub

Private Sub btnNavigate_Click()
    WebBrowser1.navigate (editNavURL)
End Sub

Private Sub btnGoForward_Click()
    WebBrowser1.GoForward
End Sub

Private Sub btnGoBack_Click()
    WebBrowser1.GoBack
End Sub

Private Sub WebBrowser1_CommandStateChange(ByVal Command As Long, ByVal Enable As Boolean)
    Select Case Command
        Case CSC_NAVIGATEBACK
            btnGoBack.Enabled = Enable
        Case CSC_NAVIGATEFORWARD
            btnGoForward.Enabled = Enable
    End Select
End Sub

Private Sub DocEvents_onmouseover()
    ' the element may not have an href
    On Error Resume Next

    Dim curWnd As HTMLWindow2
    Dim curElement As IHTMLElement

    Set curWnd = WebBrowser1.document.parentWindow
    Set curElement = curWnd.event.srcElement

    editElement = ""
    editID = ""

    editElement = curElement.tagName
    editID = curElement.href
End Sub

Private Sub DocEvents_onmouseout()
    editElement = ""
    editID = ""
End Sub

Private Sub btnGetLinks_Click()
    On Error Resume Next

    Dim theControl As WebBrowser
    Set theControl = WebBrowser1

    Dim HTMLDoc As HTMLDocument
    Set HTMLDoc = WebBrowser1.document

    listLinks.Clear
    Call RecurseFrames(HTMLDoc)
End Sub

Private Sub RecurseFrames(HTMLDoc As HTMLDocument)
    On Error GoTo handleError

    If HTMLDoc Is Nothing Then
        ' Not an HTLM document
        Exit Sub
    End If

    Dim BODYElement As IHTMLElement
    Set BODYElement = HTMLDoc.body

    If BODYElement.tagName = "BODY" Then
        Dim ELEMENTCo As IHTMLElementCollection
        Set ELEMENTCo = HTMLDoc.links

        Dim Element As HTMLAnchorElement

        For Each Element In ELEMENTCo
            listLinks.AddItem (Element.href)
        Next
    End If

    Dim HTMLFrames As IHTMLFramesCollection2
    Set HTMLFrames = HTMLDoc.frames

    Dim HTMLWnd As HTMLWindow2

    For countFrames = 0 To HTMLFrames.length - 1
        Set HTMLWnd = HTMLFrames(countFrames)
        Call RecurseFrames(HTMLWnd.document)
    Next

    Exit Sub
handleError:
        MsgBox (Err.Description)
End Sub

Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant)
    'may be word or some other server
    On Error Resume Next

    If pDisp Is WebBrowser1.Object Then
        Set DocEvents = WebBrowser1.document
    End If
End Sub


README.TXT

========================================================================
       WAB Property Inspector Tool ("wabtool")
========================================================================

The WAB Property Inspector Tool ("wabtool") sample application demonstrates
how to program to the Internet Explorer Address Book also known as the
Windows Address Book (WAB).

The sample specifically demonstrates the following:
- Loading the WAB DLL
- Getting an instance to IWABObject and IAdrBook
- Reading the contents of a WAB file
- Reading the properties of objects in a WAB
- Modifying properties of objects in a WAB
- Displaying Details on objects in a WAB
- Deleting objects in a WAB
- Creating new objects in a WAB

When you start the wabtool application, wabtool loads the contents of your
default WAB and displays them in a dialog. To view the properties of any one
of the contacts in your WAB, select an entry in the ListView. A list of
property tags for that entry are displayed in a list box. Selecting a property
tag in the listbox displays details on that property. If there are no entries
in your default WAB, you can select some other WAB file or create a new entry
and view its properties. Wabtool will also allow you to modify
string-properties on any object displayed from the WAB.

This sample has been created with Visual C++ 5.0 and MFC 5.0. To compile this
sample, its probably easiest to create a new project in Visual C++ 5.0. To
do this, start VC++ 5.0 and open the "wabtool.dsw" file. Make sure to add the
"INetSDK\include" directory in your list of include directories. If you have
MFC correctly installed, you can now build the wabtool.exe.

List of files in this project:

wabtool.h
wabtool.cpp
wabtool.rc
wabtool.rc2
wabtool.ico
Resource.h
wabtoolDlg.h
wabtoolDlg.cpp
DlgProp.h
DlgProp.cpp
wabobject.h
wabobject.cpp
StdAfx.h
StdAfx.cpp
wabtool.dsp
wabtool.mak


DLGPROP.H

#if !defined(AFX_DLGPROP_H__ED006BC1_F340_11D0_9A82_00A0C91F9C8B__INCLUDED_)
#define AFX_DLGPROP_H__ED006BC1_F340_11D0_9A82_00A0C91F9C8B__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// DlgProp.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CDlgProp dialog

class CDlgProp : public CDialog
{
// Construction
public:
CDlgProp(CWnd* pParent = NULL);   // standard constructor

    ULONG   m_ulPropTag;
    LPTSTR  m_lpszPropVal;
    ULONG   m_cbsz;

// Dialog Data
//{{AFX_DATA(CDlgProp)
enum { IDD = IDD_DIALOG_PROP };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDlgProp)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CDlgProp)
virtual BOOL OnInitDialog();
virtual void OnOK();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_DLGPROP_H__ED006BC1_F340_11D0_9A82_00A0C91F9C8B__INCLUDED_)


DLGPROP.CPP

// DlgProp.cpp : implementation file
//

#include "stdafx.h"
#include "wabtool.h"
#include "DlgProp.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDlgProp dialog

CDlgProp::CDlgProp(CWnd* pParent /*=NULL*/)
: CDialog(CDlgProp::IDD, pParent)
{
//{{AFX_DATA_INIT(CDlgProp)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}

void CDlgProp::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDlgProp)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CDlgProp, CDialog)
//{{AFX_MSG_MAP(CDlgProp)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDlgProp message handlers

BOOL CDlgProp::OnInitDialog()
{
CDialog::OnInitDialog();

    CEdit * pEdit = (CEdit *) GetDlgItem(IDC_EDIT_TAG);
    pEdit->SetLimitText(32);
    TCHAR sz[32];
    wsprintf(sz, "%x", m_ulPropTag);
    pEdit->SetWindowText(sz);

    pEdit = (CEdit *) GetDlgItem(IDC_EDIT_VALUE);
    pEdit->SetLimitText(512);
    pEdit->SetWindowText(m_lpszPropVal);

return TRUE;  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}

ULONG GetULONGVal(CEdit * pEdit)
{
    TCHAR sz[32];
    const LPTSTR lpZero = "00000000";

    pEdit->GetWindowText(sz, sizeof(sz));

    int nLen = lstrlen(sz);
    if(nLen < 8)
    {
        // pad with zeros
        TCHAR sz1[32];
        lstrcpy(sz1, sz);
        CopyMemory(sz, lpZero, 8-nLen);
        sz[8-nLen] = '\0';
        lstrcat(sz, sz1);
    }

    CharUpper(sz);
    LPTSTR lp = sz;
    ULONG ulVal = 0;

    while(lp && *lp)
    {
        int i = 0;

        if(*lp >= 'A' && *lp <= 'F')
            i = *lp - 'A' + 10;
        else if(*lp >= '0' && *lp <= '9')
            i = *lp - '0';

        ulVal = ulVal * 16 + i;

        lp = CharNext(lp);
    }

    return ulVal;
}

void CDlgProp::OnOK()
{
    m_ulPropTag = GetULONGVal((CEdit *) GetDlgItem(IDC_EDIT_TAG));

    GetDlgItemText(IDC_EDIT_VALUE, m_lpszPropVal, m_cbsz);

CDialog::OnOK();
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by wabtool.rc
//
#define IDM_ABOUTBOX                    0x0010
#define IDD_ABOUTBOX                    100
#define IDS_ABOUTBOX                    101
#define IDD_WABTOOL_DIALOG              102
#define IDR_MAINFRAME                   128
#define IDD_DIALOG_PROP                 129
#define IDC_STATIC123                   465
#define IDC_STATIC_FILENAME             987
#define IDC_LIST                        1000
#define IDC_LIST_TAGS                   1002
#define IDC_BUTTON_NEW                  1004
#define IDC_BUTTON_DELETE               1005
#define IDC_EDIT_PROPDATA               1007
#define IDC_BUTTON_BROWSE               1008
#define IDC_BUTTON_REFRESH              1009
#define IDC_BUTTON_DETAILS              1010
#define IDC_STATIC_PROPNAME             1011
#define IDC_STATIC_PROPTYPE             1012
#define IDC_BUTTON_ADDPROP              1013
#define IDC_BUTTON_MODIFYPROP           1014
#define IDC_STATIC_PROPTAG              1015
#define IDC_STATIC_NUMENTRIES           1016
#define IDC_EDIT_VALUE                  1020
#define IDC_EDIT_TAG                    1021

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        130
#define _APS_NEXT_COMMAND_VALUE         32771
#define _APS_NEXT_CONTROL_VALUE         1022
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//wabtool.pch will be the pre-compiled header
//stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"


STDAFX.H

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#if !defined(AFX_STDAFX_H__268ADD6D_EF27_11D0_9A7E_00A0C91F9C8B__INCLUDED_)
#define AFX_STDAFX_H__268ADD6D_EF27_11D0_9A7E_00A0C91F9C8B__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#define VC_EXTRALEAN// Exclude rarely-used stuff from Windows headers

#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions
#include <afxdisp.h>        // MFC OLE automation classes
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>// MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__268ADD6D_EF27_11D0_9A7E_00A0C91F9C8B__INCLUDED_)


WABOBJECT.CPP

#include "wabobject.h"

enum {
    ieidPR_DISPLAY_NAME = 0,
    ieidPR_ENTRYID,
ieidPR_OBJECT_TYPE,
    ieidMax
};
static const SizedSPropTagArray(ieidMax, ptaEid)=
{
    ieidMax,
    {
        PR_DISPLAY_NAME,
        PR_ENTRYID,
PR_OBJECT_TYPE,
    }
};

enum {
    iemailPR_DISPLAY_NAME = 0,
    iemailPR_ENTRYID,
    iemailPR_EMAIL_ADDRESS,
    iemailPR_OBJECT_TYPE,
    iemailMax
};
static const SizedSPropTagArray(iemailMax, ptaEmail)=
{
    iemailMax,
    {
        PR_DISPLAY_NAME,
        PR_ENTRYID,
        PR_EMAIL_ADDRESS,
        PR_OBJECT_TYPE
    }
};

/*********************************************************************************/

// contructor for CWAB object
//
// pszFileName - FileName of WAB file to open
//          if no file name is specified, opens the default
//
CWAB::CWAB(CString * pszFileName)
{
    // Here we load the WAB Object and initialize it
    m_bInitialized = FALSE;
m_lpPropArray = NULL;
    m_ulcValues = 0;
    m_SB.cb = 0;
    m_SB.lpb = NULL;

    {
        TCHAR  szWABDllPath[MAX_PATH];
        DWORD  dwType = 0;
        ULONG  cbData = sizeof(szWABDllPath);
        HKEY hKey = NULL;

        *szWABDllPath = '\0';

        // First we look under the default WAB DLL path location in the
        // Registry.
        // WAB_DLL_PATH_KEY is defined in wabapi.h
        //
        if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, WAB_DLL_PATH_KEY, 0, KEY_READ, &hKey))
            RegQueryValueEx( hKey, "", NULL, &dwType, (LPBYTE) szWABDllPath, &cbData);

        if(hKey) RegCloseKey(hKey);

        // if the Registry came up blank, we do a loadlibrary on the wab32.dll
        // WAB_DLL_NAME is defined in wabapi.h
        //
        m_hinstWAB = LoadLibrary( (lstrlen(szWABDllPath)) ? szWABDllPath : WAB_DLL_NAME );
    }

    if(m_hinstWAB)
    {
        // if we loaded the dll, get the entry point
        //
        m_lpfnWABOpen = (LPWABOPEN) GetProcAddress(m_hinstWAB, "WABOpen");

        if(m_lpfnWABOpen)
        {
            HRESULT hr = E_FAIL;
            WAB_PARAM wp = {0};
            wp.cbSize = sizeof(WAB_PARAM);
            wp.szFileName = (LPTSTR) (LPCTSTR) *pszFileName;

            // if we choose not to pass in a WAB_PARAM object,
            // the default WAB file will be opened up
            //
            hr = m_lpfnWABOpen(&m_lpAdrBook,&m_lpWABObject,&wp,0);

            if(!hr)
                m_bInitialized = TRUE;
        }
    }

}

// Destructor
//
CWAB::~CWAB()
{
    if(m_SB.lpb)
        LocalFree(m_SB.lpb);

    if(m_bInitialized)
    {
        if(m_lpPropArray)
            m_lpWABObject->FreeBuffer(m_lpPropArray);

        if(m_lpAdrBook)
            m_lpAdrBook->Release();

        if(m_lpWABObject)
            m_lpWABObject->Release();

        if(m_hinstWAB)
            FreeLibrary(m_hinstWAB);
    }
}

// Opens a wab file and puts its contents into the specified list view
//
HRESULT CWAB::LoadWABContents(CListCtrl * pListView)
{
    ULONG ulObjType =   0;
LPMAPITABLE lpAB =  NULL;
    LPTSTR * lppszArray=NULL;
    ULONG cRows =       0;
    LPSRowSet lpRow =   NULL;
LPSRowSet lpRowAB = NULL;
    LPABCONT  lpContainer = NULL;
int cNumRows = 0;
    int nRows=0;

    HRESULT hr = E_FAIL;

    ULONG lpcbEID;
LPENTRYID lpEID = NULL;

    // Get the entryid of the root PAB container
    //
    hr = m_lpAdrBook->GetPAB( &lpcbEID, &lpEID);

ulObjType = 0;

    // Open the root PAB container
    // This is where all the WAB contents reside
    //
    hr = m_lpAdrBook->OpenEntry(lpcbEID,
    (LPENTRYID)lpEID,
    NULL,
    0,
    &ulObjType,
    (LPUNKNOWN *)&lpContainer);

m_lpWABObject->FreeBuffer(lpEID);

lpEID = NULL;

    if(HR_FAILED(hr))
        goto exit;

    // Get a contents table of all the contents in the
    // WABs root container
    //
    hr = lpContainer->GetContentsTable( 0,
            &lpAB);

    if(HR_FAILED(hr))
        goto exit;

    // Order the columns in the ContentsTable to conform to the
    // ones we want - which are mainly DisplayName, EntryID and
    // ObjectType
    // The table is gauranteed to set the columns in the order
    // requested
    //
hr =lpAB->SetColumns( (LPSPropTagArray)&ptaEid, 0 );

    if(HR_FAILED(hr))
        goto exit;

    // Reset to the beginning of the table
    //
hr = lpAB->SeekRow( BOOKMARK_BEGINNING, 0, NULL );

    if(HR_FAILED(hr))
        goto exit;

    // Read all the rows of the table one by one
    //
do {

hr = lpAB->QueryRows(1,0, &lpRowAB);

        if(HR_FAILED(hr))
            break;

        if(lpRowAB)
        {
            cNumRows = lpRowAB->cRows;

    if (cNumRows)
    {
                LPTSTR lpsz = lpRowAB->aRow[0].lpProps[ieidPR_DISPLAY_NAME].Value.lpszA;
                LPENTRYID lpEID = (LPENTRYID) lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
                ULONG cbEID = lpRowAB->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;

                // There are 2 kinds of objects - the MAPI_MAILUSER contact object
                // and the MAPI_DISTLIST contact object
                // For the purposes of this sample, we will only consider MAILUSER
                // objects
                //
                if(lpRowAB->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_MAILUSER)
                {
                    // We will now take the entry-id of each object and cache it
                    // on the listview item representing that object. This enables
                    // us to uniquely identify the object later if we need to
                    //
                    LPSBinary lpSB = NULL;

                    m_lpWABObject->AllocateBuffer(sizeof(SBinary), (LPVOID *) &lpSB);

                    if(lpSB)
                    {
                        m_lpWABObject->AllocateMore(cbEID, lpSB, (LPVOID *) &(lpSB->lpb));

                        if(!lpSB->lpb)
                        {
                            m_lpWABObject->FreeBuffer(lpSB);
                            continue;
                        }

                        CopyMemory(lpSB->lpb, lpEID, cbEID);
                        lpSB->cb = cbEID;

                        LV_ITEM lvi = {0};
                        lvi.mask = LVIF_TEXT | LVIF_PARAM;
                        lvi.iItem = pListView->GetItemCount();
                        lvi.iSubItem = 0;
                        lvi.pszText = lpsz;
                        lvi.lParam = (LPARAM) lpSB;

                        // Now add this item to the list view
                        pListView->InsertItem(&lvi);
                    }
                }
    }
    FreeProws(lpRowAB );
        }

}while ( SUCCEEDED(hr) && cNumRows && lpRowAB)  ;

exit:

if ( lpContainer )
lpContainer->Release();

if ( lpAB )
lpAB->Release();

    return hr;
}

// Clears the contents of the specified ListView
//
void CWAB::ClearWABLVContents(CListCtrl * pListView)
{
    int i;
    int nCount = pListView->GetItemCount();

    if(nCount<=0)
        return;

    for(i=0;i<nCount;i++)
    {
        LV_ITEM lvi ={0};
        lvi.mask = LVIF_PARAM;
        lvi.iItem = i;
        lvi.iSubItem = 0;
        pListView->GetItem(&lvi);
        if(lvi.lParam)
        {
            LPSBinary lpSB = (LPSBinary) lvi.lParam;
            m_lpWABObject->FreeBuffer(lpSB);
        }
    }
    pListView->DeleteAllItems();
}

void CWAB::FreeProws(LPSRowSet prows)
{
ULONGirow;
if (!prows)
return;
for (irow = 0; irow < prows->cRows; ++irow)
m_lpWABObject->FreeBuffer(prows->aRow[irow].lpProps);
m_lpWABObject->FreeBuffer(prows);
}

// When an item is selected in the listview, we
// cache its entry id as a special selected item
//
void CWAB::SetSelection(CListCtrl * pListView)
{

    LV_ITEM lvi = {0};
    // Get the Selected Item from the listview
    lvi.mask = LVIF_PARAM;
    lvi.iSubItem = 0;
    lvi.iItem = pListView->GetNextItem(-1, LVNI_SELECTED);

    if(lvi.iItem == -1)
        return;

    pListView->GetItem(&lvi);

    if(lvi.lParam)
    {
        LPSBinary lpSB = (LPSBinary) lvi.lParam;
        if(m_SB.lpb)
            LocalFree(m_SB.lpb);
        m_SB.cb = lpSB->cb;
        m_SB.lpb = (LPBYTE) LocalAlloc(LMEM_ZEROINIT, m_SB.cb);
        if(m_SB.lpb)
            CopyMemory(m_SB.lpb, lpSB->lpb, m_SB.cb);
        else
            m_SB.cb = 0;
    }
}

// Show details on the selected item
//
void CWAB::ShowSelectedItemDetails(HWND hWndParent)
{
    HRESULT hr = S_OK;

    // if we have a specially cached entryid ..
    //
    if(m_SB.cb && m_SB.lpb)
    {
        HWND hWnd = NULL;
        LPSBinary lpSB = (LPSBinary) &m_SB;
        hr = m_lpAdrBook->Details(  (LPULONG) &hWnd,
        NULL, NULL,
    lpSB->cb,
    (LPENTRYID) lpSB->lpb,
    NULL, NULL,
    NULL, 0);
    }
    return;
}

// Gets a SPropValue array for the selected item
// This array contains all the properties for that item
// though we could actually get a subset too if we
// wanted to
//
void CWAB::GetSelectedItemPropArray()
{
    if(m_SB.lpb && m_SB.cb)
    {
        LPMAILUSER lpMailUser = NULL;
        LPSBinary lpSB = (LPSBinary) &m_SB;
        ULONG ulObjType = 0;

        // Open the selected entry
        //
        m_lpAdrBook->OpenEntry(lpSB->cb,
                               (LPENTRYID) lpSB->lpb,
                              NULL,         // interface
                              0,            // flags
                              &ulObjType,
                              (LPUNKNOWN *)&lpMailUser);

        if(lpMailUser)
        {
            // Flush away any old array we might have cached
            //
            if(m_lpPropArray)
                m_lpWABObject->FreeBuffer(m_lpPropArray);
            m_ulcValues = 0;

            lpMailUser->GetProps(NULL, 0, &m_ulcValues, &m_lpPropArray);

            lpMailUser->Release();
        }
    }
    return;
}

// Loads the proptags for the selected entry into the
// PropTags list box
//
void CWAB::LoadPropTags(CListBox * pList)
{
    if(!m_ulcValues || !m_lpPropArray)
        return;

    pList->ResetContent();

    ULONG i;
    TCHAR sz[32];
    for(i=0;i<m_ulcValues;i++)
    {
        wsprintf(sz, "0x%.8x", m_lpPropArray[i].ulPropTag);
        pList->SetItemData(pList->AddString(sz), m_lpPropArray[i].ulPropTag);
    }

    pList->SetCurSel(-1);
    pList->SetCurSel(0);
}

// Sets the property value, if understandable, into the
// given edit box
//
void CWAB::SetPropString(CEdit * pEdit, ULONG ulPropTag)
{
    pEdit->SetWindowText("");

    if(!m_ulcValues || !m_lpPropArray)
        return;

    ULONG i;

    for(i=0;i<m_ulcValues;i++)
    {
        if(m_lpPropArray[i].ulPropTag == ulPropTag)
        {
            switch(PROP_TYPE(ulPropTag))
            {
            case PT_TSTRING:
                pEdit->SetWindowText(m_lpPropArray[i].Value.LPSZ);
                break;
            case PT_MV_TSTRING:
                {
                    ULONG j;
                    LPSPropValue lpProp = &(m_lpPropArray[i]);
                    for(j=0;j<lpProp->Value.MVSZ.cValues;j++)
                    {
                        pEdit->ReplaceSel(lpProp->Value.MVSZ.LPPSZ[j]);
                        pEdit->ReplaceSel("\r\n");
                    }
                }
                break;
            case PT_BINARY:
                pEdit->SetWindowText("Binary data");
                break;
            case PT_I2:
            case PT_LONG:
            case PT_R4:
            case PT_DOUBLE:
            case PT_BOOLEAN:
                {
                    TCHAR sz[256];
                    wsprintf(sz,"%d",m_lpPropArray[i].Value.l);
                    pEdit->SetWindowText(sz);
                }
                break;
            default:
                pEdit->SetWindowText("Unrecognized or undisplayable data");
                break;
            }
            break;
        }
    }

}

enum {
    icrPR_DEF_CREATE_MAILUSER = 0,
    icrPR_DEF_CREATE_DL,
    icrMax
};

const SizedSPropTagArray(icrMax, ptaCreate)=
{
    icrMax,
    {
        PR_DEF_CREATE_MAILUSER,
        PR_DEF_CREATE_DL,
    }
};

// Gets the WABs default Template ID for MailUsers
// or DistLists. These Template IDs are needed for creating
// new mailusers and distlists
//
HRESULT CWAB::HrGetWABTemplateID(ULONG   ulObjectType,
                                ULONG * lpcbEID,
                                LPENTRYID * lppEID)
{
    LPABCONT lpContainer = NULL;
    HRESULT hr  = hrSuccess;
    SCODE sc = ERROR_SUCCESS;
    ULONG ulObjType = 0;
    ULONG cbWABEID = 0;
    LPENTRYID lpWABEID = NULL;
    LPSPropValue lpCreateEIDs = NULL;
    LPSPropValue lpNewProps = NULL;
    ULONG cNewProps;
    ULONG nIndex;

    if (    (!m_lpAdrBook) ||
           ((ulObjectType != MAPI_MAILUSER) && (ulObjectType != MAPI_DISTLIST)) )
    {
        hr = MAPI_E_INVALID_PARAMETER;
        goto out;
    }

    *lpcbEID = 0;
    *lppEID = NULL;

    if (HR_FAILED(hr = m_lpAdrBook->GetPAB( &cbWABEID,
                                      &lpWABEID)))
    {
        goto out;
    }

    if (HR_FAILED(hr = m_lpAdrBook->OpenEntry(cbWABEID,     // size of EntryID to open
                                        lpWABEID,     // EntryID to open
                                        NULL,         // interface
                                        0,            // flags
                                        &ulObjType,
                                        (LPUNKNOWN *)&lpContainer)))
    {
        goto out;
    }

    // Opened PAB container OK

    // Get us the default creation entryids
    if (HR_FAILED(hr = lpContainer->GetProps(   (LPSPropTagArray)&ptaCreate,
                                                0,
                                                &cNewProps,
                                                &lpCreateEIDs)  )   )
    {
        goto out;
    }

    // Validate the properites
    if (    lpCreateEIDs[icrPR_DEF_CREATE_MAILUSER].ulPropTag != PR_DEF_CREATE_MAILUSER ||
            lpCreateEIDs[icrPR_DEF_CREATE_DL].ulPropTag != PR_DEF_CREATE_DL)
    {
        goto out;
    }

    if(ulObjectType == MAPI_DISTLIST)
        nIndex = icrPR_DEF_CREATE_DL;
    else
        nIndex = icrPR_DEF_CREATE_MAILUSER;

    *lpcbEID = lpCreateEIDs[nIndex].Value.bin.cb;

    m_lpWABObject->AllocateBuffer(*lpcbEID, (LPVOID *) lppEID);

    if (sc != S_OK)
    {
        hr = MAPI_E_NOT_ENOUGH_MEMORY;
        goto out;
    }
    CopyMemory(*lppEID,lpCreateEIDs[nIndex].Value.bin.lpb,*lpcbEID);

out:
    if (lpCreateEIDs)
        m_lpWABObject->FreeBuffer(lpCreateEIDs);

    if (lpContainer)
        lpContainer->Release();

    if (lpWABEID)
        m_lpWABObject->FreeBuffer(lpWABEID);

    return hr;
}

// Shows the NewEntry dialog to enable creating a new contact in the WAB
//
HRESULT CWAB::ShowNewEntryDialog(HWND hWndParent)
{
ULONG cbEID=0;
LPENTRYID lpEID=NULL;

    HRESULT hr = hrSuccess;
    ULONG cbTplEID = 0;
    LPENTRYID lpTplEID = NULL;

    // Get the template id which is needed to create the
    // new object
    //
    if(HR_FAILED(hr = HrGetWABTemplateID(   MAPI_MAILUSER,
                                            &cbTplEID,
                                            &lpTplEID)))
    {
        goto out;
    }

    // Display the New Entry dialog to create the new entry
    //
if (HR_FAILED(hr = m_lpAdrBook->NewEntry((ULONG) hWndParent,
            0,
            0,NULL,
            cbTplEID,lpTplEID,
            &cbEID,&lpEID)))
    {
        goto out;
    }

out:
    return hr;
}

// Delete an entry from the WAB
//
HRESULT CWAB::DeleteEntry()
{
HRESULT hr = hrSuccess;
    ULONG cbWABEID = 0;
    LPENTRYID lpWABEID = NULL;
    LPABCONT lpWABCont = NULL;
    ULONG ulObjType;
    SBinaryArray SBA;

    hr = m_lpAdrBook->GetPAB( &cbWABEID,
                              &lpWABEID);
    if(HR_FAILED(hr))
        goto out;

    hr = m_lpAdrBook->OpenEntry(  cbWABEID,     // size of EntryID to open
                                  lpWABEID,     // EntryID to open
                                  NULL,         // interface
                                  0,            // flags
                                  &ulObjType,
                                  (LPUNKNOWN *)&lpWABCont);

    if(HR_FAILED(hr))
        goto out;

    SBA.cValues = 1;
    SBA.lpbin = &m_SB;

    hr = lpWABCont->DeleteEntries((LPENTRYLIST) &SBA, 0);

    if(m_lpPropArray)
        m_lpWABObject->FreeBuffer(m_lpPropArray);

    m_lpPropArray = NULL;
    m_ulcValues = 0;

out:
    if(lpWABCont)
        lpWABCont->Release();

    if(lpWABEID)
        m_lpWABObject->FreeBuffer(lpWABEID);

    return hr;
}

// Gets the property value for specified String property
//
BOOL CWAB::GetStringPropVal(HWND hWnd, ULONG ulPropTag, LPTSTR sz, ULONG cbsz)
{

    BOOL bRet = FALSE;

    if(PROP_TYPE(ulPropTag) != PT_TSTRING)
    {
        MessageBox(hWnd, "This tool only supports modifying string type props right now",
            "Error", MB_OK | MB_ICONINFORMATION);
        goto out;
    }

    ULONG i;

    // Since we already cached the proparray for the selected
    // item, all we need to do is look in the cached proparray
    // for the requested proptag
    //
    for(i=0;i<m_ulcValues;i++)
    {
        if(m_lpPropArray[i].ulPropTag == ulPropTag)
        {
            LPTSTR lp = m_lpPropArray[i].Value.LPSZ;
            ULONG nLen = (ULONG) lstrlen(lp);
            if(nLen >= cbsz)
            {
                CopyMemory(sz, lp, cbsz-1);
                sz[cbsz-1]='\0';
            }
            else
                lstrcpy(sz,lp);
            break;
        }
    }

    bRet = TRUE;
out:
    return bRet;
}

// Sets a single string property onto a mailuser object
//
BOOL CWAB::SetSingleStringProp(HWND hWnd, ULONG ulPropTag, LPTSTR sz)
{
    SPropValue Prop;
    BOOL bRet = FALSE;

    if(PROP_TYPE(ulPropTag) != PT_TSTRING)
    {
        MessageBox(hWnd, "This version of the tool can only set string properties.",
            "Error", MB_OK | MB_ICONINFORMATION);
        goto out;
    }

    Prop.ulPropTag = ulPropTag;
    Prop.Value.LPSZ = sz;

    // Open the cached entry and get a mailuser object
    // representing that entry
    //
    if(m_SB.lpb && m_SB.cb)
    {
        LPMAILUSER lpMailUser = NULL;
        LPSBinary lpSB = (LPSBinary) &m_SB;
        ULONG ulObjType = 0;

        // To modify an object, make sure to specify the
        // MAPI_MODIFY flag otherwise the object is always
        // opened read-only be default
        //
        m_lpAdrBook->OpenEntry(lpSB->cb,
                               (LPENTRYID) lpSB->lpb,
                              NULL,         // interface
                              MAPI_MODIFY,            // flags
                              &ulObjType,
                              (LPUNKNOWN *)&lpMailUser);

        if(lpMailUser)
        {

            // Knock out this prop if it exists so we can overwrite it
            //
            {
                SPropTagArray SPTA;
                SPTA.cValues = 1;
                SPTA.aulPropTag[0] = ulPropTag;

                lpMailUser->DeleteProps(&SPTA, NULL);
            }

            // Set the new property on the mailuser
            //
            if (!HR_FAILED(lpMailUser->SetProps(1, &Prop, NULL)))
            {
                // **NOTE** if you dont call SaveChanges, the
                // changes are not saved (duh). Also if you didnt
                // open the object with the MAPI_MODIFY flag, you
                // are likely to get an ACCESS_DENIED error
                //
                lpMailUser->SaveChanges(0);
                bRet = TRUE;
            }
            lpMailUser->Release();
        }
    }

out:

    GetSelectedItemPropArray();
    return bRet;
}


WABOBJECT.H

#include "stdafx.h"
#include "afxcview.h"
#include "wab.h"

class CWAB
{
public:
    CWAB(CString * pszFileName);
    ~CWAB();

    HRESULT LoadWABContents(CListCtrl * pListView);
    void ClearWABLVContents(CListCtrl * pListView);

    void LoadPropTags(CListBox * pList);
    void SetPropString(CEdit * pEdit, ULONG ulPropTag);

    void    ShowSelectedItemDetails(HWND hWndParent);
    HRESULT ShowNewEntryDialog(HWND hWndParent);
    HRESULT DeleteEntry();

    void GetSelectedItemPropArray();

    BOOL GetStringPropVal(HWND hWnd, ULONG ulPropTag, LPTSTR sz, ULONG cbsz);
    BOOL SetSingleStringProp(HWND hWnd, ULONG ulPropTag, LPTSTR sz);

    void SetSelection(CListCtrl * pListView);

private:
    BOOL        m_bInitialized;
    HINSTANCE   m_hinstWAB;
    LPWABOPEN   m_lpfnWABOpen;
    LPADRBOOK   m_lpAdrBook;
    LPWABOBJECT m_lpWABObject;

    // Cache Proparray of currently selected item in the list view
    LPSPropValue m_lpPropArray;
    ULONG       m_ulcValues;

    // Cache entry id of currently selected item in the listview
    SBinary     m_SB;

    void FreeProws(LPSRowSet prows);
    HRESULT HrGetWABTemplateID(ULONG   ulObjectType, ULONG * lpcbEID, LPENTRYID * lppEID);

};


WABTOOL.CPP

// wabtool.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "wabtool.h"
#include "wabtoolDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CWabtoolApp

BEGIN_MESSAGE_MAP(CWabtoolApp, CWinApp)
//{{AFX_MSG_MAP(CWabtoolApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
//    DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWabtoolApp construction

CWabtoolApp::CWabtoolApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CWabtoolApp object

CWabtoolApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CWabtoolApp initialization

BOOL CWabtoolApp::InitInstance()
{
AfxEnableControlContainer();

// Standard initialization
// If you are not using these features and wish to reduce the size
//  of your final executable, you should remove from the following
//  the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls();// Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic();// Call this when linking to MFC statically
#endif

    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);

CWabtoolDlg dlg;
    dlg.m_pszFileName = new CString(cmdInfo.m_strFileName);

m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
//  dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
//  dismissed with Cancel
}

    delete(dlg.m_pszFileName);

// Since the dialog has been closed, return FALSE so that we exit the
//  application, rather than start the application's message pump.
return FALSE;
}


WABTOOL.H

// wabtool.h : main header file for the WABTOOL application
//

#if !defined(AFX_WABTOOL_H__268ADD69_EF27_11D0_9A7E_00A0C91F9C8B__INCLUDED_)
#define AFX_WABTOOL_H__268ADD69_EF27_11D0_9A7E_00A0C91F9C8B__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif

#include "resource.h"// main symbols

/////////////////////////////////////////////////////////////////////////////
// CWabtoolApp:
// See wabtool.cpp for the implementation of this class
//

class CWabtoolApp : public CWinApp
{
public:
CWabtoolApp();

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CWabtoolApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL

// Implementation

//{{AFX_MSG(CWabtoolApp)
// NOTE - the ClassWizard will add and remove member functions here.
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_WABTOOL_H__268ADD69_EF27_11D0_9A7E_00A0C91F9C8B__INCLUDED_)


WABTOOLDLG.CPP

// wabtoolDlg.cpp : implementation file
//

#include "stdafx.h"
#include "wabtool.h"
#include "wabobject.h"
#include "wabtoolDlg.h"
#include "DlgProp.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CWAB * g_pWAB;

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
CAboutDlg();

// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWabtoolDlg dialog

CWabtoolDlg::CWabtoolDlg(CWnd* pParent /*=NULL*/)
: CDialog(CWabtoolDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CWabtoolDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CWabtoolDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CWabtoolDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CWabtoolDlg, CDialog)
//{{AFX_MSG_MAP(CWabtoolDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_NOTIFY(NM_DBLCLK, IDC_LIST, OnDblclkList)
ON_BN_CLICKED(IDC_BUTTON_BROWSE, OnButtonBrowse)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST, OnItemchangedList)
ON_LBN_SELCHANGE(IDC_LIST_TAGS, OnSelchangeListTags)
ON_BN_CLICKED(IDC_BUTTON_DETAILS, OnButtonDetails)
ON_BN_CLICKED(IDC_BUTTON_NEW, OnButtonNew)
ON_BN_CLICKED(IDC_BUTTON_DELETE, OnButtonDelete)
ON_BN_CLICKED(IDC_BUTTON_ADDPROP, OnButtonAddprop)
ON_BN_CLICKED(IDC_BUTTON_MODIFYPROP, OnButtonModifyprop)
ON_LBN_DBLCLK(IDC_LIST_TAGS, OnDblclkListTags)
ON_BN_CLICKED(IDC_BUTTON_REFRESH, OnButtonRefresh)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWabtoolDlg message handlers

BOOL CWabtoolDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// Add "About..." menu item to system menu.

// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}

// Set the icon for this dialog.  The framework does this automatically
//  when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE);// Set big icon
SetIcon(m_hIcon, FALSE);// Set small icon

// TODO: Add extra initialization here
    InitCommonControls();

    LoadContents(TRUE);

return TRUE;  // return TRUE  unless you set the focus to a control
}

void CWabtoolDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CWabtoolDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting

SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CWabtoolDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}

BOOL CWabtoolDlg::DestroyWindow()
{
// TODO: Add your specialized code here and/or call the base class
    ClearCurrentWAB(TRUE);

return CDialog::DestroyWindow();
}

//
// Double-clicking the contents list view shows Details on the
// selected entries
//
void CWabtoolDlg::OnDblclkList(NMHDR* pNMHDR, LRESULT* pResult)
{
    g_pWAB->ShowSelectedItemDetails(m_hWnd);

*pResult = 0;
}

//
// Clicking the browse button lets the user select a new WAB file
// to inspect
//
void CWabtoolDlg::OnButtonBrowse()
{

    char szFilters[]="WAB Files (*.wab)|*.wab|All Files (*.*)|*.*||";
    CFileDialog CFileDlg (TRUE, "wab", "*.wab",
                        OFN_FILEMUSTEXIST, szFilters, this);

    if(CFileDlg.DoModal() == IDOK)
    {
        delete m_pszFileName;
        m_pszFileName = new CString(CFileDlg.GetPathName());

        ClearCurrentWAB(TRUE);

        LoadContents(TRUE);

    }
}

// Loads the WAB contents into the list view
//
// bLoadNew is true if we are loading a new file
//
void CWabtoolDlg::LoadContents(BOOL bLoadNew)
{

    CListCtrl * pListView = (CListCtrl *) GetDlgItem(IDC_LIST);

    if(bLoadNew)
        g_pWAB = new CWAB(m_pszFileName);

    g_pWAB->LoadWABContents(pListView);

    CStatic * pFileName = (CStatic *) GetDlgItem(IDC_STATIC_FILENAME);
    pFileName->SetWindowText((LPTSTR) (LPCTSTR) *m_pszFileName);

    TCHAR sz[256];
    wsprintf(sz, "%d entries", pListView->GetItemCount());
    CStatic * pCount = (CStatic *) GetDlgItem(IDC_STATIC_NUMENTRIES);
    pCount->SetWindowText(sz);

    // select the first item in the list view
    pListView->SetItem( 0, 0,
                        LVIF_STATE,NULL,
                        0,
                        LVNI_SELECTED | LVNI_FOCUSED,
                        LVNI_SELECTED | LVNI_FOCUSED,
                        NULL);

}

//
// Clear out the currently loaded WAB
//
void CWabtoolDlg::ClearCurrentWAB(BOOL bLoadNew)
{
    CListCtrl * pListView = (CListCtrl *) GetDlgItem(IDC_LIST);
    g_pWAB->ClearWABLVContents(pListView);

    if(bLoadNew) // we'll be loading a new address book so lose this object
        delete g_pWAB;
}

void CWabtoolDlg::OnItemchangedList(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

    CListCtrl * pListView = (CListCtrl *) GetDlgItem(IDC_LIST);

    static int oldItem = -1;

    int newItem = pListView->GetNextItem(-1, LVNI_SELECTED);

    if(newItem != oldItem && newItem != -1)
    {
        g_pWAB->SetSelection(pListView);
        g_pWAB->GetSelectedItemPropArray();

        UpdatePropTagData();

        OnSelchangeListTags();

        oldItem = newItem;
    }

*pResult = 0;
}

void CWabtoolDlg::UpdatePropTagData()
{
    g_pWAB->LoadPropTags((CListBox *) GetDlgItem(IDC_LIST_TAGS));
}

void CWabtoolDlg::OnSelchangeListTags()
{
    TCHAR sz[64];

    CListBox * pList = (CListBox *) GetDlgItem(IDC_LIST_TAGS);

    ULONG ulPropTag = (ULONG) pList->GetItemData(pList->GetCurSel());

    pList->GetText(pList->GetCurSel(), sz);

    SetPropTagString(sz);
    SetPropTypeString(ulPropTag);
    SetPropNameString(ulPropTag);
    SetPropDataString(ulPropTag);

}

LPTSTR PropTypeString(ULONG ulPropType) {
    switch (ulPropType) {
        case PT_UNSPECIFIED:
            return("PT_UNSPECIFIED");
        case PT_NULL:
            return("PT_NULL       ");
        case PT_I2:
            return("PT_I2         ");
        case PT_LONG:
            return("PT_LONG       ");
        case PT_R4:
            return("PT_R4         ");
        case PT_DOUBLE:
            return("PT_DOUBLE     ");
        case PT_CURRENCY:
            return("PT_CURRENCY   ");
        case PT_APPTIME:
            return("PT_APPTIME    ");
        case PT_ERROR:
            return("PT_ERROR      ");
        case PT_BOOLEAN:
            return("PT_BOOLEAN    ");
        case PT_OBJECT:
            return("PT_OBJECT     ");
        case PT_I8:
            return("PT_I8         ");
        case PT_STRING8:
            return("PT_STRING8    ");
        case PT_UNICODE:
            return("PT_UNICODE    ");
        case PT_SYSTIME:
            return("PT_SYSTIME    ");
        case PT_CLSID:
            return("PT_CLSID      ");
        case PT_BINARY:
            return("PT_BINARY     ");
        case PT_MV_I2:
            return("PT_MV_I2      ");
        case PT_MV_LONG:
            return("PT_MV_LONG    ");
        case PT_MV_R4:
            return("PT_MV_R4      ");
        case PT_MV_DOUBLE:
            return("PT_MV_DOUBLE  ");
        case PT_MV_CURRENCY:
            return("PT_MV_CURRENCY");
        case PT_MV_APPTIME:
            return("PT_MV_APPTIME ");
        case PT_MV_SYSTIME:
            return("PT_MV_SYSTIME ");
        case PT_MV_STRING8:
            return("PT_MV_STRING8 ");
        case PT_MV_BINARY:
            return("PT_MV_BINARY  ");
        case PT_MV_UNICODE:
            return("PT_MV_UNICODE ");
        case PT_MV_CLSID:
            return("PT_MV_CLSID   ");
        case PT_MV_I8:
            return("PT_MV_I8      ");
        default:
            return("   <unknown>  ");
    }
}

void CWabtoolDlg::SetPropTypeString(ULONG ulPropTag)
{
    ((CStatic *) GetDlgItem(IDC_STATIC_PROPTYPE))->SetWindowText(PropTypeString(PROP_TYPE(ulPropTag)));
}

void CWabtoolDlg::SetPropTagString(LPTSTR lpTag)
{
    ((CStatic *) GetDlgItem(IDC_STATIC_PROPTAG))->SetWindowText(lpTag);
}

void CWabtoolDlg::OnButtonDetails()
{
    g_pWAB->ShowSelectedItemDetails(m_hWnd);
    // refresh the list view
    OnButtonRefresh();
}

#define RETURN_PROP_CASE(pt) case PROP_ID(pt): return(#pt)

/***************************************************************************

    Name      : PropTagName

    Purpose   : Associate a name with a property tag

    Parameters: ulPropTag = property tag

    Returns   : none

    Comment   : Add new Property ID's as they become known

***************************************************************************/
LPTSTR PropTagName(ULONG ulPropTag) {
    static TCHAR szPropTag[35]; // see string on default

    switch (PROP_ID(ulPropTag)) {
        RETURN_PROP_CASE(PR_7BIT_DISPLAY_NAME);
        RETURN_PROP_CASE(PR_ACCOUNT);
        RETURN_PROP_CASE(PR_ADDRTYPE);
        RETURN_PROP_CASE(PR_ALTERNATE_RECIPIENT);
        RETURN_PROP_CASE(PR_ASSISTANT);
        RETURN_PROP_CASE(PR_ASSISTANT_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_BIRTHDAY);
        RETURN_PROP_CASE(PR_BUSINESS_FAX_NUMBER);
        RETURN_PROP_CASE(PR_BUSINESS_HOME_PAGE);
        RETURN_PROP_CASE(PR_BUSINESS_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_BUSINESS2_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_CALLBACK_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_CAR_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_CELLULAR_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_CHILDRENS_NAMES);
        RETURN_PROP_CASE(PR_COMMENT);
        RETURN_PROP_CASE(PR_COMPANY_MAIN_PHONE_NUMBER);
        RETURN_PROP_CASE(PR_COMPANY_NAME);
        RETURN_PROP_CASE(PR_COMPUTER_NETWORK_NAME);
        RETURN_PROP_CASE(PR_CONTACT_ADDRTYPES);
        RETURN_PROP_CASE(PR_CONTACT_DEFAULT_ADDRESS_INDEX);
        RETURN_PROP_CASE(PR_CONTACT_EMAIL_ADDRESSES);
        RETURN_PROP_CASE(PR_CONTACT_ENTRYIDS);
        RETURN_PROP_CASE(PR_CONTACT_VERSION);
        RETURN_PROP_CASE(PR_CONVERSION_PROHIBITED);
        RETURN_PROP_CASE(PR_COUNTRY);
        RETURN_PROP_CASE(PR_CUSTOMER_ID);
        RETURN_PROP_CASE(PR_DEPARTMENT_NAME);
        RETURN_PROP_CASE(PR_DISCLOSE_RECIPIENTS);
        RETURN_PROP_CASE(PR_DISPLAY_NAME);
        RETURN_PROP_CASE(PR_DISPLAY_NAME_PREFIX);
        RETURN_PROP_CASE(PR_EMAIL_ADDRESS);
        RETURN_PROP_CASE(PR_ENTRYID);
        RETURN_PROP_CASE(PR_FTP_SITE);
        RETURN_PROP_CASE(PR_GENDER);
        RETURN_PROP_CASE(PR_GENERATION);
        RETURN_PROP_CASE(PR_GIVEN_NAME);
        RETURN_PROP_CASE(PR_GOVERNMENT_ID_NUMBER);
        RETURN_PROP_CASE(PR_HOBBIES);
        RETURN_PROP_CASE(PR_HOME_ADDRESS_CITY);
        RETURN_PROP_CASE(PR_HOME_ADDRESS_COUNTRY);
        RETURN_PROP_CASE(PR_HOME_ADDRESS_POST_OFFICE_BOX);
        RETURN_PROP_CASE(PR_HOME_ADDRESS_POSTAL_CODE);
        RETURN_PROP_CASE(PR_HOME_ADDRESS_STATE_OR_PROVINCE);
        RETURN_PROP_CASE(PR_HOME_ADDRESS_STREET);
        RETURN_PROP_CASE(PR_HOME_FAX_NUMBER);
        RETURN_PROP_CASE(PR_HOME_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_HOME2_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_INITIALS);
        RETURN_PROP_CASE(PR_ISDN_NUMBER);
        RETURN_PROP_CASE(PR_KEYWORD);
        RETURN_PROP_CASE(PR_LANGUAGE);
        RETURN_PROP_CASE(PR_LOCALITY);
        RETURN_PROP_CASE(PR_LOCATION);
        RETURN_PROP_CASE(PR_MAIL_PERMISSION);
        RETURN_PROP_CASE(PR_MANAGER_NAME);
        RETURN_PROP_CASE(PR_MHS_COMMON_NAME);
        RETURN_PROP_CASE(PR_MIDDLE_NAME);
        RETURN_PROP_CASE(PR_NICKNAME);
        RETURN_PROP_CASE(PR_OBJECT_TYPE);
        RETURN_PROP_CASE(PR_OFFICE_LOCATION);
        RETURN_PROP_CASE(PR_ORGANIZATIONAL_ID_NUMBER);
        RETURN_PROP_CASE(PR_ORIGINAL_DISPLAY_NAME);
        RETURN_PROP_CASE(PR_ORIGINAL_ENTRYID);
        RETURN_PROP_CASE(PR_ORIGINAL_SEARCH_KEY);
        RETURN_PROP_CASE(PR_OTHER_ADDRESS_CITY);
        RETURN_PROP_CASE(PR_OTHER_ADDRESS_COUNTRY);
        RETURN_PROP_CASE(PR_OTHER_ADDRESS_POST_OFFICE_BOX);
        RETURN_PROP_CASE(PR_OTHER_ADDRESS_POSTAL_CODE);
        RETURN_PROP_CASE(PR_OTHER_ADDRESS_STATE_OR_PROVINCE);
        RETURN_PROP_CASE(PR_OTHER_ADDRESS_STREET);
        RETURN_PROP_CASE(PR_OTHER_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_PAGER_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_PERSONAL_HOME_PAGE);
        RETURN_PROP_CASE(PR_POST_OFFICE_BOX);
        RETURN_PROP_CASE(PR_POSTAL_ADDRESS);
        RETURN_PROP_CASE(PR_POSTAL_CODE);
        RETURN_PROP_CASE(PR_PREFERRED_BY_NAME);
        RETURN_PROP_CASE(PR_PRIMARY_FAX_NUMBER);
        RETURN_PROP_CASE(PR_PRIMARY_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_PROFESSION);
        RETURN_PROP_CASE(PR_RADIO_TELEPHONE_NUMBER);
        RETURN_PROP_CASE(PR_SEND_INTERNET_ENCODING);
        RETURN_PROP_CASE(PR_SEND_RICH_INFO);
        RETURN_PROP_CASE(PR_SPOUSE_NAME);
        RETURN_PROP_CASE(PR_STATE_OR_PROVINCE);
        RETURN_PROP_CASE(PR_STREET_ADDRESS);
        RETURN_PROP_CASE(PR_SURNAME);
        RETURN_PROP_CASE(PR_TELEX_NUMBER);
        RETURN_PROP_CASE(PR_TITLE);
        RETURN_PROP_CASE(PR_TRANSMITABLE_DISPLAY_NAME);
        RETURN_PROP_CASE(PR_TTYTDD_PHONE_NUMBER);
        RETURN_PROP_CASE(PR_USER_CERTIFICATE);
        RETURN_PROP_CASE(PR_USER_X509_CERTIFICATE);
        RETURN_PROP_CASE(PR_WEDDING_ANNIVERSARY);
        RETURN_PROP_CASE(PR_SEARCH_KEY);

        default:
            wsprintf(szPropTag, "Unknown property tag 0x%x", PROP_ID(ulPropTag));
            return(szPropTag);
    }
}

void CWabtoolDlg::SetPropNameString(ULONG ulPropTag)
{
    LPTSTR lp = PropTagName(ulPropTag);

    CStatic * pType = (CStatic *) GetDlgItem(IDC_STATIC_PROPNAME);

    pType->SetWindowText(lp);
}

void CWabtoolDlg::SetPropDataString(ULONG ulPropTag)
{
    CEdit * pEdit = (CEdit *) GetDlgItem(IDC_EDIT_PROPDATA);

    g_pWAB->SetPropString(pEdit, ulPropTag);
}

void CWabtoolDlg::OnButtonNew()
{

    HRESULT hr = g_pWAB->ShowNewEntryDialog(m_hWnd);

    if(!hr)
        OnButtonRefresh();
}

void CWabtoolDlg::OnButtonDelete()
{
    g_pWAB->DeleteEntry();
    // refresh the list view
    OnButtonRefresh();
}

void CWabtoolDlg::OnButtonAddprop()
{
    TCHAR sz[512];
    *sz = '\0';
    CDlgProp dlgProp;

    dlgProp.m_ulPropTag = 0;
    dlgProp.m_lpszPropVal = sz;
    dlgProp.m_cbsz = sizeof(sz);

    if(dlgProp.DoModal())
    {
        g_pWAB->SetSingleStringProp(m_hWnd, dlgProp.m_ulPropTag, sz);
        UpdatePropTagData();
        OnSelchangeListTags();
    }

}

void CWabtoolDlg::OnButtonModifyprop()
{

    CListBox * pList = (CListBox *) GetDlgItem(IDC_LIST_TAGS);

    ULONG ulPropTag = (ULONG) pList->GetItemData(pList->GetCurSel());

    TCHAR sz[512];

    if(g_pWAB->GetStringPropVal(m_hWnd, ulPropTag, sz, sizeof(sz)))
    {
        CDlgProp dlgProp;

        dlgProp.m_ulPropTag = ulPropTag;
        dlgProp.m_lpszPropVal = sz;
        dlgProp.m_cbsz = sizeof(sz);

        if(dlgProp.DoModal())
        {
            g_pWAB->SetSingleStringProp(m_hWnd, ulPropTag, sz);
            UpdatePropTagData();
            OnSelchangeListTags();
        }

    }

}

void CWabtoolDlg::OnDblclkListTags()
{
    OnButtonModifyprop();
}

void CWabtoolDlg::OnButtonRefresh()
{
    // refresh the list view
    ClearCurrentWAB(FALSE);
    LoadContents(FALSE);
}


WABTOOLDLG.H

// wabtoolDlg.h : header file
//

#if !defined(AFX_WABTOOLDLG_H__268ADD6B_EF27_11D0_9A7E_00A0C91F9C8B__INCLUDED_)
#define AFX_WABTOOLDLG_H__268ADD6B_EF27_11D0_9A7E_00A0C91F9C8B__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

/////////////////////////////////////////////////////////////////////////////
// CWabtoolDlg dialog

class CWabtoolDlg : public CDialog
{
// Construction
public:
CWabtoolDlg(CWnd* pParent = NULL);// standard constructor

    void LoadContents(BOOL bLoadNew);
    void ClearCurrentWAB(BOOL bLoadNew);

    void UpdatePropTagData();
    void SetPropTagString(LPTSTR lpTag);
    void SetPropTypeString(ULONG ulPropTag);
    void SetPropNameString(ULONG ulPropTag);
    void SetPropDataString(ULONG ulPropTag);

    CString * m_pszFileName;

// Dialog Data
//{{AFX_DATA(CWabtoolDlg)
enum { IDD = IDD_WABTOOL_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CWabtoolDlg)
public:
virtual BOOL DestroyWindow();
protected:
virtual void DoDataExchange(CDataExchange* pDX);// DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
HICON m_hIcon;

// Generated message map functions
//{{AFX_MSG(CWabtoolDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnDblclkList(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnButtonBrowse();
afx_msg void OnItemchangedList(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnSelchangeListTags();
afx_msg void OnButtonDetails();
afx_msg void OnButtonNew();
afx_msg void OnButtonDelete();
afx_msg void OnButtonAddprop();
afx_msg void OnButtonModifyprop();
afx_msg void OnDblclkListTags();
afx_msg void OnButtonRefresh();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_WABTOOLDLG_H__268ADD6B_EF27_11D0_9A7E_00A0C91F9C8B__INCLUDED_)


README.TXT

This WALKALL sample demonstrates the use of MSHTML as a UI-less HTML parser. For additional information about this sample see comments at the top of the main source file walkall.cpp.


WALKALL.CPP

// ===========================================================================
// File: W A L K A L L . 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 sample demonstrates the use of MSHTML as a UI-less HTML parser.
// To successfully walk the HTML Scripting Object Model that the parser
// exposes after loading the specified document, the host application must
// wait until MSHTML has finished loading the document. To track MSHTML's
// READYSTATE, the host implements a simple COM object that exposes
// IPropertyNotifySink. The host application connects to MSHTML using the
// standard connection point interfaces. As MSHTML loads the document
// it's readiness state changes. To notify the host of these changes,
// it executes IPropertyNotifySink::OnChanged passing along the DISPID
// of the property that has changed (DISPID_READYSTATE). The host uses MSHTML's
// automation interface obtained at creation time to retrieve the current value
// of this property. When the value equals READYSTATE_COMPLETE,
// MSHTML has finished loading the document.

// If the loaded document is an HTML page which contains scripts, Java Applets and/or
// ActiveX controls, and those scripts are coded for immediate execution, MSHTML will
// execute them by default. To disable this feature, the host must implement two
// additional interfaces: IOleClientSite and IDispatch. At initialization time, after
// MSHTML is instantiated, the host should query MSHTML for its IOleObject interface,
// pass MSHTML a reference to its IOleClientSite interface, query MSHTML for its IOleControl
// interface, and call OnAmbientPropertyChange(DISPID_AMBIENT_DLCONTROL). MSHTML will in
// turn query the host's IOleClientSite interface for IDispatch and then request the value
// of this property from the host. This is the hosts opportunity to control MSHTML, disabling
// the execution of scripts, etc. A full list of control flags are available in <mshtmdid.h>.

// Requirements:
//   * Internet Explorer version 4.0
//   * Microsoft Visual C++ 5.0. The code uses types (_bstr_t) introduced with VC5.
//   * Be sure that the INETSDK include and lib directories are in your INCLUDE and LIB
//     environment variables respectively.

// Instructions:
//
//  To use this sample:
//   * build it using the NMAKE command. NMAKE will create WALKALL.EXE.
//   * run WALKALL.EXE specifying the resource to download by passing an
//     URL on the command-line. use no command-line argument to default to
//     "http://example.microsoft.com".
//   * The program prints all HTML tags to the console. If an IMG tag is encountered
//     the SRC attribute is displayed.
//
// Modification History
// 30 May 1997 - Matt Oshry - created
// 12 Jun 1997 - Matt Oshry - added minimal support for IOleClientSite and IDispatch
// Copyright 1995-1997 Microsoft Corporation.  All Rights Reserved.
// ===========================================================================

#include <windows.h>
#include <tchar.h>
#include <wininet.h>
#include <urlmon.h>
#include <mshtml.h>
#include <iostream.h>
#include <comdef.h>
#include <assert.h>

#include <servprov.h>

#include <mshtmdid.h>

#define C_USAGE _T("Usage: walkall <URL>")

#define ODS(x) OutputDebugString(x)

#define WM_USER_STARTWALKING((WM_USER) + 1)

const TCHAR c_szDefaultUrl[] = _T("http://example.microsoft.com/");

INTERNET_SCHEME GetScheme(LPCTSTR szURL);

void PrintDISPID(DISPID dispidMember);

class CApp : public IPropertyNotifySink, IOleClientSite, IDispatch
{
public:
CApp() : m_pMSHTML(NULL), m_dwRef(1),
m_hrConnected(CONNECT_E_CANNOTCONNECT),
m_dwCookie(0), m_pCP(NULL), m_szURL(NULL), m_nScheme(INTERNET_SCHEME_UNKNOWN),
m_lReadyState(READYSTATE_UNINITIALIZED)
{}
~CApp()
{
if (m_pMSHTML)
{
m_pMSHTML->Release();
}
}

// IUnknown methods
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppv);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

// IPropertyNotifySink methods
    STDMETHOD(OnChanged)(DISPID dispID);
    STDMETHOD(OnRequestEdit)(DISPID dispID);

// IOleClientSite methods
STDMETHOD(SaveObject)(void) { ODS("SaveObject\n"); return E_NOTIMPL; }

STDMETHOD(GetMoniker)(DWORD dwAssign,
            DWORD dwWhichMoniker,
            IMoniker** ppmk)
{ ODS("GetMoniker\n"); return E_NOTIMPL; }

STDMETHOD(GetContainer)(IOleContainer** ppContainer)
{ ODS("GetContainer\n"); return E_NOTIMPL; }

STDMETHOD(ShowObject)(void)
{ ODS("ShowObject\n"); return E_NOTIMPL; }

    STDMETHOD(OnShowWindow)(BOOL fShow)
{ ODS("OnShowWindow\n"); return E_NOTIMPL; }

    STDMETHOD(RequestNewObjectLayout)(void)
{ ODS("RequestNewObjectLayout\n"); return E_NOTIMPL; }

// IDispatch method
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
{ ODS("GetTypeInfoCount\n"); return E_NOTIMPL; }

STDMETHOD(GetTypeInfo)(UINT iTInfo,
            LCID lcid,
            ITypeInfo** ppTInfo)
{ ODS("GetTypeInfo\n"); return E_NOTIMPL; }

STDMETHOD(GetIDsOfNames)(REFIID riid,
            LPOLESTR* rgszNames,
            UINT cNames,
            LCID lcid,
            DISPID* rgDispId)
{ ODS("GetIDsOfNames\n"); return E_NOTIMPL; }

STDMETHOD(Invoke)(DISPID dispIdMember,
            REFIID riid,
            LCID lcid,
            WORD wFlags,
            DISPPARAMS __RPC_FAR *pDispParams,
            VARIANT __RPC_FAR *pVarResult,
            EXCEPINFO __RPC_FAR *pExcepInfo,
            UINT __RPC_FAR *puArgErr);

// Additional class methods
HRESULT Init(int argc, char* argv[]);
HRESULT Run();
HRESULT Term();
HRESULT Walk();

protected:
// Persistence helpers
HRESULT LoadURLFromFile();
HRESULT LoadURLFromMoniker();

IHTMLDocument2* m_pMSHTML;
DWORD m_dwRef;
DWORD m_dwCookie;
LPCONNECTIONPOINT m_pCP;
HRESULT m_hrConnected;
LPTSTR m_szURL;
INTERNET_SCHEME m_nScheme;
READYSTATE m_lReadyState;

IBinding* m_pBinding;
};

STDMETHODIMP CApp::QueryInterface(REFIID riid, LPVOID* ppv)
{
*ppv = NULL;

if (IID_IUnknown == riid || IID_IPropertyNotifySink == riid)
{
*ppv = (LPUNKNOWN)(IPropertyNotifySink*)this;
AddRef();
return NOERROR;
}
else if (IID_IOleClientSite == riid)
{
*ppv = (IOleClientSite*)this;
AddRef();
return NOERROR;
}
else if (IID_IDispatch == riid)
{
*ppv = (IDispatch*)this;
AddRef();
return NOERROR;
}
else
{
OLECHAR wszBuff[39];
int i = StringFromGUID2(riid, wszBuff, 39);
TCHAR szBuff[39];
i = WideCharToMultiByte(CP_ACP, 0, wszBuff, -1, szBuff, 39, NULL, NULL);
ODS("QI: "); ODS(szBuff); ODS("\n");
return E_NOTIMPL;
}
}

STDMETHODIMP_(ULONG) CApp::AddRef()
{
TCHAR szBuff[255];
wsprintf(szBuff, "CApp refcount increased to %d\n", m_dwRef+1);
ODS(szBuff);
return ++m_dwRef;
}

STDMETHODIMP_(ULONG) CApp::Release()
{
TCHAR szBuff[255];

if (--m_dwRef == 0)
{
ODS("Deleting object\n");
delete this;
return 0;
}

wsprintf(szBuff, "CApp refcount reduced to %d\n", m_dwRef);
ODS(szBuff);
return m_dwRef;
}

// Fired on change of the value of a 'bindable' property
STDMETHODIMP CApp::OnChanged(DISPID dispID)
{
HRESULT hr;
TCHAR szBuff[255];

if (DISPID_READYSTATE == dispID)
{
// check the value of the readystate property
assert(m_pMSHTML);

VARIANT vResult = {0};
EXCEPINFO excepInfo;
UINT uArgErr;

DISPPARAMS dp = {NULL, NULL, 0, 0};
if (SUCCEEDED(hr = m_pMSHTML->Invoke(DISPID_READYSTATE, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET, &dp, &vResult, &excepInfo, &uArgErr)))
{
TCHAR szReadyState[20];
assert(VT_I4 == V_VT(&vResult));
m_lReadyState = (READYSTATE)V_I4(&vResult);
switch (m_lReadyState)
{
case READYSTATE_UNINITIALIZED://= 0,
lstrcpy(szReadyState, _T("Uninitialized"));
break;
case READYSTATE_LOADING: //= 1,
lstrcpy(szReadyState, _T("Loading"));
break;
case READYSTATE_LOADED:// = 2,
lstrcpy(szReadyState, _T("Loaded"));
break;
case READYSTATE_INTERACTIVE: //= 3,
lstrcpy(szReadyState, _T("Interactive"));
break;
case READYSTATE_COMPLETE: // = 4
lstrcpy(szReadyState, _T("Complete"));
BOOL fRet = PostThreadMessage(GetCurrentThreadId(),
WM_USER_STARTWALKING,
(WPARAM)0,
(LPARAM)0);
break;
}
wsprintf(szBuff, _T("OnChanged: readyState = %s\n"), szReadyState);
VariantClear(&vResult);
}
else
{
lstrcpy(szBuff, _T("Unable to obtain readyState value\n"));
}
}
else
{
wsprintf(szBuff, _T("OnChanged: %d\n"), dispID);
}

ODS(szBuff);
cout << szBuff;

return NOERROR;
}

STDMETHODIMP CApp::OnRequestEdit(DISPID dispID)
{
// Property changes are OK any time as far as this app is concerned
TCHAR szBuff[255];
wsprintf(szBuff, _T("OnRequestEdit: %d\n"), dispID);
ODS(szBuff);

return NOERROR;
}

typedef CApp* LPAPP;

LPAPP g_pApp;

int main(int argc, char* argv[])
{
HRESULT hr = NOERROR;

g_pApp = new CApp;
if (!g_pApp)
{
return -1;
}

if (FAILED(hr = CoInitialize(NULL)))
{
return -1;
}

if (FAILED(g_pApp->Init(argc, argv)))
{
goto Error;
}

g_pApp->Run();

g_pApp->Term();

Error:
if (g_pApp) g_pApp->Release();

CoUninitialize();

if (FAILED(hr))
{
return -1;
}
else
{
return 0;
}
}

// Return the protocol associated with the specified URL
INTERNET_SCHEME GetScheme(LPCTSTR szURL)
{
URL_COMPONENTS urlComponents;
DWORD dwFlags = 0;
INTERNET_SCHEME nScheme = INTERNET_SCHEME_UNKNOWN;

ZeroMemory((PVOID)&urlComponents, sizeof(URL_COMPONENTS));
urlComponents.dwStructSize = sizeof(URL_COMPONENTS);

if (szURL)
{
if (InternetCrackUrl(szURL, 0, dwFlags, &urlComponents))
{
nScheme = urlComponents.nScheme;
}
}

return nScheme;
}

// Initialize the app. Load MSHTML, hook up property notification sink, etc
HRESULT CApp::Init(int argc, char* argv[])
{
HRESULT hr;
LPCONNECTIONPOINTCONTAINER pCPC = NULL;
LPOLEOBJECT pOleObject = NULL;
LPOLECONTROL pOleControl = NULL;

if (argc < 2)
{
// If no arguments were passed, load a default URL
m_szURL = (LPTSTR)c_szDefaultUrl;
m_nScheme = INTERNET_SCHEME_HTTP;
}
else
{
// otherwise, check the scheme (protocol)
// to determine which persistence interface to use
// see the Run method for where the scheme is used
m_szURL = argv[1];
m_nScheme = GetScheme(m_szURL);
}

// Create an instance of an dynamic HTML document
if (FAILED(hr = CoCreateInstance( CLSID_HTMLDocument, NULL,
CLSCTX_INPROC_SERVER, IID_IHTMLDocument2,
(LPVOID*)&g_pApp->m_pMSHTML )))
{
goto Error;
}

if (FAILED(hr = m_pMSHTML->QueryInterface(IID_IOleObject, (LPVOID*)&pOleObject)))
{
goto Error;
}
hr = pOleObject->SetClientSite((IOleClientSite*)this);
pOleObject->Release();

if (FAILED(hr = m_pMSHTML->QueryInterface(IID_IOleControl, (LPVOID*)&pOleControl)))
{
goto Error;
}
hr = pOleControl->OnAmbientPropertyChange(DISPID_AMBIENT_DLCONTROL);
pOleControl->Release();

// Hook up sink to catch ready state property change
if (FAILED(hr = m_pMSHTML->QueryInterface(IID_IConnectionPointContainer, (LPVOID*)&pCPC)))
{
goto Error;
}

if (FAILED(hr = pCPC->FindConnectionPoint(IID_IPropertyNotifySink, &m_pCP)))
{
goto Error;
}

m_hrConnected = m_pCP->Advise((LPUNKNOWN)(IPropertyNotifySink*)this, &m_dwCookie);

Error:
if (pCPC) pCPC->Release();

return hr;
}

// Clean up connection point
HRESULT CApp::Term()
{
HRESULT hr = NOERROR;

// Disconnect from property change notifications
if (SUCCEEDED(m_hrConnected))
{
// ASSERT(m_pCP);
hr = m_pCP->Unadvise(m_dwCookie);
}

// Release the connection point
if (m_pCP)
{
m_pCP->Release();
}

return hr;
}

// Load the specified document and start pumping messages
HRESULT CApp::Run()
{
HRESULT hr;
MSG msg;

switch(m_nScheme)
{
case INTERNET_SCHEME_HTTP:
    case INTERNET_SCHEME_FTP:
    case INTERNET_SCHEME_GOPHER:
    case INTERNET_SCHEME_HTTPS:
    case INTERNET_SCHEME_FILE:
// load URL using IPersistMoniker
hr = LoadURLFromMoniker();
break;
    case INTERNET_SCHEME_NEWS:
    case INTERNET_SCHEME_MAILTO:
    case INTERNET_SCHEME_SOCKS:
// we don't handle these
return E_FAIL;
break;
    //case INTERNET_SCHEME_DEFAULT:
    //case INTERNET_SCHEME_PARTIAL = -2,
    //case INTERNET_SCHEME_UNKNOWN = -1,
default:
// try loading URL using IPersistFile
hr = LoadURLFromFile();
break;
}

if (SUCCEEDED(hr) || E_PENDING == hr)
{

while (GetMessage(&msg, NULL, 0, 0))
{
if (WM_USER_STARTWALKING == msg.message && NULL == msg.hwnd)
{
g_pApp->Walk();
}
else
{
DispatchMessage(&msg);
}
}
}

return hr;
}

// Use an asynchronous Moniker to load the specified resource
HRESULT CApp::LoadURLFromMoniker()
{
HRESULT hr;
OLECHAR  wszURL[MAX_PATH*sizeof(OLECHAR)];
if (0 == MultiByteToWideChar(CP_ACP, 0, m_szURL, -1, wszURL, MAX_PATH*sizeof(OLECHAR)))
{
return E_FAIL;
}

// Ask the system for a URL Moniker
LPMONIKER pMk = NULL;
LPBINDCTX pBCtx = NULL;
LPPERSISTMONIKER pPMk = NULL;

if (FAILED(hr = CreateURLMoniker(NULL, wszURL, &pMk)))
{
return hr;
}

if (FAILED(hr = CreateBindCtx(0, &pBCtx)))
{
goto Error;
}

// Use MSHTML moniker services to load the specified document
if (SUCCEEDED(hr = m_pMSHTML->QueryInterface(IID_IPersistMoniker,
(LPVOID*)&pPMk)))
{
// Call Load on the IPersistMoniker
// This may return immediately, but the document isn't loaded until
// MSHTML reports READYSTATE_COMPLETE. See the implementation of
// IPropertyNotifySink::OnChanged above and see how the app waits
// for this state change before walking the document's object model.
TCHAR szBuff[MAX_PATH];
wsprintf(szBuff, "Loading %s...\n", m_szURL);
ODS(szBuff);
cout << szBuff;
hr = pPMk->Load(FALSE, pMk, pBCtx, STGM_READ);
}

Error:
if (pMk) pMk->Release();
if (pBCtx) pBCtx->Release();
return hr;
}

// A more traditional form of persistence.
// MSHTML performs this asynchronously as well.
HRESULT CApp::LoadURLFromFile()
{
HRESULT hr;
OLECHAR  wszURL[MAX_PATH*sizeof(OLECHAR)];
if (0 == MultiByteToWideChar(CP_ACP, 0, m_szURL, -1, wszURL, MAX_PATH*sizeof(OLECHAR)))
{
return E_FAIL;
}

LPPERSISTFILE  pPF;
// MSHTML supports file persistence for ordinary files.
    if ( SUCCEEDED(hr = m_pMSHTML->QueryInterface(IID_IPersistFile, (LPVOID*) &pPF)))
{
TCHAR szBuff[MAX_PATH];
wsprintf(szBuff, "Loading %s...\n", m_szURL);
ODS(szBuff);
cout << szBuff;
hr = pPF->Load(wszURL, 0);
pPF->Release();
}

return hr;
}

// Walk the object model.
HRESULT CApp::Walk()
{
HRESULT hr;
IHTMLElementCollection* pColl = NULL;

assert(m_pMSHTML);
if (!m_pMSHTML)
{
return E_UNEXPECTED;
}

if (READYSTATE_COMPLETE != m_lReadyState)
{
ODS("Shouldn't get here 'til MSHTML changes readyState to READYSTATE_COMPLETE\n");
DebugBreak();
return E_UNEXPECTED;
}

// retrieve a reference to the ALL collection
if (SUCCEEDED(hr = m_pMSHTML->get_all( &pColl )))
{
long cElems;

assert(pColl);

// retrieve the count of elements in the collection
if (SUCCEEDED(hr = pColl->get_length( &cElems )))
{
// for each element retrieve properties such as TAGNAME and HREF
for ( int i=0; i<cElems; i++ )
{
VARIANT vIndex;
vIndex.vt = VT_UINT;
vIndex.lVal = i;
VARIANT var2 = { 0 };
LPDISPATCH pDisp;

if (SUCCEEDED(hr = pColl->item( vIndex, var2, &pDisp )))
{
IHTMLElement* pElem = NULL;
if (SUCCEEDED(hr = pDisp->QueryInterface( IID_IHTMLElement, (LPVOID*)&pElem )))
{
_bstr_t bstrTag;
BSTR bstr;

assert(pElem);

hr = pElem->get_tagName(&bstr);
if (bstr)
{
bstrTag = bstr;
SysFreeString(bstr);
}

// if the element is an <IMG> get its SRC attribute
IHTMLImgElement* pImage = NULL;
if (SUCCEEDED(hr = pDisp->QueryInterface( IID_IHTMLImgElement, (LPVOID*)&pImage )))
{
assert(pImage);

pImage->get_src(&bstr);
if (bstr)
{
bstrTag += " - ";
bstrTag += bstr;
SysFreeString(bstr);
}
pImage->Release();
}

TCHAR szTag[2048];
LPOLESTR wszTag = bstrTag; UINT uLen = bstrTag.length();
int iBytes = WideCharToMultiByte(CP_ACP, 0,
wszTag, bstrTag.length(),
szTag, 2048, NULL, NULL);

if (iBytes < 2047)
{
szTag[uLen] = '\0';
}
else
{
szTag[2047] = '\0';
}
cout << szTag << endl;
ODS(szTag);
ODS("\n");

pElem->Release();
} // QI(IHTMLElement)
pDisp->Release();
} // item
} // for
} // get_length

pColl->Release();
} // get_all

// We're done so post ourselves a quit to terminate the message pump.
PostQuitMessage(0);

return hr;
}

// MSHTML Queries for the IDispatch interface of the host through the IOleClientSite
// interface that MSHTML is passed through its implementation of IOleObject::SetClientSite()
STDMETHODIMP CApp::Invoke(DISPID dispIdMember,
            REFIID riid,
            LCID lcid,
            WORD wFlags,
            DISPPARAMS __RPC_FAR *pDispParams,
            VARIANT __RPC_FAR *pVarResult,
            EXCEPINFO __RPC_FAR *pExcepInfo,
            UINT __RPC_FAR *puArgErr)
{
if (!pVarResult)
{
return E_POINTER;
}

PrintDISPID(dispIdMember);

switch(dispIdMember)
{
case DISPID_AMBIENT_DLCONTROL:
// respond to this ambient to indicate that we only want to
// download the page, but we don't want to run scripts,
// Java applets, or ActiveX controls
V_VT(pVarResult) = VT_I4;
V_I4(pVarResult) =  DLCTL_DOWNLOADONLY |
DLCTL_NO_SCRIPTS |
DLCTL_NO_JAVA |
DLCTL_NO_DLACTIVEXCTLS |
DLCTL_NO_RUNACTIVEXCTLS;
break;
default:
return DISP_E_MEMBERNOTFOUND;
}

return NOERROR;
}

// Diagnostic helper to discover what ambient properties MSHTML
// asks of the host
void PrintDISPID(DISPID dispidMember)
{
#define ALEN(x) (sizeof(x)/(sizeof(x[0])))

typedef struct {
DISPID dispid;
LPCTSTR szDesc;
} DISPIDDESC;

static DISPIDDESC aDISPIDS[] = {
{DISPID_AMBIENT_DLCONTROL, _T("DISPID_AMBIENT_DLCONTROL")}, // (-5512)
{DISPID_AMBIENT_USERAGENT, _T("DISPID_AMBIENT_USERAGENT")}, // (-5513)
{DISPID_AMBIENT_BACKCOLOR, _T("DISPID_AMBIENT_BACKCOLOR")},  //        (-701)
{DISPID_AMBIENT_DISPLAYNAME, _T("DISPID_AMBIENT_DISPLAYNAME")},  //      (-702)
{DISPID_AMBIENT_FONT, _T("DISPID_AMBIENT_FONT")},  //             (-703)
{DISPID_AMBIENT_FORECOLOR, _T("DISPID_AMBIENT_FORECOLOR")},  //        (-704)
{DISPID_AMBIENT_LOCALEID, _T("DISPID_AMBIENT_LOCALEID")},  //         (-705)
{DISPID_AMBIENT_MESSAGEREFLECT, _T("DISPID_AMBIENT_MESSAGEREFLECT")},  //   (-706)
{DISPID_AMBIENT_SCALEUNITS, _T("DISPID_AMBIENT_SCALEUNITS")},  //       (-707)
{DISPID_AMBIENT_TEXTALIGN, _T("DISPID_AMBIENT_TEXTALIGN")},  //        (-708)
{DISPID_AMBIENT_USERMODE, _T("DISPID_AMBIENT_USERMODE")},  //         (-709)
{DISPID_AMBIENT_UIDEAD, _T("DISPID_AMBIENT_UIDEAD")},  //           (-710)
{DISPID_AMBIENT_SHOWGRABHANDLES, _T("DISPID_AMBIENT_SHOWGRABHANDLES")},  //  (-711)
{DISPID_AMBIENT_SHOWHATCHING, _T("DISPID_AMBIENT_SHOWHATCHING")},  //     (-712)
{DISPID_AMBIENT_DISPLAYASDEFAULT, _T("DISPID_AMBIENT_DISPLAYASDEFAULT")},  // (-713)
{DISPID_AMBIENT_SUPPORTSMNEMONICS, _T("DISPID_AMBIENT_SUPPORTSMNEMONICS")},  // (-714)
{DISPID_AMBIENT_AUTOCLIP, _T("DISPID_AMBIENT_AUTOCLIP")},  //         (-715)
{DISPID_AMBIENT_APPEARANCE, _T("DISPID_AMBIENT_APPEARANCE")},  //       (-716)
{DISPID_AMBIENT_PALETTE, _T("DISPID_AMBIENT_PALETTE")},  //          (-726)
{DISPID_AMBIENT_TRANSFERPRIORITY, _T("DISPID_AMBIENT_TRANSFERPRIORITY")},  // (-728)
{DISPID_AMBIENT_RIGHTTOLEFT, _T("DISPID_AMBIENT_RIGHTTOLEFT")},  //      (-732)
{DISPID_AMBIENT_TOPTOBOTTOM, _T("DISPID_AMBIENT_TOPTOBOTTOM")}  //      (-733)
};

TCHAR szBuff[255];

for (int i = 0; i < ALEN(aDISPIDS); i++)
{
if (dispidMember == aDISPIDS[i].dispid)
{
wsprintf(szBuff, "MSHTML requesting '%s'\n", aDISPIDS[i].szDesc);
ODS(szBuff);
return;
}
}

wsprintf(szBuff, "MSHTML requesting DISPID '%d'\n", dispidMember);
ODS(szBuff);
}


README.TXT

Overview:
FtpJr sample demonstrates FTP WinInet APIs. FtpJr mimics FTP client, which comes
with Windows 95 or with Windows NT, but it is entirely written with WinInet APIs.

Usage:
1.  To start the sample with the direct connection to the Internet:
    c:> ftpjr
2.  To start the sample with the proxy connection to the Internet:
    c:> ftpjr FtpProxy

Upon starting FtpJr, it prompts user to enter FTP command. It resembles to the
standard command like based FTP server.

Once you see the FTP Jr> prompt, you can type any command supported by the sample.
To get the list of the supported commands type "help"

For example, to get the file from the remote FTP server following sequence of
commands needed:
    open -> to open a session to the remote server
    ls   -> to list the contains of the server
    type -> to set the proper file type (BIN or ASCII)
    get  -> to get the file

If the command requires extra parameters (such as file names, for example), it
will prompt the user for it.

Error reporting:
All errors are repored in form of the numeric error codes and
extended information reported by the server (if available). For example, if you try
to change directory which does not exist following will take place:

FTP Jr>cd MyBogusDir
FtpSetCurrentDirectory error 12003
 550 MyBogusDir: The sytem cannot find the file specified.

In this sample server reported an extended information. FtpSetCurrentDirectory in its
turn failed with the error 12003. This error could be located in the WinInet.h file and
it is:

#define ERROR_INTERNET_EXTENDED_ERROR           (INTERNET_ERROR_BASE + 3)

It is possible that some WinInet fails without any extended information supplied by the
server, for example if you use wrong password:

FTP Jr>open ftp.microsoft.com
Enter user, pass: WrongUser BogusPass
InternetConnect error 12014

Session closed.

Error 12014 (from WinInet.h) is:
#define ERROR_INTERNET_INCORRECT_PASSWORD       (INTERNET_ERROR_BASE + 14)

Notes:
    CERN type proxy does not work with FTP WinInet APIs, because it returns
information in HTML format. You will need to use TIS style FTP proxy with FTP
WinInet APIs. If you have Remote Windows Sockets installed (client part of the
Catapult server), FTP WinInet API may fail because of the following reason: server
will specify a random port for a client to connect to for a data connection.
If access via this port is disallowed, then error 10061 ("connection refused")
may be returned by the FTP APIs such as FtpFindFirstFile.


FTPJR.CPP

//////////////////////////////////////////////////////////
//
//         FTP Jr  -- A Poor Man's FTP Client
//         Sample WinInet Application to demonstrate
//         The FTP APIs
//         Microsoft Corporation (C)
//
//////////////////////////////////////////////////////////

#include <iostream.h>
#include <windows.h>
#include <direct.h>
#include <wininet.h>

#define PROMPT  "FTP Jr>"

BOOL ParseCommand(char *, char *, char *);
BOOL Open (CHAR *);
BOOL Close ();
BOOL ErrorOut ( DWORD, CHAR *);
void FtpEnumDirectory ();
void lcd (char *);
void rcd (char *);
void type (char *);
void DisplayHelp();
void ShowUsage();

char arg1[256], arg2[256], arg3[256];
char szProxy[256] = "";
char szServer[256] = "";
char szCmdLine[256] = "";
HINTERNET hOpen, hConnect;
BOOL bActiveSession = FALSE;
DWORD dwType = FTP_TRANSFER_TYPE_BINARY;

void main(int argc, char *argv[])
{

// Display our banner
cout << "FTP Jr. WinInet FTP Sample Application" << endl;
cout << "Microsoft Corporation" << endl << endl;

// Do they want the display syntax?
if ((lstrcmp(argv[1], "/?") == 0) ||
        (lstrcmp(argv[1], "?") == 0) ||
(lstrcmp(argv[1], "-?") == 0) ||
(lstrcmp(argv[1], "help") == 0) ||
        (lstrcmp(argv[1], "/help") == 0) ||
(lstrcmp(argv[1], "/HELP") == 0) ||
(lstrcmp(argv[1], "HELP") == 0))
{
ShowUsage();
return;
}

switch (argc)
{
case 2:
// Specified proxy, Open WININET.DLL w/ Proxy
lstrcpy(szProxy, argv[1]);
        if ( !(hOpen = InternetOpen ( "FTP Jr (C)",  CERN_PROXY_INTERNET_ACCESS, szProxy, NULL, 0) ) )
        {
            ErrorOut ( GetLastError(), "InternetOpen");
           return ;
        }
        break;
    case 1:
// No proxy specified.  Open WININET.DLL w/ local access
        if ( !(hOpen = InternetOpen ( "FTP Jr (C)",  LOCAL_INTERNET_ACCESS , NULL, 0, 0) ) )
        {
            ErrorOut ( GetLastError(), "InternetOpen");
            return ;
        }
        break;
    default:
ShowUsage();
return;
}

cout << PROMPT;

// Parse commands 1 line at a time
while(TRUE)
{
char *pszArg1, *pszArg2, *pszArg3, *pszTemp;

// Read line
cin.getline(szCmdLine, 256);
pszArg1 = szCmdLine;

//Remove white space before 1st arg
while((*pszArg1 != 0) &&
  (*pszArg1 == ' ')) pszArg1++;

pszArg2 = pszArg1;

//Find end of 1st arg and zero terminate
while ((*pszArg2 != 0) &&
   (*pszArg2 != ' ')) pszArg2++;

if (*pszArg2 != 0) *pszArg2++ = 0;

//Remove white space before 2nd arg
while ((*pszArg2 !=0) &&
   (*pszArg2 == ' ')) pszArg2++;

pszArg3 = pszArg2;

//Find end of 2nd arg and zero terminate
while ((*pszArg3 != 0) &&
   (*pszArg3 != ' ')) pszArg3++;

if (*pszArg3 != 0) *pszArg3++ = 0;

//Remove white space before 3rd Arg
while ((*pszArg3 !=0) &&
   (*pszArg3 == ' ')) pszArg3++;

pszTemp = pszArg3;

//Find end of 3rd arg and zero terminate
while((*pszTemp != 0) &&
  (*pszTemp != ' ')) pszTemp++;

if (*pszTemp != 0) *pszTemp = 0;

//Loop indefintely until ParseCommand returns false
if (ParseCommand(pszArg1, pszArg2, pszArg3) == FALSE)
return;

}
}

///////////////////////////////////////////////////////////
//
//      ParseCommand Function
//
//      Input:   FTP command arguments
//      Returns: TRUE if any command called but quit
//               FALSE if command is quit
//
//////////////////////////////////////////////////////////
BOOL ParseCommand(char *pszArg1, char *pszArg2, char *pszArg3)
{
_strupr(pszArg1);
    CHAR szFile1[256] ="", szFile2[256] ="", szTemp[256];

// Quit Command
if (lstrcmp(pszArg1, "QUIT") == 0)
return FALSE;

// Get Command
else if (lstrcmp(pszArg1, "GET") == 0)
{
    if (bActiveSession)
        {
            cout << "Enter RemoteFile LocalFile: ";
            cin >> szFile1 >> szFile2;
            cin.getline(szTemp, 256);
            if (!FtpGetFile (hConnect,szFile1, szFile2, FALSE,INTERNET_FLAG_RELOAD, dwType, 0 ) )
                ErrorOut (GetLastError(), "FtpGetFile");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;
}

// Put Command
else if (lstrcmp(pszArg1, "PUT") == 0)
{
    if (bActiveSession)
        {
            cout << "Enter LocalFile RemoteFile : ";
            cin >> szFile1 >> szFile2;
            cin.getline(szTemp, 256);
            if (!FtpPutFile (hConnect,szFile1, szFile2, dwType, 0 ) )
                ErrorOut (GetLastError(), "FtpPutFile");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;
}

// Del Command
else if (lstrcmp(pszArg1, "DEL") == 0)
{
    if (bActiveSession)
        {
            cout << "Enter RemoteFile : ";
            cin >> szFile1;
            cin.getline(szTemp, 256);
            if (!FtpDeleteFile (hConnect,szFile1 ) )
                ErrorOut (GetLastError(), "FtpDelFile");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;
}

// Ren Command
else if (lstrcmp(pszArg1, "REN") == 0)
{
    if (bActiveSession)
        {
            cout << "Enter ExistingFile NewFile : ";
            cin >> szFile1 >> szFile2;
            cin.getline(szTemp, 256);
            if (!FtpRenameFile (hConnect,szFile1, szFile2 ) )
                ErrorOut (GetLastError(), "FtpRenameFile");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;

}

// ls command
else if (lstrcmp(pszArg1, "LS") == 0)
{
    if (bActiveSession)
            FtpEnumDirectory();
        else
            cout << "No active session" ;
        cout << endl << PROMPT;
}

// md command
else if (lstrcmp(pszArg1, "MD") == 0)
{
    if (bActiveSession)
        {
            cout << "Enter Directory : ";
            cin >> szFile1;
            cin.getline(szTemp, 256);
            if (!FtpCreateDirectory (hConnect,szFile1 ) )
                ErrorOut (GetLastError(), "FtpCreateDirectory");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;
}

// rd command
else if (lstrcmp(pszArg1, "RD") == 0)
{
    if (bActiveSession)
        {
            cout << "Enter Directory : ";
            cin >> szFile1;
            cin.getline(szTemp, 256);
            if (!FtpRemoveDirectory (hConnect,szFile1 ) )
                ErrorOut (GetLastError(), "FtpRemoveDirectory");
        }
        else
            cout << "No active session" ;
        cout << endl << PROMPT;
}

// type command
else if (lstrcmp(pszArg1, "TYPE") == 0)
{
        if (bActiveSession)
            type (pszArg2);
        else
            cout << "No active session" << endl << PROMPT;
}

// cd command
else if (lstrcmp(pszArg1, "CD") == 0)
{
        if (bActiveSession)
            rcd (pszArg2);
        else
            cout << "No active session" << endl << PROMPT;
}

// lcd command
else if (lstrcmp(pszArg1, "LCD") == 0)
{
lcd(pszArg2);
}

// open command
else if (lstrcmp(pszArg1, "OPEN") == 0)
{
        if (!bActiveSession)
        {
        if (Open (pszArg2))
                cout << "Connected to " << pszArg2 << endl << PROMPT;
            else
                cout << "Session closed" << endl << PROMPT;
        }
        else
            cout << "Already connected to " << szServer << endl << PROMPT;

}

// close command
else if (lstrcmp(pszArg1, "CLOSE") == 0)
{
        if (bActiveSession)
        {
        Close();
            cout << "Session closed" << endl << PROMPT;
        }
        else
            cout << "No active session" << endl << PROMPT;
}

// help command
else if (lstrcmp(pszArg1, "HELP") == 0)
{
DisplayHelp();
}

// error Unknown command
else
{
cout << "?Unknown Command" << endl << PROMPT;
}
return TRUE;
}

void DisplayHelp()
{
cout << "Commands are:" << endl;
cout << "   cd" << endl;
cout << "   close" << endl;
cout << "   del" << endl;
cout << "   get" << endl;
cout << "   help" << endl;
cout << "   lcd" << endl;
cout << "   ls" << endl;
cout << "   md" << endl;
cout << "   open" << endl;
cout << "   put" << endl;
cout << "   quit" << endl;
cout << "   rd" << endl;
cout << "   ren" << endl;
cout << "   type" << endl;
    ShowUsage ();
cout << endl << PROMPT;
}

void lcd(char *pszDir)
{
if ((*pszDir != 0) && (_chdir(pszDir) == -1))
cout << "?Invalid Local Directory" << endl << PROMPT;
else
{
char dir[256];
GetCurrentDirectory(256, dir);
cout << "Local Directory is " << dir << endl << PROMPT;
}
}

void rcd(char *pszDir)
{
    char dir[256];
if (*pszDir != 0)
    {
if (!FtpSetCurrentDirectory (hConnect, pszDir))
        {
            ErrorOut (GetLastError(), "FtpSetCurrentDirectory");
            cout <<  PROMPT;
        }
        else
    cout << "Remote Directory is " << pszDir << endl << PROMPT;
    }
else
{
        DWORD dwSize = sizeof(dir);
if (!FtpGetCurrentDirectory (hConnect, dir, &dwSize))
            cout << "Can not determine current remote directory " << endl << PROMPT;
        else
    cout << "Remote Directory is " << dir << endl << PROMPT;
}
}

void type (char *szType)
{

_strupr(szType);
if (*szType != 0)
    {
    if (lstrcmp(szType, "BIN") == 0)
        {
    dwType = FTP_TRANSFER_TYPE_BINARY;
            cout << "BIN mode selected";
        }
    else if (lstrcmp(szType, "ASCII") == 0)
        {
    dwType = FTP_TRANSFER_TYPE_ASCII;
            cout << "ASCII mode selected";
        }
        else
            cout << "Invalid mode";
        cout << endl << PROMPT;
    }
else
{
if ( dwType == FTP_TRANSFER_TYPE_BINARY)
            cout << "BIN ";
        else
    cout << "ASCII ";
        cout << "mode selected " << endl << PROMPT;
}
}

void ShowUsage()
{
cout << "Syntax:" << endl << "  FTPJR <proxy>" << endl ;
    cout << "Note: a CERN style proxy will not work. You will need to "<< endl;
    cout << "specify a TIS style FTP proxy in the form:" << endl << endl;
cout << "     FTPJR ftp-gw" << endl << endl;
cout << "where ftp-gw is the name of the host that is running the gateway" << endl;
}

BOOL Open (CHAR * szHost)
{
    CHAR szUser[256], szPass[256];
    if ( *szHost == 0)
    {
        cout << "Enter host, user, pass: ";
        cin >> szHost >> szUser >> szPass;
    }
    else
    {
        cout << "Enter user, pass: ";
        cin >> szUser >> szPass;
    }
    CHAR szTemp[256];
    cin.getline(szTemp, 256);
    if ( !(hConnect = InternetConnect ( hOpen, szHost , INTERNET_INVALID_PORT_NUMBER, szUser,  szPass, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE , 0) ) )
    {
    ErrorOut (GetLastError(), "InternetConnect");
        return FALSE;
    }
    bActiveSession = TRUE;
    lstrcpy (szServer, szHost);
    return TRUE;
}

BOOL Close ()
{
    if (!InternetCloseHandle (hConnect) )
    {
        ErrorOut (GetLastError(), "InternetCloseHandle");
        return FALSE;
    }
    bActiveSession = FALSE;
    return TRUE;
}

void FtpEnumDirectory ()
{
WIN32_FIND_DATA pData;
HINTERNET hFind;
DWORD dError;

// Start enumeration and get file handle
if ( !(hFind = FtpFindFirstFile (hConnect, TEXT ("*.*"), &pData, 0, 0) ))
if (GetLastError()  == ERROR_NO_MORE_FILES)
{
cout << "This directory is empty" << endl;
return ;
}
else
{
ErrorOut (GetLastError (), "FindFirst error: ");
return ;
}

cout << pData.cFileName;
if (pData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
// Entry is a directory, mark it as such
cout << " <DIR> " << endl;
else
cout << endl;

dError = NO_ERROR;
do
{
 if (!InternetFindNextFile (hFind, &pData))
 {
 dError = GetLastError();
 if ( dError == ERROR_NO_MORE_FILES )
break;
 else
 {
 ErrorOut (dError, "InternetFindNextFile");
 return ;
 }
 }
 else
 {
cout << pData.cFileName;
if (pData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
cout << " <DIR> " << endl;
else
cout << endl;
 }
}
while ( TRUE);
if (!InternetCloseHandle (hFind) )
{
ErrorOut (GetLastError(), "InternetCloseHandle error");
return ;
}
return ;
}

/****************************************************************************
*
*    FUNCTION: ErrorOut
*
*    PURPOSE: This function is used to get extended Internet error.
*
*    COMMENTS:  Function returns TRUE on success and FALSE on failure.
*
****************************************************************************/

BOOL ErrorOut ( DWORD dError, TCHAR * szCallFunc)
{
    TCHAR szTemp[100] = "", *szBuffer=NULL, *szBufferFinal = NULL;
    DWORD  dwIntError , dwLength = 0;
    wsprintf (szTemp,  "%s error %d\n ", szCallFunc, dError );
if (dError == ERROR_INTERNET_EXTENDED_ERROR)
{
InternetGetLastResponseInfo (&dwIntError, NULL, &dwLength);
if (dwLength)
{
if ( !(szBuffer = (TCHAR *) LocalAlloc ( LPTR,  dwLength) ) )
{
lstrcat (szTemp, TEXT ( "Unable to allocate memory to display Internet error code. Error code: ") );
lstrcat (szTemp, TEXT (_itoa (GetLastError(), szBuffer, 10)  ) );
lstrcat (szTemp, TEXT ("\n") );
cerr << szTemp << endl;
return FALSE;
}
if (!InternetGetLastResponseInfo (&dwIntError, (LPTSTR) szBuffer, &dwLength))
{
lstrcat (szTemp, TEXT ( "Unable to get Intrnet error. Error code: ") );
lstrcat (szTemp, TEXT (_itoa (GetLastError(), szBuffer, 10)  ) );
lstrcat (szTemp, TEXT ("\n") );
cerr << szTemp << endl;
return FALSE;
}
if ( !(szBufferFinal = (TCHAR *) LocalAlloc ( LPTR,  (strlen (szBuffer) +strlen (szTemp) + 1)  ) )  )
{
lstrcat (szTemp, TEXT ( "Unable to allocate memory. Error code: ") );
lstrcat (szTemp, TEXT (_itoa (GetLastError(), szBuffer, 10)  ) );
lstrcat (szTemp, TEXT ("\n") );
cerr << szTemp << endl;
return FALSE;
}
lstrcpy (szBufferFinal, szTemp);
lstrcat (szBufferFinal, szBuffer);
LocalFree (szBuffer);
cerr <<  szBufferFinal  << endl;
LocalFree (szBufferFinal);
}
}
else
        cerr << szTemp << endl;
    return TRUE;
}


README.TXT

This wininet sample demonstrates how to handle http authentication
using InternetErrorDlg or custom UI.


HTTPAUTH.CPP

// ===========================================================================
// 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.
//
// Copyright 1997 Microsoft Corporation.  All Rights Reserved.
// ===========================================================================
#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <wininet.h>

//==============================================================================
BOOL NeedAuth (HINTERNET hRequest)
{
    // Get status code.
    DWORD dwStatus;
    DWORD cbStatus = sizeof(dwStatus);
    HttpQueryInfo
    (
        hRequest,
        HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE,
        &dwStatus,
        &cbStatus,
        NULL
    );
    fprintf (stderr, "Status: %d\n", dwStatus);

    // Look for 401 or 407.
    DWORD dwFlags;
    switch (dwStatus)
    {
        case HTTP_STATUS_DENIED:
            dwFlags = HTTP_QUERY_WWW_AUTHENTICATE;
            break;
        case HTTP_STATUS_PROXY_AUTH_REQ:
            dwFlags = HTTP_QUERY_PROXY_AUTHENTICATE;
            break;
        default:
            return FALSE;
    }

    // Enumerate the authentication types.
    BOOL fRet;
    char szScheme[64];
    DWORD dwIndex = 0;
    do
    {
        DWORD cbScheme = sizeof(szScheme);
        fRet = HttpQueryInfo
            (hRequest, dwFlags, szScheme, &cbScheme, &dwIndex);
        if (fRet)
            fprintf (stderr, "Found auth scheme: %s\n", szScheme);
    }
        while (fRet);

    return TRUE;
}

//==============================================================================
DWORD DoCustomUI (HINTERNET hConnect, HINTERNET hRequest)
{
    // Prompt for username and password.
    char  szUser[64], szPass[64];
    fprintf (stderr, "Enter Username: ");
    if (!fscanf (stdin, "%s", szUser))
        return ERROR_INTERNET_LOGIN_FAILURE;
    fprintf (stderr, "Enter Password: ");
    if (!fscanf (stdin, "%s", szPass))
        return ERROR_INTERNET_LOGIN_FAILURE;

    // Set the values in the handle.
    InternetSetOption
        (hConnect, INTERNET_OPTION_USERNAME, szUser, sizeof(szUser));
    InternetSetOption
        (hConnect, INTERNET_OPTION_PASSWORD, szPass, sizeof(szPass));

    // Drain the socket.
    BYTE bBuf[1000];
    DWORD cbBuf = sizeof(bBuf);
    DWORD cbRead;
    while (InternetReadFile (hRequest, bBuf, cbBuf, &cbRead) && cbRead);

    return ERROR_INTERNET_FORCE_RETRY;
}

//==============================================================================
int main (int argc, char **argv)
{
    HINTERNET hInternet = NULL;
    HINTERNET hConnect  = NULL;
    HINTERNET hRequest  = NULL;
    PSTR pszErr = NULL;
    BOOL fRet;

#define CHECK_ERROR(cond, err) if (!(cond)) {pszErr=(err); goto done;}

    // Check usage.
    if (argc < 2)
    {
        fprintf (stderr, "Usage:   httpauth [-c] <server> [<object> [<user> [<pass>]]]\n");
        fprintf (stderr, "  -c: Use custom UI to prompt for user/pass");
        exit (1);
    }

    // Parse arguments.
    BOOL fAllowCustomUI = FALSE;
    if (argc >= 2 && argv[1][0] == '-')
    {
        fAllowCustomUI = TRUE;
        argv++;
        argc--;
    }
    PSTR pszHost   = argv[1];
    PSTR pszObject = argc >= 3 ? argv[2] : "/";
    PSTR pszUser   = argc >= 4 ? argv[3] : NULL;
    PSTR pszPass   = argc >= 5 ? argv[4] : NULL;

    // Initialize wininet.
    hInternet = InternetOpen
    (
        "HttpAuth Sample",            // app name
        INTERNET_OPEN_TYPE_PRECONFIG, // access type
        NULL,                         // proxy server
        0,                            // proxy port
        0                             // flags
    );
    CHECK_ERROR (hInternet, "InternetOpen");

    // Connect to host.
    hConnect = InternetConnect
    (
        hInternet,                    // wininet handle,
        pszHost,                      // host
        0,                            // port
        pszUser,                      // user
        NULL,                         // pass
        INTERNET_SERVICE_HTTP,        // service
        0,                            // flags
        0                             // context
    );
    CHECK_ERROR (hConnect, "InternetConnect");

    if (pszPass)
    {
        // Work around InternetConnect disallowing empty passwords.
        InternetSetOption (hConnect, INTERNET_OPTION_PASSWORD,
            pszPass, lstrlen(pszPass)+1);
    }

    // Create request.
    hRequest = HttpOpenRequest
    (
        hConnect,                     // connect handle
        "GET",                        // request method
        pszObject,                    // object name
        NULL,                         // version
        NULL,                         // referer
        NULL,                         // accept types
        INTERNET_FLAG_KEEP_CONNECTION // flags: keep-alive
             | INTERNET_FLAG_RELOAD,  // flags: bypass cache
        0                             // context
    );
    CHECK_ERROR (hRequest, "HttpOpenRequest");

resend:

    // Send request.
    fRet = HttpSendRequest
    (
        hRequest,                     // request handle
        "",                           // header string
        0,                            // header length
        NULL,                         // post data
        0                             // post length
    );

    // Handle any authentication dialogs.
    if (NeedAuth(hRequest) && fAllowCustomUI)
    {
        if (DoCustomUI (hConnect, hRequest) == ERROR_INTERNET_FORCE_RETRY)
            goto resend;
    }
    else
    {
        DWORD dwErr;
        dwErr = InternetErrorDlg
        (
            GetDesktopWindow(),
            hRequest,
            fRet ? ERROR_SUCCESS : GetLastError(),
            FLAGS_ERROR_UI_FILTER_FOR_ERRORS  |
                FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
                FLAGS_ERROR_UI_FLAGS_GENERATE_DATA,
            NULL
        );

        if (dwErr == ERROR_INTERNET_FORCE_RETRY)
            goto resend;
    }

    // Dump some bytes.
    BYTE bBuf[1024];
    DWORD cbBuf;
    DWORD cbRead;
    cbBuf = sizeof(bBuf);
    _setmode( _fileno( stdout ), _O_BINARY );
    while (InternetReadFile (hRequest, bBuf, cbBuf, &cbRead) && cbRead)
        fwrite (bBuf, 1, cbRead, stdout);

done: // Clean up.

    if (pszErr)
        fprintf (stderr, "Failed on %s, last error %d\n", pszErr, GetLastError());
    if (hRequest)
        InternetCloseHandle (hRequest);
    if (hConnect)
        InternetCloseHandle (hConnect);
    if (hInternet)
        InternetCloseHandle (hInternet);
    return 0;
}


README.TXT

 HeadDump: requests the default HTML document from the server and prints
along with HTTP headers.
Microsoft Corporation (C). Leon Braginski.

 PURPOSE:
 This sample demonstrates how to create and submit HTTP
 request. Sample requests the default HTML document from the
 server and then display it along with the HTTP transaction headers.
 This sample also allows to access password protected pages. It
 checks for HTTP server response code and it is "401 Access Denied"
 it asks password and user name and then resubmit request.

 This sample features:
  - SSL/PCT support
  - NTLM Authentication (when IE 3.0 or above is installed)
  - Basic Authentication
  - Generating stadart user name/password UI to get user credentials.
  - Getting user name and passwors without UI
  - getting any document of any MIME type
  - translation of Internet error codes to the extended error messeges
  - ability to save remote object to a local file (when no local file
 specify output goes to STDOUT).

 Usage:
   headdump.exe -h host [-o object] [-p proxy] [-u] [-s] [-?] [-f file]
-h host is a HTTP server such as www.server.com
-p proxy is optional proxy, in form: http://proxy:80
-o object is optional document to GET, in form: /foobar.htm
-u use this flag to bring user name and password UI (optional)
-f save remote object as local file  (optional)
-? get help
-s use SSL (optional)

 Samples:
 To get help on command line parameters:
C:> headdump.exe -?

 Note that HeadDump can also invoke ISAPI dll or CGI like this:
 C:> headdump.exe -s www.server.name -o /scripts/myisapi.dll

 Since sample generates large output, you may want to pipe to more:
 C:> headdump.exe -s www.server.name -o /scripts/myisapi.dll  | more

 To save remote HTML object as file:
C:> headdump.exe -s www.server.name -o /exe_files/foo.exe -f foo.exe


HEADDUMP.CPP

//=--------------------------------------------------------------------------=
//  (C) Copyright 1996-1997 Microsoft Corporation. All Rights Reserved.
//=--------------------------------------------------------------------------=
/****************************************************************************
*
* HeadDump: requests the default HTML document from the server and prints
*along with HTTP headers.
*Microsoft Corporation (C). Leon Braginski.
*
* PURPOSE:
* This sample demonstrates how to create and submit HTTP
* request. Sample requests the default HTML document from the
* server and then display it along with the HTTP transaction headers.
* This sample also allows to access password protected pages. It
* checks for HTTP server response code and it is "401 Access Denied"
* it asks password and user name and then resubmit request.

*
* This sample features:
*  - SSL/PCT support
*  - NTLM Authentication (when IE 3.0 or above is installed)
*  - Basic Authentication
*  - Generating stadart user name/password UI to get user credentials.
*  - Getting user name and passwors without UI
*  - getting any document of any MIME type
*  - translation of Internet error codes to the extended error messeges
*  - ability to save remote object to a local file (when no local file
* specify output goes to STDOUT).
*
* Usage:
*   headdump.exe -h host [-o object] [-p proxy] [-u] [-s] [-?] [-f file]
*-h host is a HTTP server such as www.server.com
*-p proxy is optional proxy, in form: http://proxy:80
*-o object is optional document to GET, in form: /foobar.htm
*-u use this flag to bring user name and password UI (optional)
*-f save remote object as local file  (optional)
*-? get help
*-s use SSL (optional)
*
* Samples:
* To get help on command line parameters:
*C:> headdump.exe -?
*
* Note that HeadDump can also invoke ISAPI dll or CGI like this:
* C:> headdump.exe -s www.server.name -o /scripts/myisapi.dll
*
* Since sample generates large output, you may want to pipe to more:
* C:> headdump.exe -s www.server.name -o /scripts/myisapi.dll  | more
*
* To save remote HTML object as file:
*C:> headdump.exe -s www.server.name -o /exe_files/foo.exe -f foo.exe
*
*
*
****************************************************************************/

#include <windows.h>
#include <wininet.h>
#include <iostream.h>

BOOL ErrorOut  ( DWORD dError, TCHAR * szCallingFunc);
BOOL ParseParams (int argc , CHAR ** argv, CHAR* szServer, CHAR* szObject,
                  CHAR* szProxy, CHAR * szFile, BOOL& bUi, BOOL& bSSL);
void Usage (CHAR *szName);

int main (int argc, char *argv[])
{
 HINTERNET hOpen, hConnect, hReq;
 DWORD  dwSize, dwCode;
 DWORD  dwFlags = INTERNET_FLAG_RELOAD |
                  INTERNET_FLAG_NO_CACHE_WRITE |
                  INTERNET_FLAG_KEEP_CONNECTION;

 CHAR *lpBuffer, szData[51];
 CHAR szServer[256], szObject [256], szProxy [256], szFile [256];
 BOOL bSSL, bUi;
 HANDLE hFile = NULL;
 INTERNET_PORT dwPort;
 TCHAR szAccept[] = "*/*";
 LPSTR AcceptTypes[2]={0};

 AcceptTypes[0]=szAccept;

if (!ParseParams ( argc , argv, szServer, szObject,
                  szProxy,  szFile, bUi, bSSL))
    return (FALSE);

if (szFile [0])
{
// Open local file
SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, FALSE};
if  ( !(hFile = CreateFile (szFile,
                        GENERIC_WRITE,
FILE_SHARE_READ,
&sa,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL) ))
{
cerr << "Can't open file: " << szFile << " Error: " << GetLastError() << endl;
return 0;
}
}

if (szProxy [0] == NULL)
{
    // No proxy was specified
    if ( !(hOpen = InternetOpen ( "HeadDump",  LOCAL_INTERNET_ACCESS , NULL, 0, 0) ) )
    {
        ErrorOut ( GetLastError(), "InternetOpen");
        return 0;
    }
 }
 else
 {
    // Use proxy server
   if ( !(hOpen = InternetOpen ( "HeadDump",  CERN_PROXY_INTERNET_ACCESS, szProxy, NULL, 0) ) )
   {
        ErrorOut ( GetLastError(), "InternetOpen");
       return 0;
   }
  }

if  (bSSL)
{
    dwPort = INTERNET_DEFAULT_HTTPS_PORT;
    dwFlags |= INTERNET_FLAG_SECURE |
               INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
               INTERNET_FLAG_IGNORE_CERT_DATE_INVALID  ;
}
else
    dwPort = INTERNET_INVALID_PORT_NUMBER;

if ( !(hConnect = InternetConnect ( hOpen, szServer , dwPort, "",  "", INTERNET_SERVICE_HTTP, 0  , 0) ) )
{
    ErrorOut (GetLastError(), "InternetConnect");
    return 0;
}

if ( !(hReq = HttpOpenRequest (hConnect, "GET", szObject, HTTP_VERSION, "",
                                (LPCTSTR*) AcceptTypes, dwFlags ,0 )))
{
    ErrorOut (GetLastError(), "HttpOpenRequest");
    return 0;
}

again:
if (!HttpSendRequest (hReq, NULL, 0, NULL, 0) )
{
    ErrorOut (GetLastError(), "HttpSend");
    return 0;
}

dwSize = sizeof (DWORD) ;
if ( !HttpQueryInfo (hReq, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwCode, &dwSize, NULL))
{
ErrorOut (GetLastError(), "HttpQueryInfo");
return FALSE;
}

if ( dwCode == HTTP_STATUS_DENIED || dwCode == HTTP_STATUS_PROXY_AUTH_REQ)
{

    // We have to read all outstanding data on the Internet handle
    // before we can resubmit request. Just discard the data.
    do
{
InternetReadFile (hReq, (LPVOID)szData, 50, &dwSize);
}
while (dwSize != 0);

    if (!bUi)
    {
        // This is a secure page.
        cerr << "This page is password protected. " << endl;
    CHAR szUser[50]="";
    CHAR szPass[50]="";

    cerr << "User: ";
    cin  >> szUser;
    cerr << "Pass: ";
    cin  >> szPass;
        if ( !InternetSetOption (hConnect, INTERNET_OPTION_USERNAME, (LPVOID) szUser, lstrlen (szUser) ))
        {
            cerr << "InternetSetOptionFailed: " << GetLastError() << endl;
            return FALSE;
        }

        if ( !InternetSetOption (hConnect, INTERNET_OPTION_PASSWORD, (LPVOID) szPass, lstrlen (szPass) ))
        {
            cerr << "InternetSetOptionFailed: " << GetLastError() << endl;
            return FALSE;
        }
        goto again;
    }
    else
    {
        if ( InternetErrorDlg (GetDesktopWindow(),
                               hReq,
                               ERROR_INTERNET_INCORRECT_PASSWORD,
                               FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
                               FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |
                               FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
                               NULL) == ERROR_INTERNET_FORCE_RETRY)
            goto again;
    }

}

// First time we will find out the size of the headers.
HttpQueryInfo (hReq,HTTP_QUERY_RAW_HEADERS_CRLF,  NULL, &dwSize, NULL);
lpBuffer =  new char [dwSize + 1 ];

// Now we call HttpQueryInfo again to get the headers.
if (!HttpQueryInfo (hReq,HTTP_QUERY_RAW_HEADERS_CRLF, (LPVOID) lpBuffer,
    &dwSize, NULL))
{
ErrorOut (GetLastError(), "HttpQueryInfo");
return FALSE;
}
*(lpBuffer + dwSize) = '\0';
cout << lpBuffer << endl;

do
{
if (!InternetReadFile (hReq, (LPVOID)szData, 50, &dwSize) )
{
 ErrorOut (GetLastError (), "InternetReadFile");
return FALSE;
}
if (dwSize != 0)
{
if (!hFile)
{
// dump text to the screen
szData[dwSize] = '\0';
cout << szData;
}
else
{
DWORD dwWritten;
if (!WriteFile(hFile,  szData, dwSize,  &dwWritten,  NULL))
            {
              CloseHandle (hFile);
              cerr << "WriteFile to " << szFile << " failed: " <<
    GetLastError () << endl;
              return FALSE;
            }
}
}
}
while (dwSize);

cout << endl;

if (!InternetCloseHandle (hReq) )
{
    ErrorOut (GetLastError (), "CloseHandle on hReq");
    return FALSE;
}
if (!InternetCloseHandle (hConnect) )
{
    ErrorOut (GetLastError (), "CloseHandle on hConnect");
    return FALSE;
}
if (!InternetCloseHandle (hOpen) )
{
    ErrorOut (GetLastError (), "CloseHandle on hOpen");
    return FALSE;
}

delete [] lpBuffer;

if (hFile)
{
FlushFileBuffers (hFile);
CloseHandle (hFile);
}
return TRUE;
}

/****************************************************************************
*
*    FUNCTION: ErrorOut
*
*    PURPOSE: This function is used to get extended Internet error.
*
*    COMMENTS:  Function returns TRUE on success and FALSE on failure.
*
****************************************************************************/

BOOL ErrorOut ( DWORD dError, TCHAR * szCallFunc)
{
    TCHAR szTemp[100] = "", *szBuffer=NULL, *szBufferFinal = NULL;
    char strName[256]="";
    DWORD  dwIntError , dwLength = 0;

    FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
               GetModuleHandle("wininet.dll"),dError,0,
                  (LPSTR)strName,256,NULL);
    wsprintf (szTemp,  "%s error code: %d\nMessage: %s\n",
    szCallFunc, dError, strName);

if (dError == ERROR_INTERNET_EXTENDED_ERROR)
{
InternetGetLastResponseInfo (&dwIntError, NULL, &dwLength);
if (dwLength)
{
if ( !(szBuffer = (TCHAR *) LocalAlloc ( LPTR,  dwLength) ) )
{
lstrcat (szTemp, TEXT ( "Unable to allocate memory to display Internet error code. Error code: ") );
lstrcat (szTemp, TEXT (_itoa (GetLastError(), szBuffer, 10)  ) );
lstrcat (szTemp, TEXT ("\n") );
cerr << szTemp << endl;
return FALSE;
}
if (!InternetGetLastResponseInfo (&dwIntError, (LPTSTR) szBuffer, &dwLength))
{
lstrcat (szTemp, TEXT ( "Unable to get Internet error. Error code: ") );
lstrcat (szTemp, TEXT (_itoa (GetLastError(), szBuffer, 10)  ) );
lstrcat (szTemp, TEXT ("\n") );
cerr << szTemp << endl;
return FALSE;
}
if ( !(szBufferFinal = (TCHAR *) LocalAlloc ( LPTR,  (strlen (szBuffer) +strlen (szTemp) + 1)  ) )  )
{
lstrcat (szTemp, TEXT ( "Unable to allocate memory. Error code: ") );
lstrcat (szTemp, TEXT (_itoa (GetLastError(), szBuffer, 10)  ) );
lstrcat (szTemp, TEXT ("\n") );
cerr << szTemp << endl;
return FALSE;
}
lstrcpy (szBufferFinal, szTemp);
lstrcat (szBufferFinal, szBuffer);
LocalFree (szBuffer);
cerr <<  szBufferFinal  << endl;
LocalFree (szBufferFinal);
}
}
else
        cerr << szTemp << endl;
    return TRUE;
}
BOOL ParseParams (int argc , CHAR ** argv, CHAR* szServer, CHAR* szObject,
                  CHAR* szProxy, CHAR * szFile, BOOL& bUi, BOOL& bSSL)
{
    int i = 1;
    BOOL bServer = FALSE;

    szProxy[0]  = NULL, bUi = FALSE, bSSL = FALSE, szFile[0]  = NULL;
    lstrcpy (szObject, "");

    while (i < argc)
    {
       if ( *argv[i] != '-')
       {
           Usage (argv[0]);
           return FALSE;
       }
       switch (argv[i][1])
       {
           case 'H':
           case 'h':
              lstrcpy (szServer,argv[i+1]);
              i += 2;
              bServer = TRUE;
           break;
           case 'o':
           case 'O':
              lstrcpy (szObject, argv[i+1]);
              i += 2;
           break;

           case 'p':
           case 'P':
              lstrcpy (szProxy, argv[i+1]);
              i += 2;
           break;

           case 'u':
           case 'U':
              bUi = TRUE;
              i++;
           break;

           case 's':
           case 'S':
              bSSL = TRUE;
              i++;
           break;

           case 'f':
           case 'F':
  lstrcpy (szFile, argv[i+1]);
              i += 2;
           break;

   case '?':
   Usage (argv[0]);
   return FALSE;
   break;
           default:
               cerr << "Invalid command line argument: "<< argv[i] << endl;
               Usage (argv[0]);
               return FALSE;
        }
    }
    if ( bServer )
        return TRUE;
    else
    {
        Usage(argv[0]);
        return FALSE;
    }
}

void Usage (CHAR *szName)
{
    cerr << "Usage: " << szName <<" -h host [-o object] [-p proxy] [-u] [-s] [-?] [-f file]" << endl;
    cerr << "\t-h host is a HTTP server such as www.server.com" << endl;
    cerr << "\t-p proxy is optional proxy in form: http://proxy:80" << endl;
    cerr << "\t-o object is optional document to GET in form: /foobar.htm" << endl;
    cerr << "\t-u use this flag to bring user name and password UI " << endl;
cerr << "\t-f save remote object as local file " << endl;
cerr << "\t-? get help " << endl;
    cerr << "\t-s use SSL " << endl;
}


README.TXT

INetAll sample is a client application that can download files using FTP URL, HTTP
URL or FILE URL. It implements FTP and HTTP URL access using Wininet functions, and
FILE URL access using Win32 CopyFile API.

This sample uses InternetOpenUrl, InternetReadFile to do FTP and HTTP access. When
accessing a FTP server with direct Internet access or a TIS proxy server, it also uses
the INTERNET_FLAG_RAW_DATA flag in InternetOpenUrl and InternetFindNextFile to
enumerate the directories and files on the FTP server. When a CERN proxy is used to do
FTP, the proxy server uses FTP protocol to communication with the FTP server, and HTTP
Protocol with the client.  You can not use INTERNET_FLAG_RAW_DATA with a CERN proxy
server. The following diagram show how a FTP request is forwarded using a CERN-compatible
proxy service.

(Better viewed with fixed with font such as Courier.)
  Get ftp://host.com/root/test.doc HTTP 1.0        FTP Request
  |=========|                 |=============|          |=============|
  |         |---------------->| Cern-based  |--------->|             |
  | Client  |    HTTP         | Proxy Server|   FTP    |  Ftp Server |
  |         |<----------------|             |<---------|             |
  |=========|                 |=============|          |=============|
HTTP/1.0 200 <document>                  FTP Response

To pass username and password to InternetOpenUrl, the sample internally constructs a
URL in the following format: http://username:password@server:port/path

To enumerate directories and files for a FILE URL, the sample uses WNetOpenEnum,
WNetEnumResource, WNetClose to enumerates shares given a machine name; and
FindFileFile, FindNextFile, FindClose to enumerate sub-directories and files
of a complete UNC name.


INETALL.CPP

///////////////////////////////////////////////////////
/*
 *  inetcpp.cpp
 *
 *  Written by Wei Hua
 *  of Microsoft Developer Support
 *
 *  Functions implemented in inetall.cpp
 *
 *  MyAllocMem()         - memory allocation helper function
 *  MyFreeMem()          - free memory allocated by MyAllocMem
 *  CMyItemArray::CMyItemArray()
 *                       - CMyItemArray constructor
 *  CMyItemArray::~CMyItemArray()
 *                       - CMyItemArray destructor
 *  CMyItemArray::Add()  - Add an item to the array
 *  CMyItemArray::RemoveAll()
 *                       - Remove all items in the array
 *  CMyItemArray::GetAt()- Get an item given an index
 *  CMyItemArray::GetSize()
 *                       - Get the size of the array
 *  WinMain()           - WinMain, display our main dialog box
 *  Dlg_Proc()           - Message Proc for the main dialog box
 *  Dlg_OnInitDialog()   - WM_INITDIALOG handler for the main dialog box
 *  Dlg_OnCommand()      - WM_COMMAND handler for the main dialog box
 *  Dlg_OnDestroy()      - WM_DESTROY handler for the main dialog box
 *  BOOL UpdateUI()      - Exchange data between UI and member variables
 *  FillFtpOrFileObjArray()
 *                       - Enumerate a FTP or FILE URL, and populate a CMyItemArray
 *  FillTreeControl()    - Draw tree structure of a FTP or FILE URL in Treeview Ctrl
 *  Copyright (C) 1997 Microsoft Corporation. All rights reserved
 *
 */
//////////////////////////////////////////////////////
#include <windows.h>

#include <windowsx.h>
//we use message crackers to make code easier to read

#include <commctrl.h>
#include <tchar.h>
#include <string.h>
#include <wininet.h>
#include "inetall.h"

LPVOID
MyAllocMem(
    DWORD cb)
{
    return HeapAlloc(GetProcessHeap(),
    HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, cb);
}

BOOL
MyFreeMem(
    LPVOID pMem)
{
    return HeapFree(GetProcessHeap(), 0, pMem);
}

//////////////////////////////////////////////////////
//CMyItemArray Implementation functions
CMyItemArray::CMyItemArray()
{
m_nCount = 0;
m_pItemlist = NULL;
}

CMyItemArray::~CMyItemArray()
{
if (m_pItemlist) MyFreeMem(m_pItemlist);
}

int CMyItemArray::Add(ItemAttrib& item)
{
if (m_nCount%10 == 0)
{
//Allocating 10 ItemAttrib at a time
LPVOID pTemp = MyAllocMem((m_nCount+10) * sizeof (ItemAttrib));
if (!pTemp) return -1;
//The called function should check return value
CopyMemory (pTemp, m_pItemlist, m_nCount*sizeof(ItemAttrib));
MyFreeMem(m_pItemlist);
m_pItemlist = (PItemAttrib)pTemp;
}
lstrcpy(m_pItemlist[m_nCount].szObjectPath, item.szObjectPath);
m_pItemlist[m_nCount].nObjectType = item.nObjectType;
return m_nCount++; //return index of the newly added item. Increment count.
}

void CMyItemArray::RemoveAll()
{
if (m_pItemlist) MyFreeMem(m_pItemlist);
m_pItemlist = NULL;
m_nCount = 0;
}

ItemAttrib CMyItemArray::GetAt(int nIndex)
{
if (nIndex>=0 && nIndex < m_nCount)
return m_pItemlist[nIndex];
else
{
ItemAttrib nullItem;
(nullItem.szObjectPath)[0]='\0';
nullItem.nObjectType = -1;
return nullItem;
}
}

int CMyItemArray::GetSize()
{
return m_nCount;
}
//End of CMyItemArray Implementation
//////////////////////////////////////////////////////

//////////////////////////////////////////////////////
//Declare Dialog callback function.
BOOL CALLBACK Dlg_Proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

//WinMain just displays a dialogbox
int APIENTRY WinMain (HINSTANCE hInstance,
   HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) {

g_hInst = hInstance;
InitCommonControls();
if (DialogBox(hInstance, MAKEINTRESOURCE(IDD_INETALL_DIALOG),
NULL, (DLGPROC)Dlg_Proc) == -1)
MessageBox(NULL, "Failure in creating main dialog window", "Error", MB_OK);
return(0);
}

BOOL Dlg_OnInitDialog (HWND hwnd, HWND hwndFocus,
   LPARAM lParam)
{
BOOL bRet =  FALSE;

g_Inet.m_dwSizeHtml = 1024;
g_Inet.m_phtml = (LPTSTR)MyAllocMem(g_Inet.m_dwSizeHtml);
if (!g_Inet.m_phtml)
{
MessageBox(hwnd, "Memory Allocation Failure", "Error", MB_OK);
return bRet;
}

bRet = (g_hDlg = hwnd)
&& (g_Inet.hCheckBoxCern = GetDlgItem(g_hDlg, IDC_CERN))
&& (g_Inet.hwndUrl = GetDlgItem(g_hDlg, IDC_URL))
&& (g_Inet.hwndPassword = GetDlgItem(g_hDlg, IDC_PASSWORD))
&& (g_Inet.hwndUsername = GetDlgItem(g_hDlg, IDC_USERNAME))
&& (g_Inet.hTreeViewCtrl = GetDlgItem(g_hDlg, IDC_TREE1))
&& (g_Inet.hwndHTML = GetDlgItem(hwnd, IDC_HTML));

if (!bRet) return bRet;

//Set inital service to HTTP, and select the option button.
g_Inet.m_dwServiceType = SERVICE_TYPE_HTTP;
ShowWindow(g_Inet.hCheckBoxCern, SW_HIDE); //CERN check box only shown when doing ftp
g_Inet.m_phtml[0]=g_Inet.m_url[0] = g_Inet.m_password[0] = g_Inet.m_username[0] = '\0';

g_Inet.m_cern = TRUE;

//Send g_Inet to update UI
UpdateUI(FALSE);

g_Inet.m_curItem = NULL; //No current selected item in tree control

//Set up image list and tree control
g_Inet.hImgList = ImageList_Create(16, 15, 0, 3, 2);
HBITMAP hBmTemp;
for (int iCnt=IDB_BMERR; iCnt<=IDB_BMFILE; iCnt++)
{
hBmTemp = LoadBitmap(g_hInst, MAKEINTRESOURCE(iCnt));
ImageList_Add(g_Inet.hImgList, hBmTemp, NULL);
}
TreeView_SetImageList(g_Inet.hTreeViewCtrl, g_Inet.hImgList, TVSIL_NORMAL);
TV_INSERTSTRUCT tvstruct;
tvstruct.hParent = NULL;
tvstruct.hInsertAfter = TVI_FIRST;
tvstruct.item.iImage = 0;
tvstruct.item.iSelectedImage = 0;
tvstruct.item.pszText = _T("Root");
tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
g_Inet.m_curItem= (HTREEITEM)SendMessage(g_Inet.hTreeViewCtrl, TVM_INSERTITEM,0, (LPARAM)(LPTV_INSERTSTRUCT)&tvstruct);
g_Inet.m_bRedrawTree = TRUE;
return(TRUE);
}

void Dlg_OnDestroy (HWND hwnd) {
if (g_Inet.hImgList) ImageList_Destroy(g_Inet.hImgList);
if (g_Inet.m_phtml) MyFreeMem(g_Inet.m_phtml);
}

void Dlg_OnCommand (HWND hwnd, int id,
   HWND hwndCtl, UINT codeNotify)
{
   switch (id) {
      case IDCANCEL:
         EndDialog(hwnd, id);
         break;

  case IDC_HTTP: //http option button is clicked.
  case IDC_FTP:  //ftp option button is clicked.
  case IDC_FILE:  //file option button is clicked.
g_Inet.m_url[0] = g_Inet.m_password[0] = g_Inet.m_username[0]
= g_Inet.m_phtml[0]= '\0';
g_Inet.m_dwServiceType = (id == IDC_HTTP)? SERVICE_TYPE_HTTP:
(id ==  IDC_FTP)?SERVICE_TYPE_FTP : SERVICE_TYPE_FILE;
//Show CERN check box for FTP URL only
if (id == IDC_FTP) ShowWindow(g_Inet.hCheckBoxCern, SW_SHOW);
else ShowWindow(g_Inet.hCheckBoxCern, SW_HIDE);
UpdateUI(FALSE); //Send g_Inet to update UI
g_Inet.m_itemArray.RemoveAll();
FillTreeControl();
break;

  case IDC_CERN:  //CERN checked box is clicked.
g_Inet.m_cern = IsDlgButtonChecked(g_hDlg, IDC_CERN)==BST_CHECKED;
break;

  case IDC_GETDATA: //GetData button is clicked
HCURSORhCursor;
hCursor = SetCursor(LoadCursor (NULL, IDC_WAIT));
ShowCursor(TRUE);

//Get the UI setting to g_Inet
UpdateUI(TRUE);
g_Inet.m_phtml[0]= '\0';
UpdateUI(FALSE); //cleaning up the html window

//strip out white space in url
int i=0, j=0;
while (g_Inet.m_url[i])
{
while (g_Inet.m_url[i] == ' ') i++;
g_Inet.m_url[j++] = g_Inet.m_url[i++];
}
g_Inet.m_url[j]='\0';
if (!lstrlen(g_Inet.m_url))
{
MessageBox(g_hDlg, "You haven't enter a URL yet.", "Error", MB_OK);
break;
}

CharLower(g_Inet.m_url); //change to all lower case
if (_tcsstr(g_Inet.m_url, _T("http://")) ||
_tcsstr(g_Inet.m_url, _T("ftp://")) ||
_tcsstr(g_Inet.m_url, _T("file://")))
{
MessageBox(hwnd, "Don't type method name in the url text box!", g_Inet.m_url, MB_OK);
goto CursorBackandBailOut;
}

if (g_Inet.m_dwServiceType == SERVICE_TYPE_FILE)
{ //For file url, change all / to \ in the url
i=0;
while (g_Inet.m_url[i])
{
if (g_Inet.m_url[i] == '/') g_Inet.m_url[i] = '\\';
i++;
}
lstrcpy(szFullUrl, "file://");
lstrcat(szFullUrl, g_Inet.m_url);
}
else
{ //For ftp or http, change all \ to / in the url
i=0;
while (g_Inet.m_url[i])
{
if (g_Inet.m_url[i] == '\\') g_Inet.m_url[i] = '/';
i++;
}
//If the server requires username and password, we will construct a url like this:
//http://username:password@server:port/path.
//Since we only use InternetOpenUrl, there is no other way to pass in username and
//password.Doesn't apply to FILE url.
lstrcpy(szFullUrl, g_Inet.m_dwServiceType == SERVICE_TYPE_FTP?
"ftp://":"http://");
if (lstrlen(g_Inet.m_username) && lstrlen(g_Inet.m_password))
{
lstrcat(szFullUrl,g_Inet.m_username);
lstrcat(szFullUrl, ":");
lstrcat(szFullUrl,(LPCTSTR)g_Inet.m_password);
lstrcat(szFullUrl, "@");
}
lstrcat(szFullUrl,(LPCTSTR)g_Inet.m_url);
}

//if m_bRedrawTree is FALSE, which means user has clicked a treecontrol item,
//we won't redraw the whole tree. We will simply add sub-item to the currnet
//selected item. Also if the service HTTP, we don't change tree control at all.
if ((g_Inet.m_bRedrawTree && g_Inet.m_dwServiceType != SERVICE_TYPE_HTTP)
|| g_Inet.m_dwServiceType == SERVICE_TYPE_HTTP)
{
TreeView_DeleteAllItems( g_Inet.hTreeViewCtrl);
TV_INSERTSTRUCT tvstruct;
tvstruct.hParent = NULL;
tvstruct.hInsertAfter = TVI_FIRST;
tvstruct.item.iImage = 0;
tvstruct.item.iSelectedImage = 0;
tvstruct.item.pszText = g_Inet.m_url;
tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
g_Inet.m_curItem = (HTREEITEM)SendMessage(g_Inet.hTreeViewCtrl, TVM_INSERTITEM,0, (LPARAM)(LPTV_INSERTSTRUCT)&tvstruct);
}
g_Inet.m_bRedrawTree = TRUE;

HINTERNET hInternetSession;
hInternetSession = InternetOpen(_T("InetAll"), INTERNET_OPEN_TYPE_PRECONFIG,
NULL, NULL, 0);
if (!hInternetSession)
{
MessageBox(hwnd, "InternetOpen Fails", "Error", MB_OK);
goto CursorBackandBailOut;
}
switch (g_Inet.m_dwServiceType)
{
case SERVICE_TYPE_HTTP:
HINTERNET hInternetFile;
//We just download html and text. Modify if needs to download binary file.
hInternetFile = InternetOpenUrl(hInternetSession, szFullUrl,
NULL, 0, INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 0);
if (!hInternetFile)
{
MessageBox(hwnd, "InternetOpenUrl failed! Check URL or connection", szFullUrl, MB_OK);
goto CursorBackandBailOut;
}
DWORD dwRead;
TCHAR szTemp[1024];
int nTimes;
nTimes=1;
while (InternetReadFile(hInternetFile, (LPVOID)szTemp, 1023, &dwRead))
{
if (!dwRead) break;
szTemp[dwRead]='\0';
//Enlarge buffer only on a need base
if (++nTimes * 1024>(int)g_Inet.m_dwSizeHtml)
{
LPVOID pTemp = MyAllocMem(g_Inet.m_dwSizeHtml + 1024);
if (!pTemp) {
MessageBox(hwnd, "Mem alloc fails", "Error", MB_OK);
InternetCloseHandle(hInternetFile);
g_Inet.m_phtml[0] = '\0';
goto CursorBackandBailOut;
}
CopyMemory (pTemp, g_Inet.m_phtml, g_Inet.m_dwSizeHtml);
g_Inet.m_dwSizeHtml += 1024;

MyFreeMem(g_Inet.m_phtml);
g_Inet.m_phtml = (LPTSTR)pTemp;
}
lstrcat(g_Inet.m_phtml, szTemp);
}
SetWindowText(g_Inet.hwndHTML, g_Inet.m_phtml);
InternetCloseHandle(hInternetFile);
break;

case SERVICE_TYPE_FILE:
case SERVICE_TYPE_FTP:
//FillFtpOrFileObjArray enumerates and FTP or FILE url, and populates
//g_Inet.m_itemArray.
if (!FillFtpOrFileObjArray(hInternetSession, szFullUrl)) break;
//FillTreeControl draws the treeview control using g_Inet.m_itemArray
FillTreeControl();
break;
}

CursorBackandBailOut:
InternetCloseHandle(hInternetSession);
UpdateUI(FALSE);
ShowCursor(FALSE);
SetCursor(hCursor);
break; //break for switch id
}
}

BOOL CALLBACK Dlg_Proc (HWND hDlg, UINT uMsg,
   WPARAM wParam, LPARAM lParam) {

   BOOL fProcessed = TRUE;
   switch (uMsg) {
      HANDLE_MSG(hDlg, WM_INITDIALOG, Dlg_OnInitDialog);
      HANDLE_MSG(hDlg, WM_DESTROY, Dlg_OnDestroy);
      HANDLE_MSG(hDlg, WM_COMMAND, Dlg_OnCommand);

  //No message crackers defined in windowsx.h for WM_NOTIFY, so we just handled
  //it directly.
  //We only process WM_NOTIFY when TreeView items are double clicked.
  case WM_NOTIFY:
int idCtrl;
idCtrl  = (int) wParam;
LPNMHDR pnmh;
pnmh =  (LPNMHDR) lParam;

if (idCtrl == IDC_TREE1 && pnmh->code == NM_DBLCLK)
{
g_Inet.m_curItem = TreeView_GetSelection(g_Inet.hTreeViewCtrl);

//if it's already root, we don't need to enumerate again.
if (! TreeView_GetParent(g_Inet.hTreeViewCtrl, g_Inet.m_curItem)) return fProcessed;
TV_ITEM tvItem;
tvItem.mask =TVIF_TEXT;
tvItem.hItem = g_Inet.m_curItem;
tvItem.pszText = g_Inet.m_url;
TreeView_GetItem(g_Inet.hTreeViewCtrl, &tvItem);

//change UI to have new m_url
UpdateUI(FALSE);

//if the URL ends with a / or \, it's a directory, we call Dlg_OnCommand directly to
//construct new m_itemArray. FillFtpOrFileObjArray
//and FillTreeControl will be called by Dlg_OnCommand.
TCHAR cRight;
cRight = g_Inet.m_url[lstrlen(g_Inet.m_url)-1];
if (cRight =='/' ||cRight =='\\')
{
g_Inet.m_bRedrawTree = FALSE;
Dlg_OnCommand (g_hDlg, IDC_GETDATA, GetDlgItem(g_hDlg, IDC_GETDATA),0);
}
else
{
//if it's a file, we are downloading it.
if (g_Inet.m_dwServiceType == SERVICE_TYPE_FTP)
{
HINTERNET hInternetFile, hInternetSession;
hInternetSession = InternetOpen(_T("InetAll"), INTERNET_OPEN_TYPE_PRECONFIG,
NULL, NULL, 0);
if (!hInternetSession)
{
MessageBox(g_hDlg, "InternetOpen Fails", "Error", MB_OK);
return fProcessed;
}

UpdateUI(TRUE);
lstrcpy(szFullUrl, g_Inet.m_dwServiceType == SERVICE_TYPE_FTP?
"ftp://":"http://");
if (lstrlen(g_Inet.m_username) && lstrlen(g_Inet.m_password))
{
lstrcat(szFullUrl,g_Inet.m_username);
lstrcat(szFullUrl, ":");
lstrcat(szFullUrl,(LPCTSTR)g_Inet.m_password);
lstrcat(szFullUrl, "@");
}
lstrcat(szFullUrl,(LPCTSTR)g_Inet.m_url);
DeleteFile("c:\\deleteme");
HANDLE f;
DWORD dwRead;
//download the file to c:\deleteme
f= CreateFile("c:\\deleteme", GENERIC_WRITE,  0, NULL,
CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL);

if (f==  INVALID_HANDLE_VALUE)
{
MessageBox (hDlg, "Can't open c:\\deleteme to write! Enough free space on C:???",
szFullUrl, MB_OK);
return fProcessed;
}
 hInternetFile = InternetOpenUrl(hInternetSession,(LPCTSTR)szFullUrl, NULL, 0,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 0);
if (!hInternetFile)
{
CloseHandle(f);
return fProcessed;
}
TCHAR szTemp[1024];
while (InternetReadFile(hInternetFile, (LPVOID)szTemp, 1024, &dwRead))
{
if (!dwRead) break;
DWORD dwWritten;
WriteFile(f, (LPVOID)szTemp, dwRead, &dwWritten, NULL);
}
CloseHandle(f);
InternetCloseHandle(hInternetFile);
InternetCloseHandle(hInternetSession);
MessageBox(g_hDlg, "The url is downloaded in c:\\deleteme!", szFullUrl, MB_OK);

}////////////
else
{ ///file type url
DeleteFile("c:\\deleteme");
if (CopyFile(
g_Inet.m_url,// pointer to name of an existing file
"c:\\deleteme",// pointer to filename to copy to
FALSE // flag for operation if file exists
))
MessageBox(g_hDlg, "The url is downloaded in c:\\deleteme!", g_Inet.m_url, MB_OK);
else
{
MessageBox(g_hDlg, "CopyFile failed!", g_Inet.m_url, MB_OK);
}
}
}
//Double click will actually collapse a tree. To expand it in double click event
//we will simply post a TVM_EXPAND message to the treeview control.
PostMessage(g_Inet.hTreeViewCtrl, TVM_EXPAND, (WPARAM) (UINT) TVE_EXPAND, (LPARAM) g_Inet.m_curItem);
}
break;

      default:
         fProcessed = FALSE;
         break;
   }

   return(fProcessed);
}

//Update UI or Collect input from UI
//bDirection FALSE g_Inet to UI
//bDirection TRUE UI to g_Inet

BOOL UpdateUI(BOOL bDirection)
{
BOOL bRet = FALSE;
if (bDirection) //Get UI Input to g_Inet
{
if (BST_CHECKED ==IsDlgButtonChecked(g_hDlg, IDC_CERN)) g_Inet.m_cern = TRUE;
else g_Inet.m_cern =  FALSE;

if (BST_CHECKED ==IsDlgButtonChecked(g_hDlg, IDC_HTTP))
g_Inet.m_dwServiceType == SERVICE_TYPE_HTTP;
if (BST_CHECKED ==IsDlgButtonChecked(g_hDlg, IDC_FTP))
g_Inet.m_dwServiceType == SERVICE_TYPE_FTP;
if (BST_CHECKED ==IsDlgButtonChecked(g_hDlg, IDC_FILE))
g_Inet.m_dwServiceType == SERVICE_TYPE_FILE;

bRet = GetDlgItemText(g_hDlg, IDC_URL, g_Inet.m_url, MAX_STRING_SIZE)
  && GetDlgItemText(g_hDlg, IDC_USERNAME, g_Inet.m_username, MAX_STRING_SIZE)
  && GetDlgItemText(g_hDlg, IDC_PASSWORD, g_Inet.m_password, MAX_STRING_SIZE);
}
else //Update UI from g_Inet
{
int nService;

if (g_Inet.m_dwServiceType == SERVICE_TYPE_HTTP) nService = IDC_HTTP;
else if (g_Inet.m_dwServiceType == SERVICE_TYPE_FTP) nService = IDC_FTP;
else nService = IDC_FILE;
bRet = CheckRadioButton(g_hDlg, IDC_HTTP, IDC_FILE, nService)
&& SetWindowText(g_Inet.hwndUrl, g_Inet.m_url)
    && SetWindowText(g_Inet.hwndPassword, g_Inet.m_password)
&& SetWindowText(g_Inet.hwndUsername, g_Inet.m_username);;
bRet  = bRet && SetWindowText(g_Inet.hwndHTML, g_Inet.m_phtml);

SendMessage(g_Inet.hCheckBoxCern, BM_SETCHECK, g_Inet.m_cern, 0);
}
return bRet;
}

//Enumerate strURL, and populate g_Inet.m_itemArray
BOOL FillFtpOrFileObjArray(HINTERNET hInternetSession, LPTSTR strURL)
{
BOOL bRet = FALSE;
if (!hInternetSession) return bRet;

if (g_Inet.m_dwServiceType == SERVICE_TYPE_HTTP)
{
MessageBox(g_hDlg, "only ftp and file url needs enumeration!", "Error", MB_OK);
return bRet;
}

ItemAttrib tempObj;
g_Inet.m_itemArray.RemoveAll();

if (g_Inet.m_dwServiceType == SERVICE_TYPE_FTP) //////// start_if_ftp
{
//CERN or NO CERN we will download HTML anyway.
HINTERNET hInternetFile;
hInternetFile = InternetOpenUrl(hInternetSession, szFullUrl,
NULL, 0, INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 0);
DWORD dwRead;
TCHAR szTemp[1024];
int nTimes;
nTimes=1;
while (InternetReadFile(hInternetFile, (LPVOID)szTemp, 1023, &dwRead))
{
if (!dwRead) break;
szTemp[dwRead]='\0';
if (++nTimes * 1024>(int)g_Inet.m_dwSizeHtml)
{
LPVOID pTemp = MyAllocMem(g_Inet.m_dwSizeHtml + 1024);
if (!pTemp) {
MessageBox(g_hDlg, "Mem alloc fails", "Error", MB_OK);
InternetCloseHandle(hInternetFile);
g_Inet.m_phtml[0] = '\0';
break;
}
CopyMemory (pTemp, g_Inet.m_phtml, g_Inet.m_dwSizeHtml);
g_Inet.m_dwSizeHtml += 1024;

MyFreeMem(g_Inet.m_phtml);
g_Inet.m_phtml = (LPTSTR)pTemp;
}
lstrcat(g_Inet.m_phtml, szTemp);
}
SetWindowText(g_Inet.hwndHTML, g_Inet.m_phtml);
InternetCloseHandle(hInternetFile);

//If direct access to FTP server, we will use
//InternetOpenUrl(INTERNET_FLAG_RAW_DATA) and InternetFindNextFile
//to enumerate the sub directories and files
if (!g_Inet.m_cern)  //start_if_no_cern
{
WIN32_FIND_DATA wfd;

HINTERNET hInternetFind = InternetOpenUrl(hInternetSession,(LPCTSTR)strURL, NULL, 0,
INTERNET_FLAG_RAW_DATA| INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 0);
if (!InternetFindNextFile (hInternetFind, &wfd))
{
bRet = FALSE; //Actually this may or may not be a failure.
MessageBox(g_hDlg, "No files or sub-directories under this url directory", strURL, MB_OK);
return bRet;
}
do {
LPCTSTR strFileName;
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
strFileName = (LPCTSTR) &wfd.cFileName;
if (strFileName[0] == '.') continue;
lstrcpy(tempObj.szObjectPath, g_Inet.m_url);
if (strURL[lstrlen(strURL)-1] != '/') lstrcat(tempObj.szObjectPath, "/");
lstrcat (tempObj.szObjectPath, strFileName);
if (tempObj.szObjectPath[lstrlen(tempObj.szObjectPath)-1] != '/' )
lstrcat(tempObj.szObjectPath, "/");
tempObj.nObjectType = 1;
g_Inet.m_itemArray.Add(tempObj);
}
else
{
strFileName = (LPCTSTR) &wfd.cFileName;
lstrcpy(tempObj.szObjectPath , g_Inet.m_url);
if (strURL[lstrlen(strURL)-1] != '/') lstrcat(tempObj.szObjectPath, "/");
lstrcat(tempObj.szObjectPath, strFileName);
tempObj.nObjectType = 0;
g_Inet.m_itemArray.Add(tempObj);
}
} while (InternetFindNextFile (hInternetFind, &wfd));
InternetCloseHandle (hInternetFind);
}  //end_if_no_cern
else
{ //start_if_cern
LPTSTR strTemp;
TCHAR strTempUrl[MAX_STRING_SIZE];
strTemp = g_Inet.m_phtml;
//we have the html file listings in the strTemp, we just need to parse it
//a sample html is provided at the bottom of this file. Note that the parsing
//code may not work with proxies other than MS Proxy server.
while (TRUE)
{
int nPos;
TCHAR strLeft[] = "<A HREF=";
TCHAR strRight[] = ">";

nPos = _tcsstr(strTemp, strLeft) - strTemp;
if (nPos < 0) break;
strTemp = strTemp + nPos +lstrlen(strLeft)+1;
nPos = _tcsstr(strTemp, strRight) - strTemp;
for (int i=0; i<nPos-1; i++)
strTempUrl[i]=strTemp[i];
strTempUrl[nPos-1] = '\0';
strTemp =  strTemp +i +lstrlen(strRight);
if (strTempUrl[0] == '.') continue;

if (g_Inet.m_url[lstrlen(g_Inet.m_url)-1] != '/')
{
g_Inet.m_url[lstrlen(g_Inet.m_url)] ='/';
g_Inet.m_url[lstrlen(g_Inet.m_url)+1] ='\0';
}

TCHAR strServerName[256];
i=0;
while (TRUE)
{
strServerName[i]= g_Inet.m_url[i];
if (g_Inet.m_url[i] == '/')
{
strServerName[i] = '\0';
break;
}
i++;
}
lstrcpy(tempObj.szObjectPath, strServerName);
lstrcat(tempObj.szObjectPath, strTempUrl);
if (strTempUrl[lstrlen(strTempUrl)-1] == '/' ) tempObj.nObjectType = 1;
else tempObj.nObjectType = 0;
g_Inet.m_itemArray.Add(tempObj);
}
if (!g_Inet.m_itemArray.GetSize()){
MessageBox(g_hDlg, "No files or sub-directories under the url", g_Inet.m_url, MB_OK);
return bRet;
}
}//end_if_cern
}////end_if_ftp
else
{  //start_if_file
TCHAR strFileSpec[MAX_STRING_SIZE];

lstrcpy(strFileSpec, g_Inet.m_url);

//trying to determine if it's a single machine name to enumerate, or
//it's already a unc name.
LPTSTR szTempRight;

//in case user type in \\machine name in url text box,
szTempRight = strFileSpec+2;
int i=0;
BOOL bServerNameOnly;
bServerNameOnly= TRUE;
for (i=0; i<lstrlen(szTempRight)-1; i++)
{
if (szTempRight[i] == '\\')
{
bServerNameOnly = FALSE;
break;
}
}

if (bServerNameOnly)
{
//user enters a single machine name, we will enumerate the shares.
lstrcpy(g_Inet.m_url, "\\\\");
int i=0;
while (strFileSpec[i] == '\\') i++;
lstrcat(g_Inet.m_url, (LPTSTR)(strFileSpec + i));
if (g_Inet.m_url[lstrlen(g_Inet.m_url)-1] == '\\')
g_Inet.m_url[lstrlen(g_Inet.m_url)-1] = '\0';
//now the g_Inet.m_url is "\\server name
NETRESOURCE nr;
nr.dwScope = RESOURCE_GLOBALNET;
nr.dwType = RESOURCETYPE_ANY;
nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
nr.dwUsage = RESOURCEUSAGE_CONTAINER;
nr.lpLocalName = NULL;
nr.lpRemoteName = g_Inet.m_url;
nr.lpComment = NULL;
nr.lpProvider = NULL;
LPVOID pMem =  HeapAlloc(GetProcessHeap(),
    HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, 16384);
if (!pMem)
{
MessageBox(g_hDlg, "Not enough memory to allocate for WNetEnumResource",g_Inet.m_url, MB_OK );
return bRet;
}
HANDLE hWNetEnum=NULL;
DWORD dwStatus = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
RESOURCEUSAGE_CONNECTABLE, &nr, &hWNetEnum);
if (dwStatus != NO_ERROR)
{
DWORD dwErr = GetLastError();
MessageBox(g_hDlg, "WNetOpenEnum failed", g_Inet.m_url, MB_OK );
HeapFree(GetProcessHeap(), 0, pMem);
return bRet;
}
do
{
DWORD dwEntries;
DWORD dwBuffSize;
dwEntries =1;
dwBuffSize= 16384;
dwStatus = WNetEnumResource(hWNetEnum, &dwEntries, pMem, &dwBuffSize);
if (dwStatus == NO_ERROR)
{
lstrcpy(tempObj.szObjectPath, ((LPNETRESOURCE)pMem)->lpRemoteName);
lstrcat(tempObj.szObjectPath, "\\");
tempObj.nObjectType = 1;
g_Inet.m_itemArray.Add(tempObj);
}
} while (dwStatus == NO_ERROR);
if (dwStatus != ERROR_NO_MORE_ITEMS)
{
MessageBox(g_hDlg, "WNetEnumResource failed",g_Inet.m_url, MB_OK);
MyFreeMem(pMem);
WNetCloseEnum(hWNetEnum);
return bRet;
}
MyFreeMem(pMem);
WNetCloseEnum(hWNetEnum);
//we are done with enumerating shares on a file server.
return bRet = TRUE;
}

lstrcpy(strFileSpec, "\\\\");
i=0;
while (g_Inet.m_url[i]=='\\') //skip starting \ in m_url
{
i++;
}
lstrcat(strFileSpec, g_Inet.m_url+i);
//Now strFileSpec is a UNC such as \\server\share\filedirectory\...

WIN32_FIND_DATA fd;
HANDLE hFind = NULL;
if ((hFind = FindFirstFile ((LPCTSTR) strFileSpec, &fd)) ==
INVALID_HANDLE_VALUE) {
if (strFileSpec[lstrlen(strFileSpec)-1] != '\\') lstrcat(strFileSpec, "\\");
lstrcpy(g_Inet.m_url, strFileSpec);
lstrcat(strFileSpec, "*.*");
if ((hFind = FindFirstFile ((LPCTSTR) strFileSpec, &fd)) ==
INVALID_HANDLE_VALUE)
{
MessageBox(g_hDlg, "No files or sub-directories under the url", g_Inet.m_url, MB_OK);
return bRet;
}
}
do {
if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
 (LPCTSTR) &fd.cFileName;
if ( ((LPCTSTR) &fd.cFileName)[0] == '.') continue;
lstrcpy(tempObj.szObjectPath, g_Inet.m_url);
lstrcat(tempObj.szObjectPath, (LPCTSTR) &fd.cFileName);
lstrcat(tempObj.szObjectPath, "\\");
tempObj.nObjectType = 1;
g_Inet.m_itemArray.Add(tempObj);
}
else
{
lstrcpy(tempObj.szObjectPath, g_Inet.m_url);
lstrcat(tempObj.szObjectPath, (LPCTSTR) &fd.cFileName);
tempObj.nObjectType = 0;
g_Inet.m_itemArray.Add(tempObj);

}
} while (FindNextFile (hFind, &fd));
FindClose (hFind);
} //end_if_file
bRet = TRUE; //happy here.

return bRet;
}

//Draw tree structure of a FTP or FILE URL in
//treeview Ctrl using g_Inet.m_itemArray
void FillTreeControl()
{
TV_INSERTSTRUCT tvstruct;
int nItem;
nItem = g_Inet.m_itemArray.GetSize();
HTREEITEM hChildItem=NULL, hNextChildItem=NULL;
if (!nItem){
TreeView_DeleteAllItems( g_Inet.hTreeViewCtrl);
tvstruct.hParent = NULL;
tvstruct.hInsertAfter = TVI_FIRST;
tvstruct.item.iImage = 0;
tvstruct.item.iSelectedImage = 0;
tvstruct.item.pszText = _T("Root");
tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
SendMessage(g_Inet.hTreeViewCtrl, TVM_INSERTITEM,0, (LPARAM)(LPTV_INSERTSTRUCT)&tvstruct);
return;
}

int nBMID;

//Remove all old items under the selected item
hChildItem = TreeView_GetChild(g_Inet.hTreeViewCtrl, g_Inet.m_curItem);

while (hChildItem)
{
hNextChildItem = TreeView_GetNextSibling(g_Inet.hTreeViewCtrl, hChildItem);
 TreeView_DeleteItem(g_Inet.hTreeViewCtrl, hChildItem);
hChildItem = hNextChildItem;
}
//add new items
for (int i=0; i<nItem; i++)
{
nBMID = ((g_Inet.m_itemArray.GetAt(i)).nObjectType==0)?2:1;
tvstruct.hParent = g_Inet.m_curItem;
tvstruct.hInsertAfter = TVI_LAST;
tvstruct.item.iImage = nBMID;
tvstruct.item.iSelectedImage = nBMID;
tvstruct.item.pszText = (g_Inet.m_itemArray.GetAt(i)).szObjectPath;
tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
SendMessage(g_Inet.hTreeViewCtrl, TVM_INSERTITEM,0, (LPARAM)(LPTV_INSERTSTRUCT)&tvstruct);
}
//Make sure the tree under the selected item is expanded.
if (g_Inet.m_curItem)
{
TreeView_EnsureVisible(g_Inet.hTreeViewCtrl, g_Inet.m_curItem);
TreeView_Expand(g_Inet.hTreeViewCtrl, g_Inet.m_curItem, TVE_EXPAND);
}
}

//A typical HTML wrapper for file listing a in ftp url from MS Proxy Server
//which is CERN-compatible proxy.
/*
<HTML>
<HEAD>
<TITLE>FTP root at ftp.microsoft.com</TITLE>
</HEAD>
<BODY>
<H1>FTP root at ftp.microsoft.com</H1>
<HR>
<H4><PRE>
This is FTP.MICROSOFT.COM.  Please see the
 dirmap.txt file for more information.

</PRE></H4>
<HR>
<PRE>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/access/">access</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/dosword/">dosword</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/excel/">excel</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/games/">games</A>
10/08/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/GEN_INFO/">GEN_INFO</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/homeapps/">homeapps</A>
03/12/97 10:23PM          &lt;DIR&gt; <A HREF="/deskapps/kids/">kids</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/miscapps/">miscapps</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/mmapps/">mmapps</A>
08/28/96 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/MONEY/">MONEY</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/office/">office</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/powerpt/">powerpt</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/project/">project</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/publishr/">publishr</A>
08/30/94 12:00AM          1,791 <A HREF="/deskapps/readme.txt">readme.txt</A>
08/24/94 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/word/">word</A>
08/28/96 12:00AM          &lt;DIR&gt; <A HREF="/deskapps/WORKS/">WORKS</A>
</PRE>
<HR>
</BODY>
</HTML>
*/

/////////////////////// End Of File ////////////////////////


INETALL.H

///////////////////////////////////////////////////////
/*
 *  inetall.h
 *
 *  Written by Wei Hua
 *  of Microsoft Developer Support
 *
 *  Copyright (C) 1997 Microsoft Corporation. All rights reserved
 *
 *  Header file for inetal.cpp.
 */
//////////////////////////////////////////////////////

//dialog box control id for rc file
///////////////////////////////////////////////////////
#define IDC_STATIC -1
#define IDD_INETALL_DIALOG              102
#define IDR_MAINFRAME                   128
#define IDB_BMERR                       130
#define IDB_BMDIR                       131
#define IDB_BMFILE                      132
#define IDC_HTTP                        1000
#define IDC_FTP                         1001
#define IDC_FILE                        1002
#define IDC_URL                         1006
#define IDC_USERNAME                    1007
#define IDC_PASSWORD                    1008
#define IDC_HTML                        1009
#define IDC_GETDATA                     1010
#define IDC_TREE1                       1012
#define IDC_CERN                        1013

///////////////////////////////////////////////////////
#define MAX_STRING_SIZE1024
#define SERVICE_TYPE_HTTP1
#define SERVICE_TYPE_FTP2
#define SERVICE_TYPE_FILE3

HINSTANCE g_hInst;//Application Instance
HWND g_hDlg;//main dialogbox window handle
TCHAR szFullUrl[MAX_STRING_SIZE];

//ItemAttrib, and CMyItemArray: data structures for enumerating
//FTP URL and FILE URL
typedef struct _tagItemAttrib
{
TCHARszObjectPath[MAX_STRING_SIZE];
intnObjectType; //0 file object, 1 directory object
} ItemAttrib, *PItemAttrib;

//Implements only the minimal functions needed for maintaining items
//info in the tree control.
class CMyItemArray
{
public:
CMyItemArray();// standard constructor
~CMyItemArray();

int Add(ItemAttrib& item); //return Item index
void RemoveAll();
ItemAttrib GetAt(int nIndex);
int GetSize();

private:
PItemAttrib m_pItemlist;
int m_nCount;
};

typedef struct _tagINETALLBAG
{
HIMAGELISThImgList;
HWNDhTreeViewCtrl;
HTREEITEMm_curItem;

HWNDhCheckBoxCern;
BOOLm_cern;

DWORDm_dwServiceType;

HWNDhwndUrl;
TCHARm_url[MAX_STRING_SIZE];
HWNDhwndPassword;
TCHARm_password[MAX_STRING_SIZE];
HWNDhwndUsername;
TCHARm_username[MAX_STRING_SIZE];

HWNDhwndHTML;
LPTSTRm_phtml;
DWORDm_dwSizeHtml;
CMyItemArraym_itemArray;

BOOLm_bRedrawTree;
//TRUE delete all items in the tree control and add new items.
//FALSE add new items as the sub items to the hCurItem

} INETALLBAG, *PINETALLBAG;

INETALLBAG g_Inet;

//Update UI or Collect input from UI
//bDirection FALSE g_Inet to UI
//bDirection TRUE UI to g_Inet
//Helper function similar to MFC UpdateData
BOOL UpdateUI(BOOL bDirection);

//Enumerate strURL, and populate g_Inet.m_itemArray
BOOL FillFtpOrFileObjArray(HINTERNET InternetSession, LPTSTR strURL);

//Draw the TreeView control using g_Inet.m_itemArray
void FillTreeControl();


README.TXT

The IIS SDK is redistributing two Win32 rpc header files: rpcndr.h
and rpcproxy.h.  It is necessary to replace the existing versions
of these header files on your NT 4.0 system or in your VC5++ environment
to compile and link C++ projects built using the new IIS 4.0 Header Files.


APPLICATION_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<%
// Ensure that this page is not cached
Response.Expires = 0;
%>

<HTML>
    <HEAD>
        <TITLE>Using Application Variables</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using Application Variables</b></font><br>

        <hr size="1" color="#000000">

        <%
            // If this is the first time any user has visited
            // the page, initialize Application Value

            if (Application("AppPageCountJScript") == null)
            {
                Application("AppPageCountJScript") = 0;
            }

            // Increment the Application AppPageCount by one.
            // Note that because this AppPageCount value is being
            // shared, locking must be used to prevent
            // two sessions from simultaneously attempting
            // to update the value.

            Application.Lock();
            Application("AppPageCountJScript") = Application("AppPageCountJScript") + 1;
            Application.UnLock();
        %>

        <!-- Output the Application Page Counter Value -->
        <!-- Note that locking does not need to be used -->
        <!-- because the value is not being changed by -->
        <!-- this user session -->

        Users have visited this page
        <%=Application("AppPageCountJScript")%> times!

        <!-- Provide a link to revisit the page -->

        <p><A href = "Application_JScript.asp">Click here to visit it again</A>

    </BODY>
</HTML>


APPLICATION_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<%
' Ensure that this page is not cached
Response.Expires = 0
%>

<HTML>
    <HEAD>
        <TITLE>Using Application Variables</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using Application Variables</b></font><br>

        <hr size="1" color="#000000">

        <%
            ' If this is the first time any user has visited
            ' the page, initialize Application Value

            If (Application("AppPageCountVB") = "") Then
                Application("AppPageCountVB") = 0
            End If

            ' Increment the Application AppPageCount by one.
            ' Note that this AppPageCount value is being
            ' shared, locking must be used to prevent
            ' two sessions from simultaneously attempting
            ' to update the value.

            Application.Lock
            Application("AppPageCountVB") = Application("AppPageCountVB") + 1
            Application.UnLock
        %>

        <!-- Output the Application Page Counter Value -->
        <!-- Note that locking does not need to be used -->
        <!-- because the value is not being changed by -->
        <!-- this user session -->

        Users have visited this page
        <%=Application("AppPageCountVB")%> times!

        <!-- Provide a link to revisit the page -->

        <p><A href = "Application_VBScript.asp">Click here to visit it again</A>

    </BODY>
</HTML>


SESSION_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<%
// Ensure that this page is not cached
Response.Expires = 0;
%>

<HTML>
    <HEAD>
        <TITLE>Using Session Variables</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using Session Variables</b></font><br>

        <hr size="1" color="#000000">

        <%
            // If this is the first time a user has visited
            // the page, initialize Session Value

            if (Session("SessionCountJScript") == null)
            {
                Session("SessionCountJScript") = 0;
            }

            // Increment the Session PageCount by one.
            // Note that this PageCount value is only
            // for this user's individual session

            Session("SessionCountJScript") = Session("SessionCountJScript") + 1;
        %>

        <!-- Output the Session Page Counter Value -->

        You have personally visited this page
        <%=Session("SessionCountJScript")%> times.

        <!-- Provide a link to revisit the page -->

        <p><A href = "Session_JScript.asp">Click here to visit it again</A>

    </BODY>
</HTML>


SESSION_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<%
' Ensure that this page is not cached
Response.Expires = 0
%>

<HTML>
    <HEAD>
        <TITLE>Using Session Variables</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using Session Variables</b></font><br>

        <hr size="1" color="#000000">

        <%
            ' If this is the first time a user has visited
            ' the page, initialize Session Value

            If (Session("SessionCountVB") = "") Then
                Session("SessionCountVB") = 0
            End If

            ' Increment the Session PageCount by one.
            ' Note that this PageCount value is only
            ' for this user's individual session

            Session("SessionCountVB") = Session("SessionCountVB") + 1
        %>

        <!-- Output the Session Page Counter Value -->

        You have personally visited this page
        <%=Session("SessionCountVB")%> times!

        <!-- Provide a link to revisit the page -->

        <p><A href = "Session_VBScript.asp">Click here to visit it again</A>

    </BODY>
</HTML>


TIMEOUT_JSCRIPT.ASP

<%@ Language=JScript %>

<HTML>
    <HEAD>
        <TITLE>Session Management</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Session Management</b></font><br>

<p>This ASP Session will timeout after 25 minutes
of inactivity.

<%
Session.Timeout = 25;
%>

    </BODY>
</HTML>


TIMEOUT_VBSCRIPT.ASP

<%@ Language=VBScript %>
<%  Option Explicit  %>

<HTML>
    <HEAD>
        <TITLE>Session Management</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Session Management</b></font><br>

<p>This ASP Session will timeout after 25 minutes
of inactivity.

<%
Session.Timeout = 25
%>

    </BODY>
</HTML>


ADROTATOR_JSCRIPT.ASP

<% @ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Ad Rotator Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Ad Rotator Sample</b></font><br>

        <hr size="1" color="#000000">

<%
var ad;

 ad = Server.CreateObject("MSWC.AdRotator");
%>

<%= ad.GetAdvertisement("adrot.txt") %>

<a href="AdRotator_JScript.asp"> Revisit Page </a>

    </BODY>
</HTML>


ADROTATOR_VBSCRIPT.ASP

<% @ LANGUAGE = VBScript %>
<% Option Explicit      %>

<HTML>
    <HEAD>
        <TITLE>Ad Rotator Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Ad Rotator Sample</b></font><br>

        <hr size="1" color="#000000">

<%
Dim ad

 Set ad = Server.CreateObject("MSWC.AdRotator")
%>

<%= ad.GetAdvertisement("adrot.txt") %>

<a href="AdRotator_VBScript.asp"> Revisit Page </a>

    </BODY>
</HTML>


BROWSERCAP_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Using the Browser Capabilities Component</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using the Browser Capabilities Component</b></font><br>

        <hr size="1" color="#000000">

        <!--  Create a BrowserType Object -->

        <% bc = Server.CreateObject("MSWC.BrowserType"); %>

        <!-- Output a table describing the capabilities -->
        <!-- of a user's browser-->

        <Table border=1>

            <tr>
            <td>Browser Name</td>
            <td> <%=bc.Browser %> </Td>

            <tr>
            <td>Browser Version</td>
            <td> <%=bc.Version %> </Td>

            <tr>
            <td>Major Version</td>
            <td> <%=bc.Majorver %> </Td>

            <tr>
            <td>Minor Version</td>
            <td> <%=bc.Minorver %> </Td>

            <tr>
            <td>Frame Support</td>
            <td> <%=bc.Frames %> </Td>

            <tr>
            <td>Table Support</td>
            <td> <%=bc.Tables %> </Td>

            <tr>
            <td>Cookie Support</td>
            <td> <%=bc.Cookies %> </Td>

            <tr>
            <td>Background Sound Support</td>
            <td> <%=bc.BackgroundSounds %> </Td>

            <tr>
            <td>VBScript Support</td>
            <td> <%=bc.VBScript %> </Td>

            <tr>
            <td>JavaScript Support</td>
            <td> <%=bc.JavaScript %> </Td>

        </table>
    </BODY>
</HTML>


BROWSERCAP_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit%>

<HTML>
    <HEAD>
        <TITLE>Using the Browser Capabilities Component</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using the Browser Capabilities Component</b></font><br>

        <hr size="1" color="#000000">

        <!-- Create a BrowserType Object -->

        <%
Dim bc
Set bc = Server.CreateObject("MSWC.BrowserType")
%>

        <!-- Output a table describing the capabilities -->
        <!-- of a user's browser-->

        <Table border=1>

            <tr>
            <td>Browser Name</td>
            <td> <%=bc.Browser %> </Td>

            <tr>
            <td>Browser Version</td>
            <td> <%=bc.Version %> </Td>

            <tr>
            <td>Major Version</td>
            <td> <%=bc.Majorver %> </Td>

            <tr>
            <td>Minor Version</td>
            <td> <%=bc.Minorver %> </Td>

            <tr>
            <td>Frame Support</td>
            <td> <%=bc.Frames %> </Td>

            <tr>
            <td>Table Support</td>
            <td> <%=bc.Tables %> </Td>

            <tr>
            <td>Cookie Support</td>
            <td> <%=bc.Cookies %> </Td>

            <tr>
            <td>Background Sound Support</td>
            <td> <%=bc.BackgroundSounds %> </Td>

            <tr>
            <td>VBScript Support</td>
            <td> <%=bc.VBScript %> </Td>

            <tr>
            <td>JavaScript Support</td>
            <td> <%=bc.JavaScript %> </Td>

        </table>
    </BODY>
</HTML>


CDO_JSCRIPT.ASP

<% @Language=JScript%>

<HTML>
    <HEAD>
        <TITLE>CDO Component</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>CDO Component</b></font><p>

This sample demonstrates how to use the Collaboration
Data Objects for NTS Component to send a simple
e-mail message.

<p>To actually send the message, you must have the SMTP
Server that comes with the Windows NT Option Pack Installed.

<%
var myMail;
myMail = Server.CreateObject("CDONTS.NewMail");

// For demonstration proposes, both From and
// To properties are set to the same address.

myMail.From = "someone@Microsoft.com";
myMail.To = "someone@Microsoft.com";

myMail.Subject = "Sample";
myMail.Body = "I hope you like the sample";

myMail.Send();
%>
    </BODY>
</HTML>


CDO_VBSCRIPT.ASP

<% @Language=VBScript %>
<% Option Explicit%>

<HTML>
    <HEAD>
        <TITLE>CDO Component</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>CDO Component</b></font><p>

This sample demonstrates how to use the Collaboration
Data Objects for NTS Component to send a simple
e-mail message.

<p>To actually send the message, you must have the SMTP
Server that comes with the Windows NT Option Pack Installed.

<%
Dim myMail
Set myMail = Server.CreateObject("CDONTS.NewMail")

' For demonstration proposes, both From and To
' properties are set to the same address.

myMail.From = "someone@Microsoft.com"
myMail.To = "someone@Microsoft.com"

myMail.Subject = "Sample"
myMail.Body = "I hope you like the sample"

myMail.Send
%>
    </BODY>
</HTML>


CONTENTROTATOR_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<%
// Ensure that the client-browser doesn't cache
Response.Expires = 0;
%>

<HTML>
    <HEAD>
        <TITLE>Content Rotator Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Contenet Rotator Sample</b></font><br>

        <hr size="1" color="#000000">

        <%
var Tip;
Tip = Server.CreateObject("MSWC.ContentRotator");

Response.Write(Tip.ChooseContent("tiprot.txt"));
%>

<hr size="1" color="#000000">
<A href="ContentRotator_VBScript.asp">Refresh (You may have to do this several times)</A>

    </BODY>
</HTML>


CONTENTROTATOR_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<%
' Ensure that the client-browser doesn't cache
Response.Expires = 0
%>

<HTML>
    <HEAD>
        <TITLE>Content Rotator Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Contenet Rotator Sample</b></font><br>

        <hr size="1" color="#000000">

        <%
Dim Tip
Set Tip = Server.CreateObject("MSWC.ContentRotator")

Response.Write(Tip.ChooseContent("tiprot.txt"))
%>

<hr size="1" color="#000000">
<A href="ContentRotator_VBScript.asp">Refresh (You may have to do this several times)</A>

    </BODY>
</HTML>


COUNTER_JSCRIPT.ASP

<HTML>
    <HEAD>
        <TITLE>Not Available At This Time</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Sorry, but this SDK sample is not available at this time.</b></font><br>

    </BODY>
</HTML>


COUNTER_VBSCRIPT.ASP

<HTML>
    <HEAD>
        <TITLE>Not Available At This Time</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Sorry, but this SDK sample is not available at this time.</b></font><br>

    </BODY>
</HTML>


FILESYSTEM_JSCRIPT.ASP

<% @Language=JScript %>

<%
// Define Constants

ForReading   = 1;
ForWriting   = 2;
ForAppending = 8;
%>

<HTML>
    <HEAD>
        <TITLE>FileSystem Component</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>FileSystem Component</b></font><br>

<hr size="1" color="#000000">

<%
// Map current path to physical path
curDir = Server.MapPath("\\iissamples\\sdk\\asp\\components\\");

// Create FileSytemObject Component
ScriptObject = Server.CreateObject("Scripting.FileSystemObject");

// Create and Write to a File
MyFile = ScriptObject.CreateTextFile(curDir + "\\" + "MyTextFile.txt", ForWriting);

for (x = 1; x < 5; x++)
{
MyFile.WriteLine("Line number " + x + " was written to MyTextFile.txt <br>");
}

MyFile.Close();
%>

<%
// Read From File and Output to Screen
MyFile = ScriptObject.OpenTextFile(curDir + "\\" + "MyTextFile.txt", ForReading);
Response.Write(MyFile.ReadAll());

MyFile.Close();
%>

    </BODY>
</HTML>


FILESYSTEM_VBSCRIPT.ASP

<% @Language=VBScript%>

<%
' Define Constants

ForReading   = 1
ForWriting   = 2
ForAppending = 8
%>

<HTML>
    <HEAD>
        <TITLE>FileSystem Component</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>FileSystem Component</b></font><br>

<hr size="1" color="#000000">

<%
' Map current path to physical path
curDir = Server.MapPath("\iissamples\sdk\asp\components\")

' Create FileSytemObject Component
Set ScriptObject = Server.CreateObject("Scripting.FileSystemObject")

' Create and Write to a File
Set MyFile = ScriptObject.CreateTextFile(curDir + "\" + "MyTextFile.txt", ForWriting)

For x = 1 to 5
MyFile.WriteLine "Line number " & x & " was written on " & now & "<br>"
Next

Myfile.Close
%>

<%
' Read From File and Output to Screen
Set MyFile = ScriptObject.OpenTextFile(curDir + "\" + "MyTextFile.txt", ForReading)
Response.Write MyFile.ReadAll
%>

    </BODY>
</HTML>


INDEX_JSCRIPT.ASP

<HTML>
    <HEAD>
        <TITLE>Index Server Component</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Index Server Component</b></font><br>

<hr size="1" color="#000000">

Index Server now comes with a collection of components that can be used from
Active Server Pages.  Please visit the Index Server Sample Site for examples
demonstrating how to Index Server with ASP.

<p><A href=/iissamples/issamples/> Click here to visit the Index Server Sample Site</A>

</BODY>
</HTML>


INDEX_VBSCRIPT.ASP

<HTML>
    <HEAD>
        <TITLE>Index Server Component</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Index Server Component</b></font><br>

<hr size="1" color="#000000">

Index Server now comes with a collection of components that can be used from
Active Server Pages.  Please visit the Index Server Sample Site for examples
demonstrating how to Index Server with ASP.

<p><A href=/iissamples/issamples/> Click here to visit the Index Server Sample Site</A>

</BODY>
</HTML>


PAGECOUNTER_JSCRIPT.ASP

<%@ Language=JScript%>

<%
// Ensure that this page is not cached

Response.Expires = 0;
%>

<HTML>
    <HEAD>
        <TITLE>Page Counter</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Page Counter Component</b></font><br>

<%
// Instantiate Page Counter Component

var MyPageCounter;
MyPageCounter = Server.CreateObject("MSWC.PageCounter");

// Determine the number of page hits

var HitMe;
HitMe = MyPageCounter.Hits("/iissamples/sdk/asp/components/PageCounter_JScript.asp");

// Output the number of hits to the page

Response.Write("<p>Times visited: " + HitMe);
%>

<p><A href=PageCounter_JScript.asp> Click here to revisit Page </A>

</BODY>
</HTML>


PAGECOUNTER_VBSCRIPT.ASP

<%@ Language=VBScript%>
<%  Option Explicit%>

<%
' Ensure that this page is not cached
Response.Expires = 0
%>

<HTML>
    <HEAD>
        <TITLE>Page Counter</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Page Counter Component</b></font><br>

<%
' Instantiate Page Counter Component

Dim MyPageCounter
Set MyPageCounter = Server.CreateObject("MSWC.PageCounter")

' Determine the number of page hits

Dim HitMe
HitMe = MyPageCounter.Hits("/iissamples/sdk/asp/components/PageCounter_VBScript.asp")

' Output the number of hits to the page

Response.Write "<p>Times visited: " & HitMe
%>

<p><A href=PageCounter_VBScript.asp> Click here to revisit Page </A>

</BODY>
</HTML>


PERMISSIONCHECK_JSCRIPT.ASP

<%@ Language=JScript%>

<HTML>
    <HEAD>
        <TITLE>Permission Checker</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Permission Checker</b></font><br>

<%
// Instantiate Permission Checker Component

Check = Server.CreateObject("MSWC.PermissionChecker");
%>

<p> Verify current user's access to a sample file:

<p> Using Physical Path =
<%= Check.HasAccess(".\\PermissionCheck_JScript.asp") %> <br>

Using Virtual Path =
<%= Check.HasAccess("/iissamples/sdk/asp/components/PermissionCheck_JScript.asp") %>

    </BODY>
</HTML>


PERMISSIONCHECK_VBSCRIPT.ASP

<%@ Language=VBScript%>
<%  Option Explicit%>

<HTML>
    <HEAD>
        <TITLE>Permission Checker</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Permission Checker</b></font><br>

<%
' Instantiate Permission Checker Component

Dim Check
Set Check = Server.CreateObject("MSWC.PermissionChecker")
%>

<p> Verify current user's access to a sample file:

<p> Using Physical Path =
<%= Check.HasAccess(".\PermissionCheck_VBScript.asp") %> <br>

Using Virtual Path =
<%= Check.HasAccess("/iissamples/sdk/asp/components/PermissionCheck_VBScript.asp") %>

    </BODY>
</HTML>


TOOLS_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Tools Component</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Tools Component</b></font><br>

        <hr size="1" color="#000000">

<%
var Tools;
var FoundFile, FileExists, FileDoesNotExist, NotFoundFile;
var RandInt, RandPosInt, RandIntBelow;

Tools = Server.CreateObject("MSWC.Tools");
%>

<h3> FileExists Example </h3>

<%
FoundFile = "Tools_VBScript.asp";
NotFoundFile = "blah.asp";

// Check if file exists

FileExists = Tools.FileExists(FoundFile);

// Output Response Appropriately

if (FileExists == true)
{
Response.Write("The File " + FoundFile + " Exists<BR>");
}
else
{
Response.Write("The File " + FoundFile + " Does Not Exist<BR>");
}

// Check if file exists

FileDoesNotExist = Tools.FileExists(NotFoundFile);

// Output Response Appropriately

if (FileDoesNotExist == true)
{
Response.Write("The File " + NotFoundFile + " Exists");
}
else
{
Response.Write("The File " + NotFoundFile + " Does Not Exist");
}
%>

<h3> Random Integer Example </h3>

<%
// Random Integer

RandInt = Tools.Random();
Response.Write("Random integer: ");
Response.Write(RandInt);
Response.Write("<BR>");

// Positive Random Integer

RandPosInt = Math.abs( Tools.Random() );
Response.Write("Positive random integer: ");
Response.Write(RandPosInt);
Response.Write("<BR>");

// Positive Random Integer between 0 and 100

RandIntBelow =  Math.abs( Tools.Random() ) % 100;
Response.Write("Random integer below 100: ");
Response.Write(RandIntBelow);
Response.Write("<BR>");
%>
    </BODY>
</HTML>


TOOLS_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
    <HEAD>
        <TITLE>Tools Component</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Tools Component</b></font><br>

        <hr size="1" color="#000000">

<%
Dim Tools
Dim FoundFile, FileExists, FileDoesNotExist, NotFoundFile
Dim RandInt, RandPosInt, RandIntBelow

Set Tools = Server.CreateObject("MSWC.Tools")
%>

<h3> FileExists Example </h3>

<%
FoundFile = "Tools_VBScript.asp"
NotFoundFile = "blah.asp"

' Check if file exists

FileExists = Tools.FileExists(FoundFile)

' Output Response Appropriately

If FileExists = TRUE Then
Response.Write "The File " & FoundFile & " Exists<BR>"
Else
Response.Write "The File " & FoundFile & " Does Not Exist<BR>"
End If

' Check if file exists

FileDoesNotExist = Tools.FileExists(NotFoundFile)

' Output Response Appropriately

If FileDoesNotExist = TRUE Then
Response.Write "The File " & NotFoundFile & " Exists"
Else
Response.write "The File " & NotFoundFile & " Does Not Exist"
End If
%>

<h3> Random Integer Example </h3>

<%
' Random Integer

RandInt = Tools.Random
Response.Write "Random integer: "
Response.Write RandInt
Response.Write "<BR>"

' Positive Random Integer

RandPosInt = Abs( Tools.Random )
Response.Write "Positive random integer: "
Response.Write RandPosInt
Response.Write "<BR>"

' Positive Random Integer between 0 and 100

RandIntBelow =  Abs( Tools.Random ) Mod 100
Response.write "Random integer below 100: "
Response.write RandIntBelow
Response.write "<BR>"
%>
    </BODY>
</HTML>


ADDDELETE_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<!--#include file="adojavas.inc"-->

<HTML>
    <HEAD>
        <TITLE>Add/Delete Database Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Add/Delete Database Sample</b></font><br>

        <hr size="1" color="#000000">

<%
var oConn;
var oRs;
var curDir;

// Map authors database to physical path

curDir = Server.MapPath("\\iissamples\\sdk\\asp\\database\\authors.mdb");

// Create ADO Connection Component to connect
// with sample database

oConn = Server.CreateObject("ADODB.Connection");
oConn.Open("DBQ="+curDir+";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;")

// Create ADO Recordset Component, and associate
// it with ADO connection

oRs = Server.CreateObject("ADODB.Recordset");
oRs.ActiveConnection = oConn;

// Get empty recordset

oRs.Source = "SELECT * FROM authors Where 1=2";
oRs.CursorType = adOpenStatic;
oRs.LockType = adLockOptimistic;
oRs.Open();

// Add New Record

oRs.Addnew();
oRs("Author").Value = "Paul Enfield";
oRs("YearBorn").Value = 1967;
oRs.Update();

// Output Result

Response.Write("<p>Inserted Author: "+oRs("Author")+"," + oRs("YearBorn"));

// Close Recordset

oRs.Close();

// Create Recordset Component for deletion operation

oRs = Server.CreateObject("ADODB.Recordset");
oRs.ActiveConnection = oConn;

// Create Recordset

oRs.Source = "SELECT * FROM authors WHERE YearBorn=1967 and Author='Paul Enfield'";
oRs.CursorType = adOpenForwardOnly;
oRs.LockType = adLockOptimistic;
oRs.Open();

// Delete record matching query

oRs.Delete();
oRs.Update();

// Output Status Result

Response.Write("<p>Deleted Author: Paul Enfield, 1967");
%>
    </BODY>
</HTML>


ADDDELETE_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<!--#include file="adovbs.inc"-->

<HTML>
    <HEAD>
        <TITLE>Add/Delete Database Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Add/Delete Database Sample</b></font><br>

        <hr size="1" color="#000000">

<%
Dim oConn
Dim oRs
Dim curDir

' Map authors database to physical path

curDir = Server.MapPath("\iissamples\sdk\asp\database\authors.mdb")

' Create ADO Connection Component to connect with sample database

Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open "DBQ="& curDir &";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;"

' Create ADO Recordset Component, and associate it
' with ADO connection

Set oRs = Server.CreateObject("ADODB.Recordset")
Set oRs.ActiveConnection = oConn

' Get empty recordset

oRs.Source = "SELECT * FROM authors Where 1=2"
oRs.CursorType = adOpenStatic
oRs.LockType = adLockOptimistic
oRs.Open

' Add New Record

oRs.Addnew
oRs("Author").Value = "Paul Enfield"
oRs("YearBorn").Value = 1967
oRs.Update

' Output Result

Response.Write("<p>Inserted Author: " & oRs("Author") & "," & oRs("YearBorn"))

' Close Recordset

oRs.Close

' Create Recordset Component for deletion operation

Set oRs = Server.CreateObject("ADODB.Recordset")
Set oRs.ActiveConnection = oConn

' Create Recordset

oRs.Source = "SELECT * FROM authors WHERE YearBorn=1967 and Author='Paul Enfield'"
oRs.CursorType = adOpenForwardOnly
oRs.LockType = adLockOptimistic' use a locktype permitting deletions
oRs.Open

' Delete record matching query

oRs.Delete
oRs.Update

' Output Status Result

Response.Write("<p>Deleted Author: Paul Enfield, 1967")
%>
</BODY>
</HTML>


BLOB_JSCRIPT.ASP

<% @ LANGUAGE=JScript%>

<!--#include file="adojavas.inc"-->

<%
// This sample utilizes the Image field in the PUB_INFO table.
// This table is installed with Microsoft SQL Server in
// the PUBS database.

var oConn;
var oRs;
var Pic;
var PicSize;

// Setup HTTP Header Information so that the browser interprets
// the returned data as a gif graphic file.  Note that browsers
// interpret returned information using MIME headers -- not file
// extensions.

Response.Buffer = true;
Response.ContentType = "image/gif";

// Create ADO Connection Object.  Use IISSDK OBDC Souce with
// default sa account and no password

oConn = Server.CreateObject("ADODB.Connection");
oConn.Open("DSN=LocalServer;UID=sa;PWD=;DATABASE=pubs");

// Query SQL to obtain recordset containing gif BLOB

oRs = oConn.Execute("SELECT logo FROM pub_info WHERE pub_id='0736'");

// Obtain local variable of GIF

PicSize = oRs("logo").ActualSize;
Pic = oRs("logo").GetChunk(PicSize);

// Write Data back to client.  Because MIME type is set to
// image/gif, the browser will automatically render as picture

Response.BinaryWrite(Pic);
Response.End();
%>


BLOB_VBSCRIPT.ASP

<% @ LANGUAGE=VBScript%>
<% Option Explicit%>

<!--#include file="adovbs.inc"-->

<%
' This sample utilizes the Image field in the PUB_INFO table.
' This table is installed with Microsoft SQL Server in the
' PUBS database.

Dim oConn
Dim oRs
Dim Pic
Dim PicSize

' Setup HTTP Header Information so that the browser interprets
' the returned data as a gif graphic file.  Note that browsers
' interpret returned information using MIME headers -- not file
' extensions.

Response.Buffer = TRUE
Response.ContentType = "image/gif"

' Create ADO Connection Object.  Use IISSDK OBDC Souce with
' default sa account and no password

Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open "DSN=LocalServer;UID=sa;PWD=;DATABASE=pubs"

' Query SQL to obtain recordset containing gif BLOB

Set oRs = oConn.Execute("SELECT logo FROM pub_info WHERE pub_id='0736'")

' Obtain local variable of GIF

PicSize = oRs("logo").ActualSize
Pic = oRs("logo").GetChunk(PicSize)

' Write Data back to client.  Because MIME type is set to
' image/gif, the browser will automatically render as picture

Response.BinaryWrite Pic
Response.End
%>


HTMLTABLE_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>
<HTML>

<HEAD>
<TITLE>Query to Fill HTML Table (using ADO Table Component)</TITLE>
</HEAD>

<BODY bgcolor="white" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Query to Fill HTML Table</b> (using ADO Table Component)
</font>
<br>
<hr size="1" color="#000000">

<%
  var oConn;// The connection object
  var SQLstatement;
  var RecSet;// The record set returned
  var oHTMLTable;// The HTML Table Formatter object
  var iRows;// The place holder for the number of rows returned in the record set

  oConn = Server.CreateObject("ADODB.Connection");

  // You must first create the file DSN for this sample to look at.
  // This SDK comes with an authors.mdb in the same directory as this file
  // Go to Control Panels | ODBC | File DSN
  // Select Add and Microsoft Access Driver (*.mdb)
  // Name the DSN "Authors.dsn" and point it to the authors.mdb
  oConn.Open("FILEDSN=Authors.dsn");

  SQLstatement = "SELECT * From Authors";

  RecSet = oConn.Execute(SQLstatement,iRows);

  // This sample does NOT include the HTMLTable object.  You will need to get this
  // component from the Microsoft Web Site.  Follow the installation instructions
  // which come with the component to install the component.
  oHTMLTable = Server.CreateObject("IISSample.htmlTbl");
  oHTMLTable.Borders = true;
  oHTMLTable.Caption = "<b>Authors of These Samples</b>";
  oHTMLTable.CaptionStyle = "align=left";
  oHTMLTable.AutoFormat(RecSet);

  RecSet.close();
  oConn.close();
%>

</BODY>

</HTML>


HTMLTABLE_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit%>

<HTML>

<HEAD>
<TITLE>Query to Fill HTML Table (using ADO Table Component)</TITLE>
</HEAD>

<BODY bgcolor="white" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Query to Fill HTML Table</b> (using ADO Table Component)
</font>
<br>
<hr size="1" color="#000000">

<%
  Dim Conn' The connection object
  Dim SQLstatement
  Dim RecSet' The record set returned
  Dim oHTMLTable' The HTML Table Formatter object
  Dim iRows' The place holder for the number of rows returned in the record set

  Set Conn = Server.CreateObject("ADODB.Connection")

  ' You must first create the file DSN for this sample to look at.
  ' This SDK comes with an authors.mdb in the same directory as this file
  ' Go to Control Panels | ODBC | File DSN
  ' Select Add and Microsoft Access Driver (*.mdb)
  ' Name the DSN "Authors.dsn" and point it to the authors.mdb
  Conn.Open "FILEDSN=Authors.dsn"

  SQLstatement = "SELECT * From Authors"

  Set RecSet = Conn.Execute(SQLstatement,iRows)

  ' This sample does NOT include the HTMLTable object.  You will need to get this
  ' component from the Microsoft Web Site.  Follow the installation instructions
  ' which come with the component to install the component.
  Set oHTMLTable = Server.CreateObject("IISSample.htmlTbl")
  oHtmlTable.Borders = True
  oHtmlTable.Caption = "<b>Authors of These Samples</b>"
  oHtmlTable.CaptionStyle = "align=left"
  oHtmlTable.AutoFormat(RecSet)

  RecSet.close
  oConn.close
%>

</BODY>

</HTML>


LIMITROWS_JSCRIPT.ASP

<%@ LANGUAGE = JScript  %>

<!--#include file="adojavas.inc"-->

<HTML>
    <HEAD>
        <TITLE>LimitRows From Database</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

    <!-- Display Header -->

    <font size="4" face="Arial, Helvetica">
    <b>LimitRows From Database</b></font><br>

    <hr size="1" color="#000000">

    Contacts within the Authors Database:<br><br>

<%
var oConn;
var oRs;
var curDir;
var Index;

// Map authors database to physical path

curDir = Server.MapPath("\\iissamples\\sdk\\asp\\database\\authors.mdb");

// Create ADO Connection Component to connect
// with sample database

oConn = Server.CreateObject("ADODB.Connection");
oConn.Open("DBQ="+curDir+";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;");

// Create ADO Recordset Component

oRs = Server.CreateObject("ADODB.Recordset");
oRs.ActiveConnection = oConn;

// Set Recordset PageSize so that it only holds 10 rows

oRs.PageSize = 10;

// Get recordset

oRs.Source = "SELECT * FROM authors";
oRs.CursorType = adOpenStatic;

// Open Recordset

oRs.Open();
%>

<TABLE border = 1>
<%
var RecordCount;
RecordCount = 0;

while ((!oRs.eof) && (RecordCount < oRs.PageSize)) { %>

<tr>
<% for(Index=0; Index < oRs.fields.count; Index++) { %>
<TD VAlign=top><% = oRs(Index)%></TD>
<% } %>
</tr>

<%
RecordCount = RecordCount + 1;
oRs.MoveNext();
}
%>

</TABLE>

<%
oRs.Close();
oConn.Close();
%>

</BODY>
</HTML>


LIMITROWS_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit%>

<!--#include file="adovbs.inc"-->

<HTML>
    <HEAD>
        <TITLE>LimitRows From Database</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

    <!-- Display Header -->

    <font size="4" face="Arial, Helvetica">
    <b>LimitRows From Database</b></font><br>

    <hr size="1" color="#000000">

    Contacts within the Authors Database:<br><br>

<%
Dim oConn
Dim oRs
Dim curDir
Dim Index

' Map authors database to physical path

curDir = Server.MapPath("\iissamples\sdk\asp\database\authors.mdb")

' Create ADO Connection Component to connect
' with sample database

Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open "DBQ="& curDir &";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;"

' Create ADO Recordset Component

Set oRs = Server.CreateObject("ADODB.Recordset")
Set oRs.ActiveConnection = oConn

' Set Recordset PageSize so that it only
' holds 10 rows

oRs.PageSize = 10

' Get recordset

oRs.Source = "SELECT * FROM authors"
oRs.CursorType = adOpenStatic

' Open Recordset
oRs.Open
%>

<TABLE border = 1>
<%
Dim RecordCount
RecordCount = 0

Do while ((Not oRs.eof) And (RecordCount < oRs.PageSize)) %>

<tr>
<% For Index=0 to (oRs.fields.count-1) %>
<TD VAlign=top><% = oRs(Index)%></TD>
<% Next %>
</tr>

<%
RecordCount = RecordCount + 1
oRs.MoveNext
Loop
%>

</TABLE>

<%
oRs.close
oConn.close
%>

</BODY>
</HTML>


MULTISCROLLING_JSCRIPT.ASP

<% @LANGUAGE="JScript" %>

<!--#include File="adojavas.inc"-->

<HTML>
    <HEAD>
        <TITLE>MultiScrolling Database Sample</TITLE>
    </HEAD>

<BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>MultiScrolling Database Sample</b></font><br>

<hr size="1" color="#000000">

Contacts within the Authors Database:<br><br>

<%
var oConn;
var oRs;
var curDir;
var Mv;
var PageNo;
var j;
var i;

// Map authors database to physical path

curDir = Server.MapPath("\\iissamples\\sdk\\asp\\database\\authors.mdb");

// Create ADO Connection Component to connect with
// sample database

oConn = Server.CreateObject("ADODB.Connection");
oConn.Open("DBQ="+curDir+";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;");

// Create ADO Recordset Component

oRs = Server.CreateObject("ADODB.Recordset");

// Determine what PageNumber the scrolling currently is on

Mv = Request("Mv");
PageNo = Request("PageNo");

if (!((PageNo > 0) || (PageNo < 999)))
{
PageNo = 1;
}

// Setup Query Recordset (4 records per page)
oRs.Open ("SELECT * FROM Authors", oConn, adOpenStatic);
oRs.PageSize = 4;

// Adjust PageNumber as Appropriate

if (Mv == "Page Up")
{
if (PageNo > 1)
{
PageNo--;
}
else
{
PageNo = 1;
}
}
else if (Mv == "Page Down")
{
if (oRs.AbsolutePage < oRs.PageCount)
{
PageNo++;
}
else
{
PageNo = oRs.PageCount;
}
}
else
{
PageNo = 1;
}

oRs.AbsolutePage = PageNo;
%>

<!-- Draw Table of Contacts in DB -->

<TABLE BORDER=1>
<%  for (j = 0; j < oRs.PageSize; j++)
{ %>
<TR>
<%
// Don't try to print the EOF record.
if (oRs.EOF)
{
break;
} %>
<%  for (i = 0; i < oRs.Fields.Count; i++)
{ %>
<TD VALIGN=TOP><%= oRs(i) %></TD>
<%  } %>
</TR>

<%
oRs.MoveNext()
}
%>
</TABLE>

<!-- Scrolling Navigation Control for Sample -->

<Form Action=MultiScrolling_JScript.asp Method="POST">
<Input Type=Hidden Name= PageNo Value=<%= PageNo %>>
<!-- Only show appropriate buttons -->

<%  if (PageNo <  oRs.PageCount) { %>
<INPUT TYPE="Submit" Name="Mv" Value="Page Down">
<%  } %>

<%  if (PageNo > 1) { %>
<INPUT TYPE="Submit" Name="Mv" Value="Page Up">
<%  } %>

</Form>

<%
oRs.close();
oConn.close();
%>

</BODY>
</HTML>


MULTISCROLLING_VBSCRIPT.ASP

<% @LANGUAGE="VBSCRIPT" %>
<% Option Explicit%>

<!--#include File="adovbs.inc"-->

<HTML>
    <HEAD>
        <TITLE>MultiScrolling Database Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

    <font size="4" face="Arial, Helvetica">
    <b>MultiScrolling Database Sample</b></font><br>

    <hr size="1" color="#000000">

    Contacts within the Authors Database:<br><br>

<%
Dim oConn
Dim oRs
Dim curDir
Dim Mv
Dim PageNo
Dimj
Dim i

' Map authors database to physical path

curDir = Server.MapPath("\iissamples\sdk\asp\database\authors.mdb")

' Create ADO Connection Component to connect with
' sample database

Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open "DBQ="& curDir &";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;"

' Create ADO Recordset Component

Set oRs = Server.CreateObject("ADODB.Recordset")

' Determine what PageNumber the scrolling currently is on

Mv = Request("Mv")

If Request("PageNo") = "" Then
PageNo = 1
Else
PageNo = Request("PageNo")
End If

' Setup Query Recordset (4 records per page)

oRs.Open "SELECT * FROM Authors", oConn, adOpenStatic
oRs.PageSize = 4

' Adjust PageNumber as Appropriate

If Mv = "Page Up" or Mv = "Page Down" Then
Select Case Mv
Case "Page Up"
If PageNo > 1 Then
PageNo = PageNo - 1
Else
PageNo = 1
End If
Case "Page Down"
If oRs.AbsolutePage < oRs.PageCount Then
PageNo = PageNo + 1
Else
PageNo = oRs.PageCount
End If
Case Else
PageNo = 1
End Select
End If

oRs.AbsolutePage = PageNo
%>

<!-- Draw Table of Contacts in DB -->

<TABLE BORDER=1>
<%  For j = 1 to oRs.PageSize %>
<TR>
<%  For i = 0 to oRs.Fields.Count - 1 %>
<TD VALIGN=TOP><%= oRs(i) %></TD>
<%  Next %>
</TR>

<%
oRs.MoveNext

' Don't try to print the EOF record.
If oRs.EOF Then
Exit For
End If
Next %>
</TABLE>

<!-- Scrolling Navigation Control for Sample -->

<Form Action=MultiScrolling_VBScript.asp Method="POST">
<Input Type="Hidden" Name="PageNo" Value="<%= PageNo %>">
<!-- Only show appropriate buttons -->
<%  If PageNo <  oRs.PageCount Then %>
<INPUT TYPE="Submit" Name="Mv" Value="Page Down">
<%  End If %>

<%  If PageNo > 1 Then %>
<INPUT TYPE="Submit" Name="Mv" Value="Page Up">
<%  End If %>

</Form>
</BODY>
</HTML>


SIMPLEQUERY_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Simple ADO Query</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Simple ADO Query with ASP</b></font><br>

<hr size="1" color="#000000">

Contacts within the Authors Database:<br><br>

<%
var oConn;
var oRs;
var curDir;

// Map authors database to physical path

curDir = Server.MapPath("\\iissamples\\sdk\\asp\\database\\authors.mdb");

// Create ADO Connection Component to connect with
// sample database

oConn = Server.CreateObject("ADODB.Connection");
oConn.Open("DBQ="+curDir+";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;");

// Execute a SQL query and store the results within
// recordset

oRs = oConn.Execute("SELECT * From authors");
%>

<TABLE border = 1>
<%
while (!oRs.eof) { %>

<tr>
<% for(Index=0; Index < (oRs.fields.count); Index++) { %>
<TD VAlign=top><% = oRs(Index)%></TD>
<% } %>
</tr>

<% oRs.MoveNext();
}
%>

</TABLE>

<%
oRs.close();
oConn.close();
%>

</BODY>
</HTML>


SIMPLEQUERY_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit%>

<HTML>
    <HEAD>
        <TITLE>Simple ADO Query</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Simple ADO Query with ASP</b></font><br>

<hr size="1" color="#000000">

Contacts within the Authors Database:<br><br>

<%
Dim oConn
Dim oRs
Dim curDir
Dim Index

' Map authors database to physical path
curDir = Server.MapPath("\iissamples\sdk\asp\database\authors.mdb")

' Create ADO Connection Component to connect
' with sample database

Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open "DBQ="& curDir &";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;"

' Execute a SQL query and store the results
' within recordset

Set oRs = oConn.Execute("SELECT * From authors")
%>

<TABLE border = 1>
<%
Do while (Not oRs.eof) %>

<tr>
<% For Index=0 to (oRs.fields.count-1) %>
<TD VAlign=top><% = oRs(Index)%></TD>
<% Next %>
</tr>

<% oRs.MoveNext
Loop
%>

</TABLE>

<%
oRs.close
oConn.close
%>

</BODY>
</HTML>


STOREDPROCEDURES_JSCRIPT.ASP

<% @ LANGUAGE="JScript" %>

<!--#include file="adojavas.inc"-->

<%
// This example calls the ByRoyalty stored procedure
// installed with the PUBS database with Microsoft SQL Server.

// This sample assumes that SQL Server is running on the local machine
%>

<HTML>
    <HEAD>
        <TITLE>Using Stored Procedures</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using Stored Procedures</b></font><p>

<%
var oConn;
var oCmd;
var oRs;

oConn = Server.CreateObject("ADODB.Connection");
oCmd = Server.CreateObject("ADODB.Command");

// Open ADO Connection using account "sa"
// and blank password

oConn.Open("DSN=LocalServer;UID=sa;PWD=;DATABASE=pubs");
oCmd.ActiveConnection = oConn;

// Setup Call to Stored Procedure and append parameters

oCmd.CommandText = "{call byroyalty(?)}";
oCmd.Parameters.Append(oCmd.CreateParameter("@Percentage", adInteger, adParamInput));

// Assign value to input parameter

oCmd("@Percentage") = 75;

// Fire the Stored Proc and assign resulting recordset
// to our previously created object variable

oRs = oCmd.Execute();
%>

Author ID = <% Response.Write(oRs("au_id")) %><BR>

    </BODY>
</HTML>


STOREDPROCEDURES_VBSCRIPT.ASP

<% @ LANGUAGE="VBSCRIPT" %>
<%Option Explicit%>

<!--#include file="adovbs.inc"-->

<%
' This example can be used to call the ByRoyalty stored procedure
' installed with the PUBS database with Microsoft SQL Server.

' This sample assumes that SQL Server is running on the local machine
%>

<HTML>
    <HEAD>
        <TITLE>Using Stored Procedures</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using Stored Procedures</b></font><p>

<%
Dim oConn
Dim oCmd
Dim oRs

Set oConn = Server.CreateObject("ADODB.Connection")
Set oCmd = Server.CreateObject("ADODB.Command")

' Open ADO Connection using account "sa"
' and blank password

oConn.Open "DSN=LocalServer;UID=sa;PWD=;DATABASE=pubs"
Set oCmd.ActiveConnection = oConn

' Setup Call to Stored Procedure and append parameters

oCmd.CommandText = "{call byroyalty(?)}"
oCmd.Parameters.Append oCmd.CreateParameter("@Percentage", adInteger, adParamInput)

' Assign value to input parameter

oCmd("@Percentage") = 75

' Fire the Stored Proc and assign resulting recordset
' to our previously created object variable

Set oRs = oCmd.Execute
%>

Author ID = <% Response.Write oRs("au_id") %><BR>

    </BODY>
</HTML>


UPDATE_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<!--#include file="adojavas.inc"-->

<HTML>
    <HEAD>
        <TITLE>Update Database</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Update Database</b></font><br>

        <hr size="1" color="#000000">

<%
var oConn;// object for ADODB.Connection obj
var oRs;// object for output recordset object
var curDir;// Directory of authors.mdb file

// Map authors database to physical path
curDir = Server.MapPath("\\iissamples\\sdk\\asp\\database\\authors.mdb");

// Create ADO Connection Component to connect with sample database
oConn = Server.CreateObject("ADODB.Connection");
oConn.Open("DBQ="+curDir+";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;");

// Create ADO Recordset Component, and associate it with ADO connection
oRs = Server.CreateObject("ADODB.Recordset");
oRs.ActiveConnection = oConn;

// Get empty recordset
oRs.Source = "SELECT * FROM authors";
oRs.CursorType = adOpenStatic;// use a cursor other than Forward Only
oRs.LockType = adLockOptimistic;// use a locktype permitting insertions
oRs.Open();

// Change values in first record found
oRs("Author").Value = "Scott Guthrie";
oRs("Yearborn").Value = 1975;
oRs.Update();
%>

Changed Author: <%= oRs("Author") %>, <%= oRs("Yearborn") %>
    </BODY>
</HTML>


UPDATE_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<!--#include file="adovbs.inc"-->

<HTML>
    <HEAD>
        <TITLE>Update Database</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Update Database</b></font><br>

        <hr size="1" color="#000000">

<%
Dim oConn' object for ADODB.Connection obj
Dim oRs' object for output recordset object
Dim curDir' Directory of authors.mdb file

' Map authors database to physical path
curDir = Server.MapPath("\iissamples\sdk\asp\database\authors.mdb")

' Create ADO Connection Component to connect with sample database
Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open "DBQ="& curDir &";Driver={Microsoft Access Driver (*.mdb)};DriverId=25;FIL=MS Access;"

' Create ADO Recordset Component, and associate it with ADO connection
Set oRs = Server.CreateObject("ADODB.Recordset")
Set oRs.ActiveConnection = oConn

' Get empty recordset
oRs.Source = "SELECT * FROM authors"
oRs.CursorType = adOpenStatic' use a cursor other than Forward Only
oRs.LockType = adLockOptimistic' use a locktype permitting insertions
oRs.Open

' Change values in first record found
oRs("Author").Value = "Scott Guthrie"
oRs("Yearborn").Value = 1975
oRs.Update
%>

Changed Author: <%= oRs("Author") %>, <%= oRs("Yearborn") %>
    </BODY>
</HTML>


CODEBRWS.ASP

<SCRIPT LANGUAGE=VBScript RUNAT=Server>
 REM Returns the minimum number greater than 0
 REM If both are 0, returns -1
 FUNCTION fMin (iNum1, iNum2)
  If iNum1 = 0 AND iNum2 = 0 Then
    fMin = -1
  ElseIf iNum2 = 0 Then
    fMin = iNum1
  ElseIf iNum1 = 0 Then
    fMin = iNum2
  ElseIf iNum1 < iNum2 Then
    fMin = iNum1
  Else
    fMin = iNum2
  End If
 END FUNCTION
</SCRIPT>

<SCRIPT LANGUAGE=VBScript RUNAT=Server>
 FUNCTION fCheckLine (ByVal strLine)

  fCheckLine = 0
  iTemp = 0

  iPos = InStr(strLine, "<" & "%")
  If fMin(iTemp, iPos) = iPos Then
    iTemp = iPos
    fCheckLine = 1
  End If

  iPos = InStr(strLine, "%" & ">")
  If fMin(iTemp, iPos) = iPos Then
    iTemp = iPos
    fCheckLine = 2
  End If

  iPos = InStr(1, strLine, "<" & "SCRIPT", 1)
  If fMin(iTemp, iPos) = iPos Then
    iTemp = iPos
    fCheckLine = 3
  End If

  iPos = InStr(1, strLine, "<" & "/SCRIPT", 1)
  If fMin(iTemp, iPos) = iPos Then
    iTemp = iPos
    fCheckLine = 4
  End If

  iPos = InStr(1, strLine, "<" & "!--", 1)
  If fMin(iTemp, iPos) = iPos Then
    iTemp = iPos
    fCheckLine = 5
  End If

  iPos = InStr(1, strLine, "-" & "->", 1)
  If fMin(iTemp, iPos) = iPos Then
    iTemp = iPos
    fCheckLine = 6
  End If

 END FUNCTION
</SCRIPT>

<SCRIPT LANGUAGE=VBScript RUNAT=Server>
SUB PrintHTML (ByVal strLine)
iSpaces = Len(strLine) - Len(LTrim(strLine))
i = 1
While Mid(Strline, i, 1) = Chr(9)
iSpaces = iSpaces + 5
i = i + 1
Wend
  If iSpaces > 0 Then
    For i = 1 to iSpaces
      Response.Write("&nbsp;")
    Next
  End If
  iPos = InStr(strLine, "<")
  If iPos Then
    Response.Write(Left(strLine, iPos - 1))
    Response.Write("&lt;")
    strLine = Right(strLine, Len(strLine) - iPos)
    Call PrintHTML(strLine)
  Else
    Response.Write(strLine)
  End If
 END SUB
</SCRIPT>

<SCRIPT LANGUAGE=VBScript RUNAT=Server>
 SUB PrintLine (ByVal strLine, iFlag)

  Select Case iFlag
    Case 0
      Call PrintHTML(strLine)

    Case 1
      iPos = InStr(strLine, "<" & "%")
      Call PrintHTML(Left(strLine, iPos - 1))
      Response.Write("<FONT COLOR=#ff0000>")
      Response.Write("&lt;%")
      strLine = Right(strLine, Len(strLine) - (iPos + 1))
      Call PrintLine(strLine, fCheckLine(strLine))

    Case 2
      iPos = InStr(strLine, "%" & ">")
      Call PrintHTML(Left(strLine, iPos -1))
      Response.Write("%&gt;")
      Response.Write("</FONT>")
      strLine = Right(strLine, Len(strLine) - (iPos + 1))
      Call PrintLine(strLine, fCheckLine(strLine))

    Case 3
      iPos = InStr(1, strLine, "<" & "SCRIPT", 1)
      Call PrintHTML(Left(strLine, iPos - 1))
      Response.Write("<FONT COLOR=#0000ff>")
      Response.Write("&lt;SCRIPT")
      strLine = Right(strLine, Len(strLine) - (iPos + 6))
      Call PrintLine(strLine, fCheckLine(strLine))

    Case 4
      iPos = InStr(1, strLine, "<" & "/SCRIPT>", 1)
      Call PrintHTML(Left(strLine, iPos - 1))
      Response.Write("&lt;/SCRIPT&gt;")
      Response.Write("</FONT>")
      strLine = Right(strLine, Len(strLine) - (iPos + 8))
      Call PrintLine(strLine, fCheckLine(strLine))

    Case 5
      iPos = InStr(1, strLine, "<" & "!--", 1)
      Call PrintHTML(Left(strLine, iPos - 1))
      Response.Write("<FONT COLOR=#0000ff>")
      Response.Write("&lt;!--")
      strLine = Right(strLine, Len(strLine) - (iPos + 3))
      Call PrintLine(strLine, fCheckLine(strLine))

    Case 6
      iPos = InStr(1, strLine, "-" & "->", 1)
      Call PrintHTML(Left(strLine, iPos - 1))
      Response.Write("--&gt;")
      Response.Write("</FONT>")
      strLine = Right(strLine, Len(strLine) - (iPos + 2))
      Call PrintLine(strLine, fCheckLine(strLine))

    Case Else
      Response.Write("FUNCTION ERROR -- PLEASE CONTACT ADMIN.")
  End Select
 END SUB
</SCRIPT>

<HTML>
    <HEAD>
        <META NAME="DESCRIPTION" CONTENT="ASP Source code browser">
        <META NAME="GENERATOR" CONTENT="Microsoft Visual InterDev 1.0">
        <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso8859-1">
    </HEAD>

    <BODY BGCOLOR="#FFFFFF">

        <FONT FACE="VERDANA, ARIAL, HELVETICA">
        <%
        strVirtualPath = Request("Source")
        %>

             <!-- DISPLAY THE COLOR LEGEND -->
        <TABLE BORDER=1>
        <TR>
          <TD WIDTH="25" BGCOLOR="#FF0000">&nbsp;&nbsp;
          </TD>
          <TD><FONT FACE="VERDANA, ARIAL, HELVETICA" SIZE="2">ASP Script</FONT>
          </TD>
            </TR>
            <TR>
          <TD BGCOLOR="#0000FF">&nbsp;&nbsp;
          </TD>
          <TD><FONT FACE="VERDANA, ARIAL, HELVETICA" SIZE="2">Comments</FONT>
          </TD>
            </tr>
            <tr>
          <TD BGCOLOR="#000000">&nbsp;&nbsp;
          </TD>
          <TD><FONT FACE="VERDANA, ARIAL, HELVETICA" SIZE="2">HTML and Text</FONT>
          </TD>
        </TR>
        </TABLE>

        <hr size="1" color="#000000">

        <FONT FACE="VERDANA, ARIAL, HELVETICA" SIZE="2">

        <%
        strFilename = Server.MapPath(strVirtualPath)
        Set FileObject = Server.CreateObject("Scripting.FileSystemObject")
        Set oInStream = FileObject.OpenTextFile(strFilename)

  While NOT oInStream.AtEndOfStream
          strOutput = oInStream.ReadLine

          Call PrintLine(strOutput, fCheckLine(strOutput))
          Response.Write("<BR>")
        Wend
        %>

        <hr>

        </FONT>
    </BODY>
</HTML>


SAMPFRAM.ASP

<HTML>

  <HEAD>
        <META NAME="DESCRIPTION" CONTENT="Sample Frame Creator">
        <META NAME="GENERATOR" CONTENT="Microsoft Notepad">
        <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso8859-1">
  </HEAD>

<%

ovfile = Request("ovfile")
srcfile = Request("srcfile")

%>

  <FRAMESET rows="25, *" FRAMEBORDER=0 FRAMESPACING=0 border=0 cellspacing=0>

        <frame src="/IIsSamples/SDK/asp/docs/Toolbar.asp?ovfile=<% =ovfile %>&srcfile=<% =srcfile %>" name="ToolBar" noresize marginheight=0 marginwidth=0 framespacing=0 frameborder="0">
        <frame src="<% =ovfile %>?DontFrame=1" name="SampMain" noresize framespacing=0 frameborder="0">

  </FRAMESET>

</HTML>


TOOLBAR.ASP

<HTML>

  <HEAD>
        <META NAME="DESCRIPTION" CONTENT="Sample Toolbar">
        <META NAME="GENERATOR" CONTENT="Microsoft Notepad">
        <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso8859-1">
  </HEAD>

<%
ovfile = Request("ovfile")
srcfile = Request("srcfile")
%>

    <BODY bgcolor="#000000" link="#FFCC00" vlink="#FFCC00"  topmargin = 2>

<center>
        <h4><b>
            <A href ="<%=ovfile%>?DontFrame=1" target = "SampMain" >Overview  </A> |
            <A href="/IIsSamples/SDK/asp/<%=srcfile%>_VBScript.asp" target = "SampMain"> Run Example  </A> |
            <A href="/IIsSamples/SDK/asp/docs/CodeBrws.asp?source=/IIsSamples/SDK/asp/<%=srcfile%>_VBScript.asp" target = "SampMain"> VBScript Source  </A> |
            <A href="/IIsSamples/SDK/asp/docs/CodeBrws.asp?source=/IIsSamples/SDK/asp/<%=srcfile%>_JScript.asp" target = "SampMain"> JScript Source</A>
        </b></h4>
        </center>

    </BODY>
</HTML>


BUFFER_JSCRIPT.ASP

<% @Language=JScript%>

<%
// Turn Buffering on (this must be done before anything is sent
Response.Buffer = true;
%>

<HTML>
    <HEAD>
        <TITLE>This is Bogus HTML</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>You shouldn't see this!!</b></font><br>

    </BODY>
</HTML>

<%
// Clear everything already printed
Response.Clear();
%>

<HTML>
    <HEAD>
        <TITLE>Buffering Example</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Buffering Example</b></font><p>

This html was buffered on the server before it was sent.

    </BODY>
</HTML>

<%
// Send the new html
Response.Flush();
%>


BUFFER_VBSCRIPT.ASP

<% @Language=VBScript%>
<% Option Explicit%>

<%
' Turn Buffering on (this must be done before anything is sent
Response.Buffer = TRUE
%>

<HTML>
    <HEAD>
        <TITLE>This is Bogus HTML</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>You shouldn't see this!!</b></font><br>

    </BODY>
</HTML>

<%
' Clear everything already printed
Response.Clear
%>

<HTML>
    <HEAD>
        <TITLE>Buffering Example</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Buffering Example</b></font><p>

This html was buffered on the server before it was sent.

    </BODY>
</HTML>

<%
' Send the new html

Response.Flush
%>


CANCEL_JSCRIPT.ASP

<%@ Language=JScript %>

<HTML>
    <HEAD>
        <TITLE>Client Connection</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Client Connection</b></font><br>

<p>About to perform a compute intensive operation... <br>

<%
var x, i;
x = 1;

// Perform a long operation.....

for(i = 0; i < 1000; i++)
{
x = x + 5;
}

// Check to see if the client is still
// connected before doing anything more

if (Response.IsClientConnected())
{
// Ouput Status
Response.Write("User still connected<br>");

// Perform another long operation

for (i = 1; i < 1000; i++)
{
x = x + 5;
}
}

// If the user is still connected, send back
// a closing message

if (Response.IsClientConnected())
{
Response.Write("<p>Finished<br>");
Response.Write("Thank you for staying around!  :-)");
}
%>

</BODY>
</HTML>


CANCEL_VBSCRIPT.ASP

<%@ Language=VBScript%>
<% Option Explicit%>

<HTML>
    <HEAD>
        <TITLE>Client Connection</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Client Connection</b></font><br>

<p>About to perform compute intensive operation<br>

<%
Dim x, i
x = 1

' Perform a long operation.....

For i = 1 to 1000
x = x + 5
Next

' Check to see if the client is still connected
' before doing anything more

If Response.IsClientConnected Then

' Ouput Status
Response.Write "User is still connected<br>"

' Perform another long operation

For i = 1 to 1000
x = x + 5
Next

End If

' If the user is still connected, send back
' a closing message

If Response.IsClientConnected Then

Response.Write "<p>Finished<br>"
Response.Write "Thank you for staying around!  :-)"

End If
%>

    </BODY>
</HTML>


CDF_JSCRIPT.ASP

<%@ LANGUAGE="JSCRIPT" %>
<%  Response.ContentType = "application/x-cdf" %>

<?XML version="1.0"?>

<CHANNEL HREF="http://www.microsoft.com/">

<TITLE>IIS SDK CDF Test</TITLE>
<ABSTRACT>CDF file with ASP</ABSTRACT>

<SCHEDULE>
<INTERVALTIME HOUR="1" />
</SCHEDULE>

<%
GetChannelItems();
%>

</CHANNEL>

<%
// GetChannelItems uses the FileSystem Object
// to access a file on the server that holds
// all the latest news headlines.

function GetChannelItems()
{
// Open FileList.txt to provide dynamic CDF information

fso = Server.CreateObject("Scripting.FileSystemObject");
prFile = fso.OpenTextFile(Server.MapPath("FileList.txt"));

// Iterate through the file, dynamically creating CDF
// item entries

while(!prFile.AtEndOfStream)
{

// Get headline for item

strTitle = prFile.ReadLine();

// Get URL of item

strLink = prFile.ReadLine();

// Write CDF Item Tag

Response.Write("<ITEM HREF=" + strLink + ">");
Response.Write("  <TITLE>" + strTitle + "!!!</TITLE>");
Response.Write("  <ABSTRACT>" + strTitle + "</ABSTRACT>");
Response.Write("</ITEM>");
}
}
%>


CDF_VBSCRIPT.ASP

<%@ LANGUAGE="VBSCRIPT" %>
<%  Response.ContentType = "application/x-cdf" %>

<?XML version="1.0"?>

<CHANNEL HREF="http://www.microsoft.com/">

<TITLE>IIS SDK CDF Test</TITLE>
<ABSTRACT>CDF file with ASP</ABSTRACT>

<SCHEDULE>
<INTERVALTIME HOUR="1" />
</SCHEDULE>

<%
GetChannelItems
%>

</CHANNEL>

<%
' GetChannelItems uses the FileSystem Object
' to access a file on the server which holds
' all the CDF items.

Sub GetChannelItems

On Error Resume Next
Dim fso, prFile, strTitle, strLink,strCategory

' Open FileList.txt to provide dynamic CDF information

Set fso = Server.CreateObject("Scripting.FileSystemObject")
Set prFile = fso.OpenTextFile(Server.MapPath("FileList.txt"))

If Err.Number <> 0 Then
Exit Sub
End If

' Iterate through the file, dynamically creating CDF item entries

Do Until prFile.AtEndOfStream

' Get headline

strTitle = prFile.ReadLine

' Get URL

strLink = prFile.ReadLine

' Write CDF Item tag back to Browser

Response.Write("<ITEM HREF=" & strLink & ">")
Response.Write("  <TITLE>" & strTitle & "!!!</TITLE>")
Response.Write("  <ABSTRACT>" & strTitle & "</ABSTRACT>")
Response.Write("</ITEM>")
Loop

End Sub
%>


CLIENTSCRIPT_JSCRIPT.ASP

<% @Language=JScript%>

<!-- Define Client Side Scripting Function to Display to User -->
<!-- Note that server-side scripting is being used to dynamically -->
<!-- construct the return message.-->

<Script Language=JavaScript>
function Doit()
{
alert("Your ASP Session ID is:" + <%=Session.SessionID %>);
}
</Script>

<HTML>
    <HEAD>
        <TITLE>Client Side Scripting</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Client Side Scripting</b></font><p>

<Input Type=Button Value="Click Here" OnClick=Doit()>

    </BODY>
</HTML>


CLIENTSCRIPT_VBSCRIPT.ASP

<% @Language=VBScript %>
<% Option Explicit%>

<!-- Define Client Side Scripting Function to Display to User -->
<!-- Note that server-side scripting is being used to dynamically -->
<!-- construct the return message.-->

<Script Language=VBScript>
Sub Doit()
msgbox "Your ASP SessionID is:" & <%=Session.SessionID %>
End Sub
</Script>

<HTML>
    <HEAD>
        <TITLE>Client Side Scripting</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Client Side Scripting</b></font><p>

<Input Type=Button Value="Click Here" OnClick=Doit>

    </BODY>
</HTML>


CONTENTTYPE_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<%
Response.ContentType = "text/html";
%>

<HTML>
    <HEAD>
        <TITLE>ContentType Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>ContentType Sample</b></font>

<hr size="1" color="#000000"><p>

We have explicitly identified this page with
the "text/html" MIME type.

</BODY>
</HTML>


CONTENTTYPE_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<%
Response.ContentType = "text/html"
%>

<HTML>
    <HEAD>
        <TITLE>ContentType Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>ContentType Sample</b></font>

<hr size="1" color="#000000"><p>

We have explicitly identified this page with
the "text/html" MIME type.

    </BODY>
</HTML>


COOKIE_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<%
// Changes to the HTTP response header, in which cookies
// are sent back to the client, must be made before
// sending any data to the user.

Response.Expires = 0;

// Get the last visit date/time string from the cookie that
// resides in the http request header.

var LastVisitCookie;
LastVisitCookie = Request.Cookies("CookieJScript");

// Send the current date/time string in a cookie enclosed
// in the response header.  Note that because IE now uses
// case-sensitive cookie paths, we have explicitly set
// the cookie path to be that of the url path to this
// page.  By default, the path would be that of the
// IIS Application context for this page ("IISSAMPLES")

var CurrentDate = new Date();
Response.Cookies("CookieJScript") = CurrentDate.toLocaleString();
Response.Cookies("CookieJScript").Path = "/IIsSamples";
%>

<HTML>
    <HEAD>
<TITLE>Using Cookies</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

    <font size="4" face="Arial, Helvetica">
    <b>Using Cookies</b></font><br>

        <hr size="1" color="#000000">

<%
if (LastVisitCookie == "")
{
// The cookie has never been set. This must
// be the user's first visit to this page.

Response.Write("Welcome to this page.");
}
else
{
// Remind the user of the last time she/he
// visited this page.

Response.Write("You last visited this page on " + LastVisitCookie);
}
%>

<p><A href="Cookie_JScript.asp">Revisit This Page</A>

</BODY>
</HTML>


COOKIE_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<%
' Changes to the HTTP response header, in which cookies
' are sent back to the client, must be made before
' sending any data to the user.

Response.Expires = 0

' Get the last visit date/time string from the cookie that
' resides in the http request header.

Dim LastVisitCookie
LastVisitCookie = Request.Cookies("CookieVBScript")

' Send the current date/time string in a cookie enclosed
' in the response header.  Note that because IE now uses
' case-sensitive cookie paths, we have explicitly set
' the cookie path to be that of the url path to this
' page.  By default, the path would be that of the
' IIS Application context for this page ("IISSAMPLES")

Response.Cookies("CookieVBScript") = FormatDateTime(NOW)
Response.Cookies("CookieVBScript").Path = "/IIsSamples"
%>

<HTML>
    <HEAD>
<TITLE>Using Cookies</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

    <font size="4" face="Arial, Helvetica">
    <b>Using Cookies</b></font><br>

        <hr size="1" color="#000000">

<%
If (LastVisitCookie = "") Then

' The cookie has never been set. This must
' be the user's first visit to this page.

Response.Write("Welcome to this page.")
Else

' Remind the user of the last time she/he
' visited this page.

Response.Write("You last visited this page on " + LastVisitCookie)
End If
%>

<p><A href="Cookie_VBScript.asp">Revisit This Page</A>

</BODY>
</HTML>


EXPIRE_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<%
// Because the Expiration Information is sent in the HTTP
// headers, it must be set before any HTML is transmitted

// Ensure that this page expires within 10 minutes...
Response.Expires = 10;

// ...or before Jan 1, 1999, which ever comes first
Response.ExpiresAbsolute="Jan 1,1999 13:30:15";
%>

<HTML>
    <HEAD>
        <TITLE>Setting Expiration Information</TITLE>
    </HEAD>

<BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Setting Expiration Information</b></font><br>

<hr size="1" color="#000000">

<p>This page will expire from your browser's cache in
10 minutes.  If it is after Jan. 1, 1999 (1:30 PM), then
the page will expire from the cache immediately.

</BODY>
</HTML>


EXPIRE_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<% Option Explicit%>

<%
' Because the Expiration Information is sent in the HTTP
' headers, it must be set before any HTML is transmitted

' Ensure that this page expires within 10 minutes...
Response.Expires = 10

' ...or before Jan 1, 1999, which ever comes first
Response.ExpiresAbsolute="Jan 1,1999 13:30:15"
%>

<HTML>
    <HEAD>
        <TITLE>Setting Expiration Information</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Setting Expiration Information</b></font><br>

<hr size="1" color="#000000">

<p>This page will expire from your browser's cache in
10 minutes.  If it is after Jan. 1, 1999 (1:30 PM), then
the page will expire from the cache immediately.

</BODY>
</HTML>


FORM_JSCRIPT.ASP

<% @Language=JScript%>

<HTML>
    <HEAD>
        <TITLE>Form Posting</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Form Posting</b></font><br>

<hr><p>

This page will take the information entered in
the form fields, and use the POST method to
send the data to an ASP page.

<FORM Name=Form1 METHOD=POST ACTION="Form_JScript.asp">

First Name: <Input Type=Text Name=fname><p>
Last Name: <Input Type=Text Name=lname><p>

<INPUT Type=Submit Value="Submit">

</FORM>

<hr>

<% Response.Write(Request.Form("fname"))%> <br>
<% Response.Write(Request.Form("lname"))%>
</BODY>
</HTML>


FORM_VBSCRIPT.ASP

<% @Language=VBScript%>
<% Option Explicit%>

<HTML>
    <HEAD>
        <TITLE>Form Posting</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Form Posting</b></font><br>

<hr><p>

This page will take the information entered in
the form fields, and use the POST method to
send the data to an ASP page.

<FORM Name=Form1 METHOD=POST ACTION="Form_VBScript.asp">

First Name: <Input Type=Text Name=fname><p>
Last Name: <Input Type=Text Name=lname><p>

<INPUT Type=Submit Value="Submit">

</FORM>

<hr>

<% Response.Write Request.form("fname")%> <br>
<% Response.Write Request.form("lname")%> <br>
</BODY>
</HTML>


LOGON_JSCRIPT.ASP

<%@ LANGUAGE = JScript  %>

<%
// Force Authentication if the LOGON_USER Server
// Variable is blank by sending the Response.Status
// of 401 Access Denied

// Finish the Page by issuing a Response.End so that a user
// cannot Cancel through the dialog Box

if (Request.ServerVariables("LOGON_USER") == "")
{
Response.Status = "401 access denied";
Response.End();
}
%>

<HTML>
    <HEAD>
        <TITLE>Login Screens</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Login Screens</b></font><br>

        <hr size="1" color="#000000">

<!-- Display LOGON_USER Server Variable -->

        You logged in as user:<B>  <% = Request.ServerVariables("LOGON_USER") %></B>

<!-- Display AUTH_TYPE Server Variable -->

<p>You were authenticated using:<B>  <% = Request.ServerVariables("AUTH_TYPE") %></B> authentication

    </BODY>
</HTML>


LOGON_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<%
' Force Authentication if the LOGON_USER Server Variable is blank
' by sending the Response.Status of 401 Access Denied

' Finish the Page by issuing a Response.End so that a user
' cannot Cancel through the dialog Box

If Request.ServerVariables("LOGON_USER") = "" Then
Response.Status = "401 access denied"
Response.End
End If
%>

<HTML>
    <HEAD>
        <TITLE>Login Screens</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Login Screens</b></font><br>

        <hr size="1" color="#000000">

<!-- Display LOGON_USER Server Variable -->

        You logged in as user:<B>  <% = Request.ServerVariables("LOGON_USER") %></B>

<!-- Display AUTH_TYPE Server Variable -->

<p>You were authenticated using:<B>  <% = Request.ServerVariables("AUTH_TYPE") %></B> authentication

    </BODY>
</HTML>


PICS_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<%
// Insert PICS ratings
Response.PICS("(PICS-1.1 <http://www.rsac.org/ratingv01.html> labels on '1997.01.05T08:15-0500' until '1999.12.31T23:59-0000' ratings (v 1 s 0 l 2 n 0))");
%>

<HTML>
    <HEAD>
        <TITLE>PICS Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>PICS Sample</b></font><br>

        <hr size="1" color="#000000">

<p>This sample has the following PICS rating:<p>

violence = 1<br>
sex = 0<br>
language = 2<br>
nudity = 0<br>
    </BODY>
</HTML>


PICS_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<%
' Insert PICS ratings
Response.PICS("(PICS-1.1 <http://www.rsac.org/ratingv01.html> labels on " & chr(34) & "1997.01.05T08:15-0500" & chr(34) & " until" & chr(34) & "1999.12.31T23:59-0000" & chr(34) & " ratings (v 1 s 0 l 2 n 0))")
%>

<HTML>
    <HEAD>
        <TITLE>PICS Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>PICS Sample</b></font><br>

        <hr size="1" color="#000000">

<p>This sample has the following PICS rating:<p>

violence = 1<br>
sex = 0<br>
language = 2<br>
nudity = 0<br>
    </BODY>
</HTML>


POPULATEFORM_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<%
// Create and Set Variables that will be used in populating
// the form.  In a typical application, these values would come
// from a database or text file.

var strFirstName;
var strLastName;
var strAddress1;
var strAddress2;
var blnInfo;

strFirstName = "John";
strLastName = "Doe";
strAddress1 = "1 Main Street";
strAddress2 = "Nowhere ZA, 12345";
%>

<HTML>
    <HEAD>
        <TITLE>PopulateForm Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>PopulateForm Sample</b></font><br>

        <hr size="1" color="#000000">

<FORM ACTION="">

<!-- Use ASP variables to fill out the form -->

<P>First Name: <INPUT TYPE="TEXT" NAME="FNAME" VALUE="<%=strFirstName%>"></p>
<P>Last Name: <INPUT TYPE="TEXT" NAME="LNAME" VALUE="<%=strLastName%>"></p>
<P>Street: <INPUT TYPE="TEXT" NAME="STREET" VALUE="<%=strAddress1%>"></p>
<P>City State, Zip: <INPUT TYPE="TEXT" NAME="FNAME" VALUE="<%=strAddress2%>"></p>

    </BODY>
</HTML>


POPULATEFORM_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<%
' Create and Set Variables that will be used in populating
' the form.  In a typical application, these values would come
' from a database or text file.

Dim strFirstName
Dim strLastName
Dim strAddress1
Dim strAddress2
Dim blnInfo

strFirstName = "John"
strLastName = "Doe"
strAddress1 = "1 Main Street"
strAddress2 = "Nowhere ZA, 12345"
%>

<HTML>
    <HEAD>
        <TITLE>PopulateForm Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>PopulateForm Sample</b></font><br>

        <hr size="1" color="#000000">

<FORM ACTION="">

<!-- Use ASP variables to fill out the form -->

<P>First Name: <INPUT TYPE="TEXT" NAME="FNAME" VALUE="<%=strFirstName%>"></p>
<P>Last Name: <INPUT TYPE="TEXT" NAME="LNAME" VALUE="<%=strLastName%>"></p>
<P>Street: <INPUT TYPE="TEXT" NAME="STREET" VALUE="<%=strAddress1%>"></p>
<P>City State, Zip: <INPUT TYPE="TEXT" NAME="FNAME" VALUE="<%=strAddress2%>"></p>

    </BODY>
</HTML>


QUERYSTRING_JSCRIPT.ASP

<% @Language=JScript%>

<HTML>
    <HEAD>
        <TITLE>Form Posting (Get Method with QueryString)</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Form Posting (Get Method with QueryString)</b></font>

<br><hr><p>

This page will take the information entered in the
form fields, and use the GET method to send the data
to an ASP page.

<FORM Name=Form1 METHOD=GET ACTION="QueryString_JScript.asp">

First Name: <Input Type=Text Name=fname><p>
Last Name: <Input Type=Text Name=lname><p>

<INPUT Type=Submit Value="Submit">

</FORM>

<hr>

<% Response.Write(Request.QueryString("fname"))%> <br>
<% Response.Write(Request.QueryString("lname"))%>

</BODY>
</HTML>


QUERYSTRING_VBSCRIPT.ASP

<% @Language=VBScript%>
<% Option Explicit%>

<HTML>
    <HEAD>
        <TITLE>Form Posting (Get Method with QueryString)</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Form Posting (Get Method with QueryString)</b></font>

<br><hr><p>

This page will take the information entered in the form
fields, and use the GET method to send the data to an
ASP page.

<FORM Name=Form1 METHOD=GET ACTION="QueryString_VBScript.asp">

First Name: <Input Type=Text Name=fname><p>
Last Name: <Input Type=Text Name=lname><p>

<INPUT Type=Submit Value="Submit">

</FORM>

<hr>

<% Response.Write(Request.QueryString("fname"))%> <br>
<% Response.Write(Request.QueryString("lname"))%>
</BODY>
</HTML>


REDIRECT_JSCRIPT.ASP

<% @Language=JScript%>

<%
// Because we might be redirecting, we must use buffering
Response.Buffer = true;
%>

<HTML>
    <HEAD>
        <TITLE>Redirect Sample</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Redirect Sample</b></font><br>

<%
// If the referring page did not come
// from the sight "trainer3", Redirect
// the user to www.microsoft.com

var referer = Request.ServerVariables("HTTP_REFERER");

if (referer == "trainer3")
{
// They came from withing the trainer3
// web site so we let them continue
}
else
{
// They just linked from outside of the site
Response.Redirect("http://www.microsoft.com");
}

// Flush Response Buffer
Response.Flush();
%>

    </BODY>
</HTML>


REDIRECT_VBSCRIPT.ASP

<% @Language=VBScript%>
<% Option Explicit%>

<%
' Because we might be redirecting, we must use buffering
Response.Buffer = True
%>

<HTML>
    <HEAD>
        <TITLE>Redirect Sample</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Redirect Sample</b></font><br>

<%
' If the referring page did not come from
' the sight "trainer3", Redirect the user
' to www.microsoft.com

If Instr(Request.ServerVariables("HTTP_REFERER"), "trainer3") > 0 Then

' They came from withing the trainer3 web
' site so we let them continue

Else

' They just linked from outside of the site
Response.Redirect("http://www.microsoft.com")

End If

' Flush Response Buffer

Response.Flush
%>

    </BODY>
</HTML>


SERVERVARIABLES_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>HTTP Server Variables</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>HTTP Server Variables</b>
        </font><br>

        <hr size="1" color="#000000">

        <!-- Use a HTML table to format the Server Variables -->
        <!-- and respective values.                          -->

        <TABLE border=1>

            <!-- Table Header -->

            <TR>
                <TD><B>Variables</B></TD>
                <TD><B>Value</B></TD>
            </TR>

            <TR>
            <TD>ALL_HTTP</TD>
            <TD>
                <%= Request.ServerVariables("ALL_HTTP")%>
            </TD>
            </TR>

            <TR>
            <TD>ALL_RAW</TD>
            <TD>
                <%= Request.ServerVariables("ALL_RAW")%>
            </TD>
            </TR>

            <TR>
            <TD>CONTENT_LENGTH</TD>
            <TD>
                <%= Request.ServerVariables("CONTENT_LENGTH")%>
            </TD>
            </TR>

            <TR>
            <TD>CONTENT_TYPE</TD>
            <TD>
                <%= Request.ServerVariables("CONTENT_TYPE")%>
            </TD>
            </TR>

            <TR>
            <TD>QUERY_STRING</TD>
            <TD>
                <%= Request.ServerVariables("QUERY_STRING")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_SOFTWARE</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_SOFTWARE")%>
            </TD>
            </TR>

            <TR>
            <TD>CONTENT_LENGTH </TD>
            <TD>
                <%= Request.ServerVariables("CONTENT_LENGTH")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTPS</TD>
            <TD>
                <%= Request.ServerVariables("HTTPS")%>
            </TD>
            </TR>

            <TR>
            <TD>LOCAL_ADDR </TD>
            <TD>
                <%= Request.ServerVariables("LOCAL_ADDR")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTPS</TD>
            <TD>
                <%= Request.ServerVariables("HTTPS")%>
            </TD>
            </TR>

            <TR>
            <TD>PATH_INFO </TD>
            <TD>
                <%= Request.ServerVariables("PATH_INFO")%>
            </TD>
            </TR>

            <TR>
            <TD>PATH_TRANSLATED </TD>
            <TD>
                <%= Request.ServerVariables("PATH_TRANSLATED")%>
            </TD>
            </TR>

            <TR>
            <TD>REMOTE_ADDR </TD>
            <TD>
                <%= Request.ServerVariables("REMOTE_ADDR")%>
            </TD>
            </TR>

            <TR>
            <TD>REMOTE_HOST  </TD>
            <TD>
                <%= Request.ServerVariables("REMOTE_HOST")%>
            </TD>
            </TR>

            <TR>
            <TD>REQUEST_METHOD</TD>
            <TD>
                <%= Request.ServerVariables("REQUEST_METHOD")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_NAME</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_NAME")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_PORT</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_PORT")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_PROTOCOL</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_PROTOCOL")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_SOFTWARE</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_SOFTWARE")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_ACCEPT</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_ACCEPT")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_USER_AGENT</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_USER_AGENT")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_UA_PIXELS</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_UA_PIXELS")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_UA_COLOR</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_UA_COLOR")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_UA_OS</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_UA_OS")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_UA_CPU</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_UA_CPU")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_REFERER </TD>
            <TD>
                <%= Request.ServerVariables("HTTP_REFERER")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_CONNECTION </TD>
            <TD>
                <%= Request.ServerVariables("HTTP_CONNECTION")%>
            </TD>
            </TR>

            <TR>
            <TD>URL </TD>
            <TD>
                <%= Request.ServerVariables("URL")%>
            </TD>
            </TR>

            <TR>
            <TD>REMOTE_USER</TD>
            <TD>
                <%= Request.ServerVariables("REMOTE_USER")%>
            </TD>
            </TR>

        </TABLE>
    </BODY>
</HTML>


SERVERVARIABLES_VBSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>HTTP Server Variables</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>HTTP Server Variables</b>
        </font><br>

        <hr size="1" color="#000000">

        <!-- Use a HTML table to format the Server Variables -->
        <!-- and respective values.                          -->

        <TABLE border=1>

            <!-- Table Header -->

            <TR>
                <TD><B>Variables</B></TD>
                <TD><B>Value</B></TD>
            </TR>

            <TR>
            <TD>ALL_HTTP</TD>
            <TD>
                <%= Request.ServerVariables("ALL_HTTP")%>
            </TD>
            </TR>

            <TR>
            <TD>ALL_RAW</TD>
            <TD>
                <%= Request.ServerVariables("ALL_RAW")%>
            </TD>
            </TR>

            <TR>
            <TD>CONTENT_LENGTH</TD>
            <TD>
                <%= Request.ServerVariables("CONTENT_LENGTH")%>
            </TD>
            </TR>

            <TR>
            <TD>CONTENT_TYPE</TD>
            <TD>
                <%= Request.ServerVariables("CONTENT_TYPE")%>
            </TD>
            </TR>

            <TR>
            <TD>QUERY_STRING</TD>
            <TD>
                <%= Request.ServerVariables("QUERY_STRING")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_SOFTWARE</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_SOFTWARE")%>
            </TD>
            </TR>

            <TR>
            <TD>CONTENT_LENGTH </TD>
            <TD>
                <%= Request.ServerVariables("CONTENT_LENGTH")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTPS</TD>
            <TD>
                <%= Request.ServerVariables("HTTPS")%>
            </TD>
            </TR>

            <TR>
            <TD>LOCAL_ADDR </TD>
            <TD>
                <%= Request.ServerVariables("LOCAL_ADDR")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTPS</TD>
            <TD>
                <%= Request.ServerVariables("HTTPS")%>
            </TD>
            </TR>

            <TR>
            <TD>PATH_INFO </TD>
            <TD>
                <%= Request.ServerVariables("PATH_INFO")%>
            </TD>
            </TR>

            <TR>
            <TD>PATH_TRANSLATED </TD>
            <TD>
                <%= Request.ServerVariables("PATH_TRANSLATED")%>
            </TD>
            </TR>

            <TR>
            <TD>REMOTE_ADDR </TD>
            <TD>
                <%= Request.ServerVariables("REMOTE_ADDR")%>
            </TD>
            </TR>

            <TR>
            <TD>REMOTE_HOST  </TD>
            <TD>
                <%= Request.ServerVariables("REMOTE_HOST")%>
            </TD>
            </TR>

            <TR>
            <TD>REQUEST_METHOD</TD>
            <TD>
                <%= Request.ServerVariables("REQUEST_METHOD")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_NAME</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_NAME")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_PORT</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_PORT")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_PROTOCOL</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_PROTOCOL")%>
            </TD>
            </TR>

            <TR>
            <TD>SERVER_SOFTWARE</TD>
            <TD>
                <%= Request.ServerVariables("SERVER_SOFTWARE")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_ACCEPT</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_ACCEPT")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_USER_AGENT</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_USER_AGENT")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_UA_PIXELS</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_UA_PIXELS")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_UA_COLOR</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_UA_COLOR")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_UA_OS</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_UA_OS")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_UA_CPU</TD>
            <TD>
                <%= Request.ServerVariables("HTTP_UA_CPU")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_REFERER </TD>
            <TD>
                <%= Request.ServerVariables("HTTP_REFERER")%>
            </TD>
            </TR>

            <TR>
            <TD>HTTP_CONNECTION </TD>
            <TD>
                <%= Request.ServerVariables("HTTP_CONNECTION")%>
            </TD>
            </TR>

            <TR>
            <TD>URL </TD>
            <TD>
                <%= Request.ServerVariables("URL")%>
            </TD>
            </TR>

            <TR>
            <TD>REMOTE_USER</TD>
            <TD>
                <%= Request.ServerVariables("REMOTE_USER")%>
            </TD>
            </TR>

        </TABLE>
    </BODY>
</HTML>


ENCRYPTEDDELIVERY_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Encrypted MSMQ Transmission</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Encrypted MSMQ Transmission</b></font><br>

        <hr size="1" color="#000000">

This sample demonstrates how to send an encrypted asynchronous message using
        the Microsoft Message Queueing Server (MSMQ).  MSMQ is one of the components
        that comes with the Windows NT 4.0 Option Pack.

        <p> For this example to work, MSMQ must be first be installed on the host machine.
        Using the MSMQ Explorer, a queue named "IIS_SDK_EXAMPLE" should then be created.
        After the example is run, return to the MSMQ Explorer and select "Refresh" from
        the "View" menu.  The recently sent message will then appear within the "IIS_SDK_EXAMPLE"
        queue.

        <%
// Create MSMQQueueInfo Component to Open
// MessageQueue

QueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo");

// Open Queue.  The queue could be physically located
// on any machine.  The period in the line below indicate
// that the queue is located on the local machine.  Note
// that because JScript is being used as the scripting
// language, and extra backslash must be inserted as
// an escape character.

QueueInfo.pathname = ".\\IIS_SDK_EXAMPLE";
Queue = QueueInfo.Open(2, 0);

// Create Message Component for Queue

Msg = Server.CreateObject("MSMQ.MSMQMessage");

// Construct Message.  Note that the PrivLevel of
// the message has been set to require encryption.
// This will ensure that the message remains encrypted
// until an authorized receiver reads it.

Msg.body = "This is the message body";
Msg.Label = "This is the message label";
Msg.PrivLevel = 1;

// Send Message

Msg.Send(Queue);

// Close Queue

Queue.Close();
%>

</BODY>
</HTML>


ENCRYPTEDDELIVERY_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
    <HEAD>
        <TITLE>Encrypted MSMQ Transmission</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Encrypted MSMQ Transmission</b></font><br>

        <hr size="1" color="#000000">

This sample demonstrates how to send an encrypted asynchronous message using
        the Microsoft Message Queueing Server (MSMQ).  MSMQ is one of the components
        that comes with the Windows NT 4.0 Option Pack.

        <p> For this example to work, MSMQ must be first be installed on the host machine.
        Using the MSMQ Explorer, a queue named "IIS_SDK_EXAMPLE" should then be created.
        After the example is run, return to the MSMQ Explorer and select "Refresh" from
        the "View" menu.  The recently sent message will then appear within the "IIS_SDK_EXAMPLE"
        queue.

<%
Dim QueueInfo
Dim Queue
Dim Msg

' Create MSMQQueueInfo Component to Open
' MessageQueue

Set QueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")

' Open Queue.  The queue could be physically located
' on any machine.  The period in the line below indicates
' that the queue is located on the local machine.

QueueInfo.pathname = ".\IIS_SDK_EXAMPLE"
Set Queue = QueueInfo.Open(2, 0)

' Create Message Component for Queue

Set Msg = Server.CreateObject("MSMQ.MSMQMessage")

' Construct Message.  Note that the PrivLevel of
' the message has been set to require encryption.
' This will ensure that the message remains encrypted
' until an authorized receiver reads it.

Msg.PrivLevel = 1
Msg.body = "This is the message body"
Msg.Label = "This is the message label"

' Send Message

Msg.Send(Queue)

' Close Queue

Queue.Close
%>

    </BODY>
</HTML>


EXPRESSDELIVERY_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Simple MSMQ Transmission (Using Express Delivery)</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Simple MSMQ Transmission (Using Express Delivery)</b></font><br>

        <hr size="1" color="#000000">

This sample demonstrates how to send a simple asynchronous message using
        the Microsoft Message Queueing Server (MSMQ).  MSMQ is one of the components
        that comes with the Windows NT 4.0 Option Pack.

        <p> For this example to work, MSMQ must be first be installed on the host machine.
        Using the MSMQ Explorer, a queue named "IIS_SDK_EXAMPLE" should then be created.
        After the example is run, return to the MSMQ Explorer and select "Refresh" from
        the "View" menu.  The recently sent message will then appear within the "IIS_SDK_EXAMPLE"
        queue.

        <%
// Create MSMQQueueInfo Component to Open
// MessageQueue

QueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo");

// Open Queue.  The queue could be physically located
// on any machine.  The period in the line below indicates
// that the queue is located on the local machine.  Note
// that because JScript is being used as the scripting
// language, and extra backslash must be inserted as an
// escape character.

QueueInfo.pathname = ".\\IIS_SDK_EXAMPLE"
Queue = QueueInfo.Open(2, 0);

// Create Message Component for Queue

Msg = Server.CreateObject("MSMQ.MSMQMessage");

// Construct Message.  Note than anything can be
// passed into both the body and label.  The developer
// is responsible for marshalling all arguments.

Msg.body = "This is the message body";
Msg.Label = "This is the message label";

// Send Message

Msg.Send(Queue);

// Close Queue

Queue.Close();
%>

</BODY>
</HTML>


EXPRESSDELIVERY_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
    <HEAD>
        <TITLE>Simple MSMQ Transmission (Using Express Delivery)</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Simple MSMQ Transmission (Using Express Delivery)</b></font><br>

        <hr size="1" color="#000000">

This sample demonstrates how to send a simple asynchronous message using
        the Microsoft Message Queueing Server (MSMQ).  MSMQ is one of the components
        that comes with the Windows NT 4.0 Option Pack.

        <p> For this example to work, MSMQ must be first be installed on the host machine.
        Using the MSMQ Explorer, a queue named "IIS_SDK_EXAMPLE" should then be created.
        After the example is run, return to the MSMQ Explorer and select "Refresh" from
        the "View" menu.  The recently sent message will then appear within the "IIS_SDK_EXAMPLE"
        queue.

        <%
Dim QueueInfo
Dim Queue
Dim Msg

' Create MSMQQueueInfo Component to Open
' MessageQueue

Set QueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")

' Open Queue.  The queue could be physically located
' on any machine.  The period in the line below
' indicates that the queue is located on the local machine.

QueueInfo.pathname = ".\IIS_SDK_EXAMPLE"
Set Queue = QueueInfo.Open(2, 0)

' Create Message Component for Queue

Set Msg = Server.CreateObject("MSMQ.MSMQMessage")

' Construct Message.  Note than anything can be passed
' into both the body and label.  The developer is
' responsible for marshalling all arguments.

Msg.body = "This is the message body"
Msg.Label = "This is the message label"

' Send Message

Msg.Send Queue

' Close Queue

Queue.Close
%>

    </BODY>
</HTML>


RECOVERABLEDELIVERY_JSCRIPT.ASP

<%@ LANGUAGE = JSCRIPT %>

<HTML>
    <HEAD>
        <TITLE>Simple MSMQ Transmission (Using Recoverable Delivery)</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Simple MSMQ Transmission (Using Recoverable Delivery)</b></font><br>

        <hr size="1" color="#000000">

This sample demonstrates how to send a simple asynchronous message using
        the Microsoft Message Queueing Server (MSMQ).  MSMQ is one of the components
        that comes with the Windows NT 4.0 Option Pack.

        <p> For this example to work, MSMQ must be first be installed on the host machine.
        Using the MSMQ Explorer, a queue named "IIS_SDK_EXAMPLE" should then be created.
        After the example is run, return to the MSMQ Explorer and select "Refresh" from
        the "View" menu.  The recently sent message will then appear within the "IIS_SDK_EXAMPLE"
        queue.

<%
// Create MSMQQueueInfo Component to Open
// MessageQueue

QueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")

// Open Queue.  The queue could be physically located
// on any machine. The period in the line below indicates
// that the queue is located on the local machine.  Note
// that because JScript is being used as the scripting
// language, and extra backslash must be inserted
// as an escape character.

QueueInfo.pathname = ".\\IIS_SDK_EXAMPLE";
Queue = QueueInfo.Open(2, 0);

// Create Message Component for Queue

Msg = Server.CreateObject("MSMQ.MSMQMessage");

// Construct Message.  Anything can be passed into both
// the body and label.  The developer is responsible
// for marshalling all arguments.  Note that the delivery
// property has been sent to "Recoverable".  This will
// guarentee that the message will survive a
// crash or shutdown on the queue machine.

Msg.body = "This is the message body";
Msg.Label = "This is the message label";
Msg.Delivery = 1;

// Send Message

Msg.Send(Queue);

// Close Queue

Queue.Close();
%>

</BODY>
</HTML>


RECOVERABLEDELIVERY_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
    <HEAD>
        <TITLE>Simple MSMQ Transmission (Using Recoverable Delivery)</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Simple MSMQ Transmission (Using Recoverable Delivery)</b></font><br>

        <hr size="1" color="#000000">

This sample demonstrates how to send a simple asynchronous message using
        the Microsoft Message Queueing Server (MSMQ).  MSMQ is one of the components
        that comes with the Windows NT 4.0 Option Pack.

        <p> For this example to work, MSMQ must be first be installed on the host machine.
        Using the MSMQ Explorer, a queue named "IIS_SDK_EXAMPLE" should then be created.
        After the example is run, return to the MSMQ Explorer and select "Refresh" from
        the "View" menu.  The recently sent message will then appear within the "IIS_SDK_EXAMPLE"
        queue.

        <%
Dim QueueInfo
Dim Queue
Dim Msg

' Create MSMQQueueInfo Component to Open
' MessageQueue

Set QueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")

' Open Queue.  The queue could be physically located
' on any machine. The period in the line below indicates
' that the queue is located on the local machine.

QueueInfo.pathname = ".\IIS_SDK_EXAMPLE"
Set Queue = QueueInfo.Open(2, 0)

' Create Message Component for Queue

Set Msg = Server.CreateObject("MSMQ.MSMQMessage")

' Construct Message.  Anything can be passed into
' both the body and label.  The developer is responsible
' for marshalling all arguments.  Note that the delivery
' property has been sent to "Recoverable".  This will
' guarentee that the message will survive a crash or
' shutdown on the queue machine.

Msg.body = "This is the message body"
Msg.Label = "This is the message label"
Msg.Delivery = 1

' Send Message

Msg.Send Queue

' Close Queue

Queue.Close
%>

</BODY>
</HTML>


TRANSACTEDDELIVERY_JSCRIPT.ASP

<%@ TRANSACTION=REQUIRED LANGUAGE=JScript %>

<HTML>
    <HEAD>
        <TITLE>Transacted MSMQ Transmission</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Transacted MSMQ Transmission</b></font><br>

        <hr size="1" color="#000000">

This sample demonstrates how to send a transacted asynchronous message using
        the Microsoft Message Queueing Server (MSMQ).  MSMQ is one of the components
        that comes with the Windows NT 4.0 Option Pack.

        <p> For this example to work, MSMQ must be first be installed on the host machine.
        Using the MSMQ Explorer, a queue named "IIS_SDK_TRANSACTED" should then be created.
        Please click the "transacted" option when creating the queue.

        <p>After the example is run, return to the MSMQ Explorer and select "Refresh" from
        the "View" menu.  The recently sent message will then appear within the "IIS_SDK_TRANSACTED"
        queue.

        <%
// Create MSMQQueueInfo Component to
// Open MessageQueue

QueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")

// Open Queue.  The queue could be physically located
// on any machine.  The period in the line below indicates
// that the queue is located on the local machine.  Note
// that because JScript is being used as the scripting
// language, and extra backslash must be inserted
// as an escape character.

QueueInfo.pathname = ".\\IIS_SDK_TRANSACTED";
Queue = QueueInfo.Open(2, 0);

// Create Message Component for Queue

Msg = Server.CreateObject("MSMQ.MSMQMessage");

// Construct Message.  Anything can be passed into
// both the body and label.  The developer is responsible
// for marshalling all arguments.  Note that the delivery
// property has been sent to "Recoverable".  This will
// guarentee that the message will survive a crash or
// shutdown on the queue machine.

Msg.body = "This is the message body";
Msg.Label = "This is the message label";
Msg.Delivery = 1;

// Send Message

Msg.Send(Queue);

// Close Queue

Queue.Close();
%>

    </BODY>
</HTML>

<%
    // The Transacted Script Commit Handler.  This function
    // will be called if the transacted script commits.

    function OnTransactionCommit()
    {
        Response.Write ("<p><b>The Transaction just comitted</b>.");
        Response.Write ("The MSMQ message was <b>successfully</b> sent");
    }

    // The Transacted Script Abort Handler.  This function
    // will be called if the script transacted aborts

    function OnTransactionAbort()
    {
        Response.Write ("<p><b>The Transaction just aborted</b>.");
        Response.Write ("The MSMQ message was <b>not</b> sent");
    }
%>


TRANSACTEDDELIVERY_VBSCRIPT.ASP

<%@ TRANSACTION=REQUIRED LANGUAGE=VBScript %>
<%  Option Explicit                        %>

<HTML>
    <HEAD>
        <TITLE>Transacted MSMQ Transmission</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Transacted MSMQ Transmission</b></font><br>

        <hr size="1" color="#000000">

This sample demonstrates how to send a transacted asynchronous message using
        the Microsoft Message Queueing Server (MSMQ).  MSMQ is one of the components
        that comes with the Windows NT 4.0 Option Pack.

        <p> For this example to work, MSMQ must be first be installed on the host machine.
        Using the MSMQ Explorer, a queue named "IIS_SDK_TRANSACTED" should then be created.
        Please click the "transacted" option when creating the queue.

        <p>After the example is run, return to the MSMQ Explorer and select "Refresh" from
        the "View" menu.  The recently sent message will then appear within the "IIS_SDK_TRANSACTED"
        queue.

        <%
Dim QueueInfo
Dim Queue
Dim Msg

' Create MSMQQueueInfo Component to Open
' MessageQueue

Set QueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")

' Open Queue.  The queue could be physically located
' on any machine.  The period in the line below indicates
' that the queue is located on the local machine.

QueueInfo.pathname = ".\IIS_SDK_TRANSACTED"
Set Queue = QueueInfo.Open(2, 0)

' Create Message Component for Queue

Set Msg = Server.CreateObject("MSMQ.MSMQMessage")

' Construct Message.  Anything can be passed into both
' the body and label.  The developer is responsible
' for marshalling all arguments.  Note that the
' delivery property has been sent to "Recoverable".
' This will guarentee that the message will survive
' a crash or shutdown on the queue machine.

Msg.body = "This is the message body"
Msg.Label = "This is the message label"
Msg.Delivery = 1

' Send Message

Msg.Send Queue

' Close Queue

Queue.Close
%>

    </BODY>
</HTML>

<%
' The Transacted Script Commit Handler.  This function
' will be called if the transacted script commits.

    Sub OnTransactionCommit

        Response.Write "<p><b>The Transaction just comitted</b>."
        Response.Write "The MSMQ message was <b>successfully</b> sent"

    End Sub

    ' The Transacted Script Abort Handler.  This function
    ' will be called if the script transacted aborts

    Sub OnTransactionAbort

        Response.Write "<p><b>The Transaction just aborted</b>."
        Response.Write "The MSMQ message was <b>not</b> sent"

    End Sub
%>


ARRAYS_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>
<%
    // Declare a simple fixed size array
    aFixed = new Array(4);

    // Declare a dynamic (resizable) array
    aColors = new Array();

    // Assign values to fixed size array

    aFixed[0] = "Fixed";
    aFixed[1] = "Size";
    aFixed[2] = "Array";
    aFixed[3] = "Session ID: " + Session.SessionID;

    // Store values representing a simple color table
    // to each of the elements

    aColors[0] = "RED";
    aColors[1] = "GREEN";
    aColors[2] = "BLUE";
    aColors[3] = "AQUA";
    aColors[4] = "YELLOW";
    aColors[5] = "FUCHSIA";
    aColors[6] = "GRAY";
    aColors[7] = "LIME";
    aColors[8] = "MAROON";
    aColors[9] = "NAVY";
    aColors[10] = "OLIVE";
    aColors[11] = "PURPLE";
    aColors[12] = "SILVER";
    aColors[13] = "TEAL";
%>

<HTML>
    <HEAD>
        <TITLE>Array Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <FONT SIZE="4" FACE="Arial, Helvetica">
        <B>Array Sample</B></FONT><BR>

        <HR SIZE="1" COLOR="#000000">

        <TABLE CELLPADDING=10 BORDER=1 CELLSPACING=0>
            <TR>
                <TD BGCOLOR=WHITE>
    <FONT SIZE="3" FACE="Arial, Helvetica">
                <B>A Resizable Array</B><br>
                    </FONT>
</TD>

                <TD BGCOLOR=WHITE>
                    <FONT SIZE="3" FACE="Arial, Helvetica">
                <B>A Fixed Size (4 element) Array</B><BR>
                    </FONT>
</TD>
</TR>

            <TR>
                <TD>
<%
// Calculate Array Size

nColors = aColors.length;

// Print out contents of resizable array
// into table column

for(i = 0; i < nColors; i++)
{
Response.Write("<FONT COLOR=" + aColors[i] + ">" + aColors[i] + "<br></FONT>");
}
%>
                </TD>

                <TD>
                    <%
// Calculate Array Size

nColors = aFixed.length;

// Print out contents of fixed array into
// table column

for(i = 0; i < nColors; i++)
{
Response.Write(aFixed[i] + "<br>");
}
%>
</TD>
            </TR>
        </TABLE>
    </BODY>
</HTML>


ARRAYS_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%
    ' Declare a simple fixed size array
    Dim aFixed(4)

    ' Declare a dynamic (resizable) array
    Dim aColors()

    ' Assign values to fixed size array

    aFixed(0) = "Fixed"
    aFixed(1) = "Size"
    aFixed(2) = "Array"
    aFixed(3) = "Session ID: " & Session.SessionID

    ' Allocate storage for the array
    Redim aColors(14)

    ' Store values representing a simple color table
    ' to each of the elements

    aColors(0) = "RED" ' [#FF0000]
    aColors(1) = "GREEN" ' [#008000]
    aColors(2) = "BLUE" ' [#0000FF]
    aColors(3) = "AQUA" ' [#00FFFF]
    aColors(4) = "YELLOW" ' [#FFFF00]
    aColors(5) = "FUCHSIA" ' [#FF00FF]
    aColors(6) = "GRAY" ' [#808080]
    aColors(7) = "LIME" ' [#00FF00]
    aColors(8) = "MAROON" ' [#800000]
    aColors(9) = "NAVY" ' [#000080]
    aColors(10) = "OLIVE" ' [#808000]
    aColors(11) = "PURPLE" ' [#800080]
    aColors(12) = "SILVER" ' [#C0C0C0]
    aColors(13) = "TEAL" ' [#008080]
%>

<HTML>
    <HEAD>
        <TITLE>Array Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <FONT SIZE="4" FACE="Arial, Helvetica">
        <B>Array Sample</B></FONT><BR>

        <HR SIZE="1" COLOR="#000000">

        <TABLE CELLPADDING=10 BORDER=1 CELLSPACING=0>
            <TR>
                <TD BGCOLOR=WHITE>
    <FONT SIZE="3" FACE="Arial, Helvetica">
                <B>A Resizable Array</B><br>
                    </FONT>
</TD>

                <TD BGCOLOR=WHITE>
                    <FONT SIZE="3" FACE="Arial, Helvetica">
                <B>A Fixed Size (4 element) Array</B><BR>
                    </FONT>
</TD>
</TR>

            <TR>
                <TD>
<%
' Calculate Array Size

nColors = UBound(aColors)

' Print out contents of resizable array into
' table column

For i = 0 To nColors - 1

Response.Write("<FONT COLOR=" & Chr(34) & aColors(i) & Chr(34) &">" & aColors(i) &"<br></FONT>")

Next
%>
                </TD>

                <TD>
<%
' Calculate Array Size

nColors = UBound(aFixed)

' Print out contents of fixed array into table
' column

For i = 0 To nColors -1

Response.Write(aFixed(i) & "<br>")

Next
%>
</TD>
            </TR>
        </TABLE>
    </BODY>
</HTML>


COMPONENTS_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Using Components</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using Components with ASP</b></font><br>

        <hr size="1" color="#000000">

This script use the Tools component
that comes with IIS to generate a random number.
<br>
<br>

<%
// Instantiate Component on the Server
example = Server.CreateObject("MSWC.Tools");
%>

Random Number = <% = example.Random() %>

    </BODY>
</HTML>


COMPONENTS_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit%>

<HTML>
    <HEAD>
        <TITLE>Using Components</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Using Components with ASP</b></font><br>

        <hr size="1" color="#000000">

This script uses the Tools component that
comes with IIS to generate a random number.
<br>
<br>

<%
Dim example

' Instantiate Component on the Server
Set example = Server.CreateObject("MSWC.Tools")
%>

Random Number = <% = example.Random() %>

    </BODY>
</HTML>


CONDITIONAL_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Conditional Operator Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Conditional Operator Sample</b></font><br>

        <hr size="1" color="#000000">

<%
// Determine Date

var dtmVar = new Date();
%>

<!-- Display Date -->

<P>The date is: <%=dtmVar.getMonth()+1%>/<%=dtmVar.getDate()%>/<%=dtmVar.getYear()%></p>

<%
    // Switch statement to display a message based on the day of the month
switch (dtmVar.getDate())
 {
  case 1:
  case 2:
  case 3:
  case 4:
  case 5:
  case 6:
  case 7:
  case 8:
  case 9:
  case 10:
   Response.Write("<P>It's the beginning of the month.</P>");
   break;
  case 11:
  case 12:
  case 13:
  case 14:
  case 15:
  case 16:
  case 17:
  case 18:
  case 19:
  case 20:
   Response.Write("<P>It's the middle of the month.</P>");
   break;
  default:
    Response.Write("<P>It's the end of the month.</P>");
 }
%>

<P>The time is: <%=dtmVar.getHours()%>:<%=dtmVar.getMinutes()%>:<%=dtmVar.getSeconds()%></P>

<%
// Check for AM/PM, and output appropriate message

if (dtmVar.getHours() >= 12)
{
Response.Write("<P>Good Evening</p>");
}
else
{
Response.Write("<P>Good Morning</p>");
}
%>

    </BODY>
</HTML>


CONDITIONAL_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
    <HEAD>
        <TITLE>Conditional Operator Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Conditional Operator Sample</b></font><br>

        <hr size="1" color="#000000">

<!-- If...Then example -->

<% Dim varDate
   varDate = Date
%>

<P>The date is: <%=varDate%></p>

<%
  ' Select Case statement to display a message based on the day of the month
Select Case Day(varDate)
  Case 1,2,3,4,5,6,7,8,9,10
    Response.Write("<P>It's the beginning of the month.</P>")
  Case 11,12,13,14,15,16,17,18,19,20
    Response.Write("<P>It's the middle of the month.</P>")
  Case Else
    Response.Write("<P>It's the end of the month.</P>")
End Select
%>

<P>The time is: <%=Time%></P>

<%
' Check for AM/PM, and output appropriate message

if (Right(Time,2)="AM") Then

Response.Write("<P>Good Morning</p>")

Else

Response.Write("<P>Good Evening</p>")

End If
%>

    </BODY>
</HTML>


FUNCTIONS_JSCRIPT.ASP

<% @Language=JScript %>

<Script Language=JScript RunAt=Server>

// Define Server Side Script Function

function PrintOutMsg(msg, count)
{
// Output Message count times

for(i = 0; i < count; i++)
{
Response.Write(msg + "<BR>");
}

// Return number of iterations

return count;
}

</Script>

<HTML>
    <HEAD>
        <TITLE>Functions</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Server Side Functions</b></font><br>

<p>
The function "PrintOutMsg" prints out a specific message a set number of times.
<p>

<%
// Call Function
x = PrintOutMsg("This is a function test!", 4);

// Output the function return value
Response.Write("<p>The function printed out the message " + x + " times.");
%>

    </BODY>
</HTML>


FUNCTIONS_VBSCRIPT.ASP

<% @Language=VBScript %>
<% Option Explicit  %>

<Script Language=VBScript RunAt=Server>

' Define Server Side Script Function

Function PrintOutMsg(msg, count)

Dim i

' Output Message count times

For i = 1 to count
Response.Write(msg & "<BR>")
Next

' Return number of iterations

PrintOutMsg = count

End Function

</Script>

<HTML>
    <HEAD>
        <TITLE>Functions</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Server Side Functions</b></font><br>

<p>
The function "PrintOutMsg" prints out a specific message a set number of times.
<p>

<%
' Store number of times Function printed message
Dim x

' Call Function
x = PrintOutMsg("This is a function test!", 4)

' Output the function return value
Response.Write("<p>The function printed out the message " & x & " times.")
%>

    </BODY>
</HTML>


HEADERINFO.ASP

<b>This text is coming from HeaderInfo.asp. </b>


INCLUDES_JSCRIPT.ASP

<% @Language=JScript %>

<HTML>
    <HEAD>
        <TITLE>Server Side Includes</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Server Side Includes</b></font><br>

<p>Server Side Includes can be done using the
#Include File command.  The below text in bold
has been generated with a server side include:

<p> <!--#INCLUDE FILE="HeaderInfo.asp"--> </p>

<P> Include files can be referenced using either
absolute or relative paths.<BR>
    </BODY>
</HTML>


INCLUDES_VBSCRIPT.ASP

<% @Language=VBScript %>
<% Option Explicit    %>

<HTML>
    <HEAD>
        <TITLE>Server Side Includes</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Server Side Includes</b></font><br>

<p>Server Side Includes can be done using the
#Include File command. The below text in bold
has been generated with a server side include:

<p> <!--#INCLUDE FILE="HeaderInfo.asp"--> </p>

<P> Include files can be referenced using either
absolute or relative paths.<BR>
    </BODY>
</HTML>


LOOPING_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Looping</TITLE>
    </HEAD>

    <BODY bgcolor="white" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Looping with ASP</b></font><br>

        <hr size="1" color="#000000">

        <!-- Looping with a for loop -->

        <% for(i = 1; i < 6; i++) { %>

            <FONT SIZE=<% = i %>>
            Hello World with a For Loop!<BR>
</FONT>

        <% } %>

        <hr>

        <!-- Looping with a while loop -->

        <%
           i = 1;
        %>

        <% while (i < 6) { %>

            <FONT SIZE=<% = i %>>
            Hello World with a While Loop!<BR>
</FONT>

            <% i = i+1; %>

        <% } %>

        <hr>

        <!-- Looping with a do..while loop -->

        <%
           i = 1;
        %>

        <% do { %>

            <FONT SIZE=<% = i %>>
            Hello World with a Do..While Loop!<BR>
</FONT>

            <% i = i+1; %>

        <% } while (i < 6); %>

    </BODY>
</HTML>


LOOPING_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit%>

<HTML>
    <HEAD>
        <TITLE>Looping</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Looping with ASP</b></font><br>

        <hr size="1" color="#000000">

<%
' Index Counter
Dim i
%>

        <!-- Looping with a for loop -->

        <% for i = 1 to 5 %>

            <FONT SIZE=<% = i %>>

            Hello World with a For Loop!<BR>
</FONT>

        <% next %>

        <hr>

        <!-- Looping with a while loop -->

        <% i = 1

           while (i < 6) %>

                <FONT SIZE=<% = i %>>

                Hello World with a While Loop!<BR>
</FONT>

                <% i = i+1 %>

           <% wend %>

        <hr>

        <!-- Looping with a do..while loop -->

        <% i = 1

           do %>

                <FONT SIZE=<% = i %>>

                Hello World with a Do..While Loop!<BR>
</FONT>

                <% i = i+1 %>

           <% loop while (i < 6) %>

    </BODY>
</HTML>


VARIABLES_JSCRIPT.ASP

<%@ LANGUAGE = JScript %>

<HTML>
    <HEAD>
        <TITLE>Variable Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Variable Sample</b></font><br>

        <hr>
<H3> Integer Manipulaiton </h3>

<%
// Declare variable
var intVariable;

// Assign the variable an integer value
intVariable = 5;
%>

<P><%=intVariable%> + <%=intVariable%> =
<%=intVariable + intVariable%></p>

<HR>
<H3> String Manipulation </h3>

<%
// Declare variable
var strVariable;

// Assign the variable a string value
strVariable = "Jemearl";
%>

<P>This example was done by <%=strVariable + " Smith" %></p>

<HR>
<H3> Boolean Manipulation </h3>

<%
// Declare variable
var blnVariable;

// Assign the variable a boolean value
blnVariable = true;

// Output Message based on value
if (blnVariable = true)
{
  Response.Write("<P>The boolean value is True.</P>");
}
else
{
  Response.Write("<P>The boolean value is False.</P>");
}
%>

<HR>
<H3> Date and Time </h3>

<%
// Declare variable
var dtmVariable;

// Assign the variable a value
dtmVariable = new Date(1997, 08, 27, 17, 11, 42);
%>

<P>The date and time is <%=dtmVariable%> </p>

<%
// Set the variable to the web server date and time
dtmVariable = new Date();
%>

<P>The <STRONG>system</strong> date and time
is <%=dtmVariable%></p>

    </BODY>
</HTML>


VARIABLES_VBSCRIPT.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
    <HEAD>
        <TITLE>Variable Sample</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Variable Sample</b></font><br>

        <hr>
<H3> Integer Manipulaiton </h3>

<%
' Declare variable
Dim intVariable

' Assign the variable an integer value
intVariable = 5
%>

<P><%=intVariable%> + <%=intVariable%> =
<%=intVariable + intVariable%></p>

<HR>
<H3> String Manipulation </h3>

<%
' Declare variable
Dim strVariable

' Assign the variable a string value
strVariable = "Jemearl"
%>

<P>This example was done by<%=strVariable + " Smith" %></p>

<HR>
<H3> Boolean Manipulation </h3>

<%
' Declare variable
Dim blnVariable

' Assign the variable a boolean value
blnVariable = true

' Output message based on value
If (blnVariable = true) Then
  Response.Write "<P>The boolean value is True.</P>"
Else
  Response.Write "<P>The boolean value is False.</P>"
End If
%>

<HR>
<H3> Date and Time </h3>

<%
' Declare variable
Dim dtmVariable

' Assign the variable a value
dtmVariable = #08 / 27 / 97 5:11:42pm#
%>

<P>The date and time is <%=dtmVariable%> </p>

<%
' Set the variable to the web server date and time
dtmVariable = Now
%>

<P>The <STRONG>system</strong> date and time is
<%=dtmVariable%></p>

    </BODY>
</HTML>


FUNDTRANSFER_JSCRIPT.ASP

<%@ TRANSACTION=Required LANGUAGE="JScript" %>

<!--#include file="adojavas.inc"-->

<HTML>
    <HEAD>
        <TITLE>Transactional Database Update</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Transactional Database Update</b></font><br>

        <hr size="1" color="#000000">

        <!-- Brief Description blurb.  -->

        This is a simple example demonstrating how to transactionally
        update a SQL 6.5 database using ADO and Transacted ASP.
        The example will obtain information regarding a book sale from
        the SQL 6.5 "Pubs" database.  It will then increment the quantity
        of books sold by one, as well as change the zip-code of the store
        in which the book was sold.

        <p> Because the two database operations are wrapped
        within a shared ASP Transaction, both will be automatically rolled
        back to their previous state in the event of a failure.

<%
var oConn;// object for ADODB.Connection obj
var oRs;// object for recordset object
var oRs2;// object for recordset object

// Create Connection and Recordset components

oConn = Server.CreateObject("ADODB.Connection");
oRs   = Server.CreateObject("ADODB.Recordset");
oRs2  = Server.CreateObject("ADODB.Recordset");

// Open ADO Connection using account "sa"
// and blank password

oConn.Open("DSN=LocalServer;UID=sa;PWD=;DATABASE=pubs");
oRs.ActiveConnection = oConn;
oRs2.ActiveConnection = oConn;

// Find a random book sale

oRs.Source = "SELECT * FROM sales";
oRs.CursorType = adOpenStatic;// use a cursor other than Forward Only
oRs.LockType = adLockOptimistic;// use a locktype permitting insertions
oRs.Open();

// Change quantity sold

if (! oRs.EOF) {
oRs("qty").Value = oRs("qty").Value + 1;
oRs.Update();
}

// Find the store in which the book was sold

oRs2.Source = "SELECT * FROM stores where stor_id='" + oRs("stor_id") + "'";
oRs2.CursorType = adOpenStatic;// use a cursor other than Forward Only
oRs2.LockType = adLockOptimistic;// use a locktype permitting insertions
oRs2.Open();

// Change zip code

if (! oRs2.EOF) {
oRs2("Zip").Value = new Number(oRs2("Zip").Value) + 1;
oRs2.Update();
}
%>

    </BODY>
</HTML>

<%
    // The Transacted Script Commit Handler.  This sub-routine
    // will be called if the transacted script commits.

    function OnTransactionCommit()
    {
Response.Write("<p><b>The update was successful</b>.");
    }

    // The Transacted Script Abort Handler.  This sub-routine
    // will be called if the script transacted aborts

    function OnTransactionAbort()
    {
Response.Write("<p><b>The update was not successful</b>.");
    }
%>


FUNDTRANSFER_VBSCRIPT.ASP

<%@ TRANSACTION=Required LANGUAGE="VBScript" %>

<!--#include file="adovbs.inc"-->

<HTML>
    <HEAD>
        <TITLE>Transactional Database Update</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Transactional Database Update</b></font><br>

        <hr size="1" color="#000000">

        <!-- Brief Description blurb.  -->

        This is a simple example demonstrating how to transactionally
        update a SQL 6.5 database using ADO and Transacted ASP.
        The example will obtain information regarding a book sale from
        the SQL 6.5 "Pubs" database.  It will then increment the quantity
        of books sold by one, as well as change the zip-code of the store
        in which the book was sold.

        <p> Because the two database operations are wrapped
        within a shared ASP Transaction, both will be automatically rolled
        back to their previous state in the event of a failure.

<%
Dim oConn' object for ADODB.Connection obj
Dim oRs' object for recordset object
Dim oRs2' object for recordset object

' Create Connection and Recordset components

Set oConn = Server.CreateObject("ADODB.Connection")
Set oRs   = Server.CreateObject("ADODB.Recordset")
Set oRs2  = Server.CreateObject("ADODB.Recordset")

' Open ADO Connection using account "sa"
' and blank password

oConn.Open "DSN=LocalServer;UID=sa;PWD=;DATABASE=pubs"
Set oRs.ActiveConnection = oConn
Set oRs2.ActiveConnection = oConn

' Find a random book sale

oRs.Source = "SELECT * FROM sales"
oRs.CursorType = adOpenStatic' use a cursor other than Forward Only
oRs.LockType = adLockOptimistic' use a locktype permitting insertions
oRs.Open

' Change quantity sold

If (Not oRs.EOF) Then
oRs("qty").Value = oRs("qty").Value + 1
oRs.Update
End If

' Find the store in which the book was sold

oRs2.Source = "SELECT * FROM stores where stor_id='" & oRs("stor_id") & "'"
oRs2.CursorType = adOpenStatic' use a cursor other than Forward Only
oRs2.LockType = adLockOptimistic' use a locktype permitting insertions
oRs2.Open

' Change zip code

If (Not oRs2.EOF) Then
oRs2("Zip").Value = oRs2("Zip").Value + 1
oRs2.Update
End If
%>

    </BODY>
</HTML>

<%
    ' The Transacted Script Commit Handler.  This sub-routine
    ' will be called if the transacted script commits.

    Sub OnTransactionCommit()
Response.Write "<p><b>The update was successful</b>."
    End sub

    ' The Transacted Script Abort Handler.  This sub-routine
    ' will be called if the script transacted aborts

    Sub OnTransactionAbort()
Response.Write "<p><b>The update was not successful</b>."
    end sub
%>


RUNTIMEERRORABORT_JSCRIPT.ASP

<%@ TRANSACTION=Required LANGUAGE="JScript" %>

<HTML>
    <HEAD>
        <TITLE>Syntax Error Abort within a Transactional Web Page</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Syntax Error Abort within a Transactional Web Page</b></font><br>

<hr size="1" color="#000000">

<!-- Brief Description blurb.  -->

This is an example demonstrating how a runtime error (that will
stop the execution of the ASP page), will force an abort
within the Transacted Web Page.  When an abort occurs,
all transacted changes within this web page (Database,
MSMQ Message Transmission, etc.) will be rolled back to
their previous state -- guarenteeing data integrity.

<%
// Syntax Error that will produce abort

blah.blah();
%>

    </BODY>
</HTML>

<%
    // The Transacted Script Commit Handler.  This function
    // will be called if the transacted script commits.
    // Note that in the example above, there is no way for the
    // script not to abort.

    function OnTransactionCommit()
    {
        Response.Write ("<p><b>The Transaction just comitted</b>.");
        Response.Write ("This message came from the ");
        Response.Write ("OnTransactionCommit() event handler.");
    }

    // The Transacted Script Abort Handler.  This function
    // will be called if the transacted script aborts
    // Note that in the example above, there is no way for the
    // script not to abort.

    function OnTransactionAbort()
    {
        Response.Write ("<p><b>The Transaction just aborted</b>.");
        Response.Write ("This message came from the ");
        Response.Write ("OnTransactionAbort() event handler.");
    }
%>


RUNTIMEERRORABORT_VBSCRIPT.ASP

<%@ TRANSACTION=Required LANGUAGE="VBScript" %>

<HTML>
    <HEAD>
        <TITLE>Syntax Error Abort within a Transactional Web Page</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

<!-- Display Header -->

<font size="4" face="Arial, Helvetica">
<b>Syntax Error Abort within a Transactional Web Page</b></font><br>

<hr size="1" color="#000000">

<!-- Brief Description blurb.  -->

This is an example demonstrating how a runtime error (that will
stop the execution of the ASP page), will force an abort
within the Transacted Web Page.  When an abort occurs,
all transacted changes within this web page (Database,
MSMQ Message Transmission, etc.) will be rolled back to
their previous state -- guarenteeing data integrity.

<%
' Runtime Error that will produce abort

blah.blah
%>

    </BODY>
</HTML>

<%
    ' The Transacted Script Commit Handler.  This sub-routine
    ' will be called if the transacted script commits.
    ' Note that in the example above, there is no way for the
    ' script not to abort.

    Sub OnTransactionCommit()
        Response.Write "<p><b>The Transaction just comitted</b>."
        Response.Write "This message came from the "
        Response.Write "OnTransactionCommit() event handler."
    end sub

    ' The Transacted Script Abort Handler.  This sub-routine
    ' will be called if the transacted script aborts
    ' Note that in the example above, there is no way for the
    ' script not to abort.

    Sub OnTransactionAbort()
        Response.Write "<p><b>The Transaction just aborted</b>."
        Response.Write "This message came from the "
        Response.Write "OnTransactionAbort() event handler."
    end sub
%>


SETABORT_JSCRIPT.ASP

<%@ TRANSACTION=Required LANGUAGE="JScript" %>

<HTML>
    <HEAD>
        <TITLE>Forced Abort with a Transactional Web Page</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Forced Abort with a Transactional Web Page</b></font><br>

        <hr size="1" color="#000000">

        <!-- Brief Description blurb.  -->

        This is an example demonstrating a forced abort
        within a Transacted Web Page.  When an abort occurs,
        all transacted changes within this web page (Database Access,
        MSMQ Message Transmission, etc.) will be rolled back to
        their previous state -- guarenteeing data integrity.

        <%
            // Abort Transaction

            ObjectContext.SetAbort();
        %>

    </BODY>
</HTML>

<%
    // The Transacted Script Commit Handler.  This function
    // will be called if the transacted script commits.
    // Note that in the example above, there is no way for the
    // script not to abort.

    function OnTransactionCommit()
    {
        Response.Write ("<p><b>The Transaction just comitted</b>.");
        Response.Write ("This message came from the ");
        Response.Write ("OnTransactionCommit() event handler.");
    }

    // The Transacted Script Abort Handler.  This function
    // will be called if the transacted script aborts
    // Note that in the example above, there is no way for the
    // script not to abort.

    function OnTransactionAbort()
    {
        Response.Write ("<p><b>The Transaction just aborted</b>.");
        Response.Write ("This message came from the ");
        Response.Write ("OnTransactionAbort() event handler.");
    }
%>


SETABORT_VBSCRIPT.ASP

<%@ TRANSACTION=Required LANGUAGE="VBScript" %>
<%  Option Explicit %>

<HTML>
    <HEAD>
        <TITLE>Forced Abort with a Transactional Web Page</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Forced Abort with a Transactional Web Page</b></font><br>

        <hr size="1" color="#000000">

        <!-- Brief Description blurb.  -->

        This is an example demonstrating a forced abort
        within a Transacted Web Page.  When an abort occurs,
        all transacted changes within this web page (Database Access,
        MSMQ Message Transmission, etc.) will be rolled back to
        their previous state -- guarenteeing data integrity.

        <%
            ' Abort Transaction

            ObjectContext.SetAbort
        %>

    </BODY>
</HTML>

<%
    ' The Transacted Script Commit Handler.  This sub-routine
    ' will be called if the transacted script commits.
    ' Note that in the example above, there is no way for the
    ' script not to abort.

    Sub OnTransactionCommit()
        Response.Write "<p><b>The Transaction just comitted</b>."
        Response.Write "This message came from the "
        Response.Write "OnTransactionCommit() event handler."
    end sub

    ' The Transacted Script Abort Handler.  This sub-routine
    ' will be called if the transacted script aborts
    ' Note that in the example above, there is no way for the
    ' script not to abort.

    Sub OnTransactionAbort()
        Response.Write "<p><b>The Transaction just aborted</b>."
        Response.Write "This message came from the "
        Response.Write "OnTransactionAbort() event handler."
    end sub
%>


SIMPLETRANSACTION_JSCRIPT.ASP

<%@ TRANSACTION=Required LANGUAGE="JScript" %>

<HTML>
    <HEAD>
        <TITLE>Simple Transactional Web Page</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Simple Transactional Web Page</b></font><br>

        <hr size="1" color="#000000">

        <!-- Brief Description blurb.  -->

        This is a simple example demonstrating the basic
        structure of a Transacted Web Page.

    </BODY>
</HTML>

<%
    // The Transacted Script Commit Handler.  This function
    // will be called if the transacted script commits.
    // Note that in the example above, there is no way for the
    // script not to commit.

    function OnTransactionCommit()
    {
        Response.Write ("<p><b>The Transaction just comitted</b>.");
        Response.Write ("This message came from the ");
        Response.Write ("OnTransactionCommit() event handler.");
    }

    // The Transacted Script Abort Handler.  This function
    // will be called if the script transacted aborts
    // Note that in the example above, there is no way for the
    // script not to commit.

    function OnTransactionAbort()
    {
        Response.Write ("<p><b>The Transaction just aborted</b>.");
        Response.Write ("This message came from the ");
        Response.Write ("OnTransactionAbort() event handler.");
    }
%>


SIMPLETRANSACTION_VBSCRIPT.ASP

<%@ TRANSACTION=Required LANGUAGE="VBScript" %>

<HTML>
    <HEAD>
        <TITLE>Simple Transactional Web Page</TITLE>
    </HEAD>

    <BODY BGCOLOR="White" topmargin="10" leftmargin="10">

        <!-- Display Header -->

        <font size="4" face="Arial, Helvetica">
        <b>Simple Transactional Web Page</b></font><br>

        <hr size="1" color="#000000">

        <!-- Brief Description blurb.  -->

        This is a simple example demonstrating the basic
        structure of a Transacted Web Page.

    </BODY>
</HTML>

<%
    ' The Transacted Script Commit Handler.  This sub-routine
    ' will be called if the transacted script commits.
    ' Note that in the example above, there is no way for the
    ' script not to commit.

    Sub OnTransactionCommit()
        Response.Write "<p><b>The Transaction just comitted</b>."
        Response.Write "This message came from the "
        Response.Write "OnTransactionCommit() event handler."
    end sub

    ' The Transacted Script Abort Handler.  This sub-routine
    ' will be called if the script transacted aborts
    ' Note that in the example above, there is no way for the
    ' script not to commit.

    Sub OnTransactionAbort()
        Response.Write "<p><b>The Transaction just aborted</b>."
        Response.Write "This message came from the "
        Response.Write "OnTransactionAbort() event handler."
    end sub
%>


README.TXT

Multilingual Simple and Power Samples
                =====================================

Table of Contents
=================

    Overview
    Developing Components
    C++
    ATL
    Installation
    Directory List
    Usage
    Sample ASP scripts
    Support
    Change Notes

Overview
========

Here, for your edification, are two Active Server Pages components,
each implemented in two different environments: C++/ATL (Microsoft
ActiveX Template Library) and Microsoft Visual Basic 5.

The implementations are functionally equivalent.  Each implementation
should behave in exactly the same way.  Each implementation is written
in an idiomatic style to demonstrate the best way to implement the
component in that environment.

The Simple component has one method (myMethod) and one property
(myProperty), which can be both retrieved (get) and modified (set).
MyMethod converts a string to uppercase; myProperty is a string in the
object that can be get or set.  There is nothing ASP-specific about
the Simple component and, if desired, it could be used from any OLE
container.

The Power component (found in the \intermediate folder) is a superset
of the Simple component.  In addition to myMethod and myProperty, it
has myPowerMethod and myPowerProperty (gettable but not settable),
which demonstrate some ASP-specific features.  Two other standard but
optional ASP methods are also provided: OnStartPage and OnEndPage.
These methods bracket the lifetime of a page.  OnStartPage is needed
to gain access to the intrinsic ASP objects: Request, Response,
Server, Application, and Session.

MyPowerMethod and myPowerProperty make use of the Request and Response
objects: Request to gain access to the ServerVariables collection and
thereby the ScriptName and the HttpUserAgent (user's browser)
variables; Response to write directly back to the user's browser.
MyPowerProperty returns the name of the ASP script from which it was
called.  MyPowerMethod writes a browser-dependent message back to the
user's browser; one for Microsoft Internet Explorer, a different one
for anything else.

Developing Components
=====================

You can develop ASP components in Visual C++, Visual J++, Visual
Basic, or any other language that builds ActiveX COM objects.

These samples are written for IIS 4.0.  You must have the latest
version of IIS to compile and use these components.  You must
also have installed Microsoft Transaction Server (part of the Windows
NT 4.0 Option Pack).

C++
===

The C++ samples require Microsoft Visual C++ 5.0 or newer along with
ATL (which comes with VC5).

If you get an error about "don't know how to make asptlb.h" for the
power samples, you will need to copy

<InstallDir>\iissamples\sdk\include

to your include directory, \Program Files\DevStudio\VC\include.
The default installation directory is C:\Inetpub.

Finally you must have the lastest version of MS Transaction Server
installed (part of Windows NT 4.0 Option Pack).  Add the include path
and library path of MTX before any system directories under
Tools:Options....  So your include path might look like this:
c:\program files\mts\include;c:program files\devstudio\vc\include;...
(depending on where you have installed MTX).

If you intend to build from the command line, you must edit
vcvars32.bat accordingly as well.

ATL
===

The ATL samples also requires the latest version of ATL (Microsoft
Active Template Library).  ATL ships with Visual C++ 5.0.

ATL is the recommended library for writing ASP and other ActiveX
components in C++:
    * produces small, fast, industrial-strength components ("lean and mean")
    * supports all COM threading models (single, apartment, free)
    * supports IDispatch interfaces
    * dual interfaces are easy
    * supports the OLE error mechanism
    * methods are called very quickly
    * fine control over COM features ("closer to the metal")
    * many other features

For ATL 2.0, in order to add a method or a property to a component, it
is necessary to modify the .idl, .h, and .cpp files by hand.  With ATL
2.1, the wizards in Visual C++ 5.0 automates much of this for you.

To create a new component with ATL:
    * Start a New Project Workspace in Developer Studio
    * Select ATL COM AppWizard
    * Enter the name and location of the project
    * Hit Create.
    * Accept the defaults and hit Finish.

Now that you've created the project, it's time to create a COM object
within the project.  In Visual C++ 5.0, you can go to the Insert menu
where you'll see New ATL Object... or you can right-click on the classes
in the ClassView pane, where you'll also see New ATL Object....

When the ATL Object Wizard pops up, you'll see two panes.  In the left
pane, click on Objects.  In the right pane, double-click on Simple Object.
If you have VC 5.0, you'll see a number of additional objects; click on
ActiveX Server Component instead.

The ATL Object Wizard Properties dialog will appear.  On the Names tab,
type in the short name of your object.  The other names will be filled in
automatically.  You can edit them if you wish.  It's quite likely that
you'll want to edit the Prog ID.

On the Attributes tab, you may want to change the Threading Model to Both.
You probably don't need to support Aggregation.  You ought to Support
ISupportErrorInfo.  The other attributes should not need to be changed.

On the ASP tab, you can enable OnStartPage and OnEndPage methods and select
which of the ASP intrinsic objects you want to use.  However, it is now
recommended that you use the Object Context (which doesn't require the
OnStartPage and OnEndPage methods) rather than the Scripting Context (which
is what OnStartPage supplies you with).  This allows you to create more
powerful application state objects.  All the samples that need it use the
Object Context rather than the scripting context.

Note the use of the ATL wrapper classes CComBSTR, CComVariant,
CComPtr, and CComQIPtr in the ATL samples to simplify management of
COM data types.

Installation
============

To install these sample components, you must first compile them with
Microsoft Visual C++.  Load the individual project files into VC++
and build the DLLs.  You can also build the DLLs by just typing
"nmake" in the command line.  Running nmake in the top level directory
(the directory this file is in) will build both the simple and power
DLLs.

These components may not compile correctly unless you have the correct
header and library files.  You may need to copy all the header files
in ...\iissamples\sdk\include (default is C:\Inetpub\...) to your VC++
include directory.  In addition, you may need to copy the MTS header
and library files to the VC++ include and library directory.  The MTS
header files are located in ...\MTS\include (default is C:\Program
Files\...) while the MTS library files are located in ...\MTS\lib.

*** NOTE: The MTS header and library files are not installed by
default.  In most cases, you will need to install this using the
Windows NT 4.0 Option Pack setup.

If you intend to run these components on a machine other than the one
on which they are compiled, you will need to copy the DLLs to the
target machine and
    regsvr32 /s /c SAMPLE.dll
(the samples will be registered automatically on the machine they're
built on).

If you have trouble registering components, you may be using the
wrong version of RegSvr32.exe.  Please use the version installed by
default in the directory \<winnt-directory>\system32\inetsrv.

Directory List
==============

Directory        Description
---------        -----------

 Simple          C++ ATL Simple Sample
 Intermediate    C++ ATL Power Sample

Usage
=====

To use the samples, simply call Server.CreateObject("IISSample.XXX")
to create the object, where "XXX" is one of
    C++ATLSimple     C++ATLPower
Then you may access the object's methods and properties from your ASP
script.  Read any of the sample ASP scripts to discover how to use the
objects.

Sample ASP scripts
==================

You must copy the sample ASP scripts to a virtual directory (it need
not be a virtual root) on the IIS Server where you have installed the
sample components before they will work.

Support
=======

This component is not officially supported by Microsoft Corporation.
Peer support is available on the Active Server Pages mailing list or on
the microsoft.public.inetserver.iis.activeserverpages newsgroup.

To subscribe to the Active Server Pages mailing list, send mail to
listserv@listserv.msn.com with

subscribe ActiveServerPages [firstname lastname]

in the body of the message, and then follow the directions carefully.
(firstname and lastname are optional.)

You can reach the newsgroup through msnews.microsoft.com and other NNTP
servers.

Change Notes
============

Version 1.0 Beta 1: February 1997
---------------------------------

First release.

Version 1.0 Beta 2: March 1997
------------------------------

* Fixed Developer Studio makefile problems in C++ components.
* Upgraded to build cleanly with ATL 2.0 (Visual C++ 4.2b) and ATL 2.0 (VC5).
* Better comments in Power components.

Version 2.0 Beta 3: September 1997
----------------------------------

* Updated for IIS4.0
* Using IObjectContext instead of IScriptingContext.

Version 2.1: October 1997
-------------------------

* Modified for the IIS4.0 SDK release


README.TXT

C++ ATL Power Sample
                        ====================

Table of Contents
=================

    Overview
    File List
    Change Notes

Overview
========

This is the ATL (ActiveX Template Library) version of the Power
sample object for Active Server Pages.  It demonstrates how to write
a component that makes use of the intrinsic ASP objects.

Please read the top-level ReadMe for the Simple and Power (found in
the \intermediate folder) samples.

Look at CATLPwr.idl, Power.h, and Power.cpp to see how these
methods and properties are declared and implemented.

File List
=========

File             Description
----             -----------

 Power.cpp       the C++ source code for the power ATL component
 Power.h         declarations for CPower
 CATLPwr.idl     declaration of IPower, the IDispatch-based interface
 Makefile        a makefile that can be used with nmake
 CATLPwr.mak     the Developer Studio makefile
 CATLPwr.cpp     )
 CATLPwr.def     )
 CATLPwr.dsp     )
 CATLPwr.rc      )
 PowerPS.def     } Generated by the ATL COM AppWizard
 PowerPS.mak     )
 Resource.h      )
 StdAfx.cpp      )
 StdAfx.h        )

 Context.cpp    )
 Context.h      ) Shared support classes

 CATLPwr.asp     Demonstrates the use of the ATL Power Sample object

Change Notes
============

Beta 1: February 1997
---------------------

First release.

Beta 2: March 1997
------------------

* Fixed Developer Studio makefile problems.
* Upgraded to build cleanly with ATL 2.0 (Visual C++ 4.2b) and ATL 2.0 (VC5).
* Better comments.

Version 2: September 1997
-------------------------
Upgraded to IIS4.0

Version 2.1: October 1997
-------------------------
Modified for the IIS4.0 SDK release


CATLPWR.CPP

// CATLPwr.cpp : Implementation of DLL Exports.

// You will need the NT SUR Beta 2 SDK or VC 4.2 in order to build this
// project.  This is because you will need MIDL 3.00.15 or higher and new
// headers and libs.  If you have VC 4.2 installed, then everything should
// already be configured correctly.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f CATLPwrps.mak in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "CATLPwr.h"
#include "Power.h"

#define IID_DEFINED
#include "CATLPwr_i.c"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_CPower, CPower)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
_Module.UnregisterServer();
return S_OK;
}


CATLPWR.IDL

// CATLPwr.idl : IDL source for CATLPwr.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (CATLPwr.tlb) and marshalling code.

    [
        object,
        uuid(61CECB3A-68D3-11D0-8AE3-00C0F00AE35A),
        dual,
        helpstring("IPower Interface"),
        pointer_default(unique),
        oleautomation
    ]
    interface IPower : IDispatch
    {
        import "oaidl.idl";

        [id(1), propget, helpstring("Get MyProperty's value")]
        HRESULT myProperty([out,retval] BSTR* pbstrOutValue);

        [id(1), propput, helpstring("Set MyProperty's value")]
        HRESULT myProperty([in] BSTR bstrInValue);

        [id(2), helpstring("Convert argument to uppercase")]
        HRESULT myMethod([in] BSTR bstrIn, [out, retval] BSTR* pbstrOut);

        [id(3), propget, helpstring("Return the script's name")]
        HRESULT myPowerProperty([out,retval] BSTR* pbstrOutValue);

        [id(4), helpstring("Write message according to browser")]
        HRESULT myPowerMethod();
    };

[
    uuid(61CECB38-68D3-11D0-8AE3-00C0F00AE35A),
    version(1.0),
    helpstring("IISSample CATLPwr Object Library")
]
library CATLPwr
{
    importlib("stdole32.tlb");

    [
        uuid(61CECB3E-68D3-11D0-8AE3-00C0F00AE35A),
        helpstring("Power Class")
    ]
    coclass CPower
    {
        [default] interface IPower;
    };

};


CONTEXT.CPP

/*++

Copyright(c)    1997    Microsoft Corporation

ModuleName :

context.h

Abstract:
A class to retrieve and release ASP intrinsics

Author:

Neil Allain   ( a-neilal )   August-1997

Revision History:

--*/
#include "stdafx.h"
#include <asptlb.h>
#include "context.h"

//---------------------------------------------------------------------------
//GetServerObject
//
//Get an instrinic object from the current Object context
//---------------------------------------------------------------------------
HRESULT
CContext::GetServerObject(
IGetContextProperties*pProps,
BSTRbstrObjName,
const IID&iid,
void**ppObj
)
{
HRESULT rc = E_FAIL;
_ASSERT( pProps );
_ASSERT( bstrObjName );
_ASSERT( ppObj );
if ( pProps && bstrObjName && ppObj )
{
*ppObj = NULL;
CComVariant vt;
if ( !FAILED( pProps->GetProperty( bstrObjName, &vt ) ) )
{
if ( V_VT(&vt) == VT_DISPATCH )
{
IDispatch* pDispatch = V_DISPATCH(&vt);
if ( pDispatch )
{
rc = pDispatch->QueryInterface( iid, ppObj );
}
}
}
}
return rc;
}

HRESULT
CContext::Init(
DWORDdwFlags // which instrinsics to initialize
)
{
HRESULT rc = E_FAIL;
CComPtr<IObjectContext> pObjContext;

rc = ::GetObjectContext( &pObjContext );
if ( !FAILED( rc ) )
{
CComPtr<IGetContextProperties> pProps;
rc = pObjContext->QueryInterface( IID_IGetContextProperties, (void**)&pProps );
if ( !FAILED( rc ) )
{
CComBSTR bstrObj;
if ( dwFlags & get_Request )
{
bstrObj = "Request";
rc = GetServerObject( pProps, bstrObj, IID_IRequest, (void**)&m_piRequest );
}
if ( !FAILED(rc) && ( dwFlags & get_Response ) )
{
bstrObj = "Response";
rc = GetServerObject( pProps, bstrObj, IID_IResponse, (void**)&m_piResponse );
}

if ( !FAILED(rc) && ( dwFlags & get_Session ) )
{
bstrObj = "Session";
rc = GetServerObject( pProps, bstrObj, IID_ISessionObject, (void**)&m_piSession );
}

if ( !FAILED(rc) && ( dwFlags & get_Server ) )
{
bstrObj = "Server";
rc = GetServerObject( pProps, bstrObj, IID_IServer, (void**)&m_piServer );
}

if ( !FAILED(rc) && ( dwFlags & get_Application ) )
{
bstrObj = "Application";
rc = GetServerObject( pProps, bstrObj, IID_IApplicationObject, (void**)&m_piApplication );
}
}
}
return rc;
}


CONTEXT.H

/*++

Copyright(c)    1997    Microsoft Corporation

Module  Name :

context.h

Abstract:
A class to retrieve and release ASP intrinsics.  This class encapsulates the new way
of getting access to ASP instinsic objects.  It does away with the need for OnStartPage
and OnEndPage methods, and, by doing so, gives Application scope objects access to the
intrinsics.  The intrincis should never be retained across calls, as the context may
change between calls.

Author:

Neil Allain     August-1997

Revision History:

--*/
#pragma once
#ifndef _CONTEXT_H_
#define _CONTEXT_H_

class CContext
{
public:
enum {
get_Server= 0x0001,
get_Response= 0x0002,
get_Request= 0x0004,
get_Session= 0x0008,
get_Application= 0x0010,
        get_All         = 0xFFFF
};

HRESULT            Init( DWORD );

IRequest*Request(){ _ASSERT(m_piRequest!=NULL); return m_piRequest; }
IResponse*Response(){ _ASSERT(m_piResponse!=NULL); return m_piResponse; }
ISessionObject*Session(){ _ASSERT(m_piSession!=NULL); return m_piSession; }
IServer*Server(){ _ASSERT(m_piServer!=NULL); return m_piServer; }
IApplicationObject*Application(){ _ASSERT(m_piApplication!=NULL); return m_piApplication; }

static HRESULTGetServerObject( IGetContextProperties*, BSTR, const IID&, void** );

private:
CComPtr<IRequest>m_piRequest;//Request Object
CComPtr<IResponse>m_piResponse;//Response Object
CComPtr<ISessionObject>m_piSession;//Session Object
CComPtr<IServer>m_piServer;//Server Object
CComPtr<IApplicationObject> m_piApplication;//Application Object
};

#endif// !_CONTEXT_H_


POWER.CPP

// Power.cpp : Implementation of CCATLPwrApp and DLL registration.

#include "stdafx.h"
#include "CATLPwr.h"
#include "Power.h"
#include "context.h"
/////////////////////////////////////////////////////////////////////////////
//

STDMETHODIMP CPower::InterfaceSupportsErrorInfo(REFIID riid)
{
    static const IID* arr[] =
    {
        &IID_IPower,
    };

    for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
    {
        if (InlineIsEqualGUID(*arr[i],riid))
            return S_OK;
    }
    return S_FALSE;
}

// Ctor
CPower::CPower()
    : m_bstrMyProperty(OLESTR("C++ ATL Power Component"))
{}

// Get function for myProperty
STDMETHODIMP CPower::get_myProperty(BSTR* pbstrOutValue)
{
    if (pbstrOutValue == NULL)
        return E_POINTER;

    // Get Value from Property
    *pbstrOutValue = m_bstrMyProperty.Copy();

    return S_OK;
}

// Put function for myProperty
STDMETHODIMP CPower::put_myProperty(BSTR bstrInValue)
{
    if (bstrInValue == NULL)
        return E_POINTER;

    m_bstrMyProperty = bstrInValue;

    return S_OK;
}

// Basic Method which Converts bstrIn to Upper Case
STDMETHODIMP CPower::myMethod(BSTR bstrIn, BSTR* pbstrOut)
{
    if (bstrIn == NULL || pbstrOut == NULL)
        return E_POINTER;

    // Create a temporary CComBSTR
    CComBSTR bstrTemp(bstrIn);

    if (!bstrTemp)
        return E_OUTOFMEMORY;

    // Make string uppercase
    wcsupr(bstrTemp);

    // Return m_str member of bstrTemp
    *pbstrOut = bstrTemp.Detach();

    return S_OK;
}

///////////// ASP-Specific Component Methods ////////////////

// Get Function Returns the Name of the Current Script
STDMETHODIMP CPower::get_myPowerProperty(BSTR* pbstrOutValue)
{
// the Context class is an easy way to use IIS 4's new way
// of getting instrinic objects.  The new method may seem
// more complex, but it is more powerful since obects no
// longer have to be page/session level to get access
// to them.
CContext cxt;
if ( FAILED( cxt.Init( CContext::get_Request ) ) )
{
return E_FAIL;
}

// Do we have somewhere valid to store the return value?
if (pbstrOutValue == NULL)
return E_POINTER;

// Initialize the return value
*pbstrOutValue = NULL;

// Get the ServerVariables Collection
CComPtr<IRequestDictionary> piServerVariables;
HRESULT hr = cxt.Request()->get_ServerVariables(&piServerVariables);

    if (FAILED(hr))
        return hr;

    // Get the SCRIPT_NAME item from the ServerVariables collection
    CComVariant vtIn(OLESTR("SCRIPT_NAME")), vtOut;
    hr = piServerVariables->get_Item(vtIn, &vtOut);

    if (FAILED(hr))
        return hr;

    // Get the SCRIPT_NAME item from the ServerVariables collection
    // vtOut Contains an IDispatch Pointer.  To fetch the value
    // for SCRIPT_NAME, you must get the Default Value for the
    // Object stored in vtOut using VariantChangeType.
    hr = VariantChangeType(&vtOut, &vtOut, 0, VT_BSTR);

    // Copy and return SCRIPT_NAME
    if (SUCCEEDED(hr))
        *pbstrOutValue = ::SysAllocString(V_BSTR(&vtOut));

    return hr;
}

// ASP-specific Power Method
STDMETHODIMP CPower::myPowerMethod()
{
CContext cxt;
if ( FAILED( cxt.Init( CContext::get_Request | CContext::get_Response ) ) )
{
return E_FAIL;
}

    // Get the ServerVariables Collection
    CComPtr<IRequestDictionary> piServerVariables;
    HRESULT hr = cxt.Request()->get_ServerVariables(&piServerVariables);

    if (FAILED(hr))
        return hr;

    // Get the HTTP_USER_AGENT item from the ServerVariables collection
    CComVariant vtIn(OLESTR("HTTP_USER_AGENT")), vtOut;
    hr = piServerVariables->get_Item(vtIn, &vtOut);

    if (FAILED(hr))
        return hr;

    // vtOut Contains an IDispatch Pointer.  To fetch the value
    // for HTTP_USER_AGENT, you must get the Default Value for the
    // Object stored in vtOut using VariantChangeType.
    hr = VariantChangeType(&vtOut, &vtOut, 0, VT_BSTR);

    if (SUCCEEDED(hr))
    {
        // Look for "MSIE" in HTTP_USER_AGENT string
        if (wcsstr(vtOut.bstrVal, L"MSIE") != NULL)
            cxt.Response()->Write(CComVariant(
                OLESTR("You are using a very powerful browser.")));
        else
            cxt.Response()->Write(CComVariant(
                OLESTR("Try Internet Explorer today!")));
    }

    return hr;
}


POWER.H

// Power.h : Declaration of the CPower

#include "resource.h"       // main symbols

#include <asptlb.h>         // ASP Definitions

// 'asptlb.h' is installed with Active Server Pages.  Either copy it
// to your Include directory or add the appropriate directory to your
// Include Path.

/////////////////////////////////////////////////////////////////////////////
// CATLPwr

class CPower :
    public CComDualImpl<IPower, &IID_IPower, &LIBID_CATLPwr>,
    public ISupportErrorInfo,
    public CComObjectRoot,
    public CComCoClass<CPower,&CLSID_CPower>
{
public:
    CPower();
BEGIN_COM_MAP(CPower)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IPower)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.p)
END_COM_MAP()
//DECLARE_NOT_AGGREGATABLE(CPower)
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it

DECLARE_REGISTRY(CPower, _T("IISSample.C++ATLPower.1"), _T("IISSample.C++ATLPower"), IDS_POWER_DESC, THREADFLAGS_BOTH)
// ISupportsErrorInfo
    STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IPower
public:
// for free-threaded marshalling
DECLARE_GET_CONTROLLING_UNKNOWN()
HRESULT FinalCountruct()
{
return CoCreateFreeThreadedMarshaler(
GetControllingUnknown(), &m_pUnkMarshaler.p );
}
void FinalRelease()
{
m_pUnkMarshaler.Release();
}

    STDMETHOD(get_myProperty)(BSTR* pbstrOutValue);
    STDMETHOD(put_myProperty)(BSTR bstrInValue);
    STDMETHOD(myMethod)(BSTR bstrIn, BSTR* pbstrOut);

    // ASP-specific Property and Method
    STDMETHOD(get_myPowerProperty)(BSTR* pbstrOutValue);
    STDMETHOD(myPowerMethod)();

private:
CComPtr<IUnknown>m_pUnkMarshaler;
    CComBSTR                    m_bstrMyProperty;     // myProperty
};


CATLPWR.ASP

<HTML>

<HEAD>
<TITLE>Testing CATLPwr</TITLE>
</HEAD>

<H1>Testing CATLPwr</H1>

<% Set myobj = Server.CreateObject("IISSample.C++ATLPower") %>
myProperty Value = <%= myobj.myProperty %><BR>

<% newvalue = "Power" %>
Set myProperty to <%= newvalue %>
<% myobj.myProperty = newvalue %><BR>
myProperty Value is now: <%= myobj.myProperty %><BR>

<% mystring = "My String" %>
Call myMethod with <%= mystring %>: <%= myobj.myMethod(mystring) %><BR>

MyPowerMethod returns: <% myobj.myPowerMethod %><BR>

MyPowerProperty returns: <%= myobj.myPowerProperty %><br>

</HTML>


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by CATLPwr.rc
//
#define IDS_POWER_DESC1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//  stdafx.pch will be the pre-compiled header
//  stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

#include <atlimpl.cpp>


STDAFX.H

// stdafx.h : include file for standard system include files,
//      or project specific include files that are used frequently,
//      but are changed infrequently

#include <atlbase.h>
#include <mtx.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>


README.TXT

C++ ATL Simple Sample
                        =====================

Table of Contents
=================

    Overview
    File List
    Change Notes

Overview
========

This is the ATL (ActiveX Template Library) version of the Simple
sample object for Active Server Pages.  It demonstrates how to write
a component that has straightforward methods and properties.

Please read the top-level ReadMe for the Simple and Power (found in
the \intermediate folder) samples.

Look at CATLSmpl.idl, Simple.h, and Simple.cpp to see how these
methods and properties are declared and implemented.

File List
=========

File             Description
----             -----------

 Simple.cpp      the C++ source code for the simple ATL component
 Simple.h        declarations for CSimple
 CATLSmpl.idl    declaration of ISimple, the IDispatch-based interface
 Makefile        a makefile that can be used with nmake
 CATLSmpl.mak    the Developer Studio makefile
 CATLSmpl.cpp    )
 CATLSmpl.def    )
 CATLSmpl.dsp    )
 CATLSmpl.rc     )
 SmplPS.def      } Generated by the ATL COM AppWizard
 SmplPS.mak      )
 Resource.h      )
 StdAfx.cpp      )
 StdAfx.h        )

 CATLSmpl.asp    Demonstrates the use of the ATL Simple Sample object

Change Notes
============

Beta 1: February 1997
---------------------

First release.

Beta 2: March 1997
------------------

* Fixed Developer Studio makefile problems.
* Upgraded to build cleanly with ATL 2.0 (Visual C++ 4.2b) and ATL 2.0 (VC5).

Version 2: September 1997
-------------------------
Upgraded to IIS4.0

Version 2.1: October 1997
-------------------------
Modified for the IIS4.0 SDK release


CATLSMPL.CPP

// CATLSmpl.cpp : Implementation of DLL Exports.

// You will need the NT SUR Beta 2 SDK or VC 4.2 in order to build this
// project.  This is because you will need MIDL 3.00.15 or higher and new
// headers and libs.  If you have VC 4.2 installed, then everything should
// already be configured correctly.

// Note: Proxy/Stub Information
//To build a separate proxy/stub DLL,
//run nmake -f CATLSmplps.mak in the project directory.

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "CATLSmpl.h"
#include "Simple.h"

#define IID_DEFINED
#include "CATLSmpl_i.c"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_CSimple, CSimple)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
_Module.UnregisterServer();
return S_OK;
}


CATLSMPL.IDL

// CATLSmpl.idl : IDL source for CATLSmpl.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (CATLSmpl.tlb) and marshalling code.

    [
        object,
        uuid(61CECB2B-68D3-11D0-8AE3-00C0F00AE35A),
        dual,
        helpstring("ISimple Interface"),
        pointer_default(unique),
        oleautomation
    ]
    interface ISimple : IDispatch
    {
        import "oaidl.idl";

        [propget, id(1), helpstring("Get MyProperty's value")]
        HRESULT myProperty([out,retval] BSTR* pbstrOutValue);

        [propput, id(1), helpstring("Set MyProperty's value")]
        HRESULT myProperty([in] BSTR bstrInValue);

        [id(2), helpstring("Convert argument to uppercase")]
        HRESULT myMethod([in] BSTR bstrIn, [out, retval] BSTR* pbstrOut);
    };

[
    uuid(61CECB29-68D3-11D0-8AE3-00C0F00AE35A),
    version(1.0),
    helpstring("IISSample CATLSmpl Object Library")
]
library CATLSmpl
{
    importlib("stdole32.tlb");

    [
        uuid(61CECB2F-68D3-11D0-8AE3-00C0F00AE35A),
        helpstring("Simple Class")
    ]
    coclass CSimple
    {
        [default] interface ISimple;
    };

};


CATLSMPL.ASP

<HTML>

<HEAD>
<TITLE>Testing CATLSmpl</TITLE>
</HEAD>

<H1>Testing CATLSmpl</H1>

<% Set myobj = Server.CreateObject("IISSample.C++ATLSimple") %>
myProperty Value = <%= myobj.myProperty %><BR>

<% newvalue = "Simple" %>
Set myProperty to <%= newvalue %>
<% myobj.myProperty = newvalue %><BR>
myProperty Value is now: <%= myobj.myProperty %><BR>

<% mystring = "My String" %>
Call myMethod with <%= mystring %>: <%= myobj.myMethod(mystring) %><BR>

</HTML>


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by CATLSmpl.rc
//
#define IDS_SIMPLE_DESC1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SIMPLE.CPP

// Simple.cpp : Implementation of CSimple

#include "stdafx.h"
#include "CATLSmpl.h"
#include "Simple.h"

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

// Created by the ATL 1.1 COM Wizard
STDMETHODIMP CSimple::InterfaceSupportsErrorInfo(REFIID riid)
{
    static const IID* arr[] =
    {
        &IID_ISimple,
    };

    for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
    {
        if (InlineIsEqualGUID(*arr[i],riid))
            return S_OK;
    }
    return S_FALSE;
}

//Ctor
CSimple::CSimple()
    : m_bstrMyProperty(OLESTR("C++ ATL Simple Component"))
{}

//Put function for myProperty
STDMETHODIMP CSimple::put_myProperty(BSTR bstrInValue)
{
    if (bstrInValue == NULL)
        return E_POINTER;

    m_bstrMyProperty = bstrInValue;

    return S_OK;
}

//Get function for myProperty
STDMETHODIMP CSimple::get_myProperty(BSTR* pbstrOutValue)
{
    if (pbstrOutValue == NULL)
        return E_POINTER;

    // Get Value from Property
    *pbstrOutValue = m_bstrMyProperty.Copy();

    return S_OK;
}

//Basic Method to convert a string to uppercase
STDMETHODIMP CSimple::myMethod(BSTR bstrIn, BSTR* pbstrOut)
{
    if (bstrIn == NULL || pbstrOut == NULL)
        return E_POINTER;

    // Create a temporary CComBSTR
    CComBSTR bstrTemp(bstrIn);

    if (!bstrTemp)
        return E_OUTOFMEMORY;

    // Make string uppercase
    wcsupr(bstrTemp);

    // Return m_str member of bstrTemp
    *pbstrOut = bstrTemp.Detach();

    return S_OK;
}


SIMPLE.H

// Simple.h : Declaration of the CSimple

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// CATLSmpl

class CSimple :
    public CComDualImpl<ISimple, &IID_ISimple, &LIBID_CATLSmpl>,
    public ISupportErrorInfo,
    public CComObjectRoot,
    public CComCoClass<CSimple,&CLSID_CSimple>
{
public:
    CSimple();
BEGIN_COM_MAP(CSimple)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(ISimple)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.p)
END_COM_MAP()
//DECLARE_NOT_AGGREGATABLE(CSimple)
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it

DECLARE_REGISTRY(CSimple, _T("IISSample.C++ATLSimple.1"), _T("IISSample.C++ATLSimple"), IDS_SIMPLE_DESC, THREADFLAGS_BOTH)
// ISupportsErrorInfo
    STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// ISimple
public:
// for free-threaded marshalling
DECLARE_GET_CONTROLLING_UNKNOWN()
HRESULT FinalCountruct()
{
return CoCreateFreeThreadedMarshaler(
GetControllingUnknown(), &m_pUnkMarshaler.p );
}
void FinalRelease()
{
m_pUnkMarshaler.Release();
}

    STDMETHOD(get_myProperty)(BSTR* pbstrOutValue);
    STDMETHOD(put_myProperty)(BSTR bstrInValue);
    STDMETHOD(myMethod) (BSTR bstrIn, BSTR* pbstrOut);

private:
    CComBSTRm_bstrMyProperty;
CComPtr<IUnknown>m_pUnkMarshaler;
};


STDAFX.H

// stdafx.h : include file for standard system include files,
//      or project specific include files that are used frequently,
//      but are changed infrequently

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>


README.TXT

This directory contains the classes that comprise the Java ASP Component
Framework. To use, do the following:

1) Compile the framework using the latest Java SDK tools. If you're working
   in VJ++, you'll need to install the new compiler from the Java SDK. This
   means copying jvc.exe, jps.dll, and msjvc.dll from the JavaSDK installation
   into your DevStudio\SharedIDE\bin directory. Also, Make sure you set the
   destination for the project (Project/settings/Output Directory) to the
   %windir%\java\trustlib directory.

   Alternatively, you can use the pre-built .class files provided in this
   directory. To do so, copy *.class to the %windir%\java\trustlib\aspcomp
   directory.

2) Try the samples in the ..\Samples directory. Check out the readme.txt file
   there.


README.TXT

This directory contains test components for the Java ASP Component Framework,
as well as ASP pages for you to use for working with the components.  To use,
do the following:

1) Build these components using the latest Java SDK tools. If you are working
   in VJ++, you will need to install the new compiler from the Java SDK. This
   means copying jvc.exe, jps.dll, and msjvc.dll from the Java SDK installation
   into your DevStudio\SharedIDE\bin directory. Also, make sure you set the
   destination for the project (Project/settings/Output Directory) to the
   %windir%\java\trustlib directory.

   Alternatively, you can use the pre-built .class files provided in this
   directory. To do so, copy *.class to the %windir%\java\trustlib\IISSample
   directory.

2) Run regit.cmd, which will register your components with COM.  This
   assumes that javareg is on your path. Javareg comes with J++ and the Java
   SDK.

3) Register the dir that houses all of these .asp files as a vroot with IIS,
   and mark it as scriptable.  This will allow you to run the asp files. There
   is a default.asp with links to the other .asp files.


CALLASPINTRINSICS_TEST.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Java Component Samples: CallAspIntrinsics</TITLE>
</HEAD>
<BODY>

A Java component accessing the ASP context directly, without using the framework:<P>

<%

    Dim Obj

    Set Obj = Server.CreateObject("IISSample.CallAspIntrinsics")

    ' Call the method -- ASP context flows automatically
    Obj.sayHello

%>

</BODY>
</HTML>


CUSTOMERINFO_LOGIN.ASP

<%
    Response.Buffer = True

    Set cust = Server.CreateObject("IISSample.CustomerInfo")

    if cust.checkIfRegistered = True Then
        Response.Redirect("CustomerInfo_welcome.asp")
    End If
%>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Welcome to Fake Corp</TITLE>
</HEAD>
<BODY>

Hello! As a new customer, please fill out the information below.<P>

<FORM METHOD=POST ACTION="CustomerInfo_process.asp"><P>
Prefix:            <INPUT TYPE="text" NAME="Prefix"      VALUE=""><P>
First Name:        <INPUT TYPE="text" NAME="FName"       VALUE=""><P>
Middle Name:    <INPUT TYPE="text" NAME="MName"       VALUE=""><P>
Last Name:        <INPUT TYPE="text" NAME="LName"       VALUE=""><P>
Suffix:            <INPUT TYPE="text" NAME="Suffix"      VALUE=""><P>
Address 1:        <INPUT TYPE="text" NAME="Addr1"       VALUE=""><P>
Address 2:        <INPUT TYPE="text" NAME="Addr2"       VALUE=""><P>
Apt. No.:        <INPUT TYPE="text" NAME="AptNo"       VALUE=""><P>
City:            <INPUT TYPE="text" NAME="City"        VALUE=""><P>
State:            <INPUT TYPE="text" NAME="State"       VALUE=""><P>
ZIP Code:        <INPUT TYPE="text" NAME="ZIP"         VALUE=""><P>
Birth Date:                <INPUT TYPE="text" NAME="Birth"        VALUE=""><P>
Social Security Number:    <INPUT TYPE="text" NAME="SocSec"    VALUE=""><P>
<INPUT TYPE="submit" NAME="Submit" VALUE="Submit Form">
<INPUT TYPE="reset"  NAME="Reset"  VALUE="Reset Form">
</FORM>

</BODY>
</HTML>


CUSTOMERINFO_PROCESS.ASP

<%
    Response.Buffer = True

    Set obj = Server.CreateObject("IISSample.CustomerInfo")
    if obj.processForm = False Then
        Response.Redirect("CustomerInfo_login.asp")
    Else
        Response.Redirect("CustomerInfo_welcome.asp")
    End If
%>


CUSTOMERINFO_WELCOME.ASP

<%
    Response.Buffer = True
%>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Welcome to Fake Corp</TITLE>
</HEAD>
<BODY>

<%
    Set cust = Server.CreateObject("IISSample.CustomerInfo")

    If cust.checkIfRegistered = False Then
%>

You are a new customer, please <A HREF="CustomerInfo_login.asp">register now</A>.<P>
(In other words, the CustomerInfo java class didn't find a valid cookie containing
your registration with this site.)<P>

<%
    Else
%>

Welcome <%= cust.getFullName %> to the Fake Corp homepage!<P>
(In other words, the CustomerInfo java class has found a valid cookie that shows
that you have registered with this site. To see this, click <A HREF=default.asp>here</A>
to go back to the main page, and then click the link to this customer tracking sample
again; you will not be asked to enter your information again. Note that this cookie
only lasts for the length of the browser session; if you restart your browser, you will
be asked to enter your information again when trying this sample.)<P>

<%
    End If
%>

</BODY>
</HTML>


DEFAULT.ASP

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Java Component Samples</TITLE>
</HEAD>
<BODY>

<H2>Java Component Samples</H2>

Below are several samples of calling Java components from ASP.<P>

<A HREF="readme.txt">Read the readme.txt</A><P>

Simple examples which do not use the Java ASP Component Framework:<P>

<LI><A HREF="HelloWorld_test.asp">A very simple java class, called from ASP</A><P>
<LI><A HREF="CallAspIntrinsics_test.asp">A java class that accesses ASP context directly, without the advantage of the Framework</A><P>

Samples which employ the Java ASP Component Framework:<P>

<LI><A HREF="EnumServerVars_test.asp">Enumerating server variables</A><P>
<LI><A HREF="ExpirePage_test.asp">Manipulating date/times via the Framework</A><P>
<LI><A HREF="WriteStream_test.asp">Writing out binary data</A><P>
<LI><A HREF="UseTableStream_test.asp">Using the Framework's Response object as an OutputStream</A><P>
<LI><A HREF="CustomerInfo_welcome.asp">Customer tracking: working with cookies</A><P>

This sample calls the IIS ADSI provider through Java (via J/Direct) to programmatically administer
the server.<P>

<LI><A HREF="ServerAdmin_readme.asp">Instructions</A> for this sample<P>

</BODY>
</HTML>


ENUMSERVERVARS_TEST.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Java Component Samples: EnumServerVars</TITLE>
</HEAD>
<BODY>

Using the Java ASP Component Framework to access server variables:<P>

<%

    Dim Obj

    Set Obj = Server.CreateObject("IISSample.EnumServerVars")

    Obj.enum

%>

</BODY>
</HTML>


EXPIREPAGE_TEST.ASP

<%@ LANGUAGE = VBScript %><% Option Explicit %><% Response.Buffer = True %>

<% ' Note: the call to turn buffering on must happen before any content of the page %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Java Component Samples: ExpirePage</TITLE>
</HEAD>
<BODY>

Using the Java ASP Component Framework to manipulate date/times:<P>

(The scenario here is that this page lists information relevant for the current year only,
so we want to have it expire right at the beginning of the next year.)<P>

<%

    Dim Obj

    Set Obj = Server.CreateObject("IISSample.ExpirePage")

    Obj.expireAtYearEnd

%>

</BODY>
</HTML>


HELLOWORLD_TEST.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Java Component Samples: HelloWorld</TITLE>
</HEAD>
<BODY>

Calling a method of a Java component from ASP:<P>

<%

    Dim Obj

    ' Create the Java component
    Set Obj = Server.CreateObject("IISSample.HelloWorld")

    ' Call the method and print the returned string
    Response.Write Obj.sayHello

%>

</BODY>
</HTML>


SERVERADMIN_README.ASP

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Instructions for Java/ADSI Sample</TITLE>
</HEAD>
<BODY>

<H2>Instructions for Java/ADSI Sample</H2>

In order to try this sample, please first follow these directions:<P>

<LI>Ensure that FTP is installed on this server.<P>

<LI>ASP scripts that access ADSI, such as the example below, require administrator
privileges on the machine on which IIS is running. You therefore must connect through a secure
connection, such as the Windows NT Challenge/Response authentication method. Therefore, use the
Internet Service Manager to set the security authentication method to Windows NT
Challenge/Response for the file "ServerAdmin_test.asp". (In Internet Service Manager, select
properties for this file, and under File Security unclick the checkbox for Allow Anonymous
Access.)<P>

<LI>Use the Java Type Library Wizard in VJ++ to import the "Active DS Type Library".<P>

Once you have completed all of these steps, you can try this
<A HREF="ServerAdmin_test.asp">example</A>.<P>

</BODY>
</HTML>


SERVERADMIN_TEST.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Java Component Samples: ServerAdmin</TITLE>
</HEAD>
<BODY>

<%

    Dim Obj

    Set Obj = Server.CreateObject("IISSample.ServerAdmin")

    If Request("action") = "" Then
%>

Control your default FTP site using Java, J/Direct, and ADSI.<P>

Currently the default FTP server status is: <%= Obj.getStatus %><P>

Click <A HREF="ServerAdmin_test.asp?action=start">here</A> to start the default FTP server<BR>
Click <A HREF="ServerAdmin_test.asp?action=stop">here</A> to stop the default FTP server<BR>

<%
    End If

    If Request("action") = "start" Then
        Response.Write("Starting the ftp service.<P>")
        Obj.startFtp()
        Response.Write("<A HREF=ServerAdmin_test.asp>Back to FTP control page</A>")
    End If

    If Request("action") = "stop" Then
        Response.Write("Stopping the ftp service.<P>")
        Obj.stopFtp()
        Response.Write("<A HREF=ServerAdmin_test.asp>Back to FTP control page</A>")
    End If

%>

</BODY>
</HTML>


USETABLESTREAM_TEST.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Java Component Samples: UseTableStream</TITLE>
</HEAD>
<BODY>

This Java class calls another Java class, HTMLTableStream, from within.<P>
HTMLTableStream expects an OutputStream, and conveniently the Framework's Request object is one.<P>

<%

    Dim Obj

    Set Obj = Server.CreateObject("IISSample.UseTableStream")

    Obj.makeTable

%>

</BODY>
</HTML>


WRITESTREAM_TEST.ASP

<%@ LANGUAGE = VBScript %>
<%  Option Explicit     %>

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio">
<META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1">
<TITLE>Java Component Samples: WriteStream</TITLE>
</HEAD>
<BODY>

Using the Java ASP Component Framework to write out binary data:<P>

<%

    Dim Obj

    Set Obj = Server.CreateObject("IISSample.WriteStream")

    Obj.writeBytes

%>

</BODY>
</HTML>


README.TXT

Multilingual Simple and Power Samples
                =====================================

Table of Contents
=================

    Overview
    Developing Components
    Visual Basic 5
    Installation
    Directory List
    Usage
    Sample ASP scripts
    Support
    Change Notes

Overview
========

Here, for your edification, are two Active Server Pages components,
each implemented in two different environments: C++/ATL (Microsoft
ActiveX Template Library) and Microsoft Visual Basic 5.

The implementations are functionally equivalent.  Each implementation
should behave in exactly the same way.  Each implementation is written
in an idiomatic style to demonstrate the best way to implement the
component in that environment.

The Simple component has one method (myMethod) and one property
(myProperty), which can be both retrieved (get) and modified (set).
MyMethod converts a string to uppercase; myProperty is a string in the
object that can be get or set.  There is nothing ASP-specific about
the Simple component and, if desired, it could be used from any OLE
container.

The Power component (located in the \intermediate directory) is a
superset of the Simple component.  In addition to myMethod and
myProperty, it has myPowerMethod and myPowerProperty (gettable but not
settable), which demonstrate some ASP-specific features.  Two other
standard but optional ASP methods are also provided: OnStartPage and
OnEndPage.  These methods bracket the lifetime of a page.  OnStartPage
is needed to gain access to the intrinsic ASP objects: Request,
Response, Server, Application, and Session.

MyPowerMethod and myPowerProperty make use of the Request and Response
objects: Request to gain access to the ServerVariables collection and
thereby the ScriptName and the HttpUserAgent (user's browser)
variables; Response to write directly back to the user's browser.
MyPowerProperty returns the name of the ASP script from which it was
called.  MyPowerMethod writes a browser-dependent message back to the
user's browser; one for Microsoft Internet Explorer, a different one
for anything else.

Developing Components
=====================

You can develop ASP components in Visual C++, Visual J++, Visual
Basic, or any other language that builds ActiveX COM objects.

These samples are written for IIS 4.0.  You must have the latest
version of IIS to compile and use these components.  You must
also have installed Microsoft Transaction Server (part of Windows NT
4.0 Option Pack).

Visual Basic 5
==============

These samples requires Visual Basic 5.0.
Follow these steps to build a project:

1) Open the .vbp file in Visual Basic.
2) From the File menu, select 'Make XXX dll...' and provide a dll name.

This procedure will build and register the sample component.

Installation
============

To install these sample components, you must first compile them with
Microsoft Visual Basic.  No binaries are supplied as these components
are of interest only to developers.  If you intend to run these
components on a machine other than the one on which they are compiled,
you will need to copy the DLLs to the target machine and
    regsvr32 /s /c SAMPLE.dll
(the samples will be registered automatically on the machine they're
built on).

If you have trouble registering components, you may be using the
wrong version of RegSvr32.exe.  Please use the version installed by
default in the directory <winnt-dir>\system32\inetsrv.

Directory List
==============

Directory        Description
---------        -----------

 Simple          Visual Basic 5 Simple Sample
 Intermediate    Visual Basic 5 Power Sample

Usage
=====

To use the samples, simply call Server.CreateObject("IISSample.XXX")
to create the object, where "XXX" is one of
    VB5Simple        VB5Power
Then you may access the object's methods and properties from your ASP
script.  Read any of the sample ASP scripts to discover how to use the
objects.

Sample ASP scripts
==================

You must copy the sample ASP scripts to a virtual directory (it need
not be a virtual root) on the IIS Server where you have installed the
sample components before they will work.

Support
=======

This component is not officially supported by Microsoft Corporation.
Peer support is available on the Active Server Pages mailing list or on
the microsoft.public.inetserver.iis.activeserverpages newsgroup.

To subscribe to the Active Server Pages mailing list, send mail to
listserv@listserv.msn.com with

subscribe ActiveServerPages [firstname lastname]

in the body of the message, and then follow the directions carefully.
(firstname and lastname are optional.)

You can reach the newsgroup through msnews.microsoft.com and other NNTP
servers.

Change Notes
============

Version 1.0 Beta 1: February 1997
---------------------------------

First release.

Version 1.0 Beta 2: March 1997
------------------------------

* Fixed Developer Studio makefile problems in C++ components.
* Upgraded to build cleanly with ATL 2.0 (Visual C++ 4.2b) and ATL 2.0 (VC5).
* Better comments in Power components.

Version 2.0 Beta 3: September 1997
----------------------------------

* Updated for IIS4.0
* Using IObjectContext instead of IScriptingContext.

Version 2.1: October 1997
-------------------------

* Modified for the IIS4.0 SDK release


README.TXT

Visual Basic 5 Power Sample
                     ===========================

Table of Contents
=================

    Overview
    File List
    Change Notes

Overview
========

This is the Visual Basic 5 version of the Power sample object for
Active Server Pages.  It demonstrates how to write a component that
makes use of the intrinsic ASP objects.

Please read the top-level ReadMe for all the Simple and Power samples.

Look at VB5Pwr.cls to see how these methods and properties are
declared and implemented.

File List
=========

File             Description
----             -----------

.\Source

 VB5Pwr.cls      the source code for the VB5 power component
 VB5Pwr.vbp      the project file

.\Samples

 VB5Pwr.asp      Demonstrates the use of the VB5 Power Sample object

Change Notes
============

Beta 1: February 1997
---------------------

First release.

Beta 2: March 1997
------------------

* Better comments.


VB5PWR.ASP

<HTML>

<HEAD>
<TITLE>Testing VB5Pwr</TITLE>
</HEAD>

<H1>Testing VB5Pwr</H1>

<% Set myobj = Server.CreateObject("IISSample.VB5Power") %>
myProperty Value = <%= myobj.myProperty %><BR>

<% newvalue = "Power" %>
Set myProperty to <%= newvalue %>
<% myobj.myProperty = newvalue %><BR>
myProperty Value is now: <%= myobj.myProperty %><BR>

<% mystring = "My String" %>
Call myMethod with <%= mystring %>: <%= myobj.myMethod(mystring) %><BR>

MyPowerMethod returns: <% myobj.myPowerMethod() %><BR>

MyPowerProperty returns: <%= myobj.myPowerProperty %><br>

</HTML>


README.TXT

Visual Basic 5 Simple Sample
                     ============================

Table of Contents
=================

    Overview
    File List
    Change Notes

Overview
========

This is the Visual Basic 5 version of the Simple sample object for
Active Server Pages.  It demonstrates how to write a component that
has straightforward methods and properties.

Please read the top-level ReadMe for all the Simple and Power samples.

Look at VB5Smpl.cls to see how these methods and properties are
declared and implemented.

File List
=========

File             Description
----             -----------

.\Source

 VB5Smpl.cls     the source code for the VB5 simple component
 VB5Smpl.vbp     the project file

.\Samples

 VB5Smpl.asp     Demonstrates the use of the VB5 Simple Sample object

Change Notes
============

Beta 1: February 1997
---------------------

First release.

Beta 2: March 1997
------------------

No changes.


VB5SMPL.ASP

<HTML>

<HEAD>
<TITLE>Testing VB5Smpl</TITLE>
</HEAD>

<H1>Testing VB5Smpl</H1>

<% Set myobj = Server.CreateObject("IISSample.VB5Simple") %>
myProperty Value = <%= myobj.myProperty %><BR>

<% newvalue = "Simple" %>
Set myProperty to <%= newvalue %>
<% myobj.myProperty = newvalue %><BR>
myProperty Value is now: <%= myobj.myProperty %><BR>

<% mystring = "My String" %>
Call myMethod with <%= mystring %>: <%= myobj.myMethod(mystring) %><BR>

</HTML>


CGIWRAP.C

/*++

File: cgiwrap.c

Demonstrates an executable which can be used to load an ISAPI DLL like
a CGI script.

--*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include <stdio.h>
#include <stdlib.h>

//
// These are things that go out in the Response Header
//

#define HTTP_VER "HTTP/1.0"
#define SERVER_VERSION "Http-Srv-Beta2/1.0"

//
// Simple wrappers for the heap APIS
//

#define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
#define xfree(s)   HeapFree(GetProcessHeap(), 0, (s))

//
// The mandatory exports from the ISAPI DLL
//

typedef BOOL(WINAPI * VersionProc) (HSE_VERSION_INFO *);
typedef DWORD(*HttpExtProc) (EXTENSION_CONTROL_BLOCK *);

//
// Prototypes of the functions this sample implements
//

BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *);
BOOL WINAPI GetServerVariable(HCONN, LPSTR, LPVOID, LPDWORD);
BOOL WINAPI ReadClient(HCONN, LPVOID, LPDWORD);
BOOL WINAPI WriteClient(HCONN, LPVOID, LPDWORD, DWORD);
BOOL WINAPI ServerSupportFunction(HCONN, DWORD, LPVOID, LPDWORD, LPDWORD);
char *MakeDateStr(VOID);
char *GetEnv(char *);

//
// In the startup of this program, we look at our executable name and
// replace the ".EXE" with ".DLL" to find the ISAPI DLL we need to load.
// This means that the executable need only be given the same "name" as
// the DLL to load. There is no recompilation required.
//

int __cdecl
main(int argc, char **argv)
{

    HINSTANCE hDll;
    VersionProc GetExtensionVersion;
    HttpExtProc HttpExtensionProc;
    HSE_VERSION_INFO version_info;
    EXTENSION_CONTROL_BLOCK ECB;
    DWORD rc;
    char szModuleFileName[256], *c;

    if (!GetModuleFileName(NULL, szModuleFileName, 256)) {
        fprintf(stderr, "cannot get ModuleFileName %d\n", GetLastError());
        return -1;
    }

    rc = strlen(szModuleFileName);
    c = szModuleFileName + rc - 4;  // Go back to the last "."

    c[1] = 'D';
    c[2] = 'L';
    c[3] = 'L';

    hDll = LoadLibrary(szModuleFileName);   // Load our DLL

    if (!hDll) {
        fprintf(stderr, "Error: Failure to load %s.dll (%d)\n",
            argv[0], GetLastError());
        return -1;
    }

    //
    // Find the exported functions
    //

    GetExtensionVersion = (VersionProc) GetProcAddress(hDll,
                                            "GetExtensionVersion");
    if (!GetExtensionVersion) {
        fprintf(stderr, "Can't Get Extension Version %d\n", GetLastError());
        return -1;
    }
    HttpExtensionProc = (HttpExtProc) GetProcAddress(hDll,
                                          "HttpExtensionProc");
    if (!HttpExtensionProc) {
        fprintf(stderr, "Can't Get Extension proc %d\n", GetLastError());
        return -1;
    }

    //
    // This should really check if the version information matches what
    // we expect.
    //

    __try {
        if (!GetExtensionVersion(&version_info)) {
            fprintf(stderr, "Fatal: GetExtensionVersion failed\n");
            return -1;
        }
    }
    __except(1) {
        return -1;
    }

    //
    // Fill the ECB with the necessary information
    //

    if (!FillExtensionControlBlock(&ECB)) {
        fprintf(stderr, "Fill Ext Block Failed\n");
        return -1;
    }

    //
    // Call the DLL
    //

    __try {
        rc = HttpExtensionProc(&ECB);
    }
    __except(1) {
        return -1;
    }

    //
    // We should really free memory (e.g., from GetEnv), but we'll be dead
    // soon enough
    //

    if (rc == HSE_STATUS_PENDING)

        //
        // We will exit in ServerSupportFunction
        //

        Sleep(INFINITE);

    return 0;

}

//
// GetServerVariable() is how the DLL calls the main program to figure out
// the environment variables it needs. This is a required function.
//

BOOL WINAPI
GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
    LPVOID lpBuffer, LPDWORD lpdwSize)
{

    DWORD rc;

    //
    // We don't really have an HCONN, so we assume a value of 0 (which is
    // passed
    // to the DLL in the ECB by HttpExtensionProc().
    // Hence the check for a non-zero HCONN

    if (hConn) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    rc = GetEnvironmentVariable(lpszVariableName, lpBuffer, *lpdwSize);

    if (!rc) {

        //
        // return of 0 indicates the variable was not found
        //
        SetLastError(ERROR_NO_DATA);
        return FALSE;
    }

    if (rc > *lpdwSize) {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    //
    // GetEnvironmentVariable does not count the NUL character
    //
    *lpdwSize = rc + 1;

    return TRUE;

}

//
// Again, we don't have an HCONN, so we simply wrap ReadClient() to
// ReadFile on stdin. The semantics of the two functions are the same
//

BOOL WINAPI
ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize)
{

    return ReadFile(GetStdHandle(STD_INPUT_HANDLE), lpBuffer, (*lpdwSize),
        lpdwSize, NULL);

}

//
// ditto for WriteClient()
//

BOOL WINAPI
WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize,
    DWORD dwReserved)
{

    return WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), lpBuffer, *lpdwSize,
        lpdwSize, NULL);

}

//
// This is a special callback function used by the DLL for certain extra
// functionality. Look at the API help for details.
//

BOOL WINAPI
ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
    LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType)
{

    char *lpszRespBuf;
    char *temp;
    DWORD dwBytes;
    BOOL bRet;

    switch (dwHSERequest) {

    case (HSE_REQ_SEND_RESPONSE_HEADER):
        lpszRespBuf = xmalloc(*lpdwSize + 80);  // accomodate our header

        if (!lpszRespBuf)
            return FALSE;
        wsprintf(lpszRespBuf, "%s %s %s %s\r\n%s",
            HTTP_VER,
            lpvBuffer ? lpvBuffer : "200 Ok",   // Default response is 200 Ok
            temp = MakeDateStr(),               // Create a time string
            SERVER_VERSION,

        //
        // If this exists, it is a pointer to a data buffer to be sent.
        //
            lpdwDataType ? (char *) lpdwDataType : NULL);

        xfree(temp);

        dwBytes = strlen(lpszRespBuf);
        bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
        xfree(lpszRespBuf);

        break;

    case (HSE_REQ_DONE_WITH_SESSION):

        //
        // A real server would do cleanup here
        //

        ExitProcess(0);
        break;

    case (HSE_REQ_SEND_URL_REDIRECT_RESP):

        //
        // This sends a redirect (temporary) to the client.
        // The header construction is similar to RESPONSE_HEADER above.
        //

        lpszRespBuf = xmalloc(*lpdwSize + 80);
        if (!lpszRespBuf)
            return FALSE;
        wsprintf(lpszRespBuf, "%s %s %s\r\n",
            HTTP_VER,
            "302 Moved Temporarily",
            (lpdwSize > 0) ? lpvBuffer : 0);
        dwBytes = strlen(lpszRespBuf);
        bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
        xfree(lpszRespBuf);
        break;

    default:
        return FALSE;
        break;
    }
    return bRet;

}

//
// Makes a string of the date and time from GetSystemTime().
// This is in UTC, as required by the HTTP spec.`
//

char *
MakeDateStr(void)
{
    SYSTEMTIME systime;
    char *szDate = xmalloc(64);

    char *DaysofWeek[] =
        {"Sun", "Mon", "Tue", "Wed", "Thurs", "Fri", "Sat"};
    char *Months[] =
        {"NULL", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

    GetSystemTime(&systime);

    wsprintf(szDate, "%s, %d %s %d %d:%d.%d",
        DaysofWeek[systime.wDayOfWeek],
        systime.wDay,
        Months[systime.wMonth],
        systime.wYear,
        systime.wHour, systime.wMinute,
        systime.wSecond);

    return szDate;
}

//
// Fill the ECB up
//

BOOL WINAPI
FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK * ECB)
{

    char *temp;

    ECB->cbSize = sizeof (EXTENSION_CONTROL_BLOCK);
    ECB->dwVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
    ECB->ConnID = 0;

    //
    // Pointers to the functions the DLL will call.
    //

    ECB->GetServerVariable = GetServerVariable;
    ECB->ReadClient = ReadClient;
    ECB->WriteClient = WriteClient;
    ECB->ServerSupportFunction = ServerSupportFunction;

    //
    // Fill in the standard CGI environment variables
    //

    ECB->lpszMethod = GetEnv("REQUEST_METHOD");
    ECB->lpszQueryString = GetEnv("QUERY_STRING");
    ECB->lpszPathInfo = GetEnv("PATH_INFO");
    ECB->lpszPathTranslated = GetEnv("PATH_TRANSLATED");
    ECB->cbTotalBytes = ((temp = GetEnv("CONTENT_LENGTH")) ?
                            (atoi(temp)) : 0);
    ECB->cbAvailable = 0;
    ECB->lpbData = "";
    ECB->lpszContentType = GetEnv("CONTENT_TYPE");

    return TRUE;

}

//
// Works like _getenv(), but uses win32 functions instead.
//

char *
GetEnv(LPSTR lpszEnvVar)
{

    char *var, dummy;
    DWORD dwLen;

    if (!lpszEnvVar)
        return "";

    dwLen = GetEnvironmentVariable(lpszEnvVar, &dummy, 1);

    if (dwLen == 0)
        return "";

    var = xmalloc(dwLen);
    if (!var)
        return "";

    (void) GetEnvironmentVariable(lpszEnvVar, var, dwLen);

    return var;
}


README.TXT

CTETEST.DLL is a sample ISAPI HTTP Extension DLL to demonstrate Chunked
Transfer Encoding. The Chunked Transfer Encoding modifies the body of a
message in order to transfer it as a series of chunks, each with its own
size indicator. Unlike "normal" HTTP file transfer, Chunked Transfer
does not need to specify (or even know) the transfer length in advance.
There is no "Content-Length:" header transferred. For complete details
about Chunked Transfer Encoding, see section 3.6 "Transfer Codings" of
the latest HTTP/1.1 specifications, available from
http://www.w3.org/Protocols.

CTETEST.DLL sends the file specified on the query string. If no query
string is present, or if the query string does not identify a readable
file, CTETEST.DLL will return a plain text page describing its usage.

To install:

Build the CTETEST.DSP project and copy the .DLL into virtual directory
with "execute" permissions.

To run:

Reference CTETEST.DLL file as a URL from the browser (currently only
Microsoft Internet Explorer version 4.xx supports chunked encoding),
specifying a URL on the query string.

For example, the following URL entered on the address line of Internet
Explorer will transfer file SAMPLE.GIF, located in WWW root directory,
encoded in 1024-byte chunks:

http://localhost/scripts/ctetest.dll?file=/sample.gif+chunksize=1024

Disclaimer:

This sample is provided for the purpose of demonstrating an ISAPI
extension. It has not been tested for use in a production environment
and no support will be provided for use in a production environment.


CTE_ENC.C

/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    cte_enc.c

Abstract:

    This module contains routines implementing Chunked Transfer
    Encoding (CTE) for ISAPI Extension DLLs. See Section 3.6
    "Transfer Codings" of RFC 2068 for details.

Functions:
    CteBeginWrite
    CteWrite
    CteEndWrite

--*/

#include "ctetest.h"

//
// Encoder context structure
//

typedef struct CTE_ENCODER_STRUCT {
    EXTENSION_CONTROL_BLOCK * pECB;         // a copy of current ECB pointer
    DWORD                     dwChunkSize;  // user-specified chunk size
    DWORD                     cbData;       // number of bytes in the buffer
    BYTE *                    pData;        // pointer to chunk data bytes
} CTE_ENCODER;

//
// Chunk header consists of HEX string for the chunk size in bytes
// (DWORD needs up to 8 bytes in HEX), followed by CRLF,
// therefore the maximum chunk header size is 10 bytes.
//

#define CTE_MAX_CHUNK_HEADER_SIZE 10

//
// Chunk data is always followed by CRLF
//

#define CTE_MAX_ENCODING_OVERHEAD (CTE_MAX_CHUNK_HEADER_SIZE + 2)

//
// Total encoder size includes:
//   the size of the encoder context structure itself,
//   the chunk data size,
//   the maximum encoding overhead (header and terminating CRLF)
//

#define CTE_ENCODER_SIZE(dwChunkSize) \
    (sizeof(CTE_ENCODER) + dwChunkSize + CTE_MAX_ENCODING_OVERHEAD)

HCTE_ENCODER
CteBeginWrite(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN DWORD dwChunkSize
    )
/*++

Purpose:

    Allocate and initialize chunked transfer encoder context

Arguments:

    pECB - pointer to extension control as passed to HttpExtensionProc()
    dwChunkSize - the maximum size of the chunk to transmit

Returns:
    encoder context handle, or
    NULL if memory allocation failed or chunk size was zero

--*/
{
    HCTE_ENCODER h;

    //
    // reject zero-length chunk size
    //

    if( dwChunkSize == 0 ) {
        SetLastError( ERROR_INVALID_PARAMETER );
        return NULL;
    }

    //
    // allocate context structure
    //

    h = LocalAlloc( LMEM_FIXED, CTE_ENCODER_SIZE(dwChunkSize) );

    if( h != NULL ) {

        //
        // initialize context structure
        //

        h->pECB         = pECB;
        h->dwChunkSize  = dwChunkSize;
        h->cbData       = 0;

        //
        // chunk data bytes follow the context structure itself
        // and chunk header
        //

        h->pData = (BYTE *) h + sizeof( *h ) + CTE_MAX_CHUNK_HEADER_SIZE;

        //
        // this is the CRLF which follows chunk size
        // (and immediately precedes data)
        //

        h->pData[-2] = '\r';
        h->pData[-1] = '\n';

    } else {

        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
    }

    return h;
}

static BOOL
CteSendChunk(
    IN HCTE_ENCODER h
)
/*++

Purpose:

    Send one chunk of data using ClientWrite()
    <hex encoded chunk size>, CRLF, data bytes, if any, CRLF

Arguments:

    h - CTE Encoder handle

Returns:

    TRUE if WriteClient succeeded
    FALSE if WriteClient failed

--*/
{
    char szChunkLength[9];
    DWORD cbChunkLength;
    BYTE *buf;
    DWORD cbToSend;
    BOOL success;

    //
    // produce hex string of the number of bytes
    // and compute the length of this string
    //
    _itoa( h->cbData, szChunkLength, 16 );
    cbChunkLength = strlen( szChunkLength );

    //
    // step back to make place for hex number and CRLF,
    // copy hex string to its location
    //

    buf = h->pData - 2 - cbChunkLength;
    memmove( buf, szChunkLength, cbChunkLength );

    //
    // compute the number of bytes to send
    // (this includes chunk data size, hex string and CRLF)
    //

    cbToSend = h->cbData + cbChunkLength + 2;

    //
    // append trailing CRLF right after the data bytes
    //

    buf[cbToSend++] = '\r';
    buf[cbToSend++] = '\n';

    //
    // issue synchronous WriteClient and return result to the caller
    //

    success = h->pECB->WriteClient(
                    h->pECB->ConnID,
                    buf,
                    &cbToSend,
                    HSE_IO_SYNC
                    );

    //
    // reset buffer pointer
    //

    h->cbData = 0;

    return success;
}

BOOL
CteWrite(
    IN HCTE_ENCODER h,
    IN PVOID pData,
    IN DWORD cbData
)
/*++

Purpose:

    Write specified number of data bytes to the chunk buffer.
    When the chunk buffer becomes full, call CteSendChunk()
    to send it out.

Arguments:

    h - CTE Encoder handle
    pData - pointer to data bytes
    cbData - number of data bytes to send

Returns:

    TRUE if bytes were successfully written
    FALSE if WriteClient() failed

--*/
{
    DWORD cbToConsume;
    PBYTE pBytesToSend = (PBYTE) pData;

    for( ;; ) {

        //
        // compute the number of bytes to consume,
        // break out of the loop, if nothing is left
        //

        cbToConsume = min( cbData, h->dwChunkSize - h->cbData );

        if( cbToConsume == 0 ) {
            break;
        }

        //
        // move bytes to the buffer, advance pointers and counters
        //

        memmove( h->pData + h->cbData, pBytesToSend, cbToConsume );

        h->cbData += cbToConsume;
        pBytesToSend += cbToConsume;
        cbData -= cbToConsume;

        //
        // if the chunk buffer is full, send it
        //

        if( h->cbData == h->dwChunkSize ) {
            if( !CteSendChunk( h ) ) {
                return FALSE;
            }
        }

    }

    return TRUE;
}

BOOL
CteEndWrite(
    IN HCTE_ENCODER h
)
/*++

Purpose:

    Complete the transfer and release the encoder context

Arguments:

    h - CTE Encoder handle

Returns:

    TRUE if transfer was successfully completed
    FALSE if WriteClient() failed

--*/
{
    BOOL success;

    //
    // if there are some bytes in the chunk, send them
    //

    if( h->cbData ) {
        if( !CteSendChunk( h ) ) {
            return FALSE;
        }
    }

    //
    // send empty chunk (which means EOF)
    //

    success = CteSendChunk( h );

    //
    // release chunk transfer context
    //

    LocalFree( h );

    return success;
}


CTETEST.C

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    ctetest.c

Abstract:

    ISAPI Extension sample illustrating Chunked Transfer Encoding.

--*/

#include "ctetest.h"

//
// if chunksize= is not specified, use this value
//

#define DEFAULT_CHUNK_SIZE 1024

//
// auxiliary functions prototypes
//

static BOOL SendChunkedFile( EXTENSION_CONTROL_BLOCK *, DWORD, LPCSTR );
static BOOL SendHttpHeaders( EXTENSION_CONTROL_BLOCK *, LPCSTR, LPCSTR, BOOL );
static BOOL GetFileMimeType( LPCSTR, LPSTR, DWORD );
static BOOL GetQueryStringField( LPCSTR, LPCSTR, LPSTR, DWORD );
static void DisplayExampleUsage( EXTENSION_CONTROL_BLOCK * );

DWORD WINAPI
HttpExtensionProc(
    IN EXTENSION_CONTROL_BLOCK *pECB
)
/*++

Purpose:

    Illustrate chunk transfer encoding in ISAPI HTTP Extension DLL.
    Process "GET" requests that specify a filename and transfer
    chunk size.

Arguments:

    pECB - pointer to the extenstion control block

Returns:

    HSE_STATUS_SUCCESS on successful transmission completion
    HSE_STATUS_ERROR on failure

--*/
{
    DWORD dwChunksize = 0;
    char szPath[MAX_PATH];
    char szHeaders[1024];
    DWORD cchPath, cchHeaders;
    char szChunkSize[32];

    //
    // if request method is not "GET", bail out
    //

    if( _stricmp( pECB->lpszMethod, "GET" ) != 0 ) {
    return HSE_STATUS_ERROR;
    }

    //
    // process chunksize= query argument, if any
    //

    if( GetQueryStringField( pECB->lpszQueryString, "chunksize",
            szChunkSize, sizeof szChunkSize )) {
        dwChunksize = atoi( szChunkSize );
    }

    if( dwChunksize == 0 ) {
    dwChunksize = DEFAULT_CHUNK_SIZE;
}

    //
    // process file= query argument
    //

    cchPath = sizeof szPath;
    if( !GetQueryStringField(
            pECB->lpszQueryString,
            "file",
            szPath,
            cchPath )) {

        //
        // no file specified - display usage and report success
        //

        DisplayExampleUsage( pECB );

        return HSE_STATUS_SUCCESS;
    }

    //
    // use ServerSupportFunction to map virtual file name to local
    // path (otherwise users get access to any file on the system)
    //

    if( !pECB->ServerSupportFunction(
             pECB->ConnID,
             HSE_REQ_MAP_URL_TO_PATH,
             szPath,
             &cchPath,
             NULL ) ) {

        return HSE_STATUS_ERROR;
    }

    //
    // see if we can get file attributes, report error if not
    //

    if( GetFileAttributes( szPath ) == 0xFFFFFFFF ) {

        return HSE_STATUS_ERROR;
    }

    //
    // begin preparing the headers
    //

    strcpy( szHeaders, "Transfer-encoding: chunked\r\nContent-type: " );

    //
    // obtain MIME type for this file and append it to the headers
    //

    cchHeaders = strlen( szHeaders );
    GetFileMimeType(
        szPath,
        szHeaders + cchHeaders,
        sizeof szHeaders - cchHeaders
        );

    //
    // terminate headers with empty line
    //

    strcat( szHeaders, "\r\n\r\n" );

    //
    // try sending headers to the client
    //

    if( !SendHttpHeaders( pECB, "200 OK", szHeaders, TRUE ) ) {

        return HSE_STATUS_ERROR;
    }

    //
    // try sending the file using CTE encoding
    //

    if( !SendChunkedFile( pECB, dwChunksize, szPath ) ) {

        return HSE_STATUS_ERROR;
    }

    return HSE_STATUS_SUCCESS;
}

BOOL WINAPI
GetExtensionVersion(
    OUT HSE_VERSION_INFO *pVer
)
/*++

Purpose:

    This is required ISAPI Extension DLL entry point.

Arguments:

    pVer - poins to extension version info structure

Returns:

    always returns TRUE

--*/
{

    //
    // tell the server our version number and extension description
    //

    pVer->dwExtensionVersion =
        MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );

    lstrcpyn(
        pVer->lpszExtensionDesc,
        "ISAPI CTE test",
        HSE_MAX_EXT_DLL_NAME_LEN);

    return TRUE;
}

BOOL WINAPI
TerminateExtension(
    DWORD dwFlags
)
/*++

Purpose:

    This is optional ISAPI extension DLL entry point.
    If present, it will be called before unloading the DLL,
    giving it a chance to perform any shutdown procedures.

Arguments:

    dwFlags - specifies whether the DLL can refuse to unload or not

Returns:

    TRUE, if the DLL can be unloaded

--*/
{
    return TRUE;
}

BOOL WINAPI
DllMain (
    IN HINSTANCE hInstance,
    IN DWORD fdwReason,
    IN LPVOID lpvReserved
)
/*++

Purpose:

    Perform any required DLL initialization here.

Returns:

    TRUE if DLL was successfully initialized
    FALSE otherwise

--*/
{

    //
    // Nothing needs to be done. This function exists a template.
    //

    return TRUE;
}

static BOOL
SendChunkedFile(
    EXTENSION_CONTROL_BLOCK *pECB,
    DWORD dwChunkSize,
    LPCSTR pszPath
)
/*++

Purpose:

    Transfer the specified file using chunked encoding.

    Illustrates the usage of CteBeginWrite(), CteWrite() and
    CteEndWrite() functions.

Arguments:

    pECB - pointer to extenstion control block
    dwChunkSize - chunk size for transfer encoding
    pszPath - local file path

Returns:

    TRUE if the file was successfully transfered,
    FALSE otherwise

--*/
{
    HANDLE hFile;
    HCTE_ENCODER hCteWrite;
    BOOL fSuccess = FALSE;

    //
    // try accessing file
    //

    hFile = CreateFile(
                pszPath,
                GENERIC_READ,
                FILE_SHARE_READ,
                NULL,
                OPEN_EXISTING,
                FILE_FLAG_SEQUENTIAL_SCAN,
                NULL);

    if( hFile != INVALID_HANDLE_VALUE ) {
        BYTE buf[4096];
        DWORD cbread;

        //
        // prepare chunk transfer encoder
        //

        hCteWrite = CteBeginWrite( pECB, dwChunkSize );
        if ( hCteWrite ) {

            for( ;; ) {

                if( !ReadFile( hFile, buf, sizeof buf, &cbread, NULL ) ) {

                    //
                    // if ReadFile fails, break out of loop and cause
                    // the function to return FALSE (failure)
                    //

                    break;
                }

                if( cbread == 0 ) {

                    //
                    // ReadFile succeded, but read 0 bytes -
                    // we've achieved EOF and everything is transmitted.
                    // break out and return success!
                    //

                    fSuccess = TRUE;
                    break;
                }

                //
                // transmit one buffer full of data,
                //

                if( !CteWrite( hCteWrite, buf, cbread ) ) {

                    //
                    // CteWrite failed - break out and return FALSE
                    //

                    break;
                }
            }

            //
            // finish transfer and release encoder context
            //

            if( !CteEndWrite( hCteWrite ) ) {

                fSuccess = FALSE;
            }
        }

        CloseHandle( hFile );
    }

    return fSuccess;
}

static BOOL
SendHttpHeaders(
    EXTENSION_CONTROL_BLOCK *pECB,
    LPCSTR pszStatus,
    LPCSTR pszHeaders,
    BOOL fKeepConn
)
/*++

Purpose:
    Send specified HTTP status string and any additional header strings
    using new ServerSupportFunction() request HSE_SEND_HEADER_EX_INFO

Arguments:

    pECB - pointer to the extension control block
    pszStatus - HTTP status string (e.g. "200 OK")
    pszHeaders - any additional headers, separated by CRLFs and
                 terminated by empty line
    fKeepConn - specifies whether to keep TCP connection open or close it
                after request is processed.

Returns:

    TRUE if headers were successfully sent
    FALSE otherwise

--*/
{
    HSE_SEND_HEADER_EX_INFO header_ex_info;
    BOOL success;

    header_ex_info.pszStatus = pszStatus;
    header_ex_info.pszHeader = pszHeaders;
    header_ex_info.cchStatus = strlen( pszStatus );
    header_ex_info.cchHeader = strlen( pszHeaders );
    header_ex_info.fKeepConn = fKeepConn;

    success = pECB->ServerSupportFunction(
                  pECB->ConnID,
                  HSE_REQ_SEND_RESPONSE_HEADER_EX,
                  &header_ex_info,
                  NULL,
                  NULL
                  );

    return success;
}

static BOOL
GetFileMimeType(
    LPCSTR pszPath,
    LPSTR pszType,
    DWORD cbMax
)
/*++

Purpose:

    Given the file name, obtain MIME type for "Content-type:" header field.
    We try to find MIME type string under HCR\.xyz key, "Content Type" value.
    If that fails, we return default "application/ocetet-stream".

Arguments:

    pszPath - file path
    pszType - points to the buffer that will receive MIME type string
    cbMax - specifies the maximum number of characters to copy to the buffer,
            including the NUL character. If the text exceed this limit, it
            will be truncated.

Returns:

    TRUE, because we can always use default MIME type.

--*/
{
    LPSTR pszExt;
    HKEY hKey;
    DWORD value_type;
    LONG result;

    //
    // set MIME type to empty string
    //

    *pszType = '\0';

    //
    // try to locate file extension
    //

    pszExt = strrchr( pszPath, '.' );

    if( pszExt != NULL ) {

        //
        // for file extension .xyz, MIME Type can be found
        // HKEY_CLASSES_ROOT\.xyz key in the registry
        //

        result = RegOpenKeyEx(
                     HKEY_CLASSES_ROOT,
                     pszExt,
                     0L,
                     KEY_READ,
                     &hKey
                     );

        if( result == ERROR_SUCCESS) {

            //
            // we sucessfully opened the key.
            // try getting the "Content Type" value
            //

            result = RegQueryValueEx(
                         hKey,
                     "Content Type",
                     NULL,
                     &value_type,
                     (BYTE *)pszType,
                     &cbMax );

            //
            // if we failed to get the value or it is not string,
            // clear content-type field
            //

            if( result != ERROR_SUCCESS || value_type != REG_SZ ) {
                *pszType = '\0';
            }

            RegCloseKey( hKey );
        }
    }

    //
    // if at this point we don't have MIME type, use default
    //

    if( *pszType == '\0' ) {
        strncpy( pszType, "application/octet_stream", cbMax );
    }

    return TRUE;
}

static void
DisplayExampleUsage(
    EXTENSION_CONTROL_BLOCK *pECB
)
/*++

Purpose:

    Send short plaintext description of our usage to the user.

Arguments:

    pECB - pointer to the extension control block

--*/
{
    DWORD dwLength;
    static char szUsage[] =
        "Example usage:\r\n"
        "http://localhost/scripts/ctetest.dll"
        "?file=/default.htm+chunksize=512\r\n";

    char szHeaders[1024];

    //
    // send simple headers and sample usage instruction
    //
    dwLength = sizeof szUsage - 1;

    sprintf(
        szHeaders,
        "Content-Length: %u\r\n"
        "Content-Type: text/plain\r\n\r\n",
        dwLength
        );

    if( SendHttpHeaders( pECB, "200 OK", szHeaders, FALSE ) ) {
        pECB->WriteClient(
            pECB->ConnID,
            szUsage,
            &dwLength,
            HSE_IO_SYNC
            );
    }
}

BOOL
GetQueryStringField(
    LPCSTR pszQueryString,
    LPCSTR pszKey,
    LPSTR buf,
    DWORD cbuf
)
/*++

Purpose:
    Assuming "key1=value1+key2=value2" syntax,
    extract the value for specified key.

Arguments:
    pszQueryString - query string provided by ECB
    pszKey - key name
    buf - buffer for parameter value
    cbuf - buffer size

Returns:

    TRUE if the value was successfully extracted
    FALSE otherwise

--*/
{
    int len, keylen;
    LPCSTR p = pszQueryString;

    //
    // compute key and query lengths, bail out if either is missing
    //

    keylen = strlen( pszKey );
    len = strlen( p );

    if( keylen == 0 || len == 0 ) return FALSE;

    //
    // process one "+" delimited section at a time
    //

    for( ;; ) {

        //
        // skip any leading blanks, bail out if end of line found
        //

        while( *p <= ' ' ) {

            if( *p == '\0' ) {
                return FALSE;
            }

            p++;
            len--;
        }

        //
        // if the key won't fit into the rest of the command line, bail out
        //

        if( keylen + 1 > len ) {
            return FALSE;
        }

        //
        // is this the key we are looking for?
        //

        if( _memicmp( p, pszKey, keylen ) == 0 && p[keylen] == '=' ) {

            //
            // found it - skip '=' and break out of the loop
            //

            p += keylen + 1;
            break;
        }

        //
        // no match, try advancing to next '+' section
        //

        while( *p != '+' ) {

            if( *p == '\0' ) {
                return FALSE;
            }

            p++;
            len--;
        }

        //
        // found '+', skip it
        //

        p++;
        len--;
    }

    //
    // copy the value up to: the end of line, cbuf chars, or
    // '+' separator, whichever comes first
    //

    while( *p && *p != '+' ) {

        if( cbuf <= 1 ) {
            break;
        }

        *(buf++) = *(p++);
        cbuf--;
    }

    //
    // zero-terminate the value, report success
    //

    *buf = '\0';
    return TRUE;
}


CTETEST.H

/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    ctetest.h

Abstract:

    This header files contains the common routines used for
    Chunked Transfer Encoding ISAPI Extension DLL sample.

--*/

#ifndef _CTETEST_H
#define _CTETEST_H

#ifdef __cplusplus
extern "C" {
#endif

#include <httpext.h>
#include <stdlib.h>
#include <stdio.h>

// see cte_enc.c for details
typedef struct CTE_ENCODER_STRUCT * HCTE_ENCODER;

HCTE_ENCODER
CteBeginWrite(
    IN EXTENSION_CONTROL_BLOCK *pECB,
    IN DWORD dwChunkSize
    );

BOOL
CteWrite(
    IN HCTE_ENCODER h,
    IN PVOID pData,
    IN DWORD cbData
    );

BOOL
CteEndWrite(
    IN HCTE_ENCODER h
    );

#ifdef __cplusplus
}
#endif

#endif // _CTETEST_H


README.TXT

Description:

InvokObj is a sample ISAPI extension to demonstrate invoking
a method from an ActiveX Automation Server.  Included with the
project is an automation dll, GetUserName.dll.  This is a simple
ATL-based component with a single method, GetMyName(), which
returns the user name for the context in which the component is
running.  The InvokObj sample demonstrates creating an instance
of the object from within and ISAPI extension, invoking the
GetMyName() method, and returning the information to the browser.

This sample uses Microsoft specific compiler extensions included
with Microsoft Visual C++, and requires version 5.0 of the compiler
or later.

To Install:

Build the InvokObj.dsw project and copy the resulting InvokObj.dll
file into a virtual directory containing execute permissions.

Register GetUserName.dll with the following command line.  If you
do not do this, the browser will report error 800401f3:

   regsvr32 getusername.dll

To Run:

Reference the InvokObj.dll file as a URL from a browser.  For
example, the following URL entered on the address line of
Internet Explorer will demonstrate the dll:

   http://server/scripts/invokobj.dll

Disclaimer:

This sample, and the included automation dll, is provided for the
purposes of demonstrating an ISAPI extension.  It has not been
tested for use in a production environment and no support will
be provided for use in a production environment.


INVOKOBJ.CPP

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    InvokObj.cpp

Abstract:

    ISAPI Extension sample to invoke an automation server method

--*/

#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <httpext.h>

//
// Import type library information about the COM object
//

#import "GetUserName.dll"

BOOL WINAPI
GetExtensionVersion(
    HSE_VERSION_INFO *pVer
    )
/*++

Purpose:

    This is required ISAPI Extension DLL entry point.

Arguments:

    pVer - poins to extension version info structure

Returns:

    always returns TRUE

--*/
{
    pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR,
                                         HSE_VERSION_MAJOR );
    lstrcpyn( pVer->lpszExtensionDesc,
        "InvokObj ISAPI Sample", HSE_MAX_EXT_DLL_NAME_LEN );

    //
    // Ensure COM is initialized
    //

    CoInitialize( NULL );

    return TRUE;
}

DWORD WINAPI
HttpExtensionProc(
    EXTENSION_CONTROL_BLOCK *pECB
    )
/*++

Purpose:

    Demonstrate how to create an instance of the automation object
    using VC++ 5.0 extensions and how to invoke its method.

Arguments:

    pECB - pointer to the extenstion control block

Returns:

    HSE_STATUS_SUCCESS on successful transmission completion
    HSE_STATUS_ERROR on failure

--*/
{
    char szOutput[1024];
    DWORD dwBuffSize;
    GETUSERNAMELib::IGetUserNameObjPtr pItf;
    HSE_SEND_HEADER_EX_INFO HeaderExInfo;
    HRESULT hr;

    //
    // Send headers back to client
    //

    HeaderExInfo.pszStatus = "200 OK";
    HeaderExInfo.cchStatus = strlen( HeaderExInfo.pszStatus );
    HeaderExInfo.pszHeader = "Content-type: text/html\r\n\r\n";
    HeaderExInfo.cchHeader = strlen( HeaderExInfo.pszHeader );
    HeaderExInfo.fKeepConn = FALSE;

    pECB->ServerSupportFunction( pECB->ConnID,
        HSE_REQ_SEND_RESPONSE_HEADER_EX,
        &HeaderExInfo, NULL, NULL );

    //
    // Initialize an instance of the automation server
    //

    hr = pItf.CreateInstance( L"GetUserNameObj.GetUserNameObj.1" );

    if ( FAILED( hr ) )
    {
        wsprintf( szOutput, "<h1>Error.</h1><hr>Attempt to create instance "
            "of GetUserNameObj object failed with error %x.", hr );
        dwBuffSize = strlen( szOutput );

        pECB->WriteClient( pECB->ConnID, szOutput, &dwBuffSize, 0 );

        return HSE_STATUS_SUCCESS;
    }

    //
    // Build the output using the result of the call to
    // GetUserNameObj's GetMyName method
    //

    wsprintf( szOutput, "<h1>GetUserNameObj successfully instantiated."
        "</h1><hr>The GetMyName method returned %s.",
        (char *)pItf->GetMyName( ) );

    //
    // Send the output back to the client
    //

    dwBuffSize = strlen( szOutput );
    pECB->WriteClient( pECB->ConnID, szOutput, &dwBuffSize, 0 );

    return HSE_STATUS_SUCCESS;
}

BOOL WINAPI
TerminateExtension(
    DWORD dwFlags
    )
/*++

Purpose:

    This is optional ISAPI extension DLL entry point.
    If present, it will be called before unloading the DLL,
    giving it a chance to perform any shutdown procedures.

Arguments:

    dwFlags - specifies whether the DLL can refuse to unload or not

Returns:

    TRUE, if the DLL can be unloaded

--*/
{
    //
    // Balance the call to CoInitialize that we made in GetExtensionVersion
    //

    CoUninitialize( );

    //
    // It is now OK to unload
    //

    return TRUE;
}


README.TXT

FORMDUMP - Form Decoder and Dumper, an Internet Information Server extension

NOTE: FORMDUMP has been revised from its first release

This sample is a Microsoft Internet Server extension, similar to CGI
extensions common to many internet servers.  FORMDUMP illustrates how
to write a DLL that can be used to obtain form data from a web
browser, and also how to build a reply to the form.

This version of FORMDUMP incorporates two new features useful in
exploring how forms are submitted.  The original version of FORMDUMP
is structured into two major parts: organizing inbound data
into a memory structure, and using that data to build a HTML page.
This version adds a dump of the server variables as well as the
security context in which the DLL thread runs in.

HttpExtensionProc does the following:

1. Sends the header of the HTML response
2. Parses inbound form fields and send them back as HTML
3. Determines the security context in which the thread is running,
   and return the domain and user as HTML
4. Uses GetServerVariable to retrieve all server variables, and
   send them back as HTML
5. Sends the footer of the HTML resoponse
6. Returns control to IIS

This version also adds an HtmlPrintf() function in html.h.

To build this sample, you must have the Internet SDK installed,
and the environment of your compiler properly set.  A Visual C++
4.0 makefile is included.

The following files are included in the sample:

FORMDUMP.CPP - The main source file and entry point for the DLL.

KEYS.CPP     - A set of reusable form data decoding functions.  They
               implement an interface that you can use in your own
               extension.

HTML.CPP     - A set of wrappers for common HTML features.  These
               wrappers can also be reused.

KEYS.H       - The header file for the external interface
               implemented in KEYS.CPP.

HTML.H       - The header file for all functions available in HTML.CPP.

FORMDUMP.DEF - The definition file (one is required for all Win32 DLLs).

FORMDUMP.MAK - A Visual C++ 4.0 make file.

MAKEFILE     - A generic make file

NOTE: The source files all have .CPP extensions, though they really don't
rely on any C++ specific features.  However, you can use C++ features,
and if you use any of these .CPP files, you do not need extern "C".

To Build the DLL
----------------

Simply run NMAKE in the directory containing FORMDUMP.CPP, KEYS.CPP, HTML.CPP,
and so on.  You must have the multi-threaded C Runtime libraries installed,
and your environment must point to:

PATH=C:\MSTOOLS\BIN;C:\COMPILER\BIN
INCLUDE=C:\MSTOOLS\INCLUDE;C:\INETSDK\INCLUDE
LIB=C:\MSTOOLS\LIB
WWWROOT=C:\INETSRV\WWWROOT
WWWSCRIPTS=C:\INETSRV\SCRIPTS

Where C:\MSTOOLS points to the Win32 SDK, C:\INETSDK points to the Internet
SDK, and C:\COMPILER points to your C++ compiler.

When setting WWWROOT and WWWSCRIPTS to your Internet Information Server
locations, use a local or mapped drive.  If you want to use UNC names, be
careful with using a $ in the name, because NMAKE will treat it as a macro.
If you must have a $ in the environment variable, preceed it with two
carets (^^$) because both the command prompt and NMAKE will convert the
caret symbol.  Here is an example of how to map to \\myserver\c$:

SET WWWROOT=\\myserver\C^^$\inetsrv\wwwroot

Another issue is the type of C Runtimes that are linked with the ISAPI
extension. Make sure the DLL version of the C Runtimes is installed on your
server, in the SYSTEM32 directory.  For Visual C++, the DLLs are MSVCRT40.DLL
and MSVCR40D.DLL.  If these DLLs are not available, you will see error 500
when trying to access the DLL from a Web browser.


HTML.CPP

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    html.cpp

Abstract:

   This module contains simple HTML authoring functions.

--*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>

#include <stdio.h>
#include <stdarg.h>

#include "html.h"

void
WriteString(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpsz
)
{
    DWORD dwBytesWritten;

    dwBytesWritten = lstrlen( lpsz );
    pECB->WriteClient( pECB->ConnID, (PVOID) lpsz, &dwBytesWritten, 0 );
}

void
HtmlCreatePage(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszTitle
)
{
    WriteString( pECB, "<HTML>\r\n\r\n" );

    if ( lpszTitle ) {
        WriteString( pECB, "<HEAD><TITLE>" );
        HtmlWriteText( pECB, lpszTitle );
        WriteString( pECB, "</TITLE></HEAD>\r\n\r\n" );
    }
    WriteString( pECB, "<BODY>\r\n\r\n" );
}

void
HtmlEndPage(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</BODY>\r\n\r\n" );
    WriteString( pECB, "</HTML>\r\n" );
}

void
HtmlHeading(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN int nHeading,
    IN LPCSTR lpszText
)
{
    HtmlBeginHeading( pECB, nHeading );
    HtmlWriteText( pECB, lpszText );
    HtmlEndHeading( pECB, nHeading );
}

void
HtmlBeginHeading(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN int nHeading
)
{
    char szCode[16];

    wsprintf( szCode, "<H%i>", nHeading );
    WriteString( pECB, szCode );
}

void
HtmlEndHeading(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN int nHeading
)
{
    char szCode[16];

    wsprintf( szCode, "</H%i>", nHeading );
    WriteString( pECB, szCode );
}

void
HtmlWriteTextLine(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpsz
)
{
    HtmlWriteText( pECB, lpsz );
    WriteString( pECB, "\r\n" );
}

void
HtmlWriteText(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpsz
)
{
    char szBuf[1028];
    int nLen;
    int i;

    //
    // Build up enough data to make call to WriteString
    // worthwhile; convert special chars too.
    //
    nLen = 0;
    for ( i = 0; lpsz[i]; i++ ) {
        if ( lpsz[i] == '<' )
            lstrcpy( &szBuf[nLen], "&lt;" );
        else if ( lpsz[i] == '>' )
            lstrcpy( &szBuf[nLen], "&gt;" );
        else if ( lpsz[i] == '&' )
            lstrcpy( &szBuf[nLen], "&amp;" );
        else if ( lpsz[i] == '\"' )
            lstrcpy( &szBuf[nLen], "&quot;" );
        else {
            szBuf[nLen] = lpsz[i];
            szBuf[nLen + 1] = 0;
        }

        nLen += lstrlen( &szBuf[nLen] );

        if ( nLen >= 1024 ) {
            WriteString( pECB, szBuf );
            nLen = 0;
        }
    }

    if ( nLen )
        WriteString( pECB, szBuf );
}

void
HtmlEndParagraph(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<P>\r\n" );
}

//
// HtmlHyperLink adds a hyptertext link.  lpszDoc is the destination
// document, and lpszText is the display text.
//
// HtmlHyperLinkAndBookmark adds a hyperlink with a bookmark link.
// HtmlBookmarkLink adds only a bookmark link.
//

void
HtmlHyperLink(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszDoc,
    IN LPCSTR lpszText
)
{
    WriteString( pECB, "<A HREF=\"" );
    HtmlWriteText( pECB, lpszDoc );
    WriteString( pECB, "\">" );
    HtmlWriteText( pECB, lpszText );
    WriteString( pECB, "</A>\r\n" );
}

void
HtmlHyperLinkAndBookmark(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszDoc,
    IN LPCSTR lpszBookmark,
    IN LPCSTR lpszText
)
{
    WriteString( pECB, "<A HREF=\"" );
    if ( lpszDoc )
        HtmlWriteText( pECB, lpszDoc );
    WriteString( pECB, "#" );
    HtmlWriteText( pECB, lpszBookmark );
    WriteString( pECB, "\">" );
    HtmlWriteText( pECB, lpszText );
    WriteString( pECB, "</A>\r\n" );
}

void
HtmlBookmarkLink(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszBookmark,
    IN LPCSTR lpszText
)
{
    HtmlHyperLinkAndBookmark( pECB, NULL, lpszBookmark, lpszText );
}

//
// The following support list formatting.
//

void
HtmlBeginUnnumberedList(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<UL>\r\n" );
}

void
HtmlBeginListItem(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<LI>" );
}

void
HtmlEndUnnumberedList(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</UL>" );
}

void
HtmlBeginNumberedList(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<OL>" );
}

void
HtmlEndNumberedList(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</OL>" );
}

void
HtmlBeginDefinitionList(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<DL>" );
}

void
HtmlEndDefinitionList(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</DL>" );
}

void
HtmlDefinition(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszTerm,
    IN LPSTR lpszDef
)
{
    int nStart, nEnd, nLen;
    char tcHolder;

    WriteString( pECB, "<DT> " );
    HtmlWriteText( pECB, lpszTerm );
    WriteString( pECB, "\r\n" );
    WriteString( pECB, "<DD> " );

    nStart = 0;
    nLen = lstrlen( lpszDef );
    do {
        nEnd = nStart + 70;
        if ( nEnd >= nLen ) {
            HtmlWriteText( pECB, &lpszDef[nStart] );
            WriteString( pECB, "\r\n" );
            break;
        }
        while ( nEnd > nStart )
            if ( lpszDef[nEnd] == ' ' )
                break;

        if ( nEnd == nStart )
            // too long!
            nEnd = nStart + 70;

        // write defintion segment
        tcHolder = lpszDef[nEnd];
        lpszDef[nEnd] = 0;
        HtmlWriteText( pECB, &lpszDef[nStart] );
        WriteString( pECB, "\r\n" );
        lpszDef[nEnd] = tcHolder;
        nStart = nEnd;

        // skip excess whitespace
        while ( lpszDef[nStart] == ' ' )
            nStart++;

        // pretty formatting
        if ( nStart < nLen )
            WriteString( pECB, "     " );
    } while ( nStart < nLen );
}

// For complex defintions
void
HtmlBeginDefinitionTerm(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<DT>" );
}

void
HtmlBeginDefinition(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<DD>" );
}

//
// Text formatting
//

void
HtmlBeginPreformattedText(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<PRE>" );
}

void
HtmlEndPreformattedText(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</PRE>" );
}

void
HtmlBeginBlockQuote(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<BLOCKQUOTE>" );
}

void
HtmlEndBlockQuote(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</BLOCKQUOTE>" );
}

void
HtmlBeginAddress(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<ADDRESS>" );
}

void
HtmlEndAddress(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</ADDRESS>" );
}

void
HtmlBeginDefine(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<DFN>" );
}

void
HtmlEndDefine(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</DFN>" );
}

void
HtmlBeginEmphasis(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<EM>" );
}

void
HtmlEndEmphasis(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</EM>" );
}

void
HtmlBeginCitation(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<CITE>" );
}

void
HtmlEndCitation(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</CITE>" );
}

void
HtmlBeginCode(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<CODE>" );
}

void
HtmlEndCode(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</CODE>" );
}

void
HtmlBeginKeyboard(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<KBD>" );
}

void
HtmlEndKeyboard(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</KBD>" );
}

void
HtmlBeginStatus(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<SAMP>" );
}

void
HtmlEndStatus(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</SAMP>" );
}

void
HtmlBeginStrong(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<STRONG>" );
}

void
HtmlEndStrong(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</STRONG>" );
}

void
HtmlBeginVariable(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<VAR>" );
}

void
HtmlEndVariable(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</VAR>" );
}

void
HtmlBold(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszText
)
{
    HtmlBeginBold( pECB );
    HtmlWriteText( pECB, lpszText );
    HtmlEndBold( pECB );
}

void
HtmlBeginBold(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<B>" );
}

void
HtmlEndBold(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</B>" );
}

void
HtmlItalic(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszText
)
{
    HtmlBeginItalic( pECB );
    HtmlWriteText( pECB, lpszText );
    HtmlEndItalic( pECB );
}

void
HtmlBeginItalic(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<I>" );
}

void
HtmlEndItalic(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</I>" );
}

void
HtmlFixed(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszText
)
{
    HtmlBeginFixed( pECB );
    HtmlWriteText( pECB, lpszText );
    HtmlEndFixed( pECB );
}

void
HtmlBeginFixed(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<TT>" );
}

void
HtmlEndFixed(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "</TT>" );
}

void
HtmlLineBreak(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "<BR>\r\n" );
}

void
HtmlHorizontalRule(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    WriteString( pECB, "\r\n<HR>\r\n" );
}

void
HtmlImage(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszPicFile,
    IN LPCSTR lpszAltText
)
{
    WriteString( pECB, "<IMG SRC = \"" );
    HtmlWriteText( pECB, lpszPicFile );
    WriteString( pECB, "\"" );
    if ( lpszAltText ) {
        WriteString( pECB, " ALT = \"" );
        HtmlWriteText( pECB, lpszAltText );
        WriteString( pECB, "\"" );
    }
    WriteString( pECB, ">\r\n" );
}

void
HtmlPrintf(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCSTR lpszFormat,
    ...
)
{
    char szBuf[8192];

    va_list list;

    va_start( list, lpszFormat );

    vsprintf( szBuf, lpszFormat, list );
    WriteString( pECB, szBuf );

    va_end( list );
}


HTML.H

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    html.h

Abstract:

    Header file for HTML authoring functions

--*/

// Direct write of text, no translation
void WriteString (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpsz);

// Required page definition functions
void HtmlCreatePage (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpszTitle);
void HtmlEndPage (IN EXTENSION_CONTROL_BLOCK *pECB);

// Rest of the calls are optional
void HtmlHeading (IN EXTENSION_CONTROL_BLOCK *pECB, IN int nHeading,
                  IN LPCSTR lpszText);
void HtmlBeginHeading (IN EXTENSION_CONTROL_BLOCK *pECB, IN int nHeading);
void HtmlEndHeading (IN EXTENSION_CONTROL_BLOCK *pECB, IN int nHeading);

void HtmlWriteTextLine (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpsz);
void HtmlWriteText (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpsz);
void HtmlEndParagraph (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlHyperLink (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpszDoc,
                    IN LPCSTR lpszText);

void HtmlHyperLinkAndBookmark (IN EXTENSION_CONTROL_BLOCK *pECB,
                               IN LPCSTR lpszDoc, IN LPCSTR lpszBookmark,
                               IN LPCSTR lpszText);

void HtmlBookmarkLink (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpszBookmark,
                       IN LPCSTR lpszText);

void HtmlBeginListItem (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginUnnumberedList (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndUnnumberedList (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginNumberedList (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndNumberedList (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginDefinitionList (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndDefinitionList (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlDefinition (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpszTerm,
                     LPSTR lpszDef);

void HtmlBeginDefinitionTerm (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlBeginDefinition (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginPreformattedText (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndPreformattedText (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginBlockQuote (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndBlockQuote (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginAddress (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndAddress (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginDefine (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndDefine (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginEmphasis (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndEmphasis (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginCitation (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndCitation (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginCode (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndCode (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginKeyboard (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndKeyboard (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginStatus (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndStatus (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginStrong (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndString (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBeginVariable (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndVariable (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlBold (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpszText);
void HtmlBeginBold (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndBold (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlItalic (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpszText);
void HtmlBeginItalic (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndItalic (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlFixed (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpszText);
void HtmlBeginFixed (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlEndFixed (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlLineBreak (IN EXTENSION_CONTROL_BLOCK *pECB);
void HtmlHorizontalRule (IN EXTENSION_CONTROL_BLOCK *pECB);

void HtmlImage (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpszPicFile,
                IN LPCSTR lpszAltText);

void HtmlPrintf (IN EXTENSION_CONTROL_BLOCK *pECB, IN LPCSTR lpszFormat, ...);


KEYS.CPP

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    keys.cpp

Abstract:

   This module contains functions which deal with keys and values
   on inbound form data. Please, see keys.h for details.

--*/
#define WIN32_LEAN_AND_MEAN     // the bare essential Win32 API
#include <windows.h>
#include <httpext.h>

//
// If your application plans to receive huge amounts of data
// and you don't want megabytes of memory allocated, define
// the USE_TEMPORARY_FILES.  It is recommended to leave the
// define line below commented out unless absolutely necessary.
//

//#define USE_TEMPORARY_FILES

#include "keys.h"

#ifndef USE_MEMORY

#ifndef USE_TEMPORARY_FILES
#define USE_MEMORY
#endif

#endif

//
// If you want to record errors, modify this macro definition to
// call your own logging function.  This sample does not save
// error strings.
//
#define LOG(errorstring)        // OutputDebugString( errorstring ); \
                                // OutputDebugString ( "\r\n" )

//
//
// Intended external interface:
//
// GetKeyList       Determines if data was sent, and if it was, the
// data is extracted by GetPostKeys or GetUrlKeys,
// two private functions within this file.  A
// pointer to a linked list is returned ( as a
// handle ).
//
// GetKeyInfo       Returns a pointer to the key name,
// the length of the data, a flag indicating if
// the data has control characters in it, and an
// instance number for duplicate key names.
//
// GetKeyBuffer Returns a pointer to the buffer holding the key's
// data.
//
// FindKey          Sequentially searches linked list for key name.
//
// FreeKeyList      Deallocates memory used by the linked list of
// keys.  Also deletes content resources.
//
// GetKeyOffset Returns the offset to either the content memory
// buffer or the content temporary file.
//
// GetContentFile   Returns a pointer to the temporary file, only
// when USE_TEMPORARY_FILES is defined.
//
// CloseContentFile Closes the content file, normally left open
// until FreeKeyList is called, available only
// when USE_TEMPORARY_FILES is defined.
//
// OpenContentFile Reopens the content file for additional use
// by GetKeyBuffer, available only when
// USE_TEMPORARY_FILES is defined.
//
// GetDataBuffer    Returns a pointer to the content, only if the
// USE_TEMPORARY_FILES constant is NOT defined.
//
// Helper functions called only in this source file:
//
// GetQueryByte Similar to GetPostedByte, this function
// extracts data from the query string.
//
// HexDigitToInt    Returns the decimal value of a hex character.
//
// GetFirstByte Sets up POSDATA struct and calls GetNextByte.
// Caller specifies function used to retrieve
// data, either GetPostedByte or GetQueryByte.
//
// GetNextByte      Uses GetInboundByte ( specified in GetFirstByte )
// to retrieve inbound data, and decodes it using
// the URL encoding rules.
//
// BKL_Alloc        Allocates memory used in GetPostKeys.
//
// BKL_Dealloc      Deallocates memory used in GetPostKeys.
//
// BKL_Abort        Cleans up all resources for abnormal exits from
// GetPostKeys.
//
// IsKeySeparator   Returns TRUE if character is one of "=\r\n&\0".
//
// BuildKeyList Given a data extraction function ( i.e.
// GetPostedByte or GetQueryByte ), this function
// converts all keys into a linked list of POSTKEY
// structures.
//
// GetPostKeys      Takes inbound data off the wire by calling
// BuildKeyList with GetPostedByte as the extraction
// function.
//
// GetUrlKeys       Extracts data from the query string by calling
// BuildKeyList with GetQueryByte as the extraction
// function.
//
// GetPropAddr      Calculates the address of the list's properties,
// appended to the first key in a list.
//
// The typedef for the linked list is kept privately in this file,
// and our interface isolates other source files from the
// implementation details.
//

//
// Constants for this source file only
//

#define MAX_KEY_NAME_LENGTH 256 // maximum size of an inbound key name
#define CONTENT_BUF_LENGTH 8192 // amount of content buffered before
                                // WriteFile call
                                    // ( used for temporary files only )

#define GNB_NOTHING_LEFT 0      // GetNextByte return values
#define GNB_DECODED_CHAR 1
#define GNB_NORMAL_CHAR 2

//
// POSDATA struct is used with GetInboundByte to keep
// track of the position within incoming data.
// GETINBOUNDBYTE is a function pointer type.
//

typedef struct _tagPOSDATA {
    EXTENSION_CONTROL_BLOCK *pECB;
    int nCurrentPos;            // overall position

    int nBufferLength;          // length of buffer

    int nBufferPos;             // position within buffer

    int nAllocLength;           // size of buffer as allocated

    LPBYTE pData;
    int (*GetInboundByte)(struct _tagPOSDATA * p);
} POSDATA, *PPOSDATA;

typedef int (*GETINBOUNDBYTE)(PPOSDATA p);

#ifdef USE_MEMORY

//
// LISTPROP struct is used to maintain a set of
// list-wide properties.  This implementation
// uses the properties list to hold a buffer
// pointer.
//

typedef struct _tagLISTPROP {
    LPBYTE lpbyBuf;
} LISTPROP, *PLISTPROP;

#elif defined USE_TEMPORARY_FILES

//
// This LISTPROP struct holds temporary
// file information.
//

typedef struct _tagLISTPROP {
    char szTempFileName[MAX_PATH];
    HANDLE hFile;
} LISTPROP, *PLISTPROP;

#endif

// This private helper needs a prototype
PLISTPROP GetPropAddr( HKEYLIST hKey );

int
GetPostedByte(
    PPOSDATA pPosData
)
/*++

Purpose:

    GetPostedByte returns a waiting character that is not
    decoded yet.  We have this function to smooth out the
    inbound data: the server gives us blocks of data, one at
    a time, and there can be any number of blocks.

    For the first call, pPosData->nAllocLength must be zero,
    and pECB must be set.

Arguments:

    pPostData - pointer to POSTDATA struct

Returns:

    incoming byte value or
    -1 to indicate an error

--*/
{
    int nBytesToCopy;

    // For readability only...
    EXTENSION_CONTROL_BLOCK *pECB;

    pECB = pPosData->pECB;

    //
    // Initialize position struct on first call.
    //

    if ( !pPosData->nAllocLength ) {
        // Initialize the members
        pPosData->nCurrentPos = 0;
        pPosData->nBufferPos = 0;
        pPosData->nBufferLength = 0;
        pPosData->nAllocLength = 0x10000;   // 65536 bytes

        // Allocate the memory
        pPosData->pData = (LPBYTE) HeapAlloc(
            GetProcessHeap( ),
            HEAP_ZERO_MEMORY,
            pPosData->nAllocLength );
    }
    //
    // Was memory allocated?  Is it still allocated?
    // If not, return right away.
    //

    if ( !pPosData->pData ) {
        LOG( "GetPostedByte: Buffer not allocated." );
        return -1;
    }
    //
    // Check for end.  Deallocate and return if we're done.
    //

    if ( (DWORD) pPosData->nCurrentPos == pECB->cbTotalBytes ) {

        HeapFree( GetProcessHeap( ), 0, (LPVOID) pPosData->pData );
        pPosData->pData = 0;

        return -1;
    }

    //
    // Check for buffer not loaded.  Load if necessary.
    //

    if ( pPosData->nBufferPos == pPosData->nBufferLength ) {

        //
        // Fill the buffer with new inbound data.
        // Request it via ReadClient if necessary.
        //

        if ( pECB->cbAvailable < 1 ) {

            // Calculate how much we should go and get
            nBytesToCopy = pECB->cbTotalBytes - pPosData->nCurrentPos;
            if ( nBytesToCopy > pPosData->nAllocLength ) {
                nBytesToCopy = pPosData->nAllocLength;
            }

            // Let's go get the data
            if ( !pECB->ReadClient(
                    pECB->ConnID,
                    pPosData->pData,
                    (LPDWORD) & nBytesToCopy
                    )) {
                HeapFree( GetProcessHeap( ), 0, (LPVOID) pPosData->pData );
                pPosData->pData = 0;

                LOG( "GetPostedByte: Error reading data via ReadClient" );
                return -1;
            }
        }else{
            // Take at most nAllocLength bytes of data
            if ( pECB->cbAvailable > (DWORD) (pPosData->nAllocLength) ) {
                nBytesToCopy = pPosData->nAllocLength;
            }else{
                nBytesToCopy = pECB->cbAvailable;
            }

            // Copy the inbound data to our buffer
            memcpy(
                pPosData->pData,
                &pECB->lpbData[pPosData->nCurrentPos],
                nBytesToCopy
                );

            // Account for removed data
            pECB->cbAvailable -= nBytesToCopy;
        }

        // Our buffer is now full
        pPosData->nBufferLength = nBytesToCopy;
        pPosData->nBufferPos = 0;

        // Make sure we have something
        if ( !nBytesToCopy ) {
            HeapFree( GetProcessHeap( ), 0, (LPVOID) pPosData->pData );
            pPosData->pData = 0;
            return -1;
        }
    }
    //
    // Inc current pos, buffer pos, and return a character
    //

    pPosData->nCurrentPos++;
    pPosData->nBufferPos++;
    return ( (int)pPosData->pData[pPosData->nBufferPos - 1] );
}

int
GetQueryByte(
    IN OUT PPOSDATA pPosData
)
/*++

Purpose:

    Returns a waiting character that is not
    decoded yet.  We have this function to match GetPostedData.

    For the first call, pPosData->nAllocLength must be zero,
    and pECB must be set.

Arguments:

    pPostData - points to POSDATA structura

Returns:

    byte value or -1 to indicate an error

--*/
{
    // For readability only...
    EXTENSION_CONTROL_BLOCK *pECB;

    pECB = pPosData->pECB;

    //
    // Initialize position struct on first call.
    //

    if ( !pPosData->nAllocLength ) {
        // Initialize the useful members
        pPosData->nBufferPos = 0;
        pPosData->nBufferLength = lstrlen( (LPCSTR) pECB->lpszQueryString );
        pPosData->nAllocLength = -1;

        char szMsg[256];

        wsprintf(
            szMsg,
            "pPosData->nBufferLength=%i",
            pPosData->nBufferLength
            );
        LOG( szMsg );
    }

    //
    // Check for end.  Deallocate and return if we're done.
    //

    if ( pPosData->nBufferPos == pPosData->nBufferLength ) {
        return -1;
    }

    //
    // Inc buffer pos and return a character
    //

    pPosData->nBufferPos++;
    return ( (int)pECB->lpszQueryString[pPosData->nBufferPos - 1] );
}

//
// Now that we have GetPostedByte, and GetQueryByte, we can
// build a more useful function that decodes URL-style
// encoded characters.
//
// Recall that there are two special cases for this encoding:
//
// 1. Each plus sign must be converted to a space
// 2. A percent sign denotes a hex value-encoded character
//
// Percents are used to specify characters that are otherwise
// illegal.  This includes percents themselves, ampersands,
// control characters, and so on.
//
// GetNextByte returns the decoded byte, plus a flag indicating
// normal character, decoded character, or failure.  See top of
// file for return value constants.
//

int
HexDigitToInt(
    IN char c
)
/*++

Purpose:
    HexDigitToInt simply converts a hex-based character to an int.

Arguments:
    tc - character to convert

Returns:
    binary value of the character (0-15)
    -1 if the character is not hex digit

--*/
{
    if ( c >= '0' && c <= '9' ) {
        return ( c - '0' );
    }

    if ( tolower( c ) >= 'a' && tolower( c ) <= 'f' ) {
        return ( tolower( c ) - 'a'  + 10 );
    }

    return -1;
}

int
GetNextByte(
    IN OUT PPOSDATA pPosData,
    OUT char * pc
)
/*++

Purpose:
    Decode single byte of the input data

Arguments:
    pPostData - points to POSDATA struct
    pc - points to variable to accept decoded byte

Returns:
    GNB_NORMAL_CHAR, GNB_NOTHING_LEFT or GNB_DECODED_CHAR

--*/
{
    int nChar;
    int nDigit;

    // Initialize character pointer
    *pc = 0;

    // Fetch the next inbound character
    nChar = pPosData->GetInboundByte( pPosData );
    if ( nChar == -1 ) {
        return GNB_NOTHING_LEFT;
    }

    // Plus signs: convert to spaces
    if ( nChar == '+' ) {
        *pc = ' ';
        return GNB_DECODED_CHAR;
    }
    // Percent signs: convert hex values
    else if ( nChar == '%' ) {
        nChar = pPosData->GetInboundByte( pPosData );
        nDigit = HexDigitToInt( nChar );
        if ( nDigit == -1 ) {
            return GNB_NOTHING_LEFT;
        }

        *pc = ( char ) ( ( UINT ) nDigit << 4 );

        nChar = pPosData->GetInboundByte( pPosData );
        nDigit = HexDigitToInt( nChar );
        if ( nDigit == -1 ) {
            *pc = 0;            // incomplete

            return GNB_NOTHING_LEFT;
        }
        *pc |= ( char ) ( UINT ) nDigit;

        return GNB_DECODED_CHAR;
    }
    // Must be normal character then
    *pc = (char) nChar;

    return GNB_NORMAL_CHAR;
}

int
GetFirstByte(
    IN OUT PPOSDATA pPosData,
    IN EXTENSION_CONTROL_BLOCK * pECB,
    OUT char * pc,
    IN GETINBOUNDBYTE GetInboundByte
)
/*++

Purpose:

    GetFirstByte eliminates the guesswork from initialization.
    We call GetFirstByte with an uninitialized POSDATA structure,
    and we call GetNextByte from there on.

Arguments:
    pPosData - points to POSDATA struct to initialize
    pECB - points to the extenstion control block
    pc - points to variable to accept decoded byte
    GetInboundByte - points to function to get incoming bytes

Returns:
    same as GetNextByte()

--*/
{
    // Initialize struct
    pPosData->nAllocLength = 0;
    pPosData->pECB = pECB;
    pPosData->GetInboundByte = GetInboundByte;

    // Make the call as usual
    return GetNextByte( pPosData, pc );
}

//
// Structure used in data processing - the elements of the
// key list.
//

typedef struct _tagPOSTKEY {
    int nInstance;              // used when key name is the same as another,
                                // normally 0

    DWORD dwOffset;             // offset into content file
    DWORD dwLength;             // length of data
    BOOL bHasCtrlChars;         // a character value < 32 is in data
    struct _tagPOSTKEY *pNext;  // linked list
    struct _tagPOSTKEY *pHead;  // first in linked list
    LPBYTE lpbyBuf;             // pointer to the key's data in the list
                                // buffer

    // key string appended to structure
    // for the head key, list properties are appended
} POSTKEY, *PPOSTKEY;

//
// These three helper functions isolate the memory allocation,
// deallocation and abnormal exit code.  They are used only to
// keep BuildKeyList readable.
//

BOOL
BKL_Alloc(
    OUT LPSTR * plpszKey,
    OUT LPBYTE * plpbyBuf
)
{
    // Allocate a buffer for the key name
    *plpszKey = (LPSTR) HeapAlloc( GetProcessHeap( ),
        HEAP_ZERO_MEMORY,
        MAX_KEY_NAME_LENGTH );

    if ( !*plpszKey ) {
        return FALSE;
    }

#ifdef USE_MEMORY
    // Init buffer to NULL
    *plpbyBuf = NULL;

#elif defined USE_TEMPORARY_FILES

    // Allocate a buffer for the content
    *plpbyBuf = (LPBYTE) HeapAlloc( GetProcessHeap( ),
        HEAP_ZERO_MEMORY,
        CONTENT_BUF_LENGTH );

    if ( !*plpbyBuf ) {
        HeapFree( GetProcessHeap( ), 0, (LPVOID) * plpszKey );
        return FALSE;
    }
#endif

    return TRUE;
}

void
BKL_Dealloc(
    IN LPSTR * plpsz,
    IN LPBYTE * plpby
)
{
    if ( plpsz && *plpsz ) {
        HeapFree( GetProcessHeap( ), 0, (LPVOID) * plpsz );
    }
    if ( plpby && *plpby ) {
        HeapFree( GetProcessHeap( ), 0, (LPVOID) * plpby );
    }
}

//
// This allows us to clean up... with temporary files we have to close
// and delete them.  Otherwise, we have to free a lot of memory.
//

#ifdef USE_TEMPORARY_FILES
#define MACRO_AbortCleanup BKL_Abort( pHead, hDataFile, \
    lpszKeyNameBuf, lpbyContentBuf );\
    if ( hDataFile != INVALID_HANDLE_VALUE ) DeleteFile( szTempPath )

#elif defined USE_MEMORY

#define MACRO_AbortCleanup BKL_Abort( pHead, INVALID_HANDLE_VALUE, \
    lpszKeyNameBuf,lpbyContentBuf )
#endif

void
BKL_Abort(
    IN PPOSTKEY pHead,
    IN HANDLE hFile,
    IN LPSTR lpszKey,
    IN LPBYTE lpbyBuf
)
{
    if ( pHead ) {
        FreeKeyList( (HKEYLIST) pHead );
    }

    if ( hFile != INVALID_HANDLE_VALUE ) {
        CloseHandle( hFile );
    }

    BKL_Dealloc( &lpszKey, &lpbyBuf );
}

BOOL
IsKeySeparator(
    char c
)
/*++

Purpose:
    Identify key separators

Arguments:
    c - character

Returns:
    TRUE if character is a key separator,
    FALSE otherwise

--*/
{
    return ( c == '=' || c == '\r' || c == '\n' || c == '&' || !c );
}

PPOSTKEY
BuildKeyList(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN GETINBOUNDBYTE GetInboundByte
)
/*++

Purpose:

    Now that we have a way to get a decoded byte from the stream,
    we can parse POST data.  POST data comes in as:

    key=data&key=data&key=data\r\n

    A linked list of keys is established, and the head node
    of the list is returned.  A NULL indicates no keys or
    an error.

Arguments:
    pECB - pointer to the extension control block
    GetInboundByte - pointer to function to get input data

Returns:
    Pointer to the head node or NULL

--*/
{
    PPOSTKEY pHead = NULL;      // head of linked list ( the return val )
    PPOSTKEY pTail = NULL;      // last member in linked list
    PPOSTKEY pNewPostKey;       // pointer for unlinked, newly allocated
                                // objects

    PPOSTKEY pListWalk;         // linked list walking pointer
    PLISTPROP pProp;            // pointer to list properties
    LPSTR lpszKeyNameBuf;       // pointer to buffer, used in obtaining key
                                // name

    int nPos;                   // position within key name buffer
    DWORD dwOffset;             // offset from start of content buffer or
                                // file

    DWORD dwLength;             // length of key data
    char c;                     // general-purpose character
    int nReturn;                // general-purpose return code
    POSDATA pd;                 // POSDATA struct needed in GetInboundByte
    int nContentPos;            // position within content buffer
    LPBYTE lpbyContentBuf;      // pointer to buffer
    BOOL bHasCtrlChars;         // flag to detect ctrl chars

    // Call helper to allocate a buffer
    if ( !BKL_Alloc( &lpszKeyNameBuf, &lpbyContentBuf ) ) {
        LOG( "BuildKeyList: Memory allocation failure" );
        return NULL;
    }
    nContentPos = dwOffset = 0;

#ifdef USE_MEMORY
    //
    // Allocate enough memory for all the content.
    // For the POST method, the cbTotalBytes gives us the number
    // of bytes that are being sent by the browser.  We can
    // allocate that much but we'll really only use about 75% of it.
    // For the GET method, we need to allocate the size of the
    // query string plus 1.
    //

    lpbyContentBuf = (LPBYTE) HeapAlloc( GetProcessHeap( ),
        HEAP_ZERO_MEMORY,
        pECB->cbTotalBytes +
        lstrlen( pECB->lpszQueryString ) + 1 );

    if ( !lpbyContentBuf ) {

        LOG( "BuildKeyList: Error allocating content memory" );
        BKL_Dealloc( &lpszKeyNameBuf, &lpbyContentBuf );

        return NULL;
    }
#elif defined USE_TEMPORARY_FILES

    //
    // When USE_TEMPORARY_FILES is chosen, we create
    // a temporary file to store all the inbound data.
    // This is done to support huge amounts of inbound
    // data, like file uploads.
    //

    char szTempDir[MAX_PATH];   // directory of temporary files
    char szTempPath[MAX_PATH];  // path of content file
    HANDLE hDataFile;           // handle to content file
    DWORD dwBytesWritten;       // used with WriteFile

    // Get a temp file name
    GetTempPath( MAX_PATH, szTempDir );
    if ( !GetTempFileName( szTempDir, "key", 0, szTempPath ) ) {

        LOG( "BuildKeyList: Error creating temporary file" );
        BKL_Dealloc( &lpszKeyNameBuf, &lpbyContentBuf );

        return NULL;
    }
    // Create the content file
    hDataFile = CreateFile( szTempPath,
        GENERIC_READ | GENERIC_WRITE,
        0,                      // No sharing mode
        NULL,                   // Default security attribs
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL                    // No template file
        );

    // Return if an error occured
    if ( hDataFile == INVALID_HANDLE_VALUE ) {
        LOG( "BuildKeyList: Error opening temporary file" );
        MACRO_AbortCleanup;
        return NULL;
    }
#endif

    //
    // 'for' statement detects the start of a valid key name.
    //
    // To do inside 'for' loop:
    // Obtain key name
    // Write data to buffer or content file
    // Create POSTKEY object
    // Update links
    //

    for ( nReturn = GetFirstByte( &pd, pECB, &c, GetInboundByte );
        nReturn != GNB_NOTHING_LEFT;
        nReturn = GetNextByte( &pd, &c ) ) {

        // If \r or \n, ignore and continue
        if ( c == '\r' || c == '\n' ) {
            continue;
        }

        // Get a key name
        nPos = 0;
        while ( !IsKeySeparator( c ) ) {
            if ( nPos < MAX_KEY_NAME_LENGTH ) {
                lpszKeyNameBuf[nPos] = c;
                nPos++;
            }
            nReturn = GetNextByte( &pd, &c );
            if ( nReturn == GNB_NOTHING_LEFT )  { // abrupt end!
                break;
            }
        }

        // If no equals sign or name too long,
        // we have a browser formatting error
        if ( c != '=' || nPos == MAX_KEY_NAME_LENGTH ) {
            LOG( "BuildKeyList: Browser formatting error" );

            MACRO_AbortCleanup;
            return NULL;
        }

        // Truncate the name string, reset data info variables
        lpszKeyNameBuf[nPos] = 0;
        nPos++;
        dwLength = 0;
        bHasCtrlChars = FALSE;

        //
        // Move the data to the content buffer or file.
        //
        for ( nReturn = GetNextByte( &pd, &c );
            !IsKeySeparator( c ) || nReturn == GNB_DECODED_CHAR;
            nReturn = GetNextByte( &pd, &c ) ) {

            lpbyContentBuf[nContentPos] = c;

            nContentPos += sizeof ( char );
            dwLength++;

            // Check for ctrl chars
            if ( c < 0x20 ) {
                bHasCtrlChars = TRUE;
            }

#ifdef USE_TEMPORARY_FILES
            // If we have enough data, write buffer to disk
            if ( nContentPos == CONTENT_BUF_LENGTH ) {
                if ( !WriteFile( hDataFile, lpbyContentBuf,
                    nContentPos, &dwBytesWritten, NULL ) ) {

                    LOG( "BuildKeyList: Error writing to content file" );
                    MACRO_AbortCleanup;
                    return NULL;
                }
                nContentPos = 0;
            }
#endif

        } // for( nReturn

#ifdef USE_MEMORY
        //
        // Put a terminating NULL at the end of the key data.
        //

        lpbyContentBuf[nContentPos] = 0;
        nContentPos++;

#elif defined USE_TEMPORARY_FILES

        // Drain buffer
        if ( nContentPos ) {
            if ( !WriteFile( hDataFile, lpbyContentBuf,
                nContentPos, &dwBytesWritten, NULL ) ) {

                LOG( "BuildKeyList: Error writing to content file" );
                MACRO_AbortCleanup;
                return NULL;
            }
            nContentPos = 0;
        }
#endif

        // Allocate a POSTKEY object, allocate extra for first key
        if ( pHead ) {
            pNewPostKey = (PPOSTKEY) HeapAlloc(
                GetProcessHeap( ),
                HEAP_ZERO_MEMORY,
                sizeof (POSTKEY) + nPos
                );
        }else{
            pNewPostKey = (PPOSTKEY) HeapAlloc(
                GetProcessHeap( ),
                HEAP_ZERO_MEMORY,
                sizeof (POSTKEY) + nPos +
                sizeof (LISTPROP) );

            pProp = (PLISTPROP) ( (LPBYTE)pNewPostKey +
                    sizeof (POSTKEY) + nPos );
        }

        // Check for valid pointer
        if ( !pNewPostKey ) {
            LOG( "BuildKeyList: POSTKEY memory allocation failure" );
            MACRO_AbortCleanup;
            return NULL;
        }

        //
        // Set pNewPostKey members
        //

        // Set nInstance
        pNewPostKey->nInstance = 0;
        pListWalk = pHead;
        while ( pListWalk ) {
            // Check for duplicate key names
            if ( !lstrcmpi( (LPCSTR) ( &pListWalk[1] ), lpszKeyNameBuf )) {
                pNewPostKey->nInstance++;
            }
            pListWalk = pListWalk->pNext;
        }

        // Set dwOffset, dwLength, bHasCtrlChars, lpbyBuf
        pNewPostKey->dwOffset = dwOffset;
        pNewPostKey->dwLength = dwLength;
        pNewPostKey->bHasCtrlChars = bHasCtrlChars;

#ifdef USE_MEMORY
        pNewPostKey->lpbyBuf = &lpbyContentBuf[dwOffset];
        dwOffset += dwLength + 1;

#elif defined USE_TEMPORARY_FILES

        pNewPostKey->lpbyBuf = NULL;
        dwOffset += dwLength;
#endif

        // Copy key name
        lstrcpy( (LPSTR) ( &pNewPostKey[1] ), lpszKeyNameBuf );

        // Link
        if ( pTail ) {
            pTail->pNext = pNewPostKey;
        }else{

#ifdef USE_TEMPORARY_FILES
            // Copy content file name to list properties
            lstrcpy( pProp->szTempFileName, szTempPath );

            // Set handle
            pProp->hFile = hDataFile;

#elif defined USE_MEMORY

            // Set content buffer pointer
            pProp->lpbyBuf = lpbyContentBuf;
#endif

            // Set head
            pHead = pNewPostKey;
        }

        pNewPostKey->pNext = NULL;
        pTail = pNewPostKey;

        pNewPostKey->pHead = pHead;     // may point to itself

    } // for ( nReturn

#ifdef USE_TEMPORARY_FILES
    //
    // If content file is empty, close it and delete it
    //

    if ( !pHead ) {
        LOG( "Empty content file is being deleted." );
        CloseHandle( hDataFile );
        DeleteFile( szTempPath );
    }
    // Free work buffer
    BKL_Dealloc( &lpszKeyNameBuf, &lpbyContentBuf );

#elif defined USE_MEMORY

    // Free work buffer
    BKL_Dealloc( &lpszKeyNameBuf, pHead ? NULL : &lpbyContentBuf );
#endif

    return pHead;
}

//
// We are now pretty much done with anything complex. BuildKeyList
// will do all our parse work, so now we need a few wrappers to
// make a nice, clean external interface.
//
// GetPostKeys calls BuildKeyList with GetPostedByte.
//
// GetUrlKeys calls BuildKeyList with GetQueryByte.
//

PPOSTKEY
GetPostKeys(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    return BuildKeyList( pECB, GetPostedByte );
}

PPOSTKEY
GetUrlKeys(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
{
    return BuildKeyList( pECB, GetQueryByte );
}

PLISTPROP
GetPropAddr(
    IN HKEYLIST hKey
)
/*++

Purpose:

    GetPropAddr returns the address of the end of
    the first key.  We stuff list properties there.
    This implementation of keys.cpp keeps a pointer
    to the content buffer.  The second version ( used
    in IS2WCGI ) appends a temporary file name
    to the first key.

Arguments:
    hKey - pointer to a key list

Returns:
    The address of the end of the first key

--*/
{
    LPCSTR lpszKeyName;
    PPOSTKEY pHead;

    // Safety
    if ( !hKey ) {

return NULL;
    }

    // ContentPath follows POSTKEY struct and key name
    pHead = (PPOSTKEY) hKey;
    pHead = pHead->pHead;

    lpszKeyName = (LPCSTR) ( &pHead[1] );

    return (PLISTPROP) ( lpszKeyName + lstrlen( lpszKeyName ) + 1 );
}

HKEYLIST
GetKeyList(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:

    Examines the method and calls GetPostKeys or GetUrlKeys,
    whichever is relevant.

Arguments:
    pECB - points to the extension control block

Returns:
    GetPropAddr returns the address of the end of
    the first key.  We stuff list properties there.
    This implementation of keys.cpp keeps a pointer
    to the content buffer.  The second version ( used
    in IS2WCGI ) appends a temporary file name
    to the first key.

--*/
{
    if ( !lstrcmpi( pECB->lpszMethod, "POST" ) ) {
        LOG( "Method=POST" );
        return (HKEYLIST) GetPostKeys( pECB );
    }else if ( !lstrcmpi( pECB->lpszMethod, "GET" ) ) {
        LOG( "Method=GET" );
        return (HKEYLIST) GetUrlKeys( pECB );
    }
    LOG( "Unknown method" );

    return NULL;
}

HKEYLIST
GetKeyInfo(
    IN HKEYLIST hKey,
    OUT LPCSTR * plpszKeyName,
    OUT LPDWORD pdwLength,
    OUT BOOL * pbHasCtrlChars,
    OUT LPINT pnInstance
)
//
// GetKeyInfo is a wrapper for the POSTKEY linked list.
// It returns the members of the supplied POSTKEY object.
//
{
    PPOSTKEY pPostKey;

    // Safety
    if ( !hKey ) {
        return NULL;
    }

    pPostKey = (PPOSTKEY) hKey;

    // Set the data members
    if ( plpszKeyName )
        *plpszKeyName = ( LPCSTR ) ( &pPostKey[1] );
    if ( pdwLength )
        *pdwLength = pPostKey->dwLength;
    if ( pbHasCtrlChars )
        *pbHasCtrlChars = pPostKey->bHasCtrlChars;
    if ( pnInstance )
        *pnInstance = pPostKey->nInstance;

    // Return a handle to the next object in the list
    return ( ( HKEYLIST ) pPostKey->pNext );
}

#ifdef USE_MEMORY
LPBYTE
GetKeyBuffer(
    IN HKEYLIST hKey
)
{
    //
    // We have two versions of this function because
    // we may want to use file i/o when the extension
    // deals with massive amounts of inbound data
    // ( like multi-megabyte uploads ).
    //

    //
    // This version uses a memory buffer.
    //

    PPOSTKEY pKey;

    // Safety
    if ( !hKey ) {
        return NULL;
    }

    pKey = (PPOSTKEY) hKey;

    return (LPBYTE) pKey->lpbyBuf;
}

#elif defined USE_TEMPORARY_FILES

LPBYTE
GetKeyBuffer(
    IN HKEYLIST hKey
)
{
    //
    // This version uses slow temporary files.
    //

    PLISTPROP pProp;
    PPOSTKEY pKey;
    DWORD dwRead;

    // Get pointer to list properties
    pProp = GetPropAddr( hKey );

    // Safety
    if ( !pProp ) {
        return NULL;
    }

    pKey = (PPOSTKEY) hKey;

    // Check if memory was already loaded for this key
    if ( pKey->lpbyBuf ) {
        return pKey->lpbyBuf;
    }

    // If not, let's allocate memory and do a ReadFile
    pKey->lpbyBuf = (LPBYTE) HeapAlloc( GetProcessHeap( ),
        HEAP_ZERO_MEMORY,
        pKey->dwLength + 1 );
    if ( !pKey->lpbyBuf ) {
        LOG( "GetKeyBuffer: HeapAlloc failed" );
        return NULL;
    }
    // Do the ReadFile
    SetFilePointer( pProp->hFile, pKey->dwOffset, NULL, FILE_BEGIN );
    if ( !ReadFile( pProp->hFile, pKey->lpbyBuf,
            pKey->dwLength, &dwRead, NULL ) ||
        dwRead != pKey->dwLength ) {
        HeapFree( GetProcessHeap( ), 0, (LPVOID) pKey->lpbyBuf );
        pKey->lpbyBuf = NULL;

        LOG( "GetKeyBuffer: ReadFile failed" );
        return NULL;
    }
    return pKey->lpbyBuf;
}
#endif

HKEYLIST
FindKey(
    IN HKEYLIST hKeyList,
    IN LPCSTR lpszSearchName
)
/*++

Purpose:

    FindKey sequentially searches the linked list for a given key.

Arguments:

    hKeyList - points to key list
    lpszSearchName - points to a key name to find

Returns:

    The return handle points to the element within the linked list.
    Use it in GetKeyInfo, but not FreeKeyList.

--*/
{
    PPOSTKEY pFindKey;

    pFindKey = (PPOSTKEY) hKeyList;
    while ( pFindKey ) {
        if ( !lstrcmpi( lpszSearchName, ( LPCSTR ) ( &pFindKey[1] ) ) ) {
            return ( ( HKEYLIST ) pFindKey );
        }

        pFindKey = pFindKey->pNext;
    }

    return NULL;
}

void
FreeKeyList(
    IN HKEYLIST hHeadKey
)
/*++

Purpose:

    FreeKeyList deallocates all the objects in the key list.
    The content file is also deleted.

Arguments:
    hHeadKey - points to the list head

--*/
{
    PPOSTKEY pObject;
    PPOSTKEY pDel;
    PLISTPROP pProp;

    // Safety
    if ( !hHeadKey ) {
        return;
    }

#ifdef USE_TEMPORARY_FILES
    // Close the content file
    CloseContentFile( hHeadKey );

    // delete the content file
    pProp = GetPropAddr( hHeadKey );
    DeleteFile( pProp->szTempFileName );

#elif defined USE_MEMORY

    // delete content
    pProp = GetPropAddr( hHeadKey );
    HeapFree( GetProcessHeap( ), 0, (LPVOID) pProp->lpbyBuf );
#endif

    // delete all objects in the list
    pObject = (PPOSTKEY) hHeadKey;
    pObject = pObject->pHead;
    while ( pObject ) {

#ifdef USE_TEMPORARY_FILES
        //
        // Free each buffer when using temporary files
        //

        if ( pObject->lpbyBuf )
            HeapFree( GetProcessHeap( ), 0, (LPVOID) pObject->lpbyBuf );
#endif

        pDel = pObject;
        pObject = pObject->pNext;

        HeapFree( GetProcessHeap( ), 0, (LPVOID) pDel );
    }
}

DWORD
GetKeyOffset(
    IN HKEYLIST hKey
)
/*++

Purpose:

    GetKeyOffset returns the offset of a key into the internal
    buffer or temporary file.  This is provided for IS2WCGI
    so it can return an offset within the content file.

Arguments:

    hKey - points to a key

Returns:

    Offset of a key or NULL if no key provided

--*/
{
    // Safety
    if ( !hKey )
        return NULL;

    return ( (PPOSTKEY) hKey )->dwOffset;
}

#ifdef USE_TEMPORARY_FILES

LPCSTR
GetContentFile(
    IN HKEYLIST hKeyList
)
/*++

Purpose:

    GetContentFile returns a pointer to the name of the
    temporary file.  This is provided for the IS2WCGI
    sample.

Arguments:

Returns:

--*/
{
    PLISTPROP pProp;

    // safety
    if ( !hKeyList )
        return NULL;

    pProp = GetPropAddr( hKeyList );

    return ( LPCSTR ) pProp->szTempFileName;
}

void
CloseContentFile(
    IN HKEYLIST hKey
)
/*++

Purpose:

    CloseContentFile forces the content file to be closed.  This
    allows you to pass the file to something else that may open
    it.  Call OpenContentFile before calling any other key
    function.

Arguments:

--*/
{
    PLISTPROP pProp;

    if ( !hKey )
        return;

    pProp = GetPropAddr( hKey );
    if ( pProp->hFile != INVALID_HANDLE_VALUE ) {
        CloseHandle( pProp->hFile );
        pProp->hFile = INVALID_HANDLE_VALUE;
    }
}

void
OpenContentFile(
    IN HKEYLIST hKey
)
/*++

Purpose:

    OpenContentFile forces the content file to be reopened.
    GetKeyBuffer will fail if the content file was closed by
    CloseContentFile, but not reopened.

Arguments:

Returns:

--*/
{
    PLISTPROP pProp;

    if ( !hKey )
        return;

    pProp = GetPropAddr( hKey );

    if ( pProp->hFile != INVALID_HANDLE_VALUE )
        return;

    // Create the content file
    pProp->hFile = CreateFile( pProp->szTempFileName,
        GENERIC_READ | GENERIC_WRITE,
        0,                      // No sharing mode
         NULL,                  // Default security attribs
         OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL );
}

#elif defined USE_MEMORY

LPBYTE
GetDataBuffer(
    IN HKEYLIST hKeyList
)
/*++

Purpose:

    GetBufferPointer returns a pointer to the buffer used
    for content storage.

Arguments:

    hKeyList - points to a key list

Returns:

    pointer to the content buffer or NULL

--*/
{
    PLISTPROP pProp;

    // safety
    if ( !hKeyList )
        return NULL;

    pProp = GetPropAddr( hKeyList );

    return pProp->lpbyBuf;
}
#endif


KEYS.H

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    keys.h

Abstract:

    header file for reusable interface

--*/

// Abstracted pointer
typedef void * HKEYLIST;

//
// Retrieves and decodes inbound form data.  Builds list of keys, and
// pointers to data within a content file.  Returns handle to first
// element in the list.
//

HKEYLIST GetKeyList(IN EXTENSION_CONTROL_BLOCK *pECB);

//
// GetKeyInfo extracts the key name and content values from the
// supplied key, and returns a handle to the next key in the list.
//
// The length is the exact length of the inbound data, but a NULL
// is appended to the data.  For example, a text string of five
// characters has a *pdwLength=5, but GetKeyBuffer returns at
// least a 6 byte buffer--the five characters and a NULL.
//

HKEYLIST GetKeyInfo(IN HKEYLIST hKey, OUT LPCSTR *plpszKeyName,
                    OUT LPDWORD pdwLength, OUT BOOL *pbHasCtrlChars,
                    OUT LPINT pnInstance);

//
// GetKeyBuffer returns a pointer to the buffer holding data.
// Depending on the implementation in keys.cpp, this may or may not
// be a buffer the exact size of the data (it may be bigger).
//
// The data is zero-terminated.
//

LPBYTE GetKeyBuffer(IN HKEYLIST hKey);

//
// FindKey sequentially searches the linked list for a specific key.
// The return handle can be used with GetKeyInfo to get more details.
// FindKey returns the very first occurance of a duplicate key.
// Also, it searches from the given key which need not be the head
// key.
//

HKEYLIST FindKey(IN HKEYLIST hKeyList, IN LPCSTR lpszSearchName);

//
// FreeKeyList releases all of the memory associated with a key list.
// Also, content resources are deleted.
//

void FreeKeyList(IN HKEYLIST hKeyList);

//
// GetKeyOffset returns the offset within the internal buffer or
// the content file.  Under normal circumstances, use GetKeyInfo
// and GetKeyBuffer instead of directly accessing the buffer.
//

DWORD GetKeyOffset(IN HKEYLIST hKey);

#ifdef USE_TEMPORARY_FILES
//
// GetContentFile returns the name of the temporary file used
// to save the content.  The temporary file may be open.
//

LPCTSTR GetContentFile(IN HKEYLIST hKeyList);

//
// CloseContentFile forces the content file to be closed.  This
// allows you to pass the file to something else that may open
// it.  Call OpenContentFile before calling any other key
// function.
//

void CloseContentFile(IN HKEYLIST hKeyList);

//
// OpenContentFile forces the content file to be reopened.
// GetKeyBuffer will fail if the content file was closed by
// CloseContentFile, but not reopened.
//

void OpenContentFile(IN HKEYLIST hKeyList);

#else

//
// GetDataBuffer returns a pointer to the start of the data
// buffer which holds all content.  This function is not
// particularly useful -- use GetKeyBuffer to get a pointer
// to the buffer for a specific key.
//

LPBYTE GetDataBuffer(IN HKEYLIST hKey);

#endif


FORMDUMP.CPP

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    formdump.cpp

Abstract:

    ISAPI Extension sample illustrating how to obtain data from a web browser
    and how to build a reply to the form.

--*/

#define WIN32_LEAN_AND_MEAN     // the bare essential Win32 API
#include <windows.h>
#include <ctype.h>              // for isprint()
#include <httpext.h>

#include "keys.h"
#include "html.h"

//
// local prototypes
//
void SendVariables( EXTENSION_CONTROL_BLOCK * pECB );
void HexDumper( EXTENSION_CONTROL_BLOCK * pECB, LPBYTE lpbyBuf, DWORD dwLength );
void WhoAmI( EXTENSION_CONTROL_BLOCK * pECB );
BOOL SendHttpHeaders( EXTENSION_CONTROL_BLOCK *, LPCSTR, LPCSTR );

BOOL WINAPI
GetExtensionVersion(
    OUT HSE_VERSION_INFO * pVer
    )
/*++

Purpose:

    This is required ISAPI Extension DLL entry point.

Arguments:

    pVer - poins to extension version info structure

Returns:

    always returns TRUE

--*/
{
    //
    // set version to httpext.h version constants
    //
    pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);

    lstrcpyn((LPSTR) pVer->lpszExtensionDesc,
        "FORMDUMP - A Form Decoder and Dumper",
        HSE_MAX_EXT_DLL_NAME_LEN);

    return TRUE;
}

DWORD WINAPI
HttpExtensionProc(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:

    Pull in all inbound data.  Build a reply page
    so the user can see how forms appear in our key list.

Arguments:

    pECB - pointer to the extenstion control block

Returns:

    HSE_STATUS_SUCCESS on successful completion
    HSE_STATUS_ERROR on failure

--*/
{
    HKEYLIST hKeyList;
    char szMsg[128];

    //
    // Get the keys sent by the client
    //

    hKeyList = GetKeyList( pECB );

    //
    // Send HTTP headers
    //

    SendHttpHeaders( pECB, "200 OK", "Content-type: text/html\r\n\r\n" );

    //
    // Create a basic HTML page
    //

    HtmlCreatePage( pECB, "FormDump.dll Reply" );
    HtmlHeading( pECB, 1, "Data Available via ISAPI" );
    HtmlHorizontalRule( pECB );

    //
    // Send each form field
    //

    HtmlHeading( pECB, 2, "Form Fields" );

    if ( !hKeyList ) {

        //
        // Report no data/error
        //

        HtmlBold( pECB, "No form fields sent" );
        HtmlWriteText( pECB, " (or error decoding keys)" );
        HtmlEndParagraph( pECB );

    } else {
        HKEYLIST hKey;

        //
        // Print a quick overview
        //

        HtmlWriteTextLine( pECB, "The form you submitted data to just called" );
        HtmlWriteTextLine( pECB, "the Internet Information Server extension" );
        HtmlWriteTextLine( pECB, "FormDump.dll.  Here is a listing of what was" );
        HtmlWriteTextLine( pECB, "received and what variables inside FormDump" );
        HtmlWriteTextLine( pECB, "have the data." );
        HtmlEndParagraph( pECB );

        //
        // Loop through all of the keys
        //
        hKey = hKeyList;
        while ( hKey ) {

            //
            // Details about the key
            //

            LPCTSTR lpszKeyName;
            DWORD dwLength;
            BOOL bHasCtrlChars;
            int nInstance;
            HKEYLIST hLastKey;

            //
            // We get info, and hKey points to next key in list
            //

            hLastKey = hKey;    // keep this for later

            hKey = GetKeyInfo( hKey, &lpszKeyName, &dwLength,
                &bHasCtrlChars, &nInstance );

            //
            // Build web page
            //

            HtmlBold( pECB, "Form Field Name (lpszKeyName): " );
            HtmlWriteText( pECB, lpszKeyName );
            HtmlLineBreak( pECB );

            HtmlBold( pECB, "Length of Data (dwLength): " );
            wsprintf( szMsg, "%u", dwLength );
            HtmlWriteText( pECB, szMsg );
            HtmlLineBreak( pECB );

            HtmlBold( pECB, "Data Has Control Characters (bHasCtrlChars): " );
            wsprintf( szMsg, "%u", bHasCtrlChars );
            HtmlWriteText( pECB, szMsg );
            HtmlLineBreak( pECB );

            HtmlBold( pECB, "Instance of Form Field (nInstance): " );
            wsprintf( szMsg, "%u", nInstance );
            HtmlWriteText( pECB, szMsg );

            if ( dwLength ) {
                HtmlLineBreak( pECB );
                HtmlBold( pECB, "Data Sent for Field:" );
                HtmlLineBreak( pECB );

                HexDumper( pECB, GetKeyBuffer( hLastKey ), dwLength );
            }
            HtmlEndParagraph( pECB );
        }

        //
        // Clean up
        //

        FreeKeyList( hKeyList );
    }

    HtmlHorizontalRule( pECB );

    //
    // Get user name from SID and return it in the page
    //

    HtmlHeading( pECB, 2, "Security Context for HttpExtensionProc Thread" );
    WhoAmI( pECB );
    HtmlHorizontalRule( pECB );

    //
    // Display all server variables
    //

    HtmlHeading( pECB, 2, "Server Variables" );
    HtmlWriteTextLine( pECB,
        "Below is a list of all variables available via" );
    HtmlWriteTextLine( pECB, "GetServerVariable ISAPI API.  Much of this" );
    HtmlWriteTextLine( pECB,
        "information comes from the browser HTTP header." );
    HtmlEndParagraph( pECB );

    //
    // Send server variables obtained from the HTTP header
    //

    SendVariables( pECB );

    //
    // Finish up...
    //

    HtmlEndPage( pECB );

    return HSE_STATUS_SUCCESS;
}

BOOL WINAPI
TerminateExtension(
    DWORD dwFlags
)
/*++

Purpose:

    This is optional ISAPI extension DLL entry point.
    If present, it will be called before unloading the DLL,
    giving it a chance to perform any shutdown procedures.

Arguments:

    dwFlags - specifies whether the DLL can refuse to unload or not

Returns:

    TRUE, if the DLL can be unloaded

--*/
{
    return TRUE;
}

void
HexDumper(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPBYTE lpbyBuf,
    IN DWORD dwLength
)
/*++

Purpose:

    Put the inbound data in a hex dump format

Arguments:

    pECB - points to the extension control block
    lpbyByf - bytes to dump
    dwLength - specifies the number of bytes to dump

--*/
{
    DWORD dwSize;
    char szLine[80];
    char szHex[3];
    DWORD i;
    DWORD dwPos = 0;

    HtmlBeginPreformattedText( pECB );

    while (dwLength) {

        //
        // Take min of 16 or dwLength
        //

        dwSize = min(16, dwLength );

        //
        // Build text line
        //

        wsprintf(szLine, "  %04X ", dwPos );

        for (i = 0; i < dwSize; i++) {
            wsprintf(szHex, "%02X", lpbyBuf[i] );
            lstrcat(szLine, szHex );
            lstrcat(szLine, " " );
        }

        //
        // Add spaces for short lines
        //

        while (i < 16) {
            lstrcat(szLine, "   " );
            i++;
        }

        //
        // Add ASCII chars
        //

        for (i = 0; i < dwSize; i++) {
            if (isprint(lpbyBuf[i])) {
                wsprintf(szHex, "%c", lpbyBuf[i] );
                lstrcat(szLine, szHex );
            } else {
                lstrcat(szLine, "." );
            }
        }

        //
        // Write data to web page
        //

        HtmlWriteTextLine( pECB, szLine );

        //
        // Advance positions
        //

        dwLength -= dwSize;
        dwPos += dwSize;
        lpbyBuf += dwSize;
    }

    HtmlEndPreformattedText( pECB );
}

void
DumpVariable(
    IN EXTENSION_CONTROL_BLOCK * pECB,
    IN LPCTSTR szName
)
/*++

Purpose:

    Dump a server variable

Arguments:

    pECB - points to the extension control block
    lpbyByf - points to ASCIIZ name of the server variable to dump

--*/
{
    DWORD dwBufferSize;
    char szBuffer[4096];
    BOOL bReturn;

    dwBufferSize = sizeof szBuffer;
    bReturn = pECB->GetServerVariable( pECB->ConnID,
        (LPSTR) szName,
        szBuffer,
        &dwBufferSize );

    if ( !bReturn || !szBuffer[0] )
        return;

    HtmlWriteText( pECB, szName );
    HtmlWriteText( pECB, "=" );
    HtmlWriteText( pECB, szBuffer );
    HtmlLineBreak( pECB );
}

BOOL
SendHttpHeaders(
    EXTENSION_CONTROL_BLOCK *pECB,
    LPCSTR pszStatus,
    LPCSTR pszHeaders
)
/*++

Purpose:
    Send specified HTTP status string and any additional header strings
    using new ServerSupportFunction() request HSE_SEND_HEADER_EX_INFO

Arguments:

    pECB - pointer to the extension control block
    pszStatus - HTTP status string (e.g. "200 OK")
    pszHeaders - any additional headers, separated by CRLFs and
                 terminated by empty line

Returns:

    TRUE if headers were successfully sent
    FALSE otherwise

--*/
{
    HSE_SEND_HEADER_EX_INFO header_ex_info;
    BOOL success;

    header_ex_info.pszStatus = pszStatus;
    header_ex_info.pszHeader = pszHeaders;
    header_ex_info.cchStatus = strlen( pszStatus );
    header_ex_info.cchHeader = strlen( pszHeaders );
    header_ex_info.fKeepConn = FALSE;

    success = pECB->ServerSupportFunction(
                  pECB->ConnID,
                  HSE_REQ_SEND_RESPONSE_HEADER_EX,
                  &header_ex_info,
                  NULL,
                  NULL
                  );

    return success;
}

void
SendVariables(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:

    Send all server variables (they came in the HTTP header)

Arguments:

    pECB - pointer to the extension control block

--*/
{
    char *pChar, *pOpts, *pEnd;
    DWORD dwBufferSize;
    char szBuffer[4096];
    BOOL bReturn;

    //
    // Dump the standard variables
    //

    DumpVariable( pECB, "AUTH_TYPE" );
    DumpVariable( pECB, "CONTENT_LENGTH" );
    DumpVariable( pECB, "CONTENT_TYPE" );
    DumpVariable( pECB, "GATEWAY_INTERFACE" );
    DumpVariable( pECB, "PATH_INFO" );
    DumpVariable( pECB, "PATH_TRANSLATED" );
    DumpVariable( pECB, "QUERY_STRING" );
    DumpVariable( pECB, "REMOTE_ADDR" );
    DumpVariable( pECB, "REMOTE_HOST" );
    DumpVariable( pECB, "REMOTE_USER" );
    DumpVariable( pECB, "REQUEST_METHOD" );
    DumpVariable( pECB, "SCRIPT_NAME" );
    DumpVariable( pECB, "SERVER_NAME" );
    DumpVariable( pECB, "SERVER_PORT" );
    DumpVariable( pECB, "SERVER_PROTOCOL" );
    DumpVariable( pECB, "SERVER_SOFTWARE" );
    DumpVariable( pECB, "AUTH_PASS" );

    //
    // Dump any others (in ALL_HTTP)
    //

    dwBufferSize = sizeof szBuffer;
    bReturn = pECB->GetServerVariable( pECB->ConnID,
        "ALL_HTTP",
        szBuffer,
        &dwBufferSize );

    if ( bReturn ) {
        //
        // Find lines, split key/data pair and write them as output
        //

        pChar = szBuffer;
        while ( *pChar ) {
            if ( *pChar == '\r' || *pChar == '\n' ) {
                pChar++;
                continue;
            }
            pOpts = strchr( pChar, ':' );   // look for separator

            if ( !pOpts )
                break;
            if ( !*pOpts )
                break;

            pEnd = pOpts;
            while ( *pEnd && *pEnd != '\r' && *pEnd != '\n' )
                pEnd++;

            *pOpts = 0;         // split strings

            *pEnd = 0;

            //
            // pChar points to variable name, pOpts + 1 points to variable
            // val
            //

            HtmlWriteText( pECB, pChar );
            HtmlWriteText( pECB, "=" );
            HtmlWriteText( pECB, pOpts + 1 );
            HtmlLineBreak( pECB );

            pChar = pEnd + 1;
        }
    }
    HtmlEndParagraph( pECB );
    HtmlHorizontalRule( pECB );
}

void
WhoAmI(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:

    Get the user SID, lookup the account name and display it

Arguments:

    pECB - pointer to the extension control block

--*/
{
    HANDLE hToken;
    PTOKEN_USER pTokenUser;
    BYTE byBuf[1024];
    DWORD dwLen;
    char szName[256], szDomain[256];
    DWORD dwNameLen, dwDomainLen;
    SID_NAME_USE eUse;

    if ( !OpenThreadToken(
            GetCurrentThread( ), TOKEN_QUERY, TRUE, &hToken ) ) {

        DWORD dwError = GetLastError( );

        HtmlBold( pECB, "OpenThreadToken failed. " );
        HtmlPrintf( pECB, "Error code=%u", dwError );
        HtmlEndParagraph( pECB );
        return;
    }

    pTokenUser = (PTOKEN_USER) byBuf;
    if ( !GetTokenInformation(
            hToken, TokenUser, pTokenUser, sizeof byBuf, &dwLen ) ) {

        DWORD dwError = GetLastError( );

        CloseHandle( hToken );

        HtmlBold( pECB, "GetTokenInformation failed. " );
        HtmlPrintf( pECB, "Error code=%u dwLen=%u", dwError, dwLen );
        HtmlEndParagraph( pECB );
        return;
    }

    dwNameLen = sizeof szName;
    dwDomainLen = sizeof szDomain;
    if ( !LookupAccountSid( NULL, pTokenUser->User.Sid,
            szName, &dwNameLen,
            szDomain, &dwDomainLen, &eUse )) {

        DWORD dwError = GetLastError( );

        CloseHandle( hToken );

        HtmlBold( pECB, "LookupAccountSid failed. " );
        HtmlPrintf( pECB, "Error code=%u dwNameLen=%u dwDomainLen=%u",
            dwError, dwNameLen, dwDomainLen );
        HtmlEndParagraph( pECB );
        return;
    }

    HtmlBold( pECB, "Domain: " );
    HtmlWriteText( pECB, szDomain );
    HtmlLineBreak( pECB );
    HtmlBold( pECB, "User: " );
    HtmlWriteText( pECB, szName );
    HtmlEndParagraph( pECB );

    CloseHandle( hToken );
}


README.TXT

Asynchronous IO Support

ISAPI 1.0 support synchronous IO operations via callback
methods ReadClient() and WriteClient(). The ability to
support asynchronous operations is important; it frees
up a server pool thread from being blocked to complete
the IO operation. In addition, IIS server engine already
has built in support to manage asynchronous IO operations
using the completion ports and server thread pool.

ISAPI 2.0 supports asynchronous write operation using
the existing callback function WriteClient() with a special
flag indicating that the operation has to be performed
asynchronously. In addition, ISAPI 2.0 also provides a
mechanism to request the server to transmit a file using
the TransmitFile() .

ISAPI 3.0 supports asynchronous read operation from the
client using ServerSupportFunction() with HSE_REQ_ASYNC_READ_CLIENT
and a special flag indicating that the operation has to be
performed asynchronously. In addition, the ISAPI application
should submit a callback function and context by calling
ServerSupportFunction() with HSE_REQ_IO_COMPLETION.

To use the areadcli.dll extension created by this directory,
perform the following steps:

1) Create an IIS virtual root with execute permissions
2) Copy the isapi dlls into the directory
3) Point a browser at the virtual path

ie:

http://localhost/sdk/areadcli.dll

Disclaimer:

This sample is provided for the purposes of demonstrating an
ISAPI extension.  It has not been tested for use in a production
environment and no support will be provided for use in a
production environment.


AREADCLI.C

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    AReadCli.c

Abstract:

    This module demonstrates using asynchronous ReadClient call
    to read data sent from client. On return, this sends the
    followings back to client:

    1) the number of bytes intended to send
    2) the number of bytes actually read.
    3) data content

Author:

    Stanley Tam (stanleyt)   4-June 1997

Revision History:

--*/
#include <windows.h>
#include <stdio.h>
#include <httpext.h>

#define MAX_BUF_SIZE        (49152)                 // Max number of bytes for each read - 48K
#define MEM_ALLOC_THRESHOLD (1024 * 1024)           // Default heap size is 1M

typedef struct _IO_WORK_ITEM {

    PBYTE                       pbDATAFromClient;   // Grand read data holder
    DWORD                       cbReadSoFar;        // Number of bytes read so far,
                                                    // and is used as index for pbDATAFromClient
    EXTENSION_CONTROL_BLOCK *   pecb;

}  IO_WORK_ITEM, * PIOWI;

//
//  Sample Form for doing a POST method
//

static CHAR             g_szPostForm[] =
                        "<h2>Asychronous ReadClient Sample Post Form</h2><p>\r\n\r\n"
                        "This demonstrates a post request being sent by this form to the sample ISAPI - AReadCli.dll<p>\r\n\r\n"
                        "AReadCli.dll reads data posted by this form and send it back to the browser.<p>\r\n"
                        "<h3>Post Form</h3>\r\n"
                        "Please enter data below:<br>\r\n"
                        "<form method=POST action=\"areadcli.dll\">\r\n"
                        "<textarea name=\"Data\" cols=48 rows=4></textarea>\r\n\r\n"
                        "<input type=submit> <input type=reset>\r\n"
                        "</form>";

//
//  Report read data
//

static CHAR             g_szReport[] =
                        "Bytes count including \"Data=\"  \r\n"
                        "ECB Total Bytes:    %d.\r\n"
                        "Actual Read Bytes:  %d.\r\n";

DWORD
DoInit(IN OUT PIOWI piowi);

VOID
DoCleanUp(IN PIOWI piowi);

DWORD
DoAsyncReadClient(IN PIOWI piowi);

VOID WINAPI
AsyncReadClientIoCompletion(IN LPEXTENSION_CONTROL_BLOCK pecb,
                            IN PVOID pContext,
                            IN DWORD cbIO,
                            IN DWORD dwError);

DWORD
SendMSGToClient(IN LPEXTENSION_CONTROL_BLOCK pecb,
                IN LPCSTR pszErrorMsg);

LPVOID
AR_Allocate(IN LPEXTENSION_CONTROL_BLOCK pecb, IN DWORD dwSize);

BOOL
AR_Free(IN LPEXTENSION_CONTROL_BLOCK pecb, IN LPVOID pvData);

DllMain(
     IN HINSTANCE hinstDll,
     IN DWORD     fdwReason,
     IN LPVOID    lpvContext OPTIONAL)
/*++

 Routine Description:

   This function DllLibMain() is the main initialization function for
    this DLL. It initializes local variables and prepares it to be invoked
    subsequently.

 Arguments:

   hinstDll          Instance Handle of the DLL
   fdwReason         Reason why NT called this DLL
   lpvReserved       Reserved parameter for future use.

 fReturn Value:

    fReturns TRUE if successful; otherwise FALSE is fReturned.

--*/
{
  BOOL    fReturn = TRUE;

  switch ( fdwReason) {

    case DLL_PROCESS_ATTACH:
      {
          OutputDebugString( "Initializing the global data for areadcli.dll\n");

          //
          // Prevent the system from calling DllMain
          // when threads are created or destroyed.
          //

          DisableThreadLibraryCalls( hinstDll);

          //
          // Initialize various data and modules.
          //

          break;

      } // case DLL_PROCESS_ATTACH

    case DLL_PROCESS_DETACH:
      {

          if ( lpvContext != NULL) { }

          break;
      } // case DLL_PROCESS_DETACH

    default:
      break;
  }   // switch

  return (fReturn);
}  // DllLibMain()

BOOL WINAPI
GetExtensionVersion(HSE_VERSION_INFO * Version)
/*++

Routine Description:

    Sets the ISAPI extension version information.

Arguments:

    Version     pointer to HSE_VERSION_INFO structure

Return Value:

    TRUE

--*/
{
    Version->dwExtensionVersion =
    MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);

    strcpy(Version->lpszExtensionDesc, "Asynchronous Read Client Sample ISAPI DLL");

    return TRUE;
}

DWORD WINAPI
HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pecb)
/*++

Routine Description:

    This is the main routine for any ISAPI application. Inside DoASyncReadClient,
    proper action will be performed to read data from client asynchronously. Any data
    read from client will be sent back to the client by using synchronous WriteClient.

Arguments:

    pecb        pointer to ECB containing parameters related to the request.

Return Value:

    HSE_STATUS_SUCCESS  or
    HSE_STATUS_PENDING  or
    HSE_STATUS_ERROR

--*/
{
    PIOWI   piowi;
    DWORD   hseStatus = HSE_STATUS_SUCCESS;

    //
    // The string length of textarea name "Data=" is 5.
    // Available bytes <= 5 indicates that no user-
    // entered data has been sent, and the post form
    // is shown.
    //

    if ( pecb->cbAvailable <= 5) {

        hseStatus = SendMSGToClient(pecb, g_szPostForm);

    } else {

        piowi  = (PIOWI ) LocalAlloc( LMEM_FIXED, sizeof( IO_WORK_ITEM));

        if ( NULL == piowi) {

            SetLastError( ERROR_NOT_ENOUGH_MEMORY);
            return (HSE_STATUS_ERROR);
        }

        piowi->pecb = pecb;

        //
        // Init Grand data holder, assign the first chunk(read-ahead chunk)
        // and update the index (cbRreadSoFar) of the grand data holder.
        //

        hseStatus = DoInit( piowi);

        if ( HSE_STATUS_ERROR != hseStatus) {

            //
            // Now we are ready to do asynchronous readclient here
            //

            hseStatus = DoAsyncReadClient( piowi);

            if (hseStatus != HSE_STATUS_PENDING) {

                //
                //  When IO finishes, tell IIS we will end the
                //  session. Also clean up other resources here
                //

                DoCleanUp( piowi);

            }

        }

    }

    return (hseStatus);
}

BOOL WINAPI
TerminateExtension(DWORD dwFlags)
/*++

Routine Description:

This function is called when the WWW service is shutdown

Arguments:

dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD

Return Value:

    TRUE

--*/
{
    return TRUE;
}

DWORD
SendMSGToClient(IN LPEXTENSION_CONTROL_BLOCK  pecb, IN LPCSTR pszMsg)
/*++

Routine Description:

    Prepare header, SendHeaderExInfo struct and write whatever
    message is intended to send to the client.

Arguments:

    pecb        - pointer to ECB containing parameters related to the request.
    pszMsg      - pointer to the body of the message that is sent to the content.

Return Value:

    HSE_STATUS_SUCCESS  or HSE_STATUS_ERROR

--*/
{
    HSE_SEND_HEADER_EX_INFOSHEI;

    BOOL    fReturn;
    DWORD   cbText;
    DWORD   hseStatus = HSE_STATUS_SUCCESS;
    CHAR    *pszText = NULL;

    CHAR    szStatus[] = "200 OK";
    CHAR    szHeaderBase[] = "Content-type: text/html\r\n\r\n";

    //
    //  Populate SendHeaderExInfo struct
    //
    //  NOTE we must send Content-Length header with correct
    //  byte count in order for keep-alive to work.
    //
    //

    SHEI.pszStatus = szStatus;
    SHEI.pszHeader = szHeaderBase;
    SHEI.cchStatus = lstrlen(szStatus);
    SHEI.cchHeader = lstrlen(szHeaderBase);
    SHEI.fKeepConn = FALSE;

    //
    //  Build page
    //

    cbText = strlen("<head><title>Simple Async Read Client Sample</title></head>\n<body></body>\n")
             + strlen(pszMsg)
             + 1;

    pszText = (PCHAR) AR_Allocate(pecb, cbText);

    if ( NULL == pszText) {

        SetLastError( ERROR_NOT_ENOUGH_MEMORY);
        return (HSE_STATUS_ERROR);
    }

    strcpy(pszText, "<head><title>Simple Async Read Client Sample</title></head>\n");
    strcat(pszText, "<body>");
    strcat(pszText, pszMsg);
    strcat(pszText, "</body>\n");
    cbText = (DWORD)strlen(pszText);

    //
    // Send header and body text to client
    //

    fReturn =
    pecb->ServerSupportFunction( pecb->ConnID,
                                 HSE_REQ_SEND_RESPONSE_HEADER_EX,
                                 &SHEI,
                                 NULL,
                                 NULL)

                                 &&

    pecb->WriteClient( pecb->ConnID,
                       pszText,
                       &cbText,
                       0 );

    if ( !fReturn)  {
        hseStatus = HSE_STATUS_ERROR;
    }

    AR_Free( pecb, pszText);

    return ( hseStatus);
}

DWORD
DoAsyncReadClient(IN PIOWI piowi)
/*++

Routine Description:

    The caller of the asynchrnous read client.

Arguments:

    piowi       pointer to the work item

Return Value:

    HSE_STATUS_SUCCESS  or
    HSE_STATUS_PENDING  or
    HSE_STATUS_ERROR

--*/
{
    BOOL    fReturn;
    BYTE    *pbData = NULL;
    CHAR    szTmp[MAX_BUF_SIZE];
    DWORD   dwFlags;
    DWORD   cbTotalToRead = MAX_BUF_SIZE;
    DWORD   hseStatus =  HSE_STATUS_PENDING;

    //
    // Check if cbTotalBytes == cbAvailable
    // if so lpbData contains all the data sent by
    // the client, and complete the session.
    //

    if (piowi->pecb->cbTotalBytes == piowi->pecb->cbAvailable) {

        //
        // Construct the report and write it to client
        //

        pbData = (PBYTE) AR_Allocate(piowi->pecb, piowi->pecb->cbAvailable + MAX_BUF_SIZE);

        if ( NULL == pbData) {

            SetLastError( ERROR_NOT_ENOUGH_MEMORY);
            return (HSE_STATUS_ERROR);
        }

        wsprintf ( pbData,
                   g_szReport,
                   piowi->pecb->cbTotalBytes,
                   piowi->pecb->cbAvailable );

        strcat( pbData, piowi->pecb->lpbData);

        hseStatus = SendMSGToClient( piowi->pecb, pbData);

        AR_Free( piowi->pecb, pbData);
        DoCleanUp( piowi);

        return ( hseStatus); // HSE_STATUS_SUCCESS or HSE_STATUS_ERROR;
    }

    //
    //  More to read...
    //

    //
    //  Set a call back function and context that will
    //  be used for handling asynchrnous IO operations.
    //  This only needs to set up once.
    //

    fReturn =
    piowi->pecb->ServerSupportFunction(
                    piowi->pecb->ConnID,
                    HSE_REQ_IO_COMPLETION,
                    AsyncReadClientIoCompletion,
                    0,
                    (LPDWORD)piowi );

    if ( !fReturn) {

        wsprintf ( szTmp, "Problem occurred at ServerSupportFunction() sending HSE_REQ_IO_COMPLETION request.");
        SendMSGToClient( piowi->pecb, szTmp);

        return ( HSE_STATUS_ERROR);
    }

    //
    //  Fire off the call to perform an asynchronus read from the client.
    //

    //
    // We need to first check if the size of the remaining chunk
    // is less than MAX_BUF_SIZE, if so just read what is available,
    // otherwise read MAX_BUF_SIZE bytes of data.
    //

    cbTotalToRead = piowi->pecb->cbTotalBytes - piowi->cbReadSoFar;
    if ( cbTotalToRead > MAX_BUF_SIZE )  {

        cbTotalToRead = MAX_BUF_SIZE;
    }

    dwFlags = HSE_IO_ASYNC;
    fReturn =
    piowi->pecb->ServerSupportFunction(
                        piowi->pecb->ConnID
                      , HSE_REQ_ASYNC_READ_CLIENT
                      , piowi->pbDATAFromClient + // append the new chunk to buffer, cbReadSoFar indexes
                        piowi->cbReadSoFar        // the byte right after the last written byte in the buffer
                      , &cbTotalToRead
                      , &dwFlags);

    if (!fReturn) {

        wsprintf ( szTmp, "Problem occurred at ServerSupportFunction() sending HSE_REQ_ASYNC_READ_CLIENT request.");
        SendMSGToClient( piowi->pecb, szTmp);

        hseStatus = HSE_STATUS_ERROR;
    }

    return ( hseStatus); // HSE_STATUS_PENDING or HSE_STATUS_ERROR;

}

VOID WINAPI
AsyncReadClientIoCompletion(
            IN LPEXTENSION_CONTROL_BLOCK pECB,
            IN PVOID pContext,
            IN DWORD cbIO,
            IN DWORD dwError)
/*++

Routine Description:

    This is the callback function for handling completions of asynchronous ReadClient.
    This function resubmits additional IO to read the next chunk of data from the
    client. If there is no more data to read or problem during operation, this function
    will inform IIS that it is about to end the request session.

Arguments:

    pecb        - extension control block
    pContext    - this is a IO_WORK_ITEM
    cbIO        - bytes just read
    dwError     - Win32 error status code

fReturn Value:

    None

--*/
{
    BOOL    fReturn;
    BYTE    *pbData = NULL;
    CHAR    szTmp[MAX_BUF_SIZE];
    DWORD   dwFlags;
    DWORD   cbTotalToRead;

    PIOWI   piowi = ( PIOWI) pContext;
    EXTENSION_CONTROL_BLOCK   * pecb = piowi->pecb;

    if (ERROR_SUCCESS == dwError) {

        //
        // Read successfully, so update current
        // total bytes read (aka index of grand data holder)
        //

        piowi->cbReadSoFar += cbIO;

        //
        // If they are equal, we finish reading all bytes from client
        //

        if ( piowi->cbReadSoFar == pecb->cbTotalBytes ) {

            //
            // Construct the report and write it to client
            //

            pbData = (PBYTE) AR_Allocate( pecb, piowi->cbReadSoFar + MAX_BUF_SIZE);

            if ( NULL == pbData) {

                SetLastError( ERROR_NOT_ENOUGH_MEMORY);
                wsprintf ( szTmp, "Failed to allocate memory inside AsyncReadClientIoCompletion().");
                SendMSGToClient( pecb, szTmp);

                DoCleanUp( piowi);

                return;
            }

            wsprintf ( pbData, g_szReport, pecb->cbTotalBytes, piowi->cbReadSoFar );
            piowi->pbDATAFromClient[piowi->cbReadSoFar] = 0;
            strcat( pbData, piowi->pbDATAFromClient);
            SendMSGToClient( pecb, pbData);

            AR_Free( piowi->pecb, pbData);
            DoCleanUp( piowi);

            return;

        } else {

            //
            // Still have more data to read...
            //
            // We need to first check if the size of the remaining chunk
            // is less than MAX_BUF_SIZE, if so just read what is available,
            // otherwise read MAX_BUF_SIZE bytes of data.
            //

            cbTotalToRead = pecb->cbTotalBytes - piowi->cbReadSoFar;
            if ( cbTotalToRead > MAX_BUF_SIZE )  {

                cbTotalToRead = MAX_BUF_SIZE;
            }

            //
            // Fire off another call to perform an asynchronus read from the client.
            //

            dwFlags = HSE_IO_ASYNC;
            fReturn =
            pecb->ServerSupportFunction(
                          pecb->ConnID
                        , HSE_REQ_ASYNC_READ_CLIENT
                        , piowi->pbDATAFromClient + // append the new chunk to buffer, cbReadSoFar indexes
                          piowi->cbReadSoFar        // the byte right after the last written byte in the buffer
                        , &cbTotalToRead
                        , &dwFlags);

            if ( !fReturn) {
                wsprintf ( szTmp, "Problem occurred at ServerSupportFunction() sending HSE_REQ_ASYNC_READ_CLIENT request.");
                SendMSGToClient( pecb, szTmp);

                DoCleanUp( piowi);

                return;
            }

        }

    } else {

        //
        // Error on read
        //

        SetLastError(dwError);

        DoCleanUp( piowi);
    }

    return;

} // AsyncReadClientIoCompletion

DWORD
DoInit(IN OUT PIOWI piowi)
/*++

Routine Description:

    Initialize the  Grand data holder, assign the first chunk(read-ahead chunk)
    and update the index (cbRreadSoFar) of the grand data holder.

Arguments:

    piowi       pointer to the work item

fReturn Value:

    HSE_STATUS_SUCCESS or HSE_STATUS_ERROR

--*/
{

    piowi->pbDATAFromClient =
    (PBYTE) AR_Allocate( piowi->pecb, piowi->pecb->cbTotalBytes + MAX_BUF_SIZE);

    if ( NULL == piowi->pbDATAFromClient) {

        SetLastError( ERROR_NOT_ENOUGH_MEMORY);
        return (HSE_STATUS_ERROR);
    }

    //
    // The first chunk (read-ahead chunk) has arrived.
    //

    strcpy ( piowi->pbDATAFromClient, piowi->pecb->lpbData);
    piowi->cbReadSoFar = piowi->pecb->cbAvailable;

    return (HSE_STATUS_SUCCESS);
}

VOID
DoCleanUp(IN PIOWI piowi)
/*++

Routine Description:

    End the session with IIS and clean up other previous allocated resources.

Arguments:

    piowi       pointer to the work item

fReturn Value:

    None

--*/
{

    if ( piowi->pbDATAFromClient != NULL) {

        AR_Free( piowi->pecb, piowi->pbDATAFromClient);

    }

    piowi->pecb->ServerSupportFunction( piowi->pecb->ConnID,
                                        HSE_REQ_DONE_WITH_SESSION,
                                        NULL,
                                        NULL,
                                        NULL);

    LocalFree( piowi);

    return;

}

LPVOID
AR_Allocate(IN LPEXTENSION_CONTROL_BLOCK pecb, IN DWORD dwSize)
/*++

Routine Description:

    Memory allocation routine. Two different Win32 API's to allocate
    bytes in memory, which is based on the number of bytes coming from
    the client. If the size is greater than 1 M bytes VirtualAllocate is
    used, otherwise HeapAllocate is used.

Arguments:

    pecb        - pointer to ECB containing parameters related to the request.
    dwSize      - number of bytes to allocate

fReturn Value:

    Pointer to void

--*/
{
    LPVOID pvData = NULL;

    if ( pecb->cbTotalBytes > MEM_ALLOC_THRESHOLD) {

        pvData =
        VirtualAlloc( NULL,
                      dwSize,
                      MEM_RESERVE | MEM_COMMIT,
                      PAGE_READWRITE);

    } else {

        pvData =
        HeapAlloc( GetProcessHeap(),
                   HEAP_ZERO_MEMORY,
                   dwSize);

    }

    return ( pvData);

}

BOOL
AR_Free( IN LPEXTENSION_CONTROL_BLOCK pecb, IN LPVOID pvData)
/*++

Routine Description:

    Freeing memory routine, a complementary routine to AR_Allocate.
    Two different Win32 API's will be used to free up bytes in memory,
    which is based on the number of bytes coming from the client. If
    the size is greater than 1 M bytes VirtualFree is used. Otherwise,
    HeapFree is used.

Arguments:

    pecb        - pointer to ECB containing parameters related to the request.
    pvData      - pointer to the data to be freed.

fReturn Value:

    TRUE or FALSE

--*/
{
    BOOL fReturn = FALSE;

    if ( pecb->cbTotalBytes > MEM_ALLOC_THRESHOLD) {

        fReturn = VirtualFree( pvData, 0, MEM_RELEASE);

    } else {

        fReturn = HeapFree( GetProcessHeap(), 0, pvData);
    }

    return (fReturn);
}


README.TXT

FTRANS.dll - File Transfer using Asynchronous TransmitFile() inside ISAPI DLL

Overview
--------
FTRANS.DLL is a sample ISAPI Extension DLL to demonstrate Asynchronous
TransmitFile functionality in the ISAPI interface. This dll illustrates
how one can use the ServerSupportFunction( HSE_REQ_TRANSMIT_FILE) option
to transmit an entire file from inside the ISAPI DLL.

FTRANS.dll sends the file specified on the query string. It also converts
a virtual file-path (given in the URL name-space) into a physical file
name using the ServerSupportFunction( HSE_REQ_MAP_URL_TO_PATH) function.

Installation:
-------------
Build FTRANS.dll and copy the .dll into the virtual directory maked with
"execute" permissions.

To run:
-------

Reference FTRANS.DLL file as a URL from the browser,
specifying a file-name on the query string.

For example, the following URL entered on the address line of your browser
will transfer file SAMPLE.GIF, located in WWW root directory:

http://localhost/scripts/ftrans.dll?/sample.gif

Disclaimer:
-----------
This sample is provided for the purpose of demonstrating an ISAPI
extension. It has not been tested for use in a production environment
and no support will be provided for use in a production environment.


OPENF.C

/*++

   Copyright    (c)    1995-1997    Microsoft Corporation

   Module  Name :

      openf.c

   Abstract:

      This module implements a simple open file handle cache

   Project:

       ISAPI Extensions Sample DLL

   Functions Exported:

   Note:
      THIS IS NOT ROBUST for REAL WORLD.
      I wrote this for testing the ISAPI Async IO processing.

--*/

/************************************************************
 *     Include Headers
 ************************************************************/

# include "openf.h"

/************************************************************
 *     Type definitions and Globals
 ************************************************************/

//
// internal data structure for maintaining the list of open file handles.
//

typedef struct _OPEN_FILE {

    HANDLE  hFile;
    struct _OPEN_FILE * pNext;
    LONG    nHits;
    LONG    nRefs;
    CHAR    rgchFile[MAX_PATH+1];

} OPEN_FILE, * LPOPEN_FILE;

LPOPEN_FILE g_pOpenFiles = NULL;
CRITICAL_SECTION g_csOpenFiles;
BOOL g_fIsNt = TRUE;

//
// Set up global variables containing the flags for CreateFile
//  The flags can be masked for Windows 95 system
//

DWORD  g_dwCreateFileShareMode =
         (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);

DWORD  g_dwCreateFileFlags =
         (FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED);

/************************************************************
 *    Functions
 ************************************************************/

DWORD
InitFileHandleCache(VOID)
/*++

  This function initializes the file handle cache.
  It should be called at the initialization time.

  Arguments:
    None

  Returns:
    Win32 error code. NO_ERROR indicates that the call succeeded.
--*/
{
    OSVERSIONINFO  osInfo;

    InitializeCriticalSection( &g_csOpenFiles);

    //
    // obtain the platform type to find out how to open the file
    //

    osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

    if ( GetVersionEx( &osInfo ) ) {
        g_fIsNt = (osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
    }

    if ( !g_fIsNt) {

        //
        // Reset the flags appropriately so that Windows 95 will be happy
        //

        g_dwCreateFileShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE);
        g_dwCreateFileFlags     = (FILE_FLAG_SEQUENTIAL_SCAN);
    }

    return (NO_ERROR);

} // InitFileHandleCache()

DWORD
CleanupFileHandleCache(VOID)
{
    LPOPEN_FILE  pFileScan;

    while ( g_pOpenFiles != NULL) {

        pFileScan = g_pOpenFiles;
        g_pOpenFiles = g_pOpenFiles->pNext;

        if ( pFileScan->hFile != INVALID_HANDLE_VALUE) {

            CloseHandle( pFileScan->hFile);
        }

        LocalFree( pFileScan);
    }

    DeleteCriticalSection( &g_csOpenFiles);

    return (NO_ERROR);
} // CleanupFileHandleCache()

HANDLE
FcOpenFile(IN EXTENSION_CONTROL_BLOCK * pecb, IN LPCSTR pszFile)
/*++

 FcOpenFile()

 Description:
   This function opens the file specified in the 'pszFile'.
   If the file name starts with a '/' we use the ECB to map
   the given path into a physical file path.

 Arguments:
  pecb - pointer to the ECB block
  pszFile - pointer to file name

 Returns:
   valid File handle on success
--*/
{
    LPOPEN_FILE  pFileScan;
    HANDLE hFile = INVALID_HANDLE_VALUE;

    EnterCriticalSection( &g_csOpenFiles);

    for ( pFileScan =  g_pOpenFiles;
         NULL != pFileScan;
         pFileScan = pFileScan->pNext) {

        if ( 0 == lstrcmpi( pFileScan->rgchFile, pszFile)) {

            //
            //  there is a file match.
            //

            break;
        }

    } // for

    if ( NULL == pFileScan) {

        //
        // File was not found. Create a new file handle
        //

        CHAR   rgchFileName[ MAX_PATH]; // local copy
        LPCSTR pszInputPath = pszFile;

        lstrcpyn( rgchFileName, pszFile, MAX_PATH);
        if ( *pszFile == '/') {

            DWORD cbSize = sizeof(rgchFileName);
            BOOL  fRet;

            // reset the file pointer, so subsequent use will fail
            pszFile = NULL;

            //
            // Using the ECB map the Virtual path to the Physical path
            //

            fRet = pecb->ServerSupportFunction( pecb->ConnID,
                                                HSE_REQ_MAP_URL_TO_PATH,
                                                rgchFileName,
                                                &cbSize, NULL);

            if (fRet) {
                // we got the mapping. Use it.
                pszFile = rgchFileName;
            }
        }

        if ( NULL != pszFile) {
            pFileScan = LocalAlloc( LPTR, sizeof( *pFileScan));

            if ( NULL != pFileScan) {

                SECURITY_ATTRIBUTES sa;

                sa.nLength              = sizeof(sa);
                sa.lpSecurityDescriptor = NULL;
                sa.bInheritHandle       = FALSE;

                pFileScan->hFile =
                    CreateFile( pszFile,
                                GENERIC_READ,
                                g_dwCreateFileShareMode,
                                &sa,
                                OPEN_EXISTING,
                                g_dwCreateFileFlags,
                                NULL );

                if ( INVALID_HANDLE_VALUE == pFileScan->hFile) {

                    LocalFree( pFileScan);
                    pFileScan = NULL;
                } else {

                    // insert this into the list at the top
                    lstrcpyn( pFileScan->rgchFile, pszInputPath, MAX_PATH);
                    pFileScan->pNext = g_pOpenFiles;
                    g_pOpenFiles = pFileScan;
                    pFileScan->nRefs = 1;
                    pFileScan->nHits = 0;
                }
            }
        }
    }

    if ( NULL != pFileScan) {

        hFile = pFileScan->hFile;
        pFileScan->nHits++;
        pFileScan->nRefs++;
    }

    LeaveCriticalSection( &g_csOpenFiles);

    return (hFile);

} // FcOpenFile()

DWORD
FcCloseFile(IN HANDLE hFile)
{
    LPOPEN_FILE  pFileScan;
    DWORD dwError = NO_ERROR;

    EnterCriticalSection( &g_csOpenFiles);

    //
    // Look for the handle and decrement the ref count.
    //
    for ( pFileScan =  g_pOpenFiles;
         NULL != pFileScan;
         pFileScan = pFileScan->pNext) {

        if ( hFile == pFileScan->hFile) {

            //
            //  there is a file match.
            //

            pFileScan->nRefs--;

            //
            // BUGBUG: There is no freeing of the file when Ref hits '0' :(
            //

            break;
        }

    } // for

    if ( NULL == pFileScan) {
        //
        // file handle not found
        //
        dwError = ( ERROR_INVALID_HANDLE);
    }

    LeaveCriticalSection( &g_csOpenFiles);

    return ( dwError);

} // FcCloseFile()

BOOL
FcReadFromFile(
               IN  HANDLE hFile,
               OUT CHAR * pchBuffer,
               IN  DWORD  dwBufferSize,
               OUT LPDWORD  pcbRead,
               IN OUT LPOVERLAPPED  pov
               )
/*++
  Description:
    Reads contents of file [hFile] from the specified offset in the overlapped
    structure. The contents are read into the buffer supplied.

  Arguments:
    hFile        - handle for the File from which to read data
    pchBuffer    - pointer to the buffer into which the data is to be read
    dwBufferSize - DWORD containing the max size of the buffer supplied
    pcbRead      - number of bytes read from the file
    pov          - pointer to an overlapped structure that contains the
                     offset from where to read the contents. The
                     overlapped structure also is used for Overlapped
                     IO in NT.

  Notes:
   This function automatically handles both Windows 95 and NT

  Returns:
    TRUE on success and FALSE if there is a failure.
    Use GetLastError() to get the last error code on failure.
--*/
{
    BOOL fRet = TRUE;

    *pcbRead = 0;

    if ( !g_fIsNt ) {
        //
        // Windows95 does not support Overlapped IO.
        //  So we shall thunk it out and use Synchronous IO
        //

        fRet = (
                SetFilePointer( hFile,
                                pov->Offset,
                                NULL,
                                FILE_BEGIN) &&
                ReadFile( hFile,
                          pchBuffer,
                          dwBufferSize,
                          pcbRead,
                          NULL
                          )
                );
    } else {

            ResetEvent( pov->hEvent);

            fRet = TRUE;

            // read data from file
            if (!ReadFile(hFile,
                          pchBuffer,
                          dwBufferSize,
                          pcbRead,
                          pov
                          )) {

                DWORD err = GetLastError();

                if ( (err != ERROR_IO_PENDING) ||
                     !GetOverlappedResult( hFile, pov,
                                           pcbRead, TRUE)) {

                    fRet = FALSE;
                }
            }
    }

    if ( fRet) {
        pov->Offset += *pcbRead;
    }

    return ( fRet);
} // FcReadFromFile()

/************************ End of File ***********************/


OPENF.H

/*++

   Copyright    (c)    1995-1997    Microsoft Corporation

   Module  Name :

     openf.h

   Abstract:

     This module defines functions for opening and closing files
      and provides transparent caching for file handles

   Project:

       Internet Server API Extnensions Samples DLL

   Revision History:

--*/

# ifndef _OPENF_HXX_
# define _OPENF_HXX_

/************************************************************
 *     Include Headers
 ************************************************************/

# include <windows.h>
# include <httpext.h>

/************************************************************
 *   Type Definitions
 ************************************************************/

//
//  Doubly linked list structure.  Can be used as either a list head, or
//  as link words.
//

// typedef struct _LIST_ENTRY {
//   struct _LIST_ENTRY * volatile Flink;
//   struct _LIST_ENTRY * volatile Blink;
// } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;

//
//  Doubly-linked list manipulation routines.  Implemented as macros
//  but logically these are procedures.
//

//
//  VOID
//  InitializeListHead(
//      PLIST_ENTRY ListHead
//      );
//

#define InitializeListHead(ListHead) (\
    (ListHead)->Flink = (ListHead)->Blink = (ListHead))

//
//  BOOLEAN
//  IsListEmpty(
//      PLIST_ENTRY ListHead
//      );
//

#define IsListEmpty(ListHead) \
    ((ListHead)->Flink == (ListHead))

//
//  PLIST_ENTRY
//  RemoveHeadList(
//      PLIST_ENTRY ListHead
//      );
//

#define RemoveHeadList(ListHead) \
    (ListHead)->Flink;\
{RemoveEntryList((ListHead)->Flink)}

//
//  PLIST_ENTRY
//  RemoveTailList(
//      PLIST_ENTRY ListHead
//      );
//

#define RemoveTailList(ListHead) \
    (ListHead)->Blink;\
{RemoveEntryList((ListHead)->Blink)}

//
//  VOID
//  RemoveEntryList(
//      PLIST_ENTRY Entry
//      );
//

#define RemoveEntryList(Entry) {\
    PLIST_ENTRY _EX_Blink;\
    PLIST_ENTRY _EX_Flink;\
    _EX_Flink = (Entry)->Flink;\
    _EX_Blink = (Entry)->Blink;\
    _EX_Blink->Flink = _EX_Flink;\
    _EX_Flink->Blink = _EX_Blink;\
                            }

//
//  VOID
//  InsertTailList(
//      PLIST_ENTRY ListHead,
//      PLIST_ENTRY Entry
//      );
//

#define InsertTailList(ListHead,Entry) {\
    PLIST_ENTRY _EX_Blink;\
    PLIST_ENTRY _EX_ListHead;\
    _EX_ListHead = (ListHead);\
    _EX_Blink = _EX_ListHead->Blink;\
    (Entry)->Flink = _EX_ListHead;\
    (Entry)->Blink = _EX_Blink;\
    _EX_Blink->Flink = (Entry);\
    _EX_ListHead->Blink = (Entry);\
                                    }

//
//  VOID
//  InsertHeadList(
//      PLIST_ENTRY ListHead,
//      PLIST_ENTRY Entry
//      );
//

#define InsertHeadList(ListHead,Entry) {\
    PLIST_ENTRY _EX_Flink;\
    PLIST_ENTRY _EX_ListHead;\
    _EX_ListHead = (ListHead);\
    _EX_Flink = _EX_ListHead->Flink;\
    (Entry)->Flink = _EX_Flink;\
    (Entry)->Blink = _EX_ListHead;\
    _EX_Flink->Blink = (Entry);\
    _EX_ListHead->Flink = (Entry);\
                                    }

/************************************************************
 *   Function Definitions
 ************************************************************/

//
// Initialization and cleanup functions
//

DWORD   InitFileHandleCache(VOID);
DWORD   CleanupFileHandleCache(VOID);

HANDLE  FcOpenFile(IN EXTENSION_CONTROL_BLOCK * pecb, IN LPCSTR pszFile);
DWORD   FcCloseFile(IN HANDLE hFile);

BOOL    FcReadFromFile(
               IN  HANDLE hFile,
               OUT CHAR * pchBuffer,
               IN  DWORD  dwBufferSize,
               OUT LPDWORD  pcbRead,
               IN OUT LPOVERLAPPED  pov
               );

# endif // _OPENF_HXX_

/************************ End of File ***********************/


FTRANS.C

/*++

Copyright (c) 1995-1997  Microsoft Corporation

Module Name:

    ftrans.c

Abstract:

    This module demonstrates a simple file transfer using ISAPI application
      using the Async TransmitFile support with callback.

Revision History:
--*/

#include <windows.h>
#include "httpext.h"

# include "openf.h"

/************************************************************
 *  Global Data
 ************************************************************/

LIST_ENTRY g_lWorkItems;
CRITICAL_SECTION g_csWorkItems;

# define USE_WORK_QUEUE   (0)

DWORD
SendHeaderToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszErrorMsg);

DWORD
SendFileToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszFile);

DWORD
SendFileOver( IN EXTENSION_CONTROL_BLOCK  * pecb,
              IN HANDLE hFile,
              IN LPCSTR pszFile);

/************************************************************
 *    Functions
 ************************************************************/

BOOL WINAPI
DllMain(
     IN HINSTANCE hinstDll,
     IN DWORD     fdwReason,
     IN LPVOID    lpvContext OPTIONAL
     )
/*++

 Routine Description:

   This function DllMain() is the main initialization function for
    this DLL. It initializes local variables and prepares it to be invoked
    subsequently.

 Arguments:

   hinstDll          Instance Handle of the DLL
   fdwReason         Reason why NT called this DLL
   lpvReserved       Reserved parameter for future use.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
    BOOL    fReturn = TRUE;

    switch (fdwReason ) {

    case DLL_PROCESS_ATTACH:
        {
          OutputDebugString( " Initializing the global data in ftrans.dll\n");

          //
          // Initialize various data and modules.
          //
          InitializeCriticalSection(&g_csWorkItems);
          InitializeListHead( &g_lWorkItems);
          InitFileHandleCache();

          break;
        } /* case DLL_PROCESS_ATTACH */

    case DLL_PROCESS_DETACH:
        {

          //
          // Only cleanup when we are called because of a FreeLibrary().
          //  i.e., when lpvContext == NULL
          // If we are called because of a process termination,
          //  dont free anything. System will free resources and memory for us.
          //

          CleanupFileHandleCache();
          if ( lpvContext != NULL) {

              DeleteCriticalSection(&g_csWorkItems);
          }

          break;
        } /* case DLL_PROCESS_DETACH */

    default:
        break;
    } /* switch */

    return ( fReturn);
}  /* DllMain() */

DWORD WINAPI
HttpExtensionProc(
    EXTENSION_CONTROL_BLOCK * pecb
    )
/*++

 Routine Description:

   This function performs the necessary action to send response for the
    request received from the client. It picks up the name of a file from
    the pecb->lpszQueryString and transmits that file to the client.

 Arguments:

   pecb          pointer to ECB containing parameters related to the request.

 Return Value:

    Returns HSE_* status codes
--*/
{
    DWORD hseStatus;

    if ( pecb->lpszQueryString == NULL ||
        *pecb->lpszQueryString == '\0'
        ) {

        hseStatus = SendHeaderToClient( pecb, "File Not Specified");
    } else {

        hseStatus = SendFileToClient( pecb, pecb->lpszQueryString);
    }

    return ( hseStatus);

} // HttpExtensionProc()

BOOL WINAPI
GetExtensionVersion(
    HSE_VERSION_INFO * pver
    )
{
    pver->dwExtensionVersion = MAKELONG( 1, 0 );
    strcpy( pver->lpszExtensionDesc,
           "File Transfer using TransmitFile" );

    return TRUE;
}

BOOL WINAPI
TerminateExtension(
    DWORD dwFlags
    )
/*++

Purpose:

    This is optional ISAPI extension DLL entry point.
    If present, it will be called before unloading the DLL,
    giving it a chance to perform any shutdown procedures.

Arguments:

    dwFlags - specifies whether the DLL can refuse to unload or not

Returns:

    TRUE, if the DLL can be unloaded

--*/
{
    return TRUE;
}

DWORD
SendHeaderToClient(
    IN EXTENSION_CONTROL_BLOCK  * pecb,
    IN LPCSTR pszErrorMsg
    )
{
HSE_SEND_HEADER_EX_INFOSendHeaderExInfo;
char szStatus[]     = "200 OK";
char szHeader[1024];

    //
    //  Note the HTTP header block is terminated by a blank '\r\n' pair,
    //  followed by the document body
    //

    wsprintf( szHeader,
              "Content-Type: text/html\r\n"
              "\r\n"              // marks the end of header block
              "<head><title>Simple File Transfer (Transmit File)"
              "</title></head>\n"
              "<body><h1>%s</h1>\n"
              ,
              pszErrorMsg );

    //
    //  Populate SendHeaderExInfo struct
    //

    SendHeaderExInfo.pszStatus = szStatus;
    SendHeaderExInfo.pszHeader = szHeader;
    SendHeaderExInfo.cchStatus = lstrlen( szStatus);
    SendHeaderExInfo.cchHeader = lstrlen( szHeader);
    SendHeaderExInfo.fKeepConn = FALSE;

    //
    //  Send header - use the EX Version to send the header blob
    //

if ( !pecb->ServerSupportFunction(
                pecb->ConnID,
                HSE_REQ_SEND_RESPONSE_HEADER_EX,
                &SendHeaderExInfo,
                NULL,
                NULL
            ) ) {

    return HSE_STATUS_ERROR;
}

    return ( HSE_STATUS_SUCCESS);
} // SendHeaderToClient()

DWORD
SendFileToClient(
    IN EXTENSION_CONTROL_BLOCK * pecb,
    IN LPCSTR pszFile
    )
{
    CHAR    pchBuffer[1024];
    HANDLE  hFile;
    DWORD   hseStatus = HSE_STATUS_SUCCESS;

    hFile = FcOpenFile( pecb, pszFile);

    if ( hFile == INVALID_HANDLE_VALUE) {

        wsprintfA( pchBuffer,
                  "OpenFailed: Error (%d) while opening the file %s.\n",
                  GetLastError(), pszFile);

        hseStatus = SendHeaderToClient( pecb, pchBuffer);

    } else {

#if SEPARATE_HEADERS
        hseStatus = SendHeaderToClient( pecb, "File Transfer begins");
#else
        hseStatus = HSE_STATUS_SUCCESS;
#endif

        if ( hseStatus == HSE_STATUS_SUCCESS) {

            hseStatus = SendFileOver( pecb, hFile, pszFile);

            if ( hseStatus != HSE_STATUS_PENDING) {

                //
                // assume that the data is transmitted.
                //

                if ( hseStatus != HSE_STATUS_SUCCESS) {

                    //
                    // Error in transmitting the file. Send error message.
                    //

                    wsprintfA( pchBuffer,
                              "Send Failed: Error (%d) for file %s.\n",
                              GetLastError(), pszFile);

                    SendHeaderToClient( pecb, pchBuffer);
                }
            }
        }

        if ( hseStatus != HSE_STATUS_PENDING) {

            //
            // file handle is closed for all non-pending cases
            // if the status is pending, file handle is cleaned up in callback
            //
            FcCloseFile( hFile);
        }
    }

    return (hseStatus);

} // SendFileToClient()

# define MAX_BUFFER_SIZE  (400)

typedef struct _AIO_WORK_ITEM {

    LIST_ENTRY    listEntry;
    EXTENSION_CONTROL_BLOCK * pecb;
    HSE_TF_INFO   hseTf;
    CHAR          pchBuffer[ MAX_BUFFER_SIZE ];

}  AIO_WORK_ITEM, * PAWI;

VOID
CleanupAW( IN PAWI paw, IN BOOL fDoneWithSession)
{

    DWORD err = GetLastError();

    if ( paw->hseTf.hFile != INVALID_HANDLE_VALUE) {

        FcCloseFile( paw->hseTf.hFile);
    }

    if (fDoneWithSession) {

        paw->pecb->ServerSupportFunction( paw->pecb->ConnID,
                                         HSE_REQ_DONE_WITH_SESSION,
                                         NULL, NULL, NULL);
    }
    SetLastError( err);

    //
    // Remove from work items list
    //
#if USE_WORK_QUEUE
    EnterCriticalSection( &g_csWorkItems);
    RemoveEntryList( &paw->listEntry);
    LeaveCriticalSection( &g_csWorkItems);
# endif

    LocalFree( paw);
    return;

} // CleanupAW()

VOID WINAPI
HseIoCompletion(
                IN EXTENSION_CONTROL_BLOCK * pECB,
                IN PVOID    pContext,
                IN DWORD    cbIO,
                IN DWORD    dwError
                )
/*++

 Routine Description:

   This is the callback function for handling completions of asynchronous IO.
   This function performs necessary cleanup and resubmits additional IO
    (if required). In this case, this function is called at the end of a
    successful TransmitFile() operation. This function primarily cleans up
    the data and worker queue item and exits.

 Arguments:

   pecb          pointer to ECB containing parameters related to the request.
   pContext      context information supplied with the asynchronous IO call.
   cbIO          count of bytes of IO in the last call.
   dwError       Error if any, for the last IO operation.

 Return Value:

   None.
--*/
{
    PAWI    paw = (PAWI ) pContext;
    EXTENSION_CONTROL_BLOCK   * pecb = paw->pecb;

    // assert( pecb == paw->pecb);

    //
    // 1. if no errors, we are done transmitting the file
    // 2. cleanup and exit
    //

    //
    // Do Cleanup
    //

    CleanupAW( paw, TRUE);

    return;

} // HseIoCompletion()

DWORD
SendFileOver( IN EXTENSION_CONTROL_BLOCK  * pecb,
              IN HANDLE hFile,
              IN LPCSTR pszFile)
{

    PAWI   paw;
    DWORD  hseStatus = HSE_STATUS_PENDING;

    paw  = (PAWI ) LocalAlloc( LMEM_FIXED, sizeof(AIO_WORK_ITEM));
    if ( paw == NULL) {

        SetLastError( ERROR_NOT_ENOUGH_MEMORY);
        return (HSE_STATUS_ERROR);
    }

    //
    // Fill in all the data in AIO_WORK_ITEM
    //
    paw->pecb = pecb;
    InitializeListHead( &paw->listEntry);

    paw->hseTf.pfnHseIO = HseIoCompletion;
    paw->hseTf.pContext = paw;
    paw->hseTf.dwFlags  = (HSE_IO_ASYNC | HSE_IO_DISCONNECT_AFTER_SEND);

    paw->hseTf.hFile    = hFile;
    paw->hseTf.BytesToWrite = GetFileSize(hFile, NULL);
    paw->hseTf.Offset   = 0;
    paw->hseTf.pTail    = NULL;
    paw->hseTf.TailLength = 0;

    //
    //  Set up the header to be sentout for the file
    //

#if SEPARATE_HEADERS
    paw->hseTf.HeadLength = 0;
    paw->hseTf.pHead    = NULL;

#else
    paw->hseTf.HeadLength =
      wsprintfA ( paw->pchBuffer,
                 "HTTP/1.1 200 OK\r\n"
                 "Content-Type: text/html\r\n"
                 "\r\n"
                 "<head><title>Simple File Transfer (TransmitFile) "
                 "</title></head>\n"
                 "<h1> Transferred file contains...</h1>\n");
    paw->hseTf.pHead = paw->pchBuffer;
# endif

    // Add to the list
#if USE_WORK_QUEUE
    EnterCriticalSection( &g_csWorkItems);
    InsertTailList( &g_lWorkItems,  &paw->listEntry);
    LeaveCriticalSection( &g_csWorkItems);
#endif

    //
    // Setup the Async TransmitFile
    //

    if ( !pecb->ServerSupportFunction( pecb->ConnID,
                                       HSE_REQ_TRANSMIT_FILE,
                                       &paw->hseTf,
                                       NULL,
                                       NULL)
        ) {

        //
        // Do cleanup and return error
        //

        // File handle will be freed by the caller for errors
        paw->hseTf.hFile = INVALID_HANDLE_VALUE;

        CleanupAW( paw, FALSE);
        hseStatus =  HSE_STATUS_ERROR;
    }

    return (hseStatus);

} // SendFileOver()


README.TXT

FWASYNC.dll - File Transfer using Asynchronous WriteClient() inside ISAPI DLL

Overview
--------
FWASYNC.DLL is a sample ISAPI Extension DLL to demonstrate Asynchronous
WriteClient functionality in the ISAPI interface. This dll illustrates
how one can use the WriteClient() callback in the ECB to perform an
Asynchronous IO operation.

FWASYNC.dll sends the file specified on the query string. It also converts
a virtual file-path (given in the URL name-space) into a physical file
name using the ServerSupportFunction( HSE_REQ_MAP_URL_TO_PATH) function.
The ISAPI DLL setsup the callback context and function using the
ServerSupportFunction( HSE_REQ_IO_COMPLETION) in the ECB.

Installation:
-------------
Build FWASYNC.dll and copy the .dll into the virtual directory maked with
"execute" permissions.

To run:
-------

Reference FWASYNC.DLL file as a URL from the browser,
specifying a file-name on the query string.

For example, the following URL entered on the address line of your browser
will transfer file SAMPLE.GIF, located in WWW root directory:

http://localhost/scripts/fwasync.dll?/sample.gif

Disclaimer:
-----------
This sample is provided for the purpose of demonstrating an ISAPI
extension. It has not been tested for use in a production environment
and no support will be provided for use in a production environment.


OPENF.C

/*++

   Copyright    (c)    1995-1997    Microsoft Corporation

   Module  Name :

      openf.c

   Abstract:

      This module implements a simple open file handle cache

   Project:

       ISAPI Extensions Sample DLL

   Functions Exported:

   Note:
      THIS IS NOT ROBUST for REAL WORLD.
      I wrote this for testing the ISAPI Async IO processing.

--*/

/************************************************************
 *     Include Headers
 ************************************************************/

# include "openf.h"

/************************************************************
 *     Type definitions and Globals
 ************************************************************/

//
// internal data structure for maintaining the list of open file handles.
//

typedef struct _OPEN_FILE {

    HANDLE  hFile;
    struct _OPEN_FILE * pNext;
    LONG    nHits;
    LONG    nRefs;
    CHAR    rgchFile[MAX_PATH+1];

} OPEN_FILE, * LPOPEN_FILE;

LPOPEN_FILE g_pOpenFiles = NULL;
CRITICAL_SECTION g_csOpenFiles;
BOOL g_fIsNt = TRUE;

//
// Set up global variables containing the flags for CreateFile
//  The flags can be masked for Windows 95 system
//

DWORD  g_dwCreateFileShareMode =
         (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);

DWORD  g_dwCreateFileFlags =
         (FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED);

/************************************************************
 *    Functions
 ************************************************************/

DWORD
InitFileHandleCache(VOID)
/*++

  This function initializes the file handle cache.
  It should be called at the initialization time.

  Arguments:
    None

  Returns:
    Win32 error code. NO_ERROR indicates that the call succeeded.
--*/
{
    OSVERSIONINFO  osInfo;

    InitializeCriticalSection( &g_csOpenFiles);

    //
    // obtain the platform type to find out how to open the file
    //

    osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

    if ( GetVersionEx( &osInfo ) ) {
        g_fIsNt = (osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
    }

    if ( !g_fIsNt) {

        //
        // Reset the flags appropriately so that Windows 95 will be happy
        //

        g_dwCreateFileShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE);
        g_dwCreateFileFlags     = (FILE_FLAG_SEQUENTIAL_SCAN);
    }

    return (NO_ERROR);

} // InitFileHandleCache()

DWORD
CleanupFileHandleCache(VOID)
{
    LPOPEN_FILE  pFileScan;

    while ( g_pOpenFiles != NULL) {

        pFileScan = g_pOpenFiles;
        g_pOpenFiles = g_pOpenFiles->pNext;

        if ( pFileScan->hFile != INVALID_HANDLE_VALUE) {

            CloseHandle( pFileScan->hFile);
        }

        LocalFree( pFileScan);
    }

    DeleteCriticalSection( &g_csOpenFiles);

    return (NO_ERROR);
} // CleanupFileHandleCache()

HANDLE
FcOpenFile(IN EXTENSION_CONTROL_BLOCK * pecb, IN LPCSTR pszFile)
/*++

 FcOpenFile()

 Description:
   This function opens the file specified in the 'pszFile'.
   If the file name starts with a '/' we use the ECB to map
   the given path into a physical file path.

 Arguments:
  pecb - pointer to the ECB block
  pszFile - pointer to file name

 Returns:
   valid File handle on success
--*/
{
    LPOPEN_FILE  pFileScan;
    HANDLE hFile = INVALID_HANDLE_VALUE;

    EnterCriticalSection( &g_csOpenFiles);

    for ( pFileScan =  g_pOpenFiles;
         NULL != pFileScan;
         pFileScan = pFileScan->pNext) {

        if ( 0 == lstrcmpi( pFileScan->rgchFile, pszFile)) {

            //
            //  there is a file match.
            //

            break;
        }

    } // for

    if ( NULL == pFileScan) {

        //
        // File was not found. Create a new file handle
        //

        CHAR   rgchFileName[ MAX_PATH]; // local copy
        LPCSTR pszInputPath = pszFile;

        lstrcpyn( rgchFileName, pszFile, MAX_PATH);
        if ( *pszFile == '/') {

            DWORD cbSize = sizeof(rgchFileName);
            BOOL  fRet;

            // reset the file pointer, so subsequent use will fail
            pszFile = NULL;

            //
            // Using the ECB map the Virtual path to the Physical path
            //

            fRet = pecb->ServerSupportFunction( pecb->ConnID,
                                                HSE_REQ_MAP_URL_TO_PATH,
                                                rgchFileName,
                                                &cbSize, NULL);

            if (fRet) {
                // we got the mapping. Use it.
                pszFile = rgchFileName;
            }
        }

        if ( NULL != pszFile) {
            pFileScan = LocalAlloc( LPTR, sizeof( *pFileScan));

            if ( NULL != pFileScan) {

                SECURITY_ATTRIBUTES sa;

                sa.nLength              = sizeof(sa);
                sa.lpSecurityDescriptor = NULL;
                sa.bInheritHandle       = FALSE;

                pFileScan->hFile =
                    CreateFile( pszFile,
                                GENERIC_READ,
                                g_dwCreateFileShareMode,
                                &sa,
                                OPEN_EXISTING,
                                g_dwCreateFileFlags,
                                NULL );

                if ( INVALID_HANDLE_VALUE == pFileScan->hFile) {

                    LocalFree( pFileScan);
                    pFileScan = NULL;
                } else {

                    // insert this into the list at the top
                    lstrcpyn( pFileScan->rgchFile, pszInputPath, MAX_PATH);
                    pFileScan->pNext = g_pOpenFiles;
                    g_pOpenFiles = pFileScan;
                    pFileScan->nRefs = 1;
                    pFileScan->nHits = 0;
                }
            }
        }
    }

    if ( NULL != pFileScan) {

        hFile = pFileScan->hFile;
        pFileScan->nHits++;
        pFileScan->nRefs++;
    }

    LeaveCriticalSection( &g_csOpenFiles);

    return (hFile);

} // FcOpenFile()

DWORD
FcCloseFile(IN HANDLE hFile)
{
    LPOPEN_FILE  pFileScan;
    DWORD dwError = NO_ERROR;

    EnterCriticalSection( &g_csOpenFiles);

    //
    // Look for the handle and decrement the ref count.
    //
    for ( pFileScan =  g_pOpenFiles;
         NULL != pFileScan;
         pFileScan = pFileScan->pNext) {

        if ( hFile == pFileScan->hFile) {

            //
            //  there is a file match.
            //

            pFileScan->nRefs--;

            //
            // BUGBUG: There is no freeing of the file when Ref hits '0' :(
            //

            break;
        }

    } // for

    if ( NULL == pFileScan) {
        //
        // file handle not found
        //
        dwError = ( ERROR_INVALID_HANDLE);
    }

    LeaveCriticalSection( &g_csOpenFiles);

    return ( dwError);

} // FcCloseFile()

BOOL
FcReadFromFile(
               IN  HANDLE hFile,
               OUT CHAR * pchBuffer,
               IN  DWORD  dwBufferSize,
               OUT LPDWORD  pcbRead,
               IN OUT LPOVERLAPPED  pov
               )
/*++
  Description:
    Reads contents of file [hFile] from the specified offset in the overlapped
    structure. The contents are read into the buffer supplied.

  Arguments:
    hFile        - handle for the File from which to read data
    pchBuffer    - pointer to the buffer into which the data is to be read
    dwBufferSize - DWORD containing the max size of the buffer supplied
    pcbRead      - number of bytes read from the file
    pov          - pointer to an overlapped structure that contains the
                     offset from where to read the contents. The
                     overlapped structure also is used for Overlapped
                     IO in NT.

  Notes:
   This function automatically handles both Windows 95 and NT

  Returns:
    TRUE on success and FALSE if there is a failure.
    Use GetLastError() to get the last error code on failure.
--*/
{
    BOOL fRet = TRUE;

    *pcbRead = 0;

    if ( !g_fIsNt ) {
        //
        // Windows95 does not support Overlapped IO.
        //  So we shall thunk it out and use Synchronous IO
        //

        fRet = (
                SetFilePointer( hFile,
                                pov->Offset,
                                NULL,
                                FILE_BEGIN) &&
                ReadFile( hFile,
                          pchBuffer,
                          dwBufferSize,
                          pcbRead,
                          NULL
                          )
                );
    } else {

        ResetEvent( pov->hEvent);

        fRet = TRUE;

        // read data from file
        if (!ReadFile(hFile,
                      pchBuffer,
                      dwBufferSize,
                      pcbRead,
                      pov
                      )) {

            DWORD err = GetLastError();

            if ( (err != ERROR_IO_PENDING) ||
                 !GetOverlappedResult( hFile, pov, pcbRead, TRUE) ) {

                fRet = FALSE;
            }
        }
    }

    if ( fRet ) {
        pov->Offset += *pcbRead;
    }

    return ( fRet );
} // FcReadFromFile()

/************************ End of File ***********************/


FWASYNC.C

/*++

Copyright (c) 1995-1997  Microsoft Corporation

Module Name:

    fwasync.c

Abstract:

    This module demonstrates a simple file transfer using ISAPI application
      using the Async WriteClient() with callback.

Revision History:
--*/

# include <windows.h>
# include <stdio.h>
# include "httpext.h"
# include "openf.h"

/************************************************************
 *  Global Data
 ************************************************************/

LIST_ENTRY g_lWorkItems;
CRITICAL_SECTION g_csWorkItems;

# define USE_WORK_QUEUE   (0)

DWORD
SendHeaderToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszErrorMsg);

DWORD
SendFileToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszFile);

DWORD
SendFileOver( IN EXTENSION_CONTROL_BLOCK  * pecb, IN HANDLE hFile);

/************************************************************
 *    Functions
 ************************************************************/

BOOL WINAPI
DllMain(
     IN HINSTANCE hinstDll,
     IN DWORD     fdwReason,
     IN LPVOID    lpvContext OPTIONAL)
/*++

 Routine Description:

   This function DllLibMain() is the main initialization function for
    this DLL. It initializes local variables and prepares it to be invoked
    subsequently.

 Arguments:

   hinstDll          Instance Handle of the DLL
   fdwReason         Reason why NT called this DLL
   lpvReserved       Reserved parameter for future use.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
  BOOL    fReturn = TRUE;

  switch (fdwReason ) {

    case DLL_PROCESS_ATTACH:
      {
          OutputDebugString( "Initializing the global data for fwasync.dll\n");

          //
          // Initialize various data and modules.
          //
          InitializeCriticalSection(&g_csWorkItems);
          InitializeListHead( &g_lWorkItems);
          fReturn = (InitFileHandleCache() == NO_ERROR);

          break;
      } /* case DLL_PROCESS_ATTACH */

    case DLL_PROCESS_DETACH:
      {

          //
          // Only cleanup when we are called because of a FreeLibrary().
          //  i.e., when lpvContext == NULL
          // If we are called because of a process termination,
          //  dont free anything. System will free resources and memory for us.
          //

          CleanupFileHandleCache();
          if ( lpvContext != NULL) {

              DeleteCriticalSection(&g_csWorkItems);
          }

          break;
      } /* case DLL_PROCESS_DETACH */

    default:
      break;
  }   /* switch */

  return ( fReturn);
}  /* DllLibMain() */

DWORD WINAPI
HttpExtensionProc(
    EXTENSION_CONTROL_BLOCK * pecb
    )
/*++

 Routine Description:

   This function performs the necessary action to send response for the
    request received from the client. It picks up the name of a file from
    the pecb->lpszQueryString and sends the file using Asynchronous WriteClient

 Arguments:

   pecb          pointer to ECB containing parameters related to the request.

 Return Value:

    Returns HSE_* status codes
--*/
{
    DWORD hseStatus;

    if ( pecb->lpszQueryString == NULL) {

        hseStatus = SendHeaderToClient( pecb, "File Not Found");
    } else {

        hseStatus = SendFileToClient( pecb, pecb->lpszQueryString);
    }

    return ( hseStatus);

} // HttpExtensionProc()

BOOL WINAPI
GetExtensionVersion(
    HSE_VERSION_INFO * pver
    )
{
    pver->dwExtensionVersion = MAKELONG( 1, 0 );
    strcpy( pver->lpszExtensionDesc,
           "File Transfer using WriteClient" );

    return TRUE;
}

BOOL WINAPI
TerminateExtension(
                   /*IN */ DWORD dwFlags
    )
/*++
  This function is called when IIS decides to stop and unload the
  ISAPI DLL. We can do any custom cleanup for the module inside this function
--*/
{
    //
    // Nothing specific do here for cleanup
    // Cleanup is done in the dll process detach in DllLibMain()
    //

    return ( TRUE);
}

DWORD
SendHeaderToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszErrorMsg)
{
HSE_SEND_HEADER_EX_INFOSendHeaderExInfo;
char szStatus[]     = "200 OK";
char szHeader[1024];

    //
    //  Note the HTTP header block is terminated by a blank '\r\n' pair,
    //  followed by the document body
    //

    wsprintf( szHeader,
              "Content-Type: text/html\r\n"
              "\r\n"              // marks the end of header block
              "<head><title>Simple File Transfer (Async Write)"
              "</title></head>\n"
              "<body><h1>%s</h1>\n"
              ,
              pszErrorMsg );

    //
    //  Populate SendHeaderExInfo struct
    //

    SendHeaderExInfo.pszStatus = szStatus;
    SendHeaderExInfo.pszHeader = szHeader;
    SendHeaderExInfo.cchStatus = lstrlen( szStatus);
    SendHeaderExInfo.cchHeader = lstrlen( szHeader);
    SendHeaderExInfo.fKeepConn = FALSE;

    //
    //  Send header - use the EX Version to send the header blob
    //

if ( !pecb->ServerSupportFunction(
    pecb->ConnID
    , HSE_REQ_SEND_RESPONSE_HEADER_EX
    , &SendHeaderExInfo
    , NULL
    , NULL
            ) ) {

    return HSE_STATUS_ERROR;
}

    return ( HSE_STATUS_SUCCESS);
} // SendHeaderToClient()

DWORD
SendFileToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszFile)
{
    CHAR    pchBuffer[1024];
    HANDLE  hFile;
    DWORD   hseStatus = HSE_STATUS_SUCCESS;

    hFile = FcOpenFile( pecb, pszFile);

    if ( hFile == INVALID_HANDLE_VALUE) {

        wsprintfA( pchBuffer,
                  "OpenFailed: Error (%d) while opening the file %s.\n",
                  GetLastError(), pszFile);

        hseStatus = SendHeaderToClient( pecb, pchBuffer);

    } else {

        wsprintfA( pchBuffer, " Transferred file contains...");

        hseStatus = SendHeaderToClient( pecb, pchBuffer);

        if ( hseStatus == HSE_STATUS_SUCCESS) {

            hseStatus = SendFileOver( pecb, hFile);

            if ( hseStatus != HSE_STATUS_PENDING) {

                //
                // assume that the data is transmitted.
                //

                // close file handle
                FcCloseFile( hFile);

                if ( hseStatus != HSE_STATUS_SUCCESS) {

                    //
                    // Error in transmitting the file. Send error message.
                    //

                    wsprintfA( pchBuffer,
                              "Send Failed: Error (%d) for file %s.\n",
                              GetLastError(), pszFile);

                    SendHeaderToClient( pecb, pchBuffer);
                }
            }
        }
    }

    return (hseStatus);

} // SendFileToClient()

# define MAX_BUFFER_SIZE  (4096)

typedef struct _AIO_WORK_ITEM {

    LIST_ENTRY    listEntry;
    EXTENSION_CONTROL_BLOCK * pecb;
    HANDLE        hFile;
    OVERLAPPED    ov;
    DWORD         nRead;
    CHAR          pchBuffer[ MAX_BUFFER_SIZE ];

}  AIO_WORK_ITEM, * PAWI;

DWORD
ReadDataForPaw(IN PAWI paw)
{
    DWORD hseStatus = HSE_STATUS_SUCCESS;

    // read data from the file
    if (!FcReadFromFile( paw->hFile, paw->pchBuffer, MAX_BUFFER_SIZE,
                         &paw->nRead,
                         &paw->ov)
        ) {

        hseStatus = HSE_STATUS_ERROR;
    }

    return (hseStatus);

} // ReadDataForPaw()

DWORD
SendDataForPaw(IN PAWI paw)
{
    DWORD hseStatus = HSE_STATUS_PENDING;

    // write data to client

    if ( !paw->pecb->WriteClient( paw->pecb->ConnID,
                                 paw->pchBuffer,
                                 &paw->nRead,
                                 HSE_IO_ASYNC)
        ) {

        hseStatus = HSE_STATUS_ERROR;
    }

    return (hseStatus);
} // SendDataForPaw()

VOID
CleanupAW( IN PAWI paw, IN BOOL fDoneWithSession)
{

    DWORD err = GetLastError();

    FcCloseFile( paw->hFile);
    CloseHandle( paw->ov.hEvent);

    if (fDoneWithSession) {
        paw->pecb->ServerSupportFunction( paw->pecb->ConnID,
                                         HSE_REQ_DONE_WITH_SESSION,
                                         NULL, NULL, NULL);
    }
    SetLastError( err);

    //
    // Remove from work items list
    //
#if USE_WORK_QUEUE
    EnterCriticalSection( &g_csWorkItems);
    RemoveEntryList( &paw->listEntry);
    LeaveCriticalSection( &g_csWorkItems);
# endif

    LocalFree( paw);
    return;

} // CleanupAW()

VOID WINAPI
HseIoCompletion(
                IN EXTENSION_CONTROL_BLOCK * pECB,
                IN PVOID    pContext,
                IN DWORD    cbIO,
                IN DWORD    dwError
                )
/*++

 Routine Description:

   This is the callback function for handling completions of asynchronous IO.
   This function performs necessary cleanup and resubmits additional IO
    (if required). In this case, this function is called at the end of a
    successful Async WriteClient(). This function attempts to read the next
    chunk of data and sends it to client. If there is no next chunk this
    function cleans up the state and informs IIS about its intention to end
    the request.

 Arguments:

   pecb          pointer to ECB containing parameters related to the request.
   pContext      context information supplied with the asynchronous IO call.
   cbIO          count of bytes of IO in the last call.
   dwError       Error if any, for the last IO operation.

 Return Value:

   None.
--*/
{
    PAWI    paw = (PAWI ) pContext;
    EXTENSION_CONTROL_BLOCK   * pecb = paw->pecb;
    DWORD   hseStatus;

    //
    // 1. if no errors, do another read of the file
    // 2. send the read contents to client
    // 3. if no data present, cleanup and exit
    //

    if ( dwError != NO_ERROR) {

        //
        // Do Cleanup
        //

        CleanupAW( paw, TRUE);
        return;
    }

    //    assert( paw->nRead == cbIO);

    //
    // Read next chunk of data
    //

    paw->nRead = 0;
    hseStatus = ReadDataForPaw( paw);

    if ( hseStatus == HSE_STATUS_SUCCESS && paw->nRead > 0) {

        hseStatus = SendDataForPaw( paw);
    }

    if ( hseStatus != HSE_STATUS_PENDING) {

        CleanupAW( paw, TRUE);
    }

    return;
} // HseIoCompletion()

DWORD
SendFileOver( IN EXTENSION_CONTROL_BLOCK  * pecb, IN HANDLE hFile)
{

    PAWI   paw;
    DWORD  hseStatus = HSE_STATUS_SUCCESS;

    paw  = (PAWI ) LocalAlloc( LMEM_FIXED, sizeof(AIO_WORK_ITEM));
    if ( paw == NULL) {

        SetLastError( ERROR_NOT_ENOUGH_MEMORY);
        return (HSE_STATUS_ERROR);
    }

    //
    // Fill in all the data in AIO_WORK_ITEM
    //
    paw->pecb = pecb;
    InitializeListHead( &paw->listEntry);
    paw->hFile = hFile;
    paw->nRead = 0;
    memset( &paw->ov, 0, sizeof(OVERLAPPED));
    paw->ov.OffsetHigh = 0;
    paw->ov.Offset = 0;
    paw->ov.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL);

    if ( paw->ov.hEvent == NULL) {

        LocalFree( paw);
        return (HSE_STATUS_ERROR);
    }

    //
    // Setup the callback context for the Async IO
    //

    if ( !pecb->ServerSupportFunction( pecb->ConnID,
                                       HSE_REQ_IO_COMPLETION,
                                       HseIoCompletion,
                                       0,
                                       (LPDWORD ) paw)
        ) {

        //
        // Do cleanup and return error
        //

        CloseHandle( paw->ov.hEvent);
        LocalFree( paw);
        return HSE_STATUS_ERROR;
    }

    // Add to the list
#if USE_WORK_QUEUE
    EnterCriticalSection( &g_csWorkItems);
    InsertTailList( &g_lWorkItems,  &paw->listEntry);
    LeaveCriticalSection( &g_csWorkItems);
#endif

    hseStatus = ReadDataForPaw( paw);

    if ( hseStatus == HSE_STATUS_SUCCESS && paw->nRead > 0 ) {

        hseStatus = SendDataForPaw( paw);
    }

    if ( hseStatus != HSE_STATUS_PENDING) {

        CleanupAW(paw, FALSE);
    }

    return (hseStatus);

} // SendFileOver()


READCLI.C

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    readcli.c

Abstract:

    This module absorbs input from the client, and then returns
    it to the client.

Revision History:
--*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include <stdio.h>
#include <stdlib.h>

#define BUFFER_LENGTH 4096
#define STRING_LENGTH 80

// Prototypes
BOOL SendErrorToClient( LPEXTENSION_CONTROL_BLOCK pec, CHAR *szErrorText );

BOOL WINAPI
GetExtensionVersion( HSE_VERSION_INFO *Version )
/*++

Routine Description:

    Sets the ISAPI extension version information.

Arguments:

    Version     pointer to HSE_VERSION_INFO structure

Return Value:

    TRUE

--*/
{
    Version->dwExtensionVersion =
        MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );

    strcpy( Version->lpszExtensionDesc, "ReadClient Extension" );

    return TRUE;
}

DWORD WINAPI
HttpExtensionProc( LPEXTENSION_CONTROL_BLOCK pec )
/*++

Routine Description:

    This is the main routine for any ISAPI application.  We read in all
    the data from the client, using the ReadClient function, and then
    spit it all back out to the client using WriteClient.

Arguments:

    pec           pointer to ECB containing parameters related to the request.

Return Value:

    Either HSE_STATUS_SUCCESS or HSE_STATUS_ERROR

--*/
{
    BOOL bResult;
    CHAR *szBuffer;
    CHAR szTemp[BUFFER_LENGTH];
    CHAR szTmpBuf[BUFFER_LENGTH];
    DWORD dwBytesRead;
    DWORD dwBytesWritten;
    DWORD dwTotalRead;
    DWORD dwTotalWritten;
    DWORD dwContentLength;
    DWORD dwBufferSize;
    HSE_SEND_HEADER_EX_INFO SendHeaderExInfo;

    // Determine the amount of data available from the
    // Content-Length header
    dwBufferSize = sizeof( szTmpBuf ) - 1;
    bResult = pec->GetServerVariable(
        pec->ConnID,
        "CONTENT_LENGTH",
        szTmpBuf,
        &dwBufferSize
        );
    if ( !bResult ) {
        bResult = SendErrorToClient( pec, "Content-Length header not found" );
        if ( !bResult ) {
            return( HSE_STATUS_ERROR );
        }
        return( HSE_STATUS_SUCCESS );
    }
    dwContentLength = atol( szTmpBuf );

    // Allocate the buffer based on the Content-Length
    szBuffer = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwContentLength + 5 );
    if ( szBuffer == NULL ) {
        sprintf(
            szTemp,
            "Unable to allocate %ld bytes of memory for buffer",
            dwContentLength + 5
            );
        bResult = SendErrorToClient( pec, szTemp );
        if ( !bResult ) {
            return( HSE_STATUS_ERROR );
        }
        return( HSE_STATUS_SUCCESS );
    }

    // If the client didn't post anything, return and create a nice form
    // for them
    if ( pec->cbAvailable == 0 ) {
        bResult = SendErrorToClient( pec, "Your request did not contain any data" );
        if ( !bResult ) {
            return( HSE_STATUS_ERROR );
        }
        return( HSE_STATUS_SUCCESS );
    }

    // Initialize variables before reading in the data
    dwTotalWritten = 0;
    dwTotalRead = pec->cbAvailable;

    // Copy the first chunk of the data into the buffer
    strncpy( szBuffer, pec->lpbData, dwTotalRead );
    szBuffer[dwTotalRead] = 0;

    // Loop to read in the rest of the data from the client
    while ( dwTotalRead < pec->cbTotalBytes ) {
        // Set the size of our temporary buffer
        dwBytesRead = sizeof( szTmpBuf ) - 1;
        if ( (dwTotalRead + dwBytesRead) > pec->cbTotalBytes ) {
            dwBytesRead = pec->cbTotalBytes - dwTotalRead;
        }

        // Read the data into the temporary buffer
        bResult = pec->ReadClient(
            pec->ConnID,
            szTmpBuf,
            &dwBytesRead
            );
        if ( !bResult ) {
            HeapFree( GetProcessHeap(), 0, szBuffer );
            return( HSE_STATUS_ERROR );
        }
        // NULL-Terminate the temporary buffer
        szTmpBuf[dwBytesRead] = 0;

        // Append the temporary buffer to the real buffer
        if ( dwBytesRead != 0 ) {
            strcat( szBuffer + dwTotalRead, szTmpBuf );
        }
        dwTotalRead += dwBytesRead;
    } // while ( dwTotalRead < pec->cbTotalBytes )

    // All the data has been read in and stored in our buffer
    // Now send the data back to the client
    SendHeaderExInfo.pszStatus = "200 OK";
    SendHeaderExInfo.pszHeader = "Content-Type: text/html\r\n\r\n";
    SendHeaderExInfo.cchStatus = lstrlen( SendHeaderExInfo.pszStatus );
    SendHeaderExInfo.cchHeader = lstrlen( SendHeaderExInfo.pszHeader );
    SendHeaderExInfo.fKeepConn = FALSE;

    bResult = pec->ServerSupportFunction(
        pec->ConnID,
        HSE_REQ_SEND_RESPONSE_HEADER_EX,
        &SendHeaderExInfo,
        NULL,
        NULL
        );
    if ( !bResult ) {
        HeapFree( GetProcessHeap(), 0, szBuffer );
        return( HSE_STATUS_ERROR );
    }
    dwBytesWritten = dwTotalRead;
    bResult = pec->WriteClient(
        pec->ConnID,
        szBuffer,
        &dwBytesWritten,
        0
        );
    if ( !bResult ) {
        HeapFree( GetProcessHeap(), 0, szBuffer );
        return( HSE_STATUS_ERROR );
    }
    HeapFree( GetProcessHeap(), 0, szBuffer );
    return( HSE_STATUS_SUCCESS );
}

BOOL WINAPI
TerminateExtension( DWORD dwFlags )
/*++

Routine Description:

    This function is called when the WWW service is shutdown

Arguments:

    dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD

Return Value:

    TRUE if extension is ready to be unloaded,
    FALSE otherwise

--*/
{
    return TRUE;
}

BOOL SendErrorToClient( LPEXTENSION_CONTROL_BLOCK pec, CHAR *szErrorText )
/*++

Routine Description:

    This function sends any error messages and usage information
    to the client.  It also creates a nice little form which can
    be used to post data.

Arguments:

    pec - pointer to ECB containing parameters related to the request
    szErrorText - Helpful error text to send to the client

Return Value:

    TRUE or FALSE, depending on the success of WriteClient

--*/
{
    BOOL bResult;
    CHAR szTemp[BUFFER_LENGTH];
    DWORD dwBytesWritten;
    HSE_SEND_HEADER_EX_INFO SendHeaderExInfo;

    SendHeaderExInfo.pszStatus = "200 OK";
    SendHeaderExInfo.pszHeader = "Content-Type: text/html\r\n\r\n";
    SendHeaderExInfo.cchStatus = lstrlen( SendHeaderExInfo.pszStatus );
    SendHeaderExInfo.cchHeader = lstrlen( SendHeaderExInfo.pszHeader );
    SendHeaderExInfo.fKeepConn = FALSE;

    bResult = pec->ServerSupportFunction(
        pec->ConnID,
        HSE_REQ_SEND_RESPONSE_HEADER_EX,
        &SendHeaderExInfo,
        NULL,
        NULL
        );
    if ( !bResult ) {
        return FALSE;
    }

    dwBytesWritten = sprintf(
        szTemp,
        "<h3>Error: %s</h3><p>\r\n\r\n"
        "Usage: readcli.dll<p>\r\n\r\n"
        "Request must be a POST request, with extra data<p>\r\n"
        "<h2>Sample Form</h2>\r\n"
        "Enter data below:<br>\r\n"
        "<form method=POST action=\"readcli.dll\">\r\n"
        "<input type=text name=test size=80><br>\r\n"
        "<input type=submit>\r\n",
        szErrorText
        );
    bResult = pec->WriteClient(
        pec->ConnID,
        szTemp,
        &dwBytesWritten,
        0
        );
    return( bResult );
}


README.TXT

FWRITE.dll - File Transfer using Synchronous WriteClient() inside ISAPI DLL

Overview
--------
FWRITE.DLL is a sample ISAPI Extension DLL to demonstrate Synchronous
WriteClient functionality in the ISAPI interface. This dll illustrates
how one can use the WriteClient() callback in the ECB to perform an
Synchronous IO operation. It is intended for comparison against the
Asynchronous WriteClient() and Asynchronous TransmitFile() options.

FWRITE.dll sends the file specified on the query string. It also converts
a virtual file-path (given in the URL name-space) into a physical file
name using the ServerSupportFunction( HSE_REQ_MAP_URL_TO_PATH) function.

Installation:
-------------
Build FWRITE.dll and copy the .dll into the virtual directory maked with
"execute" permissions.

To run:
-------

Reference FWRITE.DLL file as a URL from the browser,
specifying a file-name on the query string.

For example, the following URL entered on the address line of your browser
will transfer file SAMPLE.GIF, located in WWW root directory:

http://localhost/scripts/fwrite.dll?/sample.gif

Disclaimer:
-----------
This sample is provided for the purpose of demonstrating an ISAPI
extension. It has not been tested for use in a production environment
and no support will be provided for use in a production environment.


FWRITE.C

/*++

Copyright (c) 1995-1997  Microsoft Corporation

Module Name:

    fwrite.c

Abstract:

    This module demonstrates a simple file transfer using ISAPI application
      using the WriteClient() callback.

Revision History:
--*/

# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <stdio.h>
# include "httpext.h"
# include "openf.h"

DWORD
SendHeaderToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszErrorMsg);

DWORD
SendFileToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszFile);

DWORD
SendFileOver( IN EXTENSION_CONTROL_BLOCK  * pecb, IN HANDLE hFile);

BOOL WINAPI
DllMain(
     IN HINSTANCE hinstDll,
     IN DWORD     fdwReason,
     IN LPVOID    lpvContext OPTIONAL)
/*++

 Routine Description:

   This function DllLibMain() is the main initialization function for
    this DLL. It initializes local variables and prepares it to be invoked
    subsequently.

 Arguments:

   hinstDll          Instance Handle of the DLL
   fdwReason         Reason why NT called this DLL
   lpvReserved       Reserved parameter for future use.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.

--*/
{
  BOOL    fReturn = TRUE;

  switch (fdwReason ) {

    case DLL_PROCESS_ATTACH:
      {
          OutputDebugString( " Initializing the global data in fwrite.dll\n");

          //
          // Initialize various data and modules.
          //
          fReturn = (InitFileHandleCache() == NO_ERROR);

          break;
      } /* case DLL_PROCESS_ATTACH */

    case DLL_PROCESS_DETACH:
      {

          //
          // Only cleanup when we are called because of a FreeLibrary().
          //  i.e., when lpvContext == NULL
          // If we are called because of a process termination,
          //  dont free anything. System will free resources and memory for us.
          //

          CleanupFileHandleCache();

          break;
      } /* case DLL_PROCESS_DETACH */

    default:
      break;
  }   /* switch */

  return ( fReturn);
}  /* DllLibMain() */

DWORD WINAPI
HttpExtensionProc(
    IN EXTENSION_CONTROL_BLOCK * pecb
)
{
    DWORD hseStatus;

    if ( pecb->lpszQueryString == NULL) {

        hseStatus = SendHeaderToClient( pecb, "File Not Found");
    } else {

        hseStatus = SendFileToClient( pecb, pecb->lpszQueryString);
    }

    return ( hseStatus);

} // HttpExtensionProc()

BOOL WINAPI
GetExtensionVersion(
    HSE_VERSION_INFO * pver
    )
{
    pver->dwExtensionVersion = MAKELONG( 1, 0 );
    strcpy( pver->lpszExtensionDesc,
           "File Transfer using WriteClient" );

    return TRUE;
}

BOOL WINAPI
TerminateExtension(
                   /*IN */ DWORD dwFlags
    )
/*++
  This function is called when IIS decides to stop and unload the
  ISAPI DLL. We can do any custom cleanup for the module inside this function
--*/
{
    //
    // Nothing specific do here for cleanup
    // Cleanup is done in the dll process detach in DllLibMain()
    //

    return ( TRUE);
}

DWORD
SendHeaderToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszErrorMsg)
{
HSE_SEND_HEADER_EX_INFOSendHeaderExInfo;
char szStatus[]     = "200 OK";
char szHeader[1024];

    //
    //  Note the HTTP header block is terminated by a blank '\r\n' pair,
    //  followed by the document body
    //

    wsprintf( szHeader,
              "Content-Type: text/html\r\n"
              "\r\n"              // marks the end of header block
              "<head><title>Simple File Transfer (Synchronous Write)"
              "</title></head>\n"
              "<body><h1>%s</h1>\n"
              ,
              pszErrorMsg );

    //
    //  Populate SendHeaderExInfo struct
    //

    SendHeaderExInfo.pszStatus = szStatus;
    SendHeaderExInfo.pszHeader = szHeader;
    SendHeaderExInfo.cchStatus = lstrlen( szStatus);
    SendHeaderExInfo.cchHeader = lstrlen( szHeader);
    SendHeaderExInfo.fKeepConn = FALSE;

    //
    //  Send header - use the EX Version to send the header blob
    //

if ( !pecb->ServerSupportFunction(
    pecb->ConnID
    , HSE_REQ_SEND_RESPONSE_HEADER_EX
    , &SendHeaderExInfo
    , NULL
    , NULL
            ) ) {

    return HSE_STATUS_ERROR;
}

    return ( HSE_STATUS_SUCCESS);
} // SendHeaderToClient()

DWORD
SendFileToClient( IN EXTENSION_CONTROL_BLOCK  * pecb, IN LPCSTR pszFile)
{
    CHAR    pchBuffer[1024];
    HANDLE  hFile;
    DWORD   hseStatus = HSE_STATUS_SUCCESS;

    hFile = FcOpenFile( pecb, pszFile);

    if ( hFile == INVALID_HANDLE_VALUE) {

        wsprintfA( pchBuffer,
                  "OpenFailed: Error (%d) while opening the file %s.\n",
                  GetLastError(), pszFile);

        hseStatus = SendHeaderToClient( pecb, pchBuffer);

    } else {

        wsprintfA( pchBuffer, " Transferred file contains...");

        hseStatus = SendHeaderToClient( pecb, pchBuffer);

        if ( hseStatus == HSE_STATUS_SUCCESS) {

            hseStatus = SendFileOver( pecb, hFile);

            if ( hseStatus != HSE_STATUS_PENDING) {

                if ( hseStatus != HSE_STATUS_SUCCESS) {

                    //
                    // Error in transmitting the file. Send error message.
                    //

                    wsprintfA( pchBuffer,
                              "Send Failed: Error (%d) for file %s.\n",
                              GetLastError(), pszFile);

                    SendHeaderToClient( pecb, pchBuffer);
                }
            }

            if ( hseStatus != HSE_STATUS_PENDING) {

                //
                // assume that the data is transmitted.
                //

                // close file handle
                FcCloseFile( hFile);
            }
        }
    }

    return (hseStatus);

} // SendFileToClient()

# define MAX_BUFFER_SIZE  (4096)

DWORD
SendFileOver( IN EXTENSION_CONTROL_BLOCK  * pecb, IN HANDLE hFile)
{
    CHAR  pchBuffer[MAX_BUFFER_SIZE];
    DWORD dwBytesInFile = GetFileSize(hFile, NULL);
    DWORD nRead = 0;
    DWORD hseStatus = HSE_STATUS_SUCCESS;
    OVERLAPPED  ov;
    DWORD err;

    //
    // Send the whole file.
    // Loop thru reading the file and transmitting it to client
    //

    memset(&ov, 0, sizeof(OVERLAPPED));

    ov.OffsetHigh = 0;
    ov.Offset = 0;
    ov.hEvent = CreateEvent(NULL, TRUE, FALSE, "OVERLAPPED::hEvent");

    if ( ov.hEvent == NULL) {

        return (HSE_STATUS_ERROR);
    }

    do {

        nRead = 0;

        // read data from the file
        if (!FcReadFromFile( hFile, pchBuffer, MAX_BUFFER_SIZE, &nRead,
                           &ov)
            ) {

            hseStatus = HSE_STATUS_ERROR;
            break;
        }

        // write data to client

        if ( !pecb->WriteClient( pecb->ConnID,
                                 pchBuffer, &nRead,
                                0)
            ) {

            hseStatus = HSE_STATUS_ERROR;
            break;
        }

    } while (TRUE);

    if ( ov.hEvent != NULL) {

        err = GetLastError();
        CloseHandle( ov.hEvent);
        ov.hEvent = NULL;
        SetLastError( err);
    }

    return (hseStatus);

} // SendFileOver()


KEEPALIVE.C

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    KeepAlive.c

Abstract:

   Sample ISAPI Extension demonstrating Keep-Alive.

--*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include <stdio.h>

BOOL WINAPI
GetExtensionVersion(
    OUT HSE_VERSION_INFO * pVer
    )
/*++

Purpose:

    This is required ISAPI Extension DLL entry point.

Arguments:

    pVer - poins to extension version info structure

Returns:

    always returns TRUE

--*/
{
    pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);

    strncpy(
        pVer->lpszExtensionDesc,
        "ISAPI Keep-Alive Extension Sample",
        HSE_MAX_EXT_DLL_NAME_LEN
        );

    return TRUE;
}

DWORD WINAPI
HttpExtensionProc(
    IN EXTENSION_CONTROL_BLOCK * pECB
    )
/*++

Purpose:
    Demonstrate persistent connection from ISAPI extension DLL.

Arguments:

    pECB - pointer to the extenstion control block

Returns:

    HSE_STATUS_SUCCESS_AND_KEEP_CONN

--*/
{
    //
    // Use the "Connection: Keep-Alive" header to keep the connection open;
    // also, the "Content-Length:" header is required, so that the client knows when
    // the server has finished.
    //

    char szHeader[] =
        "Connection: Keep-Alive\r\n"
        "Content-Length: %lu\r\n"
        "Content-type: text/html\r\n\r\n";

    char szContent[] =
        "<html> <form method=get action=\\scripts\\KeepAlive.dll>"
        "<h1>Keep-Alive Sample</h1><hr>"
        "<input type=submit value=\"Send Request\"></form></html>";

    char szBuffer[4096];
    HSE_SEND_HEADER_EX_INFO HeaderExInfo;
    DWORD dwSize;

    //
    // Send outgoing header with exact content length
    //

    sprintf( szBuffer, szHeader, strlen( szContent ) );
    HeaderExInfo.pszHeader = szBuffer;
    HeaderExInfo.cchHeader = strlen( szBuffer );
    HeaderExInfo.pszStatus = "200 OK";
    HeaderExInfo.cchStatus = strlen( HeaderExInfo.pszStatus );
    HeaderExInfo.fKeepConn = TRUE;

    pECB->ServerSupportFunction(
        pECB,
        HSE_REQ_SEND_RESPONSE_HEADER_EX,
        &HeaderExInfo,
        NULL,
        NULL
        );

    //
    // Send content
    //

    dwSize = strlen( szContent );

    pECB->WriteClient( pECB, szContent, &dwSize, HSE_IO_SYNC );

    //
    // This return code tells IIS not to close the socket connection.
    //

    return HSE_STATUS_SUCCESS_AND_KEEP_CONN;
}

BOOL WINAPI
TerminateExtension(
    IN DWORD dwFlags
    )
/*++

Purpose:

    This is optional ISAPI extension DLL entry point.
    If present, it will be called before unloading the DLL,
    giving it a chance to perform any shutdown procedures.

Arguments:

    dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD

Return Value:

    TRUE if extension is ready to be unloaded,
    FALSE otherwise

--*/
{
    return TRUE;
}


README.TXT

This sample demonstrates how to maintain a Keep-Alive connection while using worker
threads in an ISAPI dll.  Please note the following points:

1. The way that this dll maintains the connection is in the following
line from WorkerFunction:

dwState = HSE_STATUS_SUCCESS_AND_KEEP_CONN;
pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, &dwState, NULL, 0);

The passing of dwState is what tells IIS to keep the connection open.

2. A thread pool is used because if the thread that makes the above call
to server support function exits or is terminated, the connection will
be closed by IIS.  Using a thread pool avoids this.

3. This dll creates a number of system objects, which are not
deallocated.  The presumption is that this dll will remain loaded until
the INETINFO.EXE process terminates; at which point these objects will
be released by the operating system.  However, if the WWW service is
stopped and restarted without the INETINFO.EXE process ending, these
objects will remain allocated, and new ones will be created when the dll
is reloaded.  If it is desired to implement this sample code in a
production environment, code to deallocate these objects should be added
in DllMain or TerminateExtension.


KEEPALIVEP.C

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name: KeepAliveP.c

Abstract:

      Sample ISAPI Extension demonstrating Keep-Alive with a thread pool.

--*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include <stdio.h>
#include "threadpool.h"

BOOL WINAPI
DllMain(
    IN HINSTANCE hinstDll,
    IN DWORD fdwReason,
    IN LPVOID lpvContext
)
/*++

Purpose:

    Initialize the thread pool when the DLL is loaded by IIS.

Arguments:

    hinstDLL - DLL instance handle
    fdwReason - notification code
    lpdvContext - reserved

Returns:

    TRUE if notification was successfully processed by the DLL
    FALSE to indicate a failure

--*/
{
BOOL fReturn = TRUE;

switch ( fdwReason ) {

case DLL_PROCESS_ATTACH:
fReturn = InitThreadPool( );
break;
}

return fReturn;
}

BOOL WINAPI
GetExtensionVersion(
    OUT HSE_VERSION_INFO * pVer
)
/*++

Purpose:

    This is required ISAPI Extension DLL entry point.

Arguments:

    pVer - poins to extension version info structure

Returns:

    always returns TRUE

--*/
{
pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );
lstrcpyn(
        pVer->lpszExtensionDesc,
        "ISAPI Keep-Alive with Thread Pool Extension Sample",
HSE_MAX_EXT_DLL_NAME_LEN );

return TRUE;
}

DWORD WINAPI
HttpExtensionProc(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:
    Demonstrate usage of persistent connections serviced by a thread pool.

Arguments:

    pECB - points to the extension control block

Returns:

    HSE_STATUS_PENDING if request was successfully queued
    HSE_SUCCESS_AND_KEEP_CONN if request was served immediately
        (presumably because the queue was full)

--*/
{
DWORD dwSize;
    HSE_SEND_HEADER_EX_INFO HeaderExInfo;

char szHeader[] =
        "Connection: Keep-Alive\r\n"
        "Content-Length: %lu\r\n"
        "Content-type: text/html\r\n\r\n";

char szBusyMessage[] =
        "<html> <form method=get action=KeepAliveP.dll> <input type=submit> "
    "<br>pECB->ConnID=%lu  <br>Server was too busy. </form></html>";

char szBuffer[4096];
char szBuffer2[4096];

EnterCriticalSection( &csQueueLock );

    if ( !AddWorkQueueEntry( pECB ) ) {

        //
        // if ECB could not be assigned
        //

LeaveCriticalSection( &csQueueLock );

sprintf( szBuffer2, szBusyMessage, pECB->ConnID );

        //
// Send outgoing header
        //

sprintf( szBuffer, szHeader, strlen( szBuffer2 ) );

        HeaderExInfo.pszHeader = szBuffer;
        HeaderExInfo.cchHeader = strlen( szBuffer );
        HeaderExInfo.pszStatus = "200 OK";
        HeaderExInfo.cchStatus = strlen( HeaderExInfo.pszStatus );
        HeaderExInfo.fKeepConn = TRUE;

pECB->ServerSupportFunction(
            pECB->ConnID,
            HSE_REQ_SEND_RESPONSE_HEADER_EX,
            &HeaderExInfo,
            NULL,
NULL
            );

        //
// Send content
        //

dwSize = strlen( szBuffer2 );
pECB->WriteClient( pECB->ConnID, szBuffer2, &dwSize, 0 );

return HSE_STATUS_SUCCESS_AND_KEEP_CONN;

} else {

        //
        // release 1 thread from the pool
        //

ReleaseSemaphore( hWorkSem, 1, NULL );

LeaveCriticalSection( &csQueueLock );
}

return HSE_STATUS_PENDING;
}

BOOL WINAPI
TerminateExtension(
    IN DWORD dwFlags
)
/*++

Routine Description:

    This function is called when the WWW service is shutdown

Arguments:

    dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD

Return Value:

    TRUE if extension is ready to be unloaded,
    FALSE otherwise

--*/
{
    return TRUE;
}

DWORD WINAPI
WorkerFunction(
    IN LPVOID pvThreadNum
)
/*++

Purpose:

    Worker thread function - simulates extended processing
    of the HTTP request

Arguments:

    pvThreadNum - thread number

Returns:

    alsways returns 0

--*/
{
EXTENSION_CONTROL_BLOCK *pECB;
DWORD dwRet, dwState, dwSize, dwThreadNum;
    HSE_SEND_HEADER_EX_INFO HeaderExInfo;

//This header will be filled in with the content length
char szHeader[] =
        "Connection: Keep-Alive\r\nContent-Length: %lu\r\n"
        "Content-type: text/html\r\n\r\n";

char szContent[] =
        "<html> <form method=get action=KeepAliveP.dll><input type=submit> "
    "<br>pECB->ConnID=%lu  <br>dwThreadNum=%lu</form></html>";

char szBuffer[4096];
char szBuffer2[4096];

dwThreadNum = ( DWORD ) pvThreadNum;

while ( TRUE ) {

dwRet = WaitForSingleObject( hWorkSem, INFINITE );
if ( dwRet == WAIT_OBJECT_0 ) {

EnterCriticalSection( &csQueueLock );

            if ( GetWorkQueueEntry( &pECB ) ) {

                //
                // Found work to do
                //

LeaveCriticalSection( &csQueueLock );

sprintf( szBuffer2, szContent, pECB->ConnID, dwThreadNum );

// Send outgoing header
sprintf( szBuffer, szHeader, strlen( szBuffer2 ) );

                HeaderExInfo.pszHeader = szBuffer;
                HeaderExInfo.cchHeader = strlen( szBuffer );
                HeaderExInfo.pszStatus = "200 OK";
                HeaderExInfo.cchStatus = strlen( HeaderExInfo.pszStatus );
                HeaderExInfo.fKeepConn = TRUE;

pECB->ServerSupportFunction(
                    pECB->ConnID,
                    HSE_REQ_SEND_RESPONSE_HEADER_EX,
&HeaderExInfo,
                    NULL,
                    NULL
                    );

                //
// Simulate extended processing
                //

Sleep( 3000 );

//
                // Send content
                //

dwSize = strlen( szBuffer2 );
pECB->WriteClient( pECB->ConnID, szBuffer2, &dwSize, 0 );

//
                // Tell IIS to keep the connection open
                //

dwState = HSE_STATUS_SUCCESS_AND_KEEP_CONN;

pECB->ServerSupportFunction(
                    pECB->ConnID,
                    HSE_REQ_DONE_WITH_SESSION,
                    &dwState,
                    NULL,
                    0
                    );

            } else {

                //
                // No item found is unexpected condition - exit thread
                //

LeaveCriticalSection( &csQueueLock );
ExitThread( 0 );
}

        } else {
break;
        }
}

return 0;
}


THREADPOOL.C

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    ThreadPool.c

Abstract:

    Work queue management functions.

--*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include "threadpool.h"

//
// Structure to create simple linked list
//

typedef struct {
EXTENSION_CONTROL_BLOCK *pECB;    // Data for list entry
DWORD dwNextEntry;        // Pointer to next entry
} ECB_QUEUE_ENTRY;

//
// Array that is a simple linked list
//

ECB_QUEUE_ENTRY ECBqueue[WORK_QUEUE_ENTRIES];

//
// Index of next ECBqueue entry to use, and last Entry in use.
//

DWORD dwCurrentEntry, dwLastEntry;

//
// Flag to indicate that there are no other entries in the ECBqueue
//

BOOL fQueueEmpty;

BOOL
InitThreadPool(
    void
)
/*++
Purpose:

    Initialize our thread pool.

--*/
{
DWORD i;
DWORD dwThreadID;

    //
    // Create Semaphore in nonsignaled state
    //

    if ( (hWorkSem = CreateSemaphore( NULL, 0, 0x7fffffff, NULL )) == NULL ) {
return FALSE;
    }

InitializeCriticalSection( &csQueueLock );

fQueueEmpty = TRUE;

    //
// Create Pool Threads
    //

for ( i = 0; i < POOL_THREADS; i++ ) {
if ( CreateThread(
                NULL,
                0,
                WorkerFunction,
                (LPVOID) i,
                0,
                &dwThreadID
                ) == NULL ) {
return FALSE;
        }
}

    //
// Clear work queue
    //

ZeroMemory( ECBqueue, WORK_QUEUE_ENTRIES * sizeof (ECB_QUEUE_ENTRY) );

return TRUE;
}

BOOL
AddWorkQueueEntry(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:

    Add single work unit to the queue

Arguments:

    pECB - pointer to the extension control block

Returns:

    TRUE if the unit was successfully queued
    FALSE if queue is full

--*/
{
DWORD i;

for ( i = 0; i < WORK_QUEUE_ENTRIES; i++ ) {
if ( ECBqueue[i].pECB == NULL ) {
if ( fQueueEmpty ) {
dwCurrentEntry = i;
fQueueEmpty = FALSE;
            } else {
ECBqueue[dwLastEntry].dwNextEntry = i;
            }

ECBqueue[i].pECB = pECB;
dwLastEntry = i;

return TRUE;
}
}

// If no NULL queue entry found, indicate failure
return FALSE;
}

BOOL
GetWorkQueueEntry(
    OUT EXTENSION_CONTROL_BLOCK ** ppECB
)
/*++

Purpose:

Arguments:

Returns:

--*/
{
    if ( (*ppECB = ECBqueue[dwCurrentEntry].pECB) == NULL ) {
return FALSE;
    } else {
ECBqueue[dwCurrentEntry].pECB = NULL;
if (dwCurrentEntry == dwLastEntry)// If this is only pending item

fQueueEmpty = TRUE;
else
dwCurrentEntry = ECBqueue[dwCurrentEntry].dwNextEntry;
}

return TRUE;
}


THREADPOOL.H

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    ThreadPool.h

--*/

// Number of threads in pool
#define POOL_THREADS 2

// Number of entries in ECBqueue
#define WORK_QUEUE_ENTRIES 2

// Global critical section to control access to ECB queue
CRITICAL_SECTION csQueueLock;

// Semaphore to wait on in worker thread; each time an ECB is added to the
// ECBqueue by HttpExtensionProc, the semaphore must be released once
HANDLE hWorkSem;

// These functions will add/retrieve an ECB to/from the linked list.
// ENTER csQueueLock BEFORE CALLING AND LEAVE csQueueLock AFTER
//   RETURNING FROM THESE FUNCTIONS!!!
BOOL AddWorkQueueEntry(EXTENSION_CONTROL_BLOCK *);
BOOL GetWorkQueueEntry(EXTENSION_CONTROL_BLOCK ** ppECB);

// This function initializes the thread pool
BOOL InitThreadPool(void);

// Function that threads in pool run
DWORD WINAPI WorkerFunction(LPVOID);


README.TXT

Description:

Redirect is a sample ISAPI extension to demonstrate redirecting
a request.  It redirects requests to a URL specified on the
query string.  If no query string is present, or if the query
string is not identified as a legal target for redirection,
Redirect.dll will return a page to the client with brief
instructions for its use.

Redirections to a resource on the same server as the dll will
be handled by IIS and will be transparent to the browser.
Redirections to a resource on a different server will result
in an HTTP 302 response instructing the browser to obtain the
resource from another location.

To Install:

Build the redirect.dsw project and copy the resulting Redirect.dll
file into a virtual directory containing execute permissions.

To Run:

Reference the Redirect.dll file as a URL from a browser, specifying
a URL on the query string.

For example, the following URL entered on the address line of
Internet Explorer will redirect the request to the specified
URL on a different server:

   http://server/scripts/Redirect.dll?http://server2/virtualdir/file.htm

The following URL entered on the address line will redirect the
request to the specified resource on the same server:

   http://server/scripts/Redirect.dll?/virtualdir/file.htm

Disclaimer:

This sample, and the included automation dll, is provided for the
purposes of demonstrating an ISAPI extension.  It has not been
tested for use in a production environment and no support will
be provided for use in a production environment.


REDIRECT.CPP

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    redirect.cpp

Abstract:

    Redirect is a sample ISAPI extension to demonstrate redirecting
    a request.  It redirects requests to a URL specified on the
    query string.

--*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include <stdio.h>

#define BUFF_SIZE   2048

//
// local prototypes
//

DWORD SendInstructionPage( EXTENSION_CONTROL_BLOCK * pECB );

BOOL WINAPI
GetExtensionVersion(
    OUT HSE_VERSION_INFO * pVer
)
/*++

Purpose:

    This is required ISAPI Extension DLL entry point.

Arguments:

    pVer - poins to extension version info structure

Returns:

    always returns TRUE

--*/
{
pVer->dwExtensionVersion =
        MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );

lstrcpyn(
        pVer->lpszExtensionDesc,
        "Redirect ISAPI Sample",
        HSE_MAX_EXT_DLL_NAME_LEN );

return TRUE;
}

DWORD WINAPI
HttpExtensionProc(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:

    If no query string is present, or if the query
    string is not identified as a legal target for redirection,
    Redirect.dll will return a page to the client with brief
    instructions for its use.

    Redirections to a resource on the same server as the dll will
    be handled by IIS and will be transparent to the browser
    (HSE_REQ_SEND_URL).

    Redirections to a resource on a different server will result
    in an HTTP 302 response instructing the browser to obtain the
    resource from another location (HSE_REQ_SEND_URL_REDIRECT_RESP).

Arguments:

    pECB - pointer to the extenstion control block

Returns:

    HSE_STATUS_SUCCESS on successful completion
    HSE_STATUS_ERROR on failure

--*/
{
    //
// If no query string is present, return the instruction page
    //

    if ( !strlen( pECB->lpszQueryString ) ) {

return SendInstructionPage( pECB );
    }

    //
// Check to see if the redirect URL is on another server. If it is, use
// HSE_REQ_SEND_URL_REDIRECT_RESP.  If it's on this local server, use
// HSE_REQ_SEND_URL to return the specified URL without using an HTTP 302
// status code.
    //

DWORD dwBuffSize = strlen( pECB->lpszQueryString );

    if ( !strnicmp( pECB->lpszQueryString, "http://", 7 ) ) {

        pECB->ServerSupportFunction(
            pECB->ConnID,
            HSE_REQ_SEND_URL_REDIRECT_RESP,
            pECB->lpszQueryString,
            &dwBuffSize,
            NULL
            );

    } else {

        //
// Check to make sure that query string begins with a '/'.
        //

        if ( *( pECB->lpszQueryString ) != '/' ) {

return SendInstructionPage( pECB );
        }

pECB->ServerSupportFunction(
            pECB->ConnID,
            HSE_REQ_SEND_URL,
            pECB->lpszQueryString,
            &dwBuffSize,
            NULL
            );
}

return HSE_STATUS_SUCCESS;
}

DWORD
SendInstructionPage(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:
    Send short http usage description to the user.

Arguments:

    pECB - pointer to the extenstion control block

Returns:

    HSE_STATUS_SUCCESS on successful completion
    HSE_STATUS_ERROR on failure

--*/
{

char szStatus[] = "200 OK";
char szContent[] =
"<html>"
"<head><title>Redirect URL</title></head>"
"<body>"
"<h1>Redirect.dll</h1>\r\n<hr>\r\n"
"Redirect.dll returns the resource specified on the query string.<br>\r\n<br>\r\n"
"To specify a resource on the same server as Redirect.dll, use the following form:<br>\r\n<br>\r\n"
"<code> http://server/scripts/Redirect.dll?/virtualdir/file.htm </code><br>\r\n<br>\r\n"
"To specify a resource on another server, use the following form:<br>\r\n<br>\r\n"
"<code> http://server/scripts/Redirect.dll?http://server/virtualdir/file.htm </code>"
"</body>"
"</html>";

char szHeaderBase[] = "Content-Length: %lu\r\nContent-type: text/html\r\n\r\n";
char szHeader[BUFF_SIZE];

//
//  fill in byte count in Content-Length header
//

    DWORD cchContent = strlen( szContent );

sprintf( szHeader, szHeaderBase, cchContent );

//
//  Populate SendHeaderExInfo struct
//

HSE_SEND_HEADER_EX_INFO SendHeaderExInfo;

SendHeaderExInfo.pszStatus = szStatus;
SendHeaderExInfo.pszHeader = szHeader;
SendHeaderExInfo.cchStatus = strlen( szStatus );
SendHeaderExInfo.cchHeader = strlen( szHeader );
SendHeaderExInfo.fKeepConn = FALSE;

    //
    // Send header
    //

if ( !pECB->ServerSupportFunction(
pECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER_EX,
&SendHeaderExInfo,
NULL,
NULL
    ) ) {

return HSE_STATUS_ERROR;
}

//
//  Send content
//

if ( !pECB->WriteClient( pECB->ConnID, szContent, &cchContent, 0 ) ) {

return HSE_STATUS_ERROR;
}

return HSE_STATUS_SUCCESS;

}

BOOL WINAPI
TerminateExtension(
    IN DWORD dwFlags
)
/*++

Routine Description:

    This function is called when the WWW service is shutdown

Arguments:

    dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD

Return Value:

    TRUE if extension is ready to be unloaded,
    FALSE otherwise

--*/
{
    return TRUE;
}


README.TXT

Description:

DumpVars is an ISAPI sample to demonstrate retrieving server
variables.  It uses GetServerVariable to get the value of each
server variable documented for IIS 4.0, and sends the list
of variables and values back to the client.

To Install:

Build the DumpVars.dsw project and copy the resulting
DumpVars.dll file into a virtual directory containing execute
permissions.

To Run:

Reference the DumpVars.dll file as a URL from a browser.  For
example, the following URL entered on the address line of
Internet Explorer will demonstrate the dll:

   http://server/scripts/dumpvars.dll

Disclaimer:

This sample is provided for the purposes of demonstrating an
ISAPI extension.  It has not been tested for use in a production
environment and no support will be provided for use in a
production environment.


DUMPVARS.CPP

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    DumpVars.cpp

Abstract:

    ISAPI Extension sample to dump server variables

--*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>

DWORD WINAPI
HttpExtensionProc(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:

    Use WriteClient() function to dump the value of each
    server variable in the table.

Arguments:

    pECB - pointer to the extenstion control block

Returns:

    HSE_STATUS_SUCCESS

--*/
{
char * aszServerVariables[] =
    {"APPL_MD_PATH", "APPL_PHYSICAL_PATH", "AUTH_PASSWORD",
"AUTH_TYPE", "AUTH_USER", "CERT_COOKIE", "CERT_FLAGS",
"CERT_ISSUER", "CERT_KEYSIZE", "CERT_SECRETKEYSIZE",
"CERT_SERIALNUMBER", "CERT_SERVER_ISSUER",
"CERT_SERVER_SUBJECT", "CERT_SUBJECT", "CONTENT_LENGTH",
"CONTENT_TYPE", "HTTP_ACCEPT", "HTTPS", "HTTPS_KEYSIZE",
"HTTPS_SECRETKEYSIZE", "HTTPS_SERVER_ISSUER",
"HTTPS_SERVER_SUBJECT", "INSTANCE_ID", "INSTANCE_META_PATH",
"PATH_INFO", "PATH_TRANSLATED", "QUERY_STRING",
"REMOTE_ADDR", "REMOTE_HOST", "REMOTE_USER",
"REQUEST_METHOD", "SCRIPT_NAME", "SERVER_NAME",
"SERVER_PORT", "SERVER_PORT_SECURE", "SERVER_PROTOCOL",
"SERVER_SOFTWARE", "URL"};
char szOutput[2048], szValue[1024];
DWORD dwBuffSize, dwNumVars, dwError, x;
    HSE_SEND_HEADER_EX_INFO HeaderExInfo;

    //
// Send headers to the client
    //
    HeaderExInfo.pszStatus = "200 OK";
    HeaderExInfo.pszHeader = "Content-type: text/html\r\n\r\n";
    HeaderExInfo.cchStatus = strlen( HeaderExInfo.pszStatus );
    HeaderExInfo.cchHeader = strlen( HeaderExInfo.pszHeader );
    HeaderExInfo.fKeepConn = FALSE;

    pECB->ServerSupportFunction(
        pECB->ConnID,
        HSE_REQ_SEND_RESPONSE_HEADER_EX,
        &HeaderExInfo,
        NULL,
        NULL
        );

    //
// Begin sending back HTML to the client
    //

strcpy(
        szOutput,
        "<HTML>\r\n<BODY><h1>Server Variable Dump</h1>\r\n<hr>\r\n"
        );
dwBuffSize = strlen( szOutput );
pECB->WriteClient( pECB->ConnID, szOutput, &dwBuffSize, 0 );

dwNumVars = ( sizeof aszServerVariables )/( sizeof aszServerVariables[0] );

    //
// Get the server variables and send them
    //

for ( x = 0; x < dwNumVars; x++ ) {

dwBuffSize = 1024;
        szValue[0] = '\0';
if ( !pECB->GetServerVariable(
                pECB->ConnID,
                aszServerVariables[x],
                szValue,
                &dwBuffSize
                ) ) {

            //
            // Analyze the problem and report result to user
            //

switch (dwError = GetLastError( )) {
case ERROR_INVALID_PARAMETER:
strcpy( szValue, "ERROR_INVALID_PARAMETER" );
break;

case ERROR_INVALID_INDEX:
strcpy( szValue, "ERROR_INVALID_INDEX" );
break;

case ERROR_INSUFFICIENT_BUFFER:
wsprintf(
                    szValue,
                    "ERROR_INSUFFICIENT_BUFFER - %d bytes required.",
                    dwBuffSize
                    );
break;

case ERROR_MORE_DATA:
strcpy( szValue, "ERROR_MORE_DATA" );
break;

case ERROR_NO_DATA:
strcpy( szValue, "ERROR_NO_DATA" );
break;

default:
wsprintf(
                    szValue,
                    "*** Error %d occured retrieving server variable ***",
                    dwError
                    );
}
}

        //
        // Dump server variable name and value
        //

wsprintf( szOutput, "%s: %s<br>\r\n", aszServerVariables[x], szValue );
dwBuffSize = strlen( szOutput );

        //
        // Send the line to client
        //

pECB->WriteClient( pECB->ConnID, szOutput, &dwBuffSize, 0 );
}

//
    // End HTML page
    //

strcpy( szOutput, "</BODY>\r\n</HTML>\r\n\r\n" );
dwBuffSize = strlen( szOutput );
pECB->WriteClient( pECB->ConnID, szOutput, &dwBuffSize, 0 );

return HSE_STATUS_SUCCESS;
}

BOOL WINAPI
GetExtensionVersion(
    OUT HSE_VERSION_INFO * pVer
 )
/*++

Purpose:

    This is required ISAPI Extension DLL entry point.

Arguments:

    pVer - poins to extension version info structure

Returns:

    always returns TRUE

--*/
{
pVer->dwExtensionVersion =
        MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );

lstrcpyn(
        pVer->lpszExtensionDesc,
        "DumpVars ISAPI Sample",
        HSE_MAX_EXT_DLL_NAME_LEN );

return TRUE;
}

BOOL WINAPI
TerminateExtension(
    IN DWORD dwFlags
)
/*++

Routine Description:

    This function is called when the WWW service is shutdown

Arguments:

    dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD

Return Value:

    TRUE if extension is ready to be unloaded,
    FALSE otherwise

--*/
{
    return TRUE;
}


SIMPLE.CPP

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    Simple.cpp

Abstract:

    This module shows the basic functions needed for ISAPI extension

--*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>

BOOL WINAPI
DllMain(
    IN HINSTANCE hinstDll,
    IN DWORD dwReason,
    IN LPVOID lpvContext
)
/*++
Function :  DllMain

Description:

    The initialization function for this DLL.

Arguments:

    hinstDll - Instance handle of the DLL
    dwReason - Reason why NT called this DLL
    lpvContext - Reserved parameter for future use

Return Value:

    Returns TRUE if successfull; otherwise FALSE.

--*/
{
    // Note that appropriate initialization and termination code
    // would be written within the switch statement below.  Because
    // this example is very simple, none is currently needed.

    switch( dwReason ) {
    case DLL_PROCESS_ATTACH:
        break;

    case DLL_PROCESS_DETACH:
        break;
    }

    return(TRUE);
}

BOOL WINAPI
GetExtensionVersion(
    OUT HSE_VERSION_INFO * pVer
)
/*++

Purpose:

    The first function called after IIS successfully
    loads the DLL.  The function should use the
    version structure provided by IIS to set the ISAPI
    architectural version number of this extension.

    A simple text-string is also set so that
    administrators can identify the DLL.

    Note that HSE_VERSION_MINOR and HSE_VERSION_MAJOR
    are constants defined in httpext.h.

Arguments:

    pVer - points to extension version structure

Return Value:

    TRUE if successful; FALSE otherwise.

--*/
{
    pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR,
                                             HSE_VERSION_MAJOR );

    strcpy( pVer->lpszExtensionDesc,
            "IIS SDK Simple ISAPI Extension" );

    return TRUE;
}

DWORD WINAPI
HttpExtensionProc(
    IN EXTENSION_CONTROL_BLOCK * pECB
)
/*++

Purpose:

    Function called by the IIS Server when a request
    for the ISAPI dll arrives.  The HttpExtensionProc
    function processes the request and outputs the
    appropriate response to the web client using
    WriteClient().

Argument:

    pECB - pointer to extention control block.

Return Value:

    HSE_STATUS_SUCCESS

--*/
{
    static char szMessage[] =
    "<HTML>"
    "<HEAD><TITLE> Simple ISAPI Extension DLL </TITLE>"
    "</HEAD>\r\n"
    "<BODY>"
    "<P>Hello from Simple ISAPI Extension DLL!</P>\r\n"
    "</BODY></HTML>\r\n\r\n";

    HSE_SEND_HEADER_EX_INFO HeaderExInfo;

    //
    // prepare headers
    //

    HeaderExInfo.pszStatus = "200 OK";
    HeaderExInfo.pszHeader = "Content-type: text/html\r\n\r\n";
    HeaderExInfo.cchStatus = strlen( HeaderExInfo.pszStatus );
    HeaderExInfo.cchHeader = strlen( HeaderExInfo.pszHeader );
    HeaderExInfo.fKeepConn = FALSE;

    //
    // send headers using IIS-provided callback
    // (note - if we needed to keep connection open,
    //  we would set fKeepConn to TRUE *and* we would
    //  need to provide correct Content-Length: header)

    pECB->ServerSupportFunction(
        pECB->ConnID,
        HSE_REQ_SEND_RESPONSE_HEADER_EX,
        &HeaderExInfo,
        NULL,
        NULL
        );

    //
    // Calculate length of string to output to client
    //

    DWORD dwBytesToWrite = strlen( szMessage );

    //
    // send text using IIS-provied callback
    //

    pECB->WriteClient( pECB->ConnID, szMessage, &dwBytesToWrite, 0 );

    //
    // Indicate that the call to HttpExtensionProc was successful
    //

    return HSE_STATUS_SUCCESS;
}

BOOL WINAPI
TerminateExtension(
    IN DWORD dwFlags
)
/*++

Routine Description:

    This function is called when the WWW service is shutdown

Arguments:

    dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD

Return Value:

    TRUE if extension is ready to be unloaded,
    FALSE otherwise

--*/
{
    // Note: We must not agree to be unloaded if we have
    // any pending requests.

    return TRUE;
}


WORKERTHREAD.C

/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:    WorkerThread.c

Abstract:

    IIS maintains a pool of threads to handle incoming HTTP requests.  When all of
    these threads are in use, new requests will be rejected.  If all the pool threads
    are in a wait state (for instance, running ISAPI dlls that are waiting for a query
    on a remote database to complete), IIS may reject incoming requests even if there
    is plenty of CPU power to handle them.

    One way of avoiding this situation is to offload processing of these types of
    requests to a worker thread, releasing the IIS thread back to the pool so that it
    can be used for another request.  This basic sample demonstrates how to implement
    this in an ISAPI dll.

*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include <stdio.h>

//
// global variables
//

DWORD g_dwThreadCount = 0;

//
// local functions prototypes
//

DWORD WINAPI WorkerFunction( LPVOID );
BOOL SendHttpHeaders(EXTENSION_CONTROL_BLOCK *, LPCSTR , LPCSTR, BOOL );

BOOL WINAPI
GetExtensionVersion(
    OUT HSE_VERSION_INFO *pVer
)
/*++

Purpose:

    This is required ISAPI Extension DLL entry point.

Arguments:

    pVer - poins to extension version info structure

Returns:

    always returns TRUE

--*/
{

    //
    // tell the server our version number and extension description
    //

    pVer->dwExtensionVersion =
        MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );

    lstrcpyn(
        pVer->lpszExtensionDesc,
        "ISAPI Worker Thread Extension Sample",
        HSE_MAX_EXT_DLL_NAME_LEN
        );

return TRUE;
}

DWORD WINAPI
HttpExtensionProc(
    IN EXTENSION_CONTROL_BLOCK *pECB
)
/*++

Purpose:

    Create a thread to handle extended processing. It will be passed
    the address of a function ("WorkerFunction") to run, and the address
    of the ECB associated with this session.

Arguments:

    pECB - pointer to the extenstion control block

Returns:

    HSE_STATUS_PENDING to mark this request as pending

--*/
{
    DWORD dwThreadID;

    // (NOTE: in production environment you'd probably
    //  wanted to limit the number of threads created)

    CreateThread(NULL,              // Pointer to thread security attributes
                 0,                 // Initial thread stack size, in bytes
                 WorkerFunction,    // Pointer to thread function
                 pECB,              // The ECB is the argument for the new thread
                 0,                 // Creation flags
                 &dwThreadID        // Pointer to returned thread identifier
                 );

    //
    // update global thread count
    //

    InterlockedIncrement( &g_dwThreadCount );

    // Return HSE_STATUS_PENDING to release IIS pool thread without losing connection

    return HSE_STATUS_PENDING;
}

DWORD WINAPI
WorkerFunction(
    IN LPVOID vECB
)
/*++

Purpose:

    Show how to perform extended processing in ISAPI DLL without
    tying IIS threads.

Arguments:

    vECB - points to current extension control block

Returns:

    returns 0

--*/
{
    char szHeader[] =   "Content-type: text/html\r\n\r\n";
    char szContent[]=   "<html> <form method=get action=WorkerThread.dll><h1>Worker Thread Sample</h1><hr>"
                        "<input type=submit value=\"Send Request\"> </form></html>";
    EXTENSION_CONTROL_BLOCK *pECB;
    DWORD dwSize;

    //
    // Initialize local ECB pointer to void pointer passed to thread
    //

    pECB = vECB;

    //
    // Send outgoing header
    //

    SendHttpHeaders( pECB, "200 OK", szHeader, FALSE );

    //
    // Simulate extended processing for 5 seconds
    //

    Sleep(5000);

    //
    // Send content
    //

    dwSize = strlen( szContent );
    pECB->WriteClient( pECB->ConnID, szContent, &dwSize, 0 );

    //
    // Inform server that the request has been satisfied,
    // and the connection may now be dropped
    //

    pECB->ServerSupportFunction(
        pECB->ConnID,
        HSE_REQ_DONE_WITH_SESSION,
        NULL,
        NULL,
        NULL
        );

    //
    // update global thread count
    //

    InterlockedDecrement( &g_dwThreadCount );

    return 0;
}

BOOL
SendHttpHeaders(
    EXTENSION_CONTROL_BLOCK *pECB,
    LPCSTR pszStatus,
    LPCSTR pszHeaders,
    BOOL fKeepConnection
)
/*++

Purpose:
    Send specified HTTP status string and any additional header strings
    using new ServerSupportFunction() request HSE_SEND_HEADER_EX_INFO

Arguments:

    pECB - pointer to the extension control block
    pszStatus - HTTP status string (e.g. "200 OK")
    pszHeaders - any additional headers, separated by CRLFs and
                 terminated by empty line

Returns:

    TRUE if headers were successfully sent
    FALSE otherwise

--*/
{
    HSE_SEND_HEADER_EX_INFO header_ex_info;
    BOOL success;

    header_ex_info.pszStatus = pszStatus;
    header_ex_info.pszHeader = pszHeaders;
    header_ex_info.cchStatus = strlen( pszStatus );
    header_ex_info.cchHeader = strlen( pszHeaders );
    header_ex_info.fKeepConn = fKeepConnection;

    success = pECB->ServerSupportFunction(
                  pECB->ConnID,
                  HSE_REQ_SEND_RESPONSE_HEADER_EX,
                  &header_ex_info,
                  NULL,
                  NULL
                  );

    return success;
}

BOOL WINAPI
TerminateExtension(
    IN DWORD dwFlags
)
/*++

Routine Description:

    This function is called when the WWW service is shutdown.

Arguments:

    dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD

Return Value:

    TRUE when extension is ready to be unloaded,

--*/
{

    // (Our threads are deterministic and will complete
    //  not much later than 5 seconds from now)

    //
    // wait for all threads to terminate, sleeping for 1 sec
    //

    while( g_dwThreadCount > 0 ) {
        SleepEx( 1000, FALSE );
    }

    //
    // make sure the last thread indeed exited
    //

    SleepEx( 1000, FALSE );

    return TRUE;
}


README.TXT

Microsoft Internet Information Server
AuthFilt - A Filter for Advanced Authentication

AuthFilt demonstrates how to write an authentication filter based on an
external datasource. Authentication is the process of accepting or
denying a request from a client, so AuthFilt will be notified each time
an authentication request comes in. This sample uses a file
(c:\inetsrv\userdb.txt) to keep track of authorized users, but you might
modify this sample to access a database which holds user info.  The file
c:\inetsrv\userdb.txt does not exist.  Please look at Db.c to see how to
construct such a file and change the location of the file if you so desire.

For each authentication request, AuthFilt first looks in a cache of
recently authenticated users, and when that fails, AuthFilt looks in the
userdb.txt file. This shows an efficient way to authorize connections:
a cache allows the filter to quickly authenticate users, and because
each request comes in through the filter, speed is critical.

Steps to build the sample:

1. Type "nmake" at the command line in the AuthFilt directory.  If you
   encounter problems with missing header files, make sure your INCLUDE
   variable points to the Win32 SDK include directory, the VC++ include
   directory, and the IIS 4.0 include directory.  Also, make sure LIB
   variable points to the Win32 SDK lib directory.

OR

Load the project file (Authfilt.dsp) into VC++ and build the project
by running the build AuthFilt.dll command.

2. Run the Internet Service Manager and add the AuthFilt.dll with a fully
   qualified path either as a global filter or a Website filter.


AUTHFILT.C

/*++

Copyright (c) 1996  Microsoft Corporation

This program is released into the public domain for any purpose.

Module Name:

    authfilt.c

Abstract:

    This module is an example of an ISAPI Authentication Filter.

    It demonstrates how to do an authentication filter based on an external
    datasource.  Though this sample uses a flat file, access to a database
    could easily be plugged in.

--*/

#include <windows.h>
#include <httpfilt.h>
#include "authfilt.h"

//
// Functions
//

BOOL
WINAPI
DllMain(
     IN HINSTANCE hinstDll,
     IN DWORD     fdwReason,
     IN LPVOID    lpvContext OPTIONAL
     )
/*++

 Routine Description:

   This function DllLibMain() is the main initialization function for
    this DLL. It initializes local variables and prepares it to be invoked
    subsequently.

 Arguments:

   hinstDll          Instance Handle of the DLL
   fdwReason         Reason why NT called this DLL
   lpvReserved       Reserved parameter for future use.

 Return Value:

    Returns TRUE is successful; otherwise FALSE is returned.
--*/
{
    BOOL        fReturn = TRUE;

    switch (fdwReason )
    {
    case DLL_PROCESS_ATTACH:

        if ( !InitializeUserDatabase() ||
             !InitializeCache() )
        {
            DbgWrite(( DEST,
                       "[GetFilterVersion] Database or cache failed, error %d\n",
                       GetLastError() ))

            return FALSE;
        }

        //
        //  We don't care about thread attach/detach notifications
        //

        DisableThreadLibraryCalls( hinstDll );

        break;

    case DLL_PROCESS_DETACH:
        {

            if ( lpvContext != NULL)
            {
                TerminateCache();
                TerminateUserDatabase();
            }

            break;
        } /* case DLL_PROCESS_DETACH */

    default:
        break;
    }   /* switch */

    return ( fReturn);
}  /* DllLibMain() */

BOOL
WINAPI
GetFilterVersion(
    HTTP_FILTER_VERSION * pVer
    )
{
    DbgWrite(( DEST,
               "[GetFilterVersion] Server filter version is %d.%d\n",
               HIWORD( pVer->dwServerFilterVersion ),
               LOWORD( pVer->dwServerFilterVersion ) ));

    pVer->dwFilterVersion = HTTP_FILTER_REVISION;

    //
    //  Specify the types and order of notification
    //

    pVer->dwFlags = (SF_NOTIFY_SECURE_PORT        |
                     SF_NOTIFY_NONSECURE_PORT     |

                     SF_NOTIFY_AUTHENTICATION     |
                     SF_NOTIFY_LOG                |

                     SF_NOTIFY_ORDER_DEFAULT);

    strcpy( pVer->lpszFilterDesc, "Sample Authentication Filter, version 1.0" );

    return TRUE;
}

DWORD
WINAPI
HttpFilterProc(
    HTTP_FILTER_CONTEXT *      pfc,
    DWORD                      NotificationType,
    VOID *                     pvData
    )
/*++

Routine Description:

    Filter notification entry point

Arguments:

    pfc -              Filter context
    NotificationType - Type of notification
    pvData -           Notification specific data

Return Value:

    One of the SF_STATUS response codes

--*/
{
    BOOL                  fAllowed;
    CHAR                  achUser[SF_MAX_USERNAME];
    HTTP_FILTER_AUTHENT * pAuth;
    HTTP_FILTER_LOG *     pLog;
    CHAR *                pch;

    //
    //  Handle this notification
    //

    switch ( NotificationType )
    {
    case SF_NOTIFY_AUTHENTICATION:

        pAuth = (HTTP_FILTER_AUTHENT *) pvData;

        //
        //  Ignore the anonymous user
        //

        if ( !*pAuth->pszUser )
        {
            //
            //  Tell the server to notify any subsequent notifications in the
            //  chain
            //

            return SF_STATUS_REQ_NEXT_NOTIFICATION;
        }

        //
        //  Save the unmapped username so we can log it later
        //

        strcpy( achUser, pAuth->pszUser );

        //
        //  Make sure this user is a valid user and map to the appropriate
        //  Windows NT user
        //

        if ( !ValidateUser( pAuth->pszUser,
                            pAuth->pszPassword,
                            &fAllowed ))
        {
            DbgWrite(( DEST,
                       "[OnAuthentication] Error %d validating user %s\n",
                       GetLastError(),
                       pAuth->pszUser ));

            return SF_STATUS_REQ_ERROR;
        }

        if ( !fAllowed )
        {
            //
            //  This user isn't allowed access.  Indicate this to the server
            //

            SetLastError( ERROR_ACCESS_DENIED );

            return SF_STATUS_REQ_ERROR;
        }

        //
        //  Save the unmapped user name so we can log it later on.  We allocate
        //  enough space for two usernames so we can use this memory block
        //  for logging.  Note we may have already allocated it from a previous
        //  request on this TCP session
        //

        if ( !pfc->pFilterContext )
        {
            pfc->pFilterContext = pfc->AllocMem( pfc, 2 * SF_MAX_USERNAME + 4, 0 );

            if ( !pfc->pFilterContext )
            {
                SetLastError( ERROR_NOT_ENOUGH_MEMORY );
                return SF_STATUS_REQ_ERROR;
            }
        }

        strcpy( (CHAR *) pfc->pFilterContext, achUser );

        return SF_STATUS_REQ_HANDLED_NOTIFICATION;

    case SF_NOTIFY_LOG:

        //
        //  The unmapped username is in pFilterContext if this filter
        //  authenticated this user
        //

        if ( pfc->pFilterContext )
        {
            pch  = pfc->pFilterContext;
            pLog = (HTTP_FILTER_LOG *) pvData;

            //
            //  Put both the original username and the NT mapped username
            //  into the log in the form "Original User (NT User)"
            //

            strcat( pch, " (" );
            strcat( pch, pLog->pszClientUserName );
            strcat( pch, ")" );

            pLog->pszClientUserName = pch;
        }

        return SF_STATUS_REQ_NEXT_NOTIFICATION;

    default:
        DbgWrite(( DEST,
                "[HttpFilterProc] Unknown notification type, %d\n",
                NotificationType ));

        break;
    }

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}


AUTHFILT.H

/*++

Copyright (c) 1996  Microsoft Corporation

This program is released into the public domain for any purpose.

Module Name:

    authfilt.h

Abstract:

    This module contains the common definitions for the authentication filter
    sample

--*/

#ifndef _AUTHFILT_H_
#define _AUTHFILT_H_

//
//  Constants
//

#define ISWHITE( ch )      ((ch) && ((ch) == ' ' || (ch) == '\t' ||  \
                            (ch) == '\n' || (ch) == '\r'))

#if DBG
#define DEST               buff
#define DbgWrite( x )      {                                    \
                                char buff[256];                 \
                                wsprintf x;                     \
                                OutputDebugString( buff );      \
                           }
#else
#define DbgWrite( x )      /* nothing */
#endif

//
//  Prototypes
//

//
//  Database routines
//

BOOL
InitializeUserDatabase(
    VOID
    );

BOOL
ValidateUser(
    CHAR * pszUserName,
    CHAR * pszPassword,
    BOOL * pfValid
    );

BOOL
LookupUserInDb(
    IN CHAR * pszUser,
    OUT BOOL * pfFound,
    OUT CHAR * pszPassword,
    OUT CHAR * pszNTUser,
    OUT CHAR * pszNTUserPassword
    );

VOID
TerminateUserDatabase(
    VOID
    );

//
//  Cache routines
//

BOOL
InitializeCache(
    VOID
    );

BOOL
LookupUserInCache(
    CHAR * pszUserName,
    BOOL * pfFound,
    CHAR * pszPassword,
    CHAR * pszNTUser,
    CHAR * pszNTUserPassword
    );

BOOL
AddUserToCache(
    CHAR * pszUserName,
    CHAR * pszPassword,
    CHAR * pszNTUser,
    CHAR * pszNTUserPassword
    );

VOID
TerminateCache(
    VOID
    );

#endif //_AUTHFILT_H_


CACHE.C

/*++

Copyright (c) 1996  Microsoft Corporation

This program is released into the public domain for any purpose.

Module Name:

    cache.c

Abstract:

    This module implements a simple user cache.  The cached users are kept
    in an LRU sorted list.  If there will be a large number of simultaneous
    users, then a sorted array would be more appropriate.

--*/

#include <windows.h>
#include <httpfilt.h>
#include "authfilt.h"

//
// Constants
//

//
//  The maximum number of users we will cache.  If there will be a large number
//  of simultaneous users, bump this value
//

#define MAX_CACHED_USERS        100

//
//  The position after which we'll move a cache entry to the front of the list
//

#define LIST_REORDER_THRESHOLD  6

//
//  Cached user structure
//

typedef struct _USER_INFO
{
    LIST_ENTRY  ListEntry;  // Double linked list entry

    CHAR  achUserName[SF_MAX_USERNAME];   // External username and password
    CHAR  achPassword[SF_MAX_PASSWORD];

    CHAR  achNTUserName[SF_MAX_USERNAME]; // NT account and password to map user to
    CHAR  achNTUserPassword[SF_MAX_PASSWORD];

} USER_INFO, *PUSER_INFO;

//
//  Globals
//

//
//  Circular double linked list of cached users
//

LIST_ENTRY CacheListHead;

//
//  Critical section protects cache list
//

CRITICAL_SECTION csCacheLock;

//
//  Indicates whether we are initialized
//

BOOL fCacheInitialized = FALSE;

//
//  Number of items in the cache
//

DWORD cCacheItems = 0;

BOOL
InitializeCache(
    VOID
    )
/*++

Routine Description:

    Initializes the cache module

Return Value:

    TRUE if initialized successfully, FALSE on error

--*/
{
    if ( fCacheInitialized )
        return TRUE;

    InitializeCriticalSection( &csCacheLock );
    CacheListHead.Blink = CacheListHead.Flink = &CacheListHead;

    fCacheInitialized = TRUE;

    return TRUE;
}

BOOL
LookupUserInCache(
    CHAR * pszUserName,
    BOOL * pfFound,
    CHAR * pszPassword,
    CHAR * pszNTUser,
    CHAR * pszNTUserPassword
    )
/*++

Routine Description:

    Checks to see if a user is in the cache and returns the user properties
    if found

Arguments:

    pszUserName - Case insensitive username to find
    pfFound     - Set to TRUE if the specified user was found
    pszPassword - Receives password for specified user if found
    pszNTUser   - Receives the NT Username to map this user to
    pszNTUserPassword - Receives the NT Password for pszNTUser

    Note: pszPassword and pszNTUserPassword must be at least SF_MAX_PASSWORD
    characters.  pszNTUser must be at least SF_MAX_USERNAME characters.

Return Value:

    TRUE if no errors occurred.

--*/
{
    LIST_ENTRY * pEntry;
    USER_INFO *  pUser;
    DWORD        cPosition = 0;

    //
    //  Search the cache for the specified user
    //

    EnterCriticalSection( &csCacheLock );

    for ( pEntry  = CacheListHead.Flink;
          pEntry != &CacheListHead;
          pEntry = pEntry->Flink )
    {
        pUser = CONTAINING_RECORD( pEntry, USER_INFO, ListEntry );

        if ( !stricmp( pszUserName, pUser->achUserName ))
        {
            goto Found;
        }

        cPosition++;
    }

    LeaveCriticalSection( &csCacheLock );

    //
    //  Not Found
    //

    *pfFound = FALSE;
    return TRUE;

Found:

    //
    //  Copy out the user properties
    //

    strcpy( pszPassword,       pUser->achPassword );
    strcpy( pszNTUser,         pUser->achNTUserName );
    strcpy( pszNTUserPassword, pUser->achNTUserPassword );

    //
    //  Move this user entry to the front of the list as we're probably going
    //  to get subsequent requests for this user.  Note we only move it
    //  if it's not already near the front
    //

    if ( cPosition > LIST_REORDER_THRESHOLD )
    {
        //
        //  Remove from the old position...
        //

        pEntry->Blink->Flink = pEntry->Flink;
        pEntry->Flink->Blink = pEntry->Blink;

        //
        // ...and insert it at the beginning of the list
        //

        pEntry->Blink = &CacheListHead;
        pEntry->Flink = CacheListHead.Flink;

        CacheListHead.Flink->Blink = pEntry;
        CacheListHead.Flink        = pEntry;
    }

    LeaveCriticalSection( &csCacheLock );

    *pfFound = TRUE;

    return TRUE;
}

BOOL
AddUserToCache(
    CHAR * pszUserName,
    CHAR * pszPassword,
    CHAR * pszNTUser,
    CHAR * pszNTUserPassword
    )
/*++

Routine Description:

    Adds the specified user to the cache

Arguments:

    pszUserName - Username to add
    pszPassword - Contains the external password for this user
    pszNTUser   - Contains the NT user name to use for this user
    pszNTUserPassword - Contains the password for NTUser

Return Value:

    TRUE if no errors occurred.

--*/
{
    LIST_ENTRY * pEntry;
    USER_INFO *  pUser;

    //
    //  Check our parameters before adding them to the cache
    //

    if ( strlen( pszUserName ) > SF_MAX_USERNAME ||
         strlen( pszPassword ) > SF_MAX_PASSWORD ||
         strlen( pszNTUser   ) > SF_MAX_USERNAME ||
         strlen( pszNTUserPassword ) > SF_MAX_PASSWORD )
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

    //
    //  Search the cache for the specified user to make sure there are no
    //  duplicates
    //

    EnterCriticalSection( &csCacheLock );

    for ( pEntry  = CacheListHead.Flink;
          pEntry != &CacheListHead;
          pEntry = pEntry->Flink )
    {
        pUser = CONTAINING_RECORD( pEntry, USER_INFO, ListEntry );

        if ( !stricmp( pszUserName, pUser->achUserName ))
        {
            goto Found;
        }
    }

    //
    //  Allocate a new cache item and put it at the head of the list
    //

    pUser = (USER_INFO *) LocalAlloc( LPTR, sizeof( USER_INFO ));

    if ( !pUser )
    {
        LeaveCriticalSection( &csCacheLock );

        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
        return FALSE;
    }

    pUser->ListEntry.Flink = CacheListHead.Flink;
    pUser->ListEntry.Blink = &CacheListHead;

    CacheListHead.Flink->Blink = &pUser->ListEntry;
    CacheListHead.Flink = &pUser->ListEntry;

Found:

    //
    //  Set the various fields
    //

    strcpy( pUser->achUserName,       pszUserName );
    strcpy( pUser->achPassword,       pszPassword );
    strcpy( pUser->achNTUserName,     pszNTUser );
    strcpy( pUser->achNTUserPassword, pszNTUserPassword );

    cCacheItems++;

    //
    //  If there are too many cached users, remove the least recently
    //  used one now
    //

    if ( cCacheItems > MAX_CACHED_USERS )
    {
        pEntry = CacheListHead.Blink;

        pEntry->Blink->Flink = &CacheListHead;
        CacheListHead.Blink  = pEntry->Blink;

        LocalFree( CONTAINING_RECORD( pEntry, USER_INFO, ListEntry ));

        cCacheItems--;
    }

    LeaveCriticalSection( &csCacheLock );

    return TRUE;
}

VOID
TerminateCache(
    VOID
    )
/*++

Routine Description:

    Terminates the cache module and frees any allocated memory

--*/
{
    LIST_ENTRY * pEntry;
    LIST_ENTRY * pEntryNext;
    USER_INFO *  pUser;

    if ( !fCacheInitialized )
        return;

    EnterCriticalSection( &csCacheLock );

    //
    //  Free all of the cache entries
    //

    for ( pEntry  = CacheListHead.Flink;
          pEntry != &CacheListHead;
          pEntry  = pEntryNext )

    {
        pUser = CONTAINING_RECORD( pEntry, USER_INFO, ListEntry );

        pEntryNext = pEntry->Flink;

        //
        //  Remove this entry from the list and free it
        //

        pEntry->Blink->Flink = pEntry->Flink;
        pEntry->Flink->Blink = pEntry->Blink;

        LocalFree( pUser );
    }

    cCacheItems = 0;
    LeaveCriticalSection( &csCacheLock );

    DeleteCriticalSection( &csCacheLock );

    fCacheInitialized = FALSE;
}


DB.C

/*++

Copyright (c) 1996  Microsoft Corporation

This program is released into the public domain for any purpose.

Module Name:

    db.c

Abstract:

    This module implements the database routines for the authentication filter.

--*/

#include <windows.h>
#include <httpfilt.h>
#include "authfilt.h"

//
//  This is the name of the file that contains the username/password pairs and
//  the appropriate NT account the username/password should be mapped to.
//
//  The format of the file is:
//
//  User1:Password1, NTUser1:NTPassword1
//  User2:Password2, NTUser2:NTPassword2
//  User3:Password3, NTUser1:NTPassword1
//
//

#define USER_LIST_FILE     "c:\\inetsrv\\userdb.txt"

//
//  Globals
//

CHAR * pszUserFile = NULL;

BOOL
ValidateUser(
    IN OUT CHAR * pszUserName,
    IN OUT CHAR * pszPassword,
    OUT    BOOL * pfValid
    )
/*++

Routine Description:

    Looks up the username and confirms the user is allowed access to the
    server

Arguments:

    pszUserName - The username to validate, will contain the mapped username
                  on return.  Must be at least SF_MAX_USERNAME
    pszPassword - The password for this user.  Will contain the mapped
                  password on return.  Must be at least SF_MAX_PASSWORD
    pfValid     - Set to TRUE if the user should be logged on.

Return Value:

    TRUE on success, FALSE on failure

--*/
{
    BOOL fFound;
    CHAR achPassword[SF_MAX_PASSWORD];
    CHAR achNTUser[SF_MAX_USERNAME];
    CHAR achNTUserPassword[SF_MAX_PASSWORD];

    //
    //  Assume we're going to fail validation
    //

    *pfValid = FALSE;

    //
    //  Lookup the user in the cache, if that fails, get the user from the
    //  database and add the retrieved user to the cache
    //

    if ( !LookupUserInCache( pszUserName,
                             &fFound,
                             achPassword,
                             achNTUser,
                             achNTUserPassword ))
    {
        return FALSE;
    }

    if ( !fFound )
    {
        if ( !LookupUserInDb( pszUserName,
                              &fFound,
                              achPassword,
                              achNTUser,
                              achNTUserPassword ))
        {
            return FALSE;
        }

        if ( fFound )
        {
            AddUserToCache( pszUserName,
                            achPassword,
                            achNTUser,
                            achNTUserPassword );
        }
    }

    if ( !fFound )
    {
        DbgWrite(( DEST,
                   "[ValidateUser] Failed to find user %s\n",
                   pszUserName ));

        return TRUE;
    }

    //
    //  Do the passwords match?
    //

    if ( !strcmp( pszPassword, achPassword ))
    {
        //
        //  We have a match, map to the NT user and password
        //

        strcpy( pszUserName, achNTUser );
        strcpy( pszPassword, achNTUserPassword );
        *pfValid = TRUE;
    }

    return TRUE;
}

BOOL
InitializeUserDatabase(
    VOID
    )
/*++

Routine Description:

    Retrieves the userlist from the file.  If the users were coming from a
    database, this routine would connect to the database.

Return Value:

    TRUE on success, FALSE on failure

--*/
{
    HANDLE hFile;
    DWORD  cbFile;
    DWORD  cbRead;

    //
    //  Open and read the file.  The System account must have access to the
    //  file.
    //

    hFile = CreateFile( USER_LIST_FILE,
                        GENERIC_READ,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL );

    if ( hFile == INVALID_HANDLE_VALUE )
    {
        DbgWrite(( DEST,
                   "[InitializeUserDatabase] Error %d openning %s\n",
                   GetLastError(),
                   USER_LIST_FILE ));

        return FALSE;
    }

    cbFile = GetFileSize( hFile, NULL );

    if ( cbFile == (DWORD) -1 )
    {
        CloseHandle( hFile );
        return FALSE;
    }

    pszUserFile = LocalAlloc( LPTR, cbFile + 1 );

    if ( !pszUserFile )
    {
        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
        CloseHandle( hFile );
        return FALSE;
    }

    if ( !ReadFile( hFile,
                    pszUserFile,
                    cbFile,
                    &cbRead,
                    NULL ))
    {
        CloseHandle( hFile );
        LocalFree( pszUserFile );

        return FALSE;
    }

    CloseHandle( hFile );

    //
    //  Zero terminate the file data
    //

    pszUserFile[cbRead] = '\0';

    return TRUE;
}

BOOL
LookupUserInDb(
    IN CHAR * pszUser,
    OUT BOOL * pfFound,
    OUT CHAR * pszPassword,
    OUT CHAR * pszNTUser,
    OUT CHAR * pszNTUserPassword
    )
/*++

Routine Description:

    Looks up the username in the database and returns the other attributes
    associated with this user

    The file data is not sorted to simulate the cost of an external database
    lookup.

Arguments:

    pszUserName - The username to find in the database (case insensitive)
    pfFound     - Set to TRUE if the specified user name was found in the
                  database
    pszPassword - The external password for the found user.  Buffer must be
                  at least SF_MAX_PASSWORD bytes.
    pszNTUser   - The NT username associated with this user, Buffer must be at
                  least SF_MAX_USERNAME bytes
    pszNTUserPassword - The password for NTUser. Buffer must be at least
                  SF_MAX_PASSWORD

Return Value:

    TRUE on success, FALSE on failure

--*/
{
    CHAR * pch = pszUserFile;
    CHAR * pchEnd;
    DWORD  cchUser = strlen( pszUser );
    DWORD  cch;

    *pfFound = FALSE;

    //
    //  Find the external username.  We're expecting one user per line in
    //  the form:
    //
    //      username:password, NTUser:NTUserPassword
    //

    while ( pch && *pch )
    {
        while ( ISWHITE( *pch ) )
            pch++;

        if ( toupper( *pch ) == toupper( *pszUser ) &&
             !strnicmp( pszUser, pch, cchUser )     &&
             pch[cchUser] == ':' )
        {
            pch += cchUser + 1;
            goto Found;
        }

        pch = strchr( pch+1, '\n' );
    }

    //
    //  Not found
    //

    return TRUE;

Found:

    //
    //  Break out the external username
    //

    if ( !(pchEnd = strchr( pch, ',' )))
    {
        SetLastError( ERROR_INVALID_PASSWORDNAME );
        return FALSE;
    }

    cch = pchEnd - pch;

    if ( cch+1 > SF_MAX_PASSWORD )
    {
        SetLastError( ERROR_INVALID_PASSWORDNAME );
        return FALSE;
    }

    memcpy( pszPassword, pch, cch );
    pszPassword[cch] = '\0';

    pch = pchEnd + 1;

    //
    //  Get the NT username from the file
    //

    while ( ISWHITE( *pch ) )
        pch++;

    if ( !(pchEnd = strchr( pch, ':' )))
    {
        SetLastError( ERROR_BAD_USERNAME );
        return FALSE;
    }

    cch = pchEnd - pch;

    if ( cch+1 > SF_MAX_USERNAME )
    {
        SetLastError( ERROR_BAD_USERNAME );
        return FALSE;
    }

    memcpy( pszNTUser, pch, cch );
    pszNTUser[cch] = '\0';

    pch = pchEnd + 1;

    //
    //  Get the NT password from the file, look for a '\r' or '\n'
    //

    pchEnd = pch;

    while ( *pchEnd && *pchEnd != '\r' && *pchEnd != '\n' )
        pchEnd++;

    cch = pchEnd - pch;

    if ( cch+1 > SF_MAX_PASSWORD )
    {
        SetLastError( ERROR_INVALID_PASSWORDNAME );
        return FALSE;
    }

    memcpy( pszNTUserPassword, pch, cch );
    pszNTUserPassword[cch] = '\0';

    *pfFound = TRUE;

    return TRUE;
}

VOID
TerminateUserDatabase(
    VOID
    )
/*++

Routine Description:

    Shutsdown the user database.

--*/
{
    if ( pszUserFile )
    {
        LocalFree( pszUserFile );
    }
}


README.TXT

CkyMunge: ISAPI filter for ASP session state for cookieless browsers
  ====================================================================

Table of Contents
=================

    Overview
    Installation
    Build Notes
    Support
    Change Notes

Overview
========

This filter is a workaround that helps Active Server Pages deal with
browsers that don't understand cookies or that refuse to accept
cookies.  ASP uses the ASPSESSIONID cookie to maintain its session
state.  ASP expects the browser to send this cookie to the server with
every request that it makes.  The ASPSESSIONID cookie is unique to a
session.  Without it, ASP does not know which session the user owns and
it cannot keep the Session object up to date as the user moves from page
to page.

The CkyMunge filter works like this:

1. The filter receives a request for a particular URL.  If the
   headers of the request contain either "Cookie: ASPSESSIONID=xxx"
   (IIS 3.0) or "Cookie: ASPSESSIONIDxxxxxxxx=xxx" (IIS 4.0)
   header that ASPSESSIONID is recorded.  If the URL contains an
   encoded ASPSESSIONID (see step 2), the ASPSESSIONID is
   removed from the URL and a Cookie header is generated
   instead.  The ASPSESSIONID, if present, is recorded for this
   transaction.

2. CkyMunge filters all outgoing data.  When ASP sends out a page in
   response to the above request, CkyMunge removes the ASPSESSIONID
   Set-Cookie headers, if present (so that browsers with cookie
   warnings will have nothing to complain about), and munges any
   local, relative URLs embedded in the page (e.g., http:foo/bar.asp will
   become something like http:foo/bar.asp-ASP=PVZQGHUMEAYAHMFV).

3. If the user clicks on one of those modified URLs, the request
   comes back to the server (and filter) and step 1 starts all
   over again.

Notes:

* Filtering outgoing raw data is an expensive process.  Every
  single byte of data sent out by IIS must be streamed through
  the filter.  The filter goes to some pains to be smart about
  not unneccesarily processing data it's not interested in (such
  as GIFs), but there are unavoidable performance costs
  associated with IIS needing to copy all data from kernel to
  user memory.

  It is impossible to give an accurate prediction of how much
  performance will degrade on your server if the CkyMunge filter
  is installed.  On a lightly loaded server, the difference
  should not matter.  On a heavily loaded server, the
  performance might become unacceptable.

  It is up to you to measure the performance of your server both
  with and without the filter installed and see if the
  convenience of supporting paranoid users or users with old
  browsers outweighs the cost of poorer performance.

  User education may be a better solution in the long run.
The ASPSESSIONID cookie is not used to track longterm
information about you.  The cookie is destroyed as soon
as your browser shuts down and it is not stored on your
machine.  It simply allows our webserver to keep track
of what's in your shopping basket as you move from page
to page in our application.

If your browser doesn't support cookies, we'd like to
suggest that you upgrade to a newer one that does.  When
you do, you'll enjoy our site much more because your
browser will also support frames and tables and other
newer features of HTML that we've made use of.

* If you elect not to install this filter, you can minimize the
  number of cookies sent by Active Server Pages in one of two ways:
    * Put something into the Session object:
      <% Session("something") = whatever %>
    * Put a global.asa into the virtual root for your
      application, with a do-nothing Session_OnStart subroutine.

* The filter has 3 modes: off, on, and smart.  In the off mode
  the filter does no work (a better way of turning it off would
  be to remove it from the list of filters).  In On mode, all
  URLs will be munged as normal, and no cookies will be sent
  out.  In smart mode, the filter will munge and let cookies
  go out to the browser.  If the cookie comes back it will
  stop munging URLs and use the cookies.  If the cookie doesn't
  come back it will begin eating outgoing cookies and continue
  to munge URLs.  This will result in slightly better performance.
  The mode is controlled by a registry setting:
  /HKEY_LOCAL_MACHINE/software/microsoft/ckymunge/mungemode.  A
  mode of 0 is off, 1 is on, and 2 is smart.

* URLs will look ugly (http:foo/bar.asp-ASP=PVZQGHUMEAYAHMFV).
  Users can still bookmark them, however.  If the ASPSESSIONID
  is stale, the server will assign them a new session ID and
  start a new session.  If CkyMunge has been disabled in the
  meantime, then the URL will be unreachable.

* Only local, relative URLs are modified.  Absolute URLs are not
  modified (e.g., http://www.mycompany.com/foo/bar.asp).  Nor are
  local absolute URLS (e.g., http:/foo/bar.asp).  It is always good
  HTML style to use relative URLs wherever possible, to ensure
  location independence, so that moving a set of pages to a different
  server or a different directory will not require modification of
  the text.

* The filter does not work well with pure HTML pages (pages with
  an .HTM or .HTML extension) as the Set-Cookie header does not
  get sent out and hence the filter has no way of knowing how to
  modify any embedded URLs.  Rename your .HTM and .HTML pages to
  have a .ASP extension, even if they contain no server-side
  scripting.

* If you have some client-side scripting that dynamically
  constructs URLs in the user's browser (windows.navigate),
  these URLs will not be modified to contain the ASPSESSIONID.

* This filter is transparent to Active Server Pages.  It neither
  knows nor cares that the filter is present.

Installation
============

To install this on IIS 4.0, you must:

    * Copy ckymunge.dll to somewhere suitable, such as \InetPub\Scripts
      or \Winnt\System32\InetSrv

    * Use the Internet Service Manager to add ckymunge.dll to the list of
  ISAPI filters to be used for your web site.  The Internet Service Manager
  is found in the Start Menu.

        Start\
            Programs\
                Windows NT 4.0 Option Pack\
                    Microsoft Internet Information Server\
                        Internet Service Manager

  Right mouse click on the web site you wish to install this filter on.
  Choose Properties.  Click on the ISAPI Filters tab and add the filter.

      If you have other filters installed, the order in which they are executed
  may become important.  Ckymunge is a medium-priority filter.

Build Notes
===========

You can build this component with nmake at the command line.  Read the
Makefile for more details.


CKYPCH.H

// force strict type checking
#define STRICT

// disable rarely-used sections of Windows
#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOIME
#define NOSOUND
#define NOCOMM
#define NOKANJI
#define NORPC
#define NOPROXYSTUB
#define NOIMAGE
#define NOTAPE

// Needed by AspAssertHandler in debug.cpp
// #define _WIN32_WINNT 0x400

#include <windows.h>
#include <httpfilt.h>

#include <crtdbg.h>
#include <tchar.h>
#include <limits.h>
#include <malloc.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

// disable warning messages about truncating extremly long identifiers
#pragma warning (disable : 4786)
#include <string>
#include <set>
#include <map>
#include <vector>
#include <stack>


DEBUG.CPP

#include "CkyPch.h"
#include "debug.h"

#ifdef _DEBUG

void __cdecl
Trace(
    LPCTSTR ptszFormat,
    ...)
{
    TCHAR tszBuff[2048];
    va_list args;

    va_start(args, ptszFormat);
    _vstprintf(tszBuff, ptszFormat, args);
    va_end(args);

    OutputDebugString(tszBuff);
}

#define ACTIVE_SERVER_PAGES

# if defined(_MSC_VER)  &&  (_MSC_VER >= 1000)

#  ifdef ACTIVE_SERVER_PAGES

// The default assertion mechanism set up by Visual C++ 4 will not
// work with Active Server Pages because it's running inside a service
// and there is no desktop to interact with.

// Note: for this to work properly, #define _WIN32_WINNT 0x400 before
// including <winuser.h> or MB_SERVICE_NOTIFICATION won't be #define'd.

int
AspAssertHandler(
    int   nReportType,
    char* pszErrorText,
    int*  pnReturn)
{
    const char szInfo[] = " (Press ABORT to terminate IIS,"
                          " RETRY to debug this failure,"
                          " or IGNORE to continue.)";
    char* pszMessageTitle = NULL;

    // These flags enable message boxes to show up on the user's console
    switch (nReportType)
    {
    case _CRT_WARN:
        pszMessageTitle = "Warning";
        break;
    case _CRT_ERROR:
        pszMessageTitle = "Fatal Error";
        break;
    case _CRT_ASSERT:
        pszMessageTitle = "Assertion Failed";
        break;
    }

    char* pszMessageText =
        static_cast<char*>(_alloca(strlen(pszErrorText) + strlen(szInfo) + 1));

    strcpy(pszMessageText, pszErrorText);
    strcat(pszMessageText, szInfo);

    const int n = MessageBoxA(NULL, pszMessageText, pszMessageTitle,
                              (MB_SERVICE_NOTIFICATION | MB_TOPMOST
                               | MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION));

    if (n == IDABORT)
    {
        exit(1);
    }
    else if (n == IDRETRY)
    {
        *pnReturn = 1;   // tell _CrtDbgReport to start the debugger
        return TRUE;     // tell _CrtDbgReport to run
    }

    *pnReturn = 0;       // nothing for _CrtDbgReport to do

    return FALSE;
}

#  endif // ACTIVE_SERVER_PAGES
# endif // _MSC_VER >= 1000

void
DebugInit()
{
# if defined(_MSC_VER)  &&  (_MSC_VER >= 1000)
#  ifdef ACTIVE_SERVER_PAGES
    // If we end up in _CrtDbgReport, don't put up a message box
    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
    _CrtSetReportMode(_CRT_WARN,   _CRTDBG_MODE_DEBUG);
    _CrtSetReportMode(_CRT_ERROR,  _CRTDBG_MODE_DEBUG);

    // Use AspAssertHandler to put up a message box instead
    _CrtSetReportHook(AspAssertHandler);
#  endif // ACTIVE_SERVER_PAGES

    // Enable debug heap allocations & check for memory leaks at program exit
    // The memory leak check will not be performed if inetinfo.exe is
    // run directly under a debugger, only if it is run as a service.
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF
                   | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
# endif // _MSC_VER >= 1000
}

void
DebugTerm()
{
# if defined(_MSC_VER)  &&  (_MSC_VER >= 1000)
#  ifdef ACTIVE_SERVER_PAGES
    // Turn off AspAssertHandler, so that we don't get numerous message boxes
    // if there are memory leaks on shutdown
    _CrtSetReportHook(NULL);
#  endif // ACTIVE_SERVER_PAGES
# endif // _MSC_VER >= 1000
}

#endif //_DEBUG

BOOL
IsValidString(
    LPCTSTR ptsz,
    int nLength /* =-1 */)
{
    if (ptsz == NULL)
        return FALSE;

    return !IsBadStringPtr(ptsz, nLength);
}

BOOL
IsValidAddress(
    LPCVOID pv,
    UINT nBytes,
    BOOL fReadWrite /* =TRUE */)
{
    return (pv != NULL
            &&  !IsBadReadPtr(pv, nBytes)
            &&  (!fReadWrite  ||  !IsBadWritePtr((LPVOID) pv, nBytes)));
}


DEBUG.H

/*
 * Some simple debugging macros that look and behave a lot like their
 * namesakes in MFC.  These macros should work in both C and C++ and
 * do something useful with almost any Win32 compiler.
 *
 * George V. Reilly  <georgere@microsoft.com>
 */

#ifndef __DEBUG_H__
#define __DEBUG_H__

#include <tchar.h>

#ifdef _DEBUG

# if defined(_MSC_VER)  &&  (_MSC_VER >= 1000)
   /* Use the new debugging tools in Visual C++ 4.x */
#  include <crtdbg.h>
   /* _ASSERTE will give a more meaningful message, but the string takes
    * space.  Use _ASSERT if this is an issue. */
#  define ASSERT(f) _ASSERTE(f)
# else
#  include <assert.h>
#  define ASSERT(f) assert(f)
# endif

# define VERIFY(f)               ASSERT(f)
# define DEBUG_ONLY(f)           (f)
# define TRACE                   Trace
# define TRACE0(psz)             Trace(_T("%s"), _T(psz))
# define TRACE1(psz, p1)         Trace(_T(psz), p1)
# define TRACE2(psz, p1, p2)     Trace(_T(psz), p1, p2)
# define TRACE3(psz, p1, p2, p3) Trace(_T(psz), p1, p2, p3)
# define ASSERT_VALID(pObj)  \
    do {ASSERT((pObj) != NULL); (pObj)->AssertValid();} while (0)
# define DUMP(pObj)  \
    do {ASSERT((pObj) != NULL); (pObj)->Dump();} while (0)
# define DEBUG_INIT()            DebugInit()
# define DEBUG_TERM()            DebugTerm()

#else /* !_DEBUG */

  /* These macros should all compile away to nothing */
# define ASSERT(f)               ((void)0)
# define VERIFY(f)               ((void)(f))
# define DEBUG_ONLY(f)           ((void)0)
# define TRACE                   1 ? (void)0 : Trace
# define TRACE0(psz)
# define TRACE1(psz, p1)
# define TRACE2(psz, p1, p2)
# define TRACE3(psz, p1, p2, p3)
# define ASSERT_VALID(pObj)      ((void)0)
# define DUMP(pObj)              ((void)0)
# define DEBUG_INIT()            ((void)0)
# define DEBUG_TERM()            ((void)0)

#endif /* !_DEBUG */

#define ASSERT_POINTER(p, type) \
    ASSERT(((p) != NULL)  &&  IsValidAddress((p), sizeof(type), FALSE))

#define ASSERT_NULL_OR_POINTER(p, type) \
    ASSERT(((p) == NULL)  ||  IsValidAddress((p), sizeof(type), FALSE))

/* Declarations for non-Windows apps */

#ifndef _WINDEF_
typedef void*           LPVOID;
typedef const void*     LPCVOID;
typedef unsigned int    UINT;
typedef int             BOOL;
typedef const char*     LPCTSTR;
#endif /* _WINDEF_ */

#ifndef TRUE
# define FALSE  0
# define TRUE   1
#endif

#ifdef __cplusplus
extern "C" {

/* Low-level sanity checks for memory blocks */
BOOL IsValidAddress(LPCVOID pv, UINT nBytes, BOOL fReadWrite = TRUE);
BOOL IsValidString(LPCTSTR ptsz, int nLength = -1);

#else /* !__cplusplus */

/* Low-level sanity checks for memory blocks */
BOOL IsValidAddress(LPCVOID pv, UINT nBytes, BOOL fReadWrite);
BOOL IsValidString(LPCTSTR ptsz, int nLength);

#endif /* !__cplusplus */

/* in debug version, writes trace messages to debug stream */
void __cdecl
Trace(
    LPCTSTR pszFormat,
    ...);

/* should be called from main(), WinMain(), or DllMain() */
void
DebugInit();

void
DebugTerm();

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __DEBUG_H__ */


FILTER.CPP

#include "CkyPch.h"

#include "filter.h"
#include "keyword.h"
#include "utils.h"
#include "globals.h"

//
// First pass through the data: count how many extra bytes we're going to
// need to allocate to hold the modified data, if any.
//

int
CountExtraBytes(
    LPCTSTR ptszStart,
    UINT    cch)
{
    ASSERT(ptszStart != NULL);

    LPCTSTR ptszEnd = ptszStart + cch;
    CStateStack ss;
    int cb = 0;

    for (LPCTSTR ptsz = ptszStart;  ptsz < ptszEnd; )
    {
        int cLen;
        const CToken* ptok = g_trie.Search(ptsz, &cLen, ptszEnd - ptsz);

        if (ptok == NULL)
            ++ptsz;
        else
        {
            cb += ptok->CountBytes(ss, ptsz, ptszEnd - ptsz);
            ptsz += ptok->m_str.length();
        }
    }

    return cb;
}

//
// Second pass: munge the data
//

void
DoFilter(
    LPCTSTR ptszStart,
    UINT    cch,
    LPCTSTR ptszSessionID,
    LPTSTR  ptszOutBuf)
{
    ASSERT(ptszStart != NULL  &&  cch > 0);
    ASSERT(ptszSessionID != NULL  &&  _tcslen(ptszSessionID) > 0);
    ASSERT(ptszOutBuf != NULL);

    LPCTSTR ptszEnd = ptszStart + cch;
    // const int cchUrl = _tcslen(ptszSessionID);
    // const int cchUrlNameValue = _tcslen(s_szUrlNameValue);
    CStateStack ss(ptszSessionID);

    for (LPCTSTR ptsz = ptszStart;  ptsz < ptszEnd; )
    {
        int cLen;
        const CToken* ptok = g_trie.Search(ptsz, &cLen, ptszEnd - ptsz);

        if (ptok == NULL)
        {
            // TRACE("%c", *ptsz);
            *ptszOutBuf++ = *ptsz++;
        }
        else
        {
            // DoFilter is supposed to copy itself, if appropriate,
            // and adjust ptsz and ptszOutBuf
            ptok->DoFilter(ss, ptsz, ptszEnd - ptsz, ptszOutBuf);
        }
    }
}

int
Filter(
    PHTTP_FILTER_CONTEXT  pfc,
    PHTTP_FILTER_RAW_DATA pRawData,
    LPCSTR                pszStart,
    UINT                  cch,
    int                   iStart,
    LPCSTR                pszSessionID)
{
    ASSERT(pszSessionID != NULL);

    // If empty SessionID (typically happens on plain HTML pages), nothing
    // useful can be done
    if (strlen(pszSessionID) == 0)
        return 0;

    const int nExtra = CountExtraBytes(pszStart + iStart, cch - iStart);

    if (nExtra > 0)
    {
        TRACE("Filtering `%s', found %d extra bytes\n", pszSessionID, nExtra);
        const int nNewSize = nExtra + cch;
        TRACE("FilterNew: ");
        LPSTR pchNew = (LPSTR) AllocMem(pfc, nNewSize);

        memcpy(pchNew, pszStart, iStart);
        DoFilter(pszStart + iStart, cch - iStart,
                 pszSessionID, pchNew + iStart);
        pRawData->pvInData = pchNew;
        pRawData->cbInData = pRawData->cbInBuffer = nNewSize;
    }

    return nExtra;
}


FILTER.H

#ifndef __FILTER_H__
#define __FILTER_H__

int
Filter(
    PHTTP_FILTER_CONTEXT  pfc,
PHTTP_FILTER_RAW_DATA pRawData,
LPCSTR                pszStart,
UINT                  cch,
int                  iStart,
LPCSTR                pszSessionID);

#endif // __FILTER_H__


GLOBALS.H

#ifndef __GLOBALS_H__
#define __GLOBALS_H__

#include "token.h"

#ifndef EXTERN
# define EXTERN extern
# define INITVAL(x)
#else
# define INITVAL(x)   = (x)
#endif

EXTERN CTokenTrie    g_trie;

#define SESSION_ID_PREFIX      "-ASP=" // "@ASP:"
#define SESSION_ID_PREFIX_SIZE 5       // 5
#define SESSION_ID_SUFFIX      ""      // ":SESS"
#define SESSION_ID_SUFFIX_SIZE 0       // 5
#define COOKIE_NAME_EXTRA_SIZE 8

#endif // __GLOBALS_H__


ISAPIFLT.H

#ifndef __ISAPIFLT_H__
#define __ISAPIFLT_H__

#include <httpfilt.h>

#ifdef __cplusplus
extern "C" {
#endif

// This notification was added in IIS 3.0

#ifndef SF_NOTIFY_END_OF_REQUEST
# define SF_NOTIFY_END_OF_REQUEST            0x00000080
#endif

///////////////////////////////////////////////////////////////////////
// ISAPI Filter Notification handlers

DWORD
OnReadRawData(
    PHTTP_FILTER_CONTEXT  pfc,
    PHTTP_FILTER_RAW_DATA pRawData);

DWORD
OnPreprocHeaders(
    PHTTP_FILTER_CONTEXT         pfc,
    PHTTP_FILTER_PREPROC_HEADERS pHeaders);

DWORD
OnUrlMap(
    PHTTP_FILTER_CONTEXT pfc,
    PHTTP_FILTER_URL_MAP pMapInfo);

DWORD
OnAuthentication(
    PHTTP_FILTER_CONTEXT pfc,
    PHTTP_FILTER_AUTHENT pAuthent);

DWORD
OnAccessDenied(
    PHTTP_FILTER_CONTEXT       pfc,
    PHTTP_FILTER_ACCESS_DENIED pAccess);

DWORD
OnSendRawData(
    PHTTP_FILTER_CONTEXT  pfc,
    PHTTP_FILTER_RAW_DATA pRawData);

DWORD
OnEndOfRequest(
    PHTTP_FILTER_CONTEXT pfc);

DWORD
OnLog(
    PHTTP_FILTER_CONTEXT pfc,
    PHTTP_FILTER_LOG     pLog);

DWORD
OnEndOfNetSession(
    PHTTP_FILTER_CONTEXT pfc);

#ifdef __cplusplus
}
#endif

#endif // __ISAPIFLT_H__


KEYWORD.CPP

#include "CkyPch.h"

#include "keyword.h"
#include "filter.h"
#include "utils.h"

#undef EXTERN
#define EXTERN
#include "globals.h"

// Build table of keywords

typedef struct {
    LPCTSTR          ptszKwd;
    CToken::BOUNDARY bndPrefix;
    CToken::BOUNDARY bndSuffix;
} SKeywordData;

#define KEYWORD(KWD, tszKwd, bndStart, bndEnd, DoFilterDecl)        \
                                                      \
    class CKwd##KWD : public CToken                   \
    {                                                 \
    public:                                           \
        CKwd##KWD(                                    \
            LPCTSTR ptsz,                             \
            BOUNDARY bndPrefix,                       \
            BOUNDARY bndSuffix)                       \
            : CToken(ptsz, bndPrefix, bndSuffix)      \
        {}                                            \
                                                      \
        virtual UINT                                  \
        CountBytes(                                   \
            CStateStack& rss,                         \
            LPCTSTR      ptszData,                    \
            UINT         cchData) const;              \
                                                      \
        DoFilterDecl                                  \
    };                                                \
                                                      \
    static const SKeywordData kd##KWD =               \
        {tszKwd, CToken::bndStart, CToken::bndEnd};

#define DO_FILTER_DECL                                \
        virtual UINT                                  \
        DoFilter(                                     \
            CStateStack& rss,                         \
            LPCTSTR&     rptszData,                   \
            UINT         cchData,                     \
            LPTSTR&      rptszOutBuf) const;          \

#define NO_DO_FILTER_DECL /*none*/

KEYWORD(StartTag, _T("<"),          IRRELEVANT, IRRELEVANT, NO_DO_FILTER_DECL);
KEYWORD(Anchor,   _T("<a"),         IRRELEVANT, WHITESPACE, NO_DO_FILTER_DECL);
KEYWORD(Area,     _T("<area"),      IRRELEVANT, WHITESPACE, NO_DO_FILTER_DECL);
KEYWORD(EndTag,   _T(">"),          IRRELEVANT, IRRELEVANT, NO_DO_FILTER_DECL);
KEYWORD(HRef,     _T("href="),      WHITESPACE, IRRELEVANT, DO_FILTER_DECL);
KEYWORD(BegCmnt,  _T("<--!"),       IRRELEVANT, WHITESPACE, NO_DO_FILTER_DECL);
KEYWORD(EndCmnt,  _T("-->"),        WHITESPACE, IRRELEVANT, NO_DO_FILTER_DECL);
KEYWORD(BegCmnt2, _T("<comment>"),  IRRELEVANT, WHITESPACE, NO_DO_FILTER_DECL);
KEYWORD(EndCmnt2, _T("</comment>"), WHITESPACE, IRRELEVANT, NO_DO_FILTER_DECL);

#define KEYWORD_INIT(KWD)                             \
    g_trie.AddToken(new CKwd##KWD(kd##KWD.ptszKwd,    \
                                  kd##KWD.bndPrefix,  \
                                  kd##KWD.bndSuffix))

BOOL
InitKeywords()
{
    KEYWORD_INIT(StartTag);
    KEYWORD_INIT(Anchor);
    KEYWORD_INIT(Area);
    KEYWORD_INIT(EndTag);
    KEYWORD_INIT(HRef);
    KEYWORD_INIT(BegCmnt);
    KEYWORD_INIT(EndCmnt);
    KEYWORD_INIT(BegCmnt2);
    KEYWORD_INIT(EndCmnt2);

    DUMP(&g_trie);

    return TRUE;
}

BOOL
TerminateKeywords()
{
    g_trie.Flush();
    return TRUE;
}

//----------------------------------------------------------------
// "<" starting a tag

UINT
CKwdStartTag::CountBytes(
    CStateStack& rss,
    LPCTSTR      ptszData,
    UINT         cchData) const
{
    rss.m_fInTag = TRUE;
    return 0;
}

//----------------------------------------------------------------
// "<a"

UINT
CKwdAnchor::CountBytes(
    CStateStack& rss,
    LPCTSTR      ptszData,
    UINT         cchData) const
{
    if (!rss.m_fInComment)
        rss.push(ANCHOR);

    return 0;
}

//----------------------------------------------------------------
// "<area" (occurs inside <map> ... </map>)

UINT
CKwdArea::CountBytes(
    CStateStack& rss,
    LPCTSTR      ptszData,
    UINT         cchData) const
{
    if (!rss.m_fInComment)
        rss.push(AREA);

    return 0;
}

//----------------------------------------------------------------
// ">" closing "<a [href=...] ...>" or "<area [href=...] ...>"

UINT
CKwdEndTag::CountBytes(
    CStateStack& rss,
    LPCTSTR      ptszData,
    UINT         cchData) const
{
    if (!rss.m_fInComment  && !rss.m_fInComment2)
    {
        PARSESTATE ps = rss.top();

        if (ps == HREF)
        {
            ps = rss.top();
            rss.pop();
        }

        if (ps == ANCHOR  ||  ps == AREA)
        {
            ps = rss.top();
            rss.pop();
        }
    }

    rss.m_fInTag = FALSE;
    return 0;
}

//----------------------------------------------------------------
// "href="

UINT
CKwdHRef::CountBytes(
    CStateStack& rss,
    LPCTSTR      ptszData,
    UINT         cchData) const
{
    PARSESTATE ps = rss.top();

    if (!rss.m_fInComment  && !rss.m_fInComment2)
        if (ps == ANCHOR  ||  ps == AREA)
            rss.push(HREF);

    const TCHAR* const ptszEnd = ptszData + cchData;

    ptszData += m_str.length();

    const TCHAR tchDelim = *ptszData;

    if (tchDelim == _T('"'))
        ++ptszData;

    int cLen;
    const URLTYPE ut = UrlType(ptszData, ptszEnd, cLen);

    if (ut != UT_HTTP  &&  ut != UT_HTTPS  &&  ut != UT_NONE)
        return 0;
    else
        ptszData += cLen;

    // Is it an absolute URL?  E.g., http://server/foo/...
    // Don't want to mangle URLs that belong to some other
    // server, as it won't know what to do with them
    if (ptszData[0] == '/'  &&  ptszData[1] == '/')
        return 0;

    // Is it a server-relative absolute URL?  E.g., http:/foo/...
    // Session IDs are scoped by application (virtual root) in ASP 1.0
    if (ptszData[0] == '/')
        return 0;

    TRACE("href = %x\n",
          (SESSION_ID_PREFIX_SIZE
           + g_SessionIDSize
           + SESSION_ID_SUFFIX_SIZE));

    return (SESSION_ID_PREFIX_SIZE
            + g_SessionIDSize
            + SESSION_ID_SUFFIX_SIZE);
}

UINT
CKwdHRef::DoFilter(
    CStateStack& rss,
    LPCTSTR&     rptszData,
    UINT         cchData,
    LPTSTR&      rptszOutBuf) const
{
    UINT cb = CountBytes(rss, rptszData, cchData);

    if (cb != 0)
    {
        const TCHAR* const ptszEnd = rptszData + cchData;

        LPCTSTR ptsz = rptszData + m_str.length();

        const TCHAR tchDelim = *ptsz;

        if (tchDelim == _T('"'))
            ++ptsz;

        int cLen;
        const URLTYPE ut = UrlType(ptsz, ptszEnd, cLen);

        if (ut != UT_HTTP  &&  ut != UT_HTTPS  &&  ut != UT_NONE)
        {
            ASSERT(FALSE);
            return 0;
        }
        else
            ptsz += cLen;

        // Is it an absolute URL?  E.g., http://server/foo/...
        if (ptsz[0] == '/'  &&  ptsz[1] == '/')
        {
            ASSERT(FALSE);
            return 0;
        }

        while (*ptsz != _T('?')
               && *ptsz != _T(' ')
               && *ptsz != _T('>')
               && *ptsz != _T('"'))
        {
            // TRACE("%c", *ptsz);
            ++ptsz;
        }

        memcpy(rptszOutBuf, rptszData, ptsz - rptszData);
        rptszOutBuf += ptsz - rptszData;

        strcpy(rptszOutBuf, SESSION_ID_PREFIX);
        rptszOutBuf += SESSION_ID_PREFIX_SIZE;

        strcpy(rptszOutBuf, rss.m_szSessionID);
        rptszOutBuf += g_SessionIDSize;

        strcpy(rptszOutBuf, SESSION_ID_SUFFIX);
        rptszOutBuf += SESSION_ID_SUFFIX_SIZE;

        // TRACE("%s%s%s",
        //       SESSION_ID_PREFIX, rss.m_szSessionID, SESSION_ID_SUFFIX);

        rptszData = ptsz;
    }
    else
    {
        // we must skip over the keyword
       rptszData += m_str.length();
       memcpy( rptszOutBuf, m_str.c_str(), m_str.length() );
       rptszOutBuf += m_str.length();
    }

    return 0;   // we have taken care of updating rptszOutBuf
}

//----------------------------------------------------------------
// "<--! "

UINT
CKwdBegCmnt::CountBytes(
    CStateStack& rss,
    LPCTSTR      ptszData,
    UINT         cchData) const
{
    rss.push(COMMENT);
    rss.m_fInComment = TRUE;

    return 0;
}

//----------------------------------------------------------------
// " -->"

UINT
CKwdEndCmnt::CountBytes(
    CStateStack& rss,
    LPCTSTR      ptszData,
    UINT         cchData) const
{
    PARSESTATE ps = rss.top();

    if (ps == COMMENT)
    {
        ASSERT(rss.m_fInComment);
        ps = rss.top();
        rss.pop();
    }
    else if (rss.m_fInComment)
    {
        do
        {
            ps = rss.top();
            rss.pop();
        } while (ps != COMMENT  &&  ps != INVALID);
    }

    rss.m_fInComment = FALSE;

    return 0;
}

//----------------------------------------------------------------
// "<comment": an IE- and Mosaic-specific tag

UINT
CKwdBegCmnt2::CountBytes(
    CStateStack& rss,
    LPCTSTR      ptszData,
    UINT         cchData) const
{
    rss.push(COMMENT2);
    rss.m_fInComment2 = TRUE;

    return 0;
}

//----------------------------------------------------------------
// " </comment"

UINT
CKwdEndCmnt2::CountBytes(
    CStateStack& rss,
    LPCTSTR      ptszData,
    UINT         cchData) const
{
    PARSESTATE ps = rss.top();

    if (ps == COMMENT2)
    {
        ASSERT(rss.m_fInComment2);
        ps = rss.top();
        rss.pop();
    }
    else if (rss.m_fInComment2)
    {
        do
        {
            ps = rss.top();
            rss.pop();
        } while (ps != COMMENT2  &&  ps != INVALID);
    }

    rss.m_fInComment2 = FALSE;

    return 0;
}


KEYWORD.H

#ifndef __KEYWORD_H__
#define __KEYWORD_H__

BOOL
InitKeywords();

BOOL
TerminateKeywords();

#endif // __KEYWORD_H__


NOTIFY.CPP

#include "CkyPch.h"
#include "debug.h"
#include "utils.h"
#include "notify.h"

//
// ctor
//

CNotification::CNotification(
    LPCSTR pszCookie)
    : m_nState(HN_UNDEFINED),
      m_ct(CT_UNDEFINED),
      m_pszUrl(NULL),
      m_pbPartialToken(NULL),
      m_cbPartialToken(0),
      m_pbTokenBuffer(NULL),
      m_cbTokenBuffer(0),
      m_cchContentLength(UINT_MAX), // not specified for .ASPs
  m_fTestCookies(false)
{
// set the eat cookies and test cookies based on the munge mode.
// Here's how the flags will affect output of this session

// if EatCookies is true, cookies will be stripped and URLs will
// be munged with the cookie
// if TestCookies is true URLs will be munged
switch ( g_mungeMode )
{
case MungeMode_Off:
{
m_fEatCookies = false;
} break;

case MungeMode_On:
{
m_fEatCookies = true;
} break;

// always default to smart
default:
{
m_fEatCookies = false;
m_fTestCookies = true;
} break;
}

    *m_szSessionID = '\0';

    if (pszCookie != NULL)
    {
        if (!Cookie2SessionID(pszCookie, m_szSessionID))
        {
            TRACE("CNotification(%s): ", pszCookie);
            CopySessionID(pszCookie, m_szSessionID);
        }
    }

    TRACE("CNotification(%s)\n", (*m_szSessionID ? m_szSessionID : "<none>"));
}

//
// dtor
//

CNotification::~CNotification()
{
    TRACE("~CNotification(%s)\n", m_szSessionID);
}

//
// Create a CNotification for a Filter Context
//

CNotification*
CNotification::Create(
    PHTTP_FILTER_CONTEXT pfc,
    LPCSTR               pszCookie)
{
    ASSERT(pfc->pFilterContext == NULL);

    TRACE("Notify: ");

    // placement new, using ISAPI's fast allocator
    LPBYTE pbNotify =
        static_cast<LPBYTE>(AllocMem(pfc, sizeof(CNotification)));
    CNotification* pNotify = new (pbNotify) CNotification(pszCookie);

    pfc->pFilterContext = static_cast<PVOID>(pNotify);

    return pNotify;
}

//
// Cleanup
//

void
CNotification::Destroy(
    PHTTP_FILTER_CONTEXT pfc)
{
    CNotification* pNotify = Get(pfc);

    if (pNotify != NULL)
        pNotify->~CNotification(); // placement destruction

    pfc->pFilterContext = NULL;
}

//
// Set the filter context's session ID, creating a CNotification if necessary
//

CNotification*
CNotification::SetSessionID(
    PHTTP_FILTER_CONTEXT pfc,
    LPCSTR               pszCookie)
{
    CNotification* pNotify = Get(pfc);

    if (pNotify != NULL)
    {
        if (!Cookie2SessionID(pszCookie, pNotify->m_szSessionID))
        {
            TRACE("SetSessionID(%s): ", pszCookie);
            CopySessionID(pszCookie, pNotify->m_szSessionID);
        }
    }
    else
        pNotify = Create(pfc, pszCookie);

    return pNotify;
}

//
// Sometimes the data in OnSendRawData ends in a partial token.  We need
// to buffer that partial token for the next call to OnSendRawData.  In
// some cases, it may take several successive calls to OnSendRawData to
// accumulate a complete token.  This routine builds the partial token,
// taking care of memory (re)allocation.  There's always SPARE_BYTES bytes
// unused at the end that callers (esp. OnEndOfRequest) can write into.
//

void
CNotification::AppendToken(
    PHTTP_FILTER_CONTEXT pfc,
    LPCSTR               pszNewData,
    int                  cchNewData)
{
    ASSERT(pszNewData != NULL  &&  cchNewData > 0);

    // if there's room, append new data to the currently allocated buffer
    if (m_cbPartialToken + cchNewData <= m_cbTokenBuffer - SPARE_BYTES)
    {
        m_pbPartialToken = m_pbTokenBuffer;
        memcpy(m_pbPartialToken + m_cbPartialToken, pszNewData, cchNewData);
        m_cbPartialToken += cchNewData;
#ifdef _DEBUG
        m_pbPartialToken[m_cbPartialToken] = '\0';
#endif
        return;
    }

    // We want to allocate some extra space so that we have room to
    // grow for a while before needing to allocate more space.  If we
    // allocated only exactly enough, we'd see O(n^2) memory usage in
    // degenerate cases because AllocMem doesn't return memory to its
    // pool until the transaction is over.
    DWORD cb2 = max(1024, 2 * (cchNewData + m_cbTokenBuffer) + SPARE_BYTES);

    if (cb2 > 10000)
        cb2 = 3 * (cchNewData + m_cbTokenBuffer) / 2 + SPARE_BYTES;

    m_cbTokenBuffer = cb2;
    LPBYTE pb  = (LPBYTE) AllocMem(pfc, m_cbTokenBuffer);
    LPBYTE pb2 = pb;

    // Already have a partial token buffer?  Copy contents, if so.
    if (m_cbPartialToken > 0)
    {
        ASSERT(m_pbPartialToken != NULL
               &&  m_pbPartialToken == m_pbTokenBuffer);
        memcpy(pb, m_pbPartialToken, m_cbPartialToken);
        pb2 += m_cbPartialToken;
    }
    else
        ASSERT(m_pbPartialToken == NULL);

    memcpy(pb2, pszNewData, cchNewData);
    m_pbPartialToken = m_pbTokenBuffer = pb;
    m_cbPartialToken += cchNewData;

#ifdef _DEBUG
    m_pbPartialToken[m_cbPartialToken] = '\0';
#endif

    ASSERT(m_cbPartialToken <= m_cbTokenBuffer - SPARE_BYTES);
}


NOTIFY.H

#ifndef __NOTIFY_H__
#define __NOTIFY_H__

#define SZ_SESSION_ID_COOKIE_NAME      "ASPSESSIONID"
#define SZ_SESSION_ID_COOKIE_NAME_SIZE 12 // strlen(SZ_SESSION_ID_COOKIE_NAME)
#define MAX_SESSION_ID_SIZE 24

// the different types of munging available
// off-no munging will be done
// on-munging will always be done
// smart-the munger will "test the waters" to see if cookies are getting through for each session.
//If they are, the munger will not be effectively off for this session.  Otherwise, cookies
//will be munged as usual.
typedef enum
{
MungeMode_Off=0,
MungeMode_On,
MungeMode_Smart
} MungeModeT;

// the munging mode.  This will be read from the registry upon initialization
extern intg_mungeMode;

// the session ID size is either 16 or 24 characters (depending on the server version)
extern long g_SessionIDSize;

extern CHAR g_szCookieExtra[];

// states for OnSendRawData

enum HN_STATE {
    HN_UNDEFINED = 0,
    HN_SEEN_URL,
    HN_IN_HEADER,
    HN_IN_BODY,
};

enum CONTENT_TYPE {
    CT_UNDEFINED = 0,
    CT_TEXT_HTML,
};

class CNotification
{
public:
    enum {SPARE_BYTES = 2};

    static
    CNotification*
    Create(
        PHTTP_FILTER_CONTEXT pfc,
        LPCSTR               pszCookie);

    static
    void
    Destroy(
        PHTTP_FILTER_CONTEXT pfc);

    static
    CNotification*
    Get(
        PHTTP_FILTER_CONTEXT pfc)
    {return static_cast<CNotification*>(pfc->pFilterContext);}

    static
    CNotification*
    SetSessionID(
        PHTTP_FILTER_CONTEXT pfc,
        LPCSTR               pszCookie);

    LPCSTR
    SessionID() const
    {return m_szSessionID;}

    HN_STATE
    State() const
    {return m_nState;}

    void
    AppendToken(
        PHTTP_FILTER_CONTEXT pfc,
        LPCSTR               pszNewData,
        int                  cchNewData);

bool MungingOff() const
{return ( !m_fEatCookies ) && ( !m_fTestCookies );}

protected:
    CNotification(
        LPCSTR pszCookie);

    ~CNotification();

public:
    HN_STATE     m_nState;
    CONTENT_TYPE m_ct;
    CHAR         m_szSessionID[ MAX_SESSION_ID_SIZE ];
    LPSTR        m_pszUrl;
    LPBYTE       m_pbPartialToken;
    int          m_cbPartialToken;
    LPBYTE       m_pbTokenBuffer;
    int          m_cbTokenBuffer;
    DWORD        m_cchContentLength;
    bool         m_fEatCookies;
bool         m_fTestCookies;
};

#endif // __NOTIFY_H__


CKYMUNGE.CPP

// CkyMunge: ISAPI filter for ASP session state for cookieless browsers.
// An inglorious hack that munges URLs embedded in outgoing ASP pages,
// embedding the ASPSESSIONID cookie in them.  Also useful as an example of
// an ISAPI filter that does something non-trivial with rawdata.

#include "CkyPch.h"

#include "debug.h"
#include "isapiflt.h"
#include "utils.h"
#include "notify.h"
#include "filter.h"
#include "globals.h"
#include "keyword.h"

#define MAJOR_VERSION 1
#define MINOR_VERSION 1

// the munging mode.  This will be read from the registry upon initialization
intg_mungeMode = MungeMode_Off;

// the session ID size is either 16 or 24 chars (depends on the server version)
long g_SessionIDSize=-1;

// the "cookie extra" is the string appended after "ASPSESSIONID" and before
// "=".  It is actually the process ID (with some extra mangling)
static volatile long        g_fCookieExtraSet = 0;
static CRITICAL_SECTION     g_csCookieExtra;
CHAR                        g_szCookieExtra[ COOKIE_NAME_EXTRA_SIZE + 1 ];

static LPCTSTR szRegKey = "Software\\Microsoft\\CkyMunge";
static LPCTSTR szRegValue = "MungeMode";

void SetSessionIDSize( HTTP_FILTER_CONTEXT* );
void SetCookieExtra( LPCSTR );
bool IsValidCookieExtra( LPCSTR );

//
// Optional entry/exit point for all DLLs
//

extern "C"
BOOL
WINAPI
DllMain(
    HINSTANCE /*hInstance*/,
    DWORD     dwReason,
    LPVOID    /*lpReserved*/)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        DEBUG_INIT();
        TRACE("%s starting up\n", EVENT_MODULE);

// get the munge mode from the registry
HKEY hKey;
DWORD dwDisposition;
if ( ::RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
szRegKey,
0,
REG_NONE,
0,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwDisposition ) != ERROR_SUCCESS )
{
TRACE( "Couldn't create/open key in registry\n" );
return FALSE;
}

DWORD dwType;
DWORD dwValue;
DWORD dwBufferSize = sizeof( dwValue );

if ( ::RegQueryValueEx(
hKey,
szRegValue,
NULL,
&dwType,
reinterpret_cast<BYTE*>(&dwValue),
&dwBufferSize ) != ERROR_SUCCESS )
{
TRACE( "No munge mode set, defaulting to smart mode\n" );
dwValue = MungeMode_Smart;

::RegSetValueEx(
hKey,
szRegValue,
0,
REG_DWORD,
reinterpret_cast<BYTE*>(&dwValue),
sizeof( DWORD ) );
}

g_mungeMode = static_cast<int>( dwValue );
        TRACE("MungeMode = %d\n", g_mungeMode);

        if (! InitUtils()  ||  ! InitKeywords())
            return FALSE;

        g_szCookieExtra[0]='\0';
        ::InitializeCriticalSection( &g_csCookieExtra );

    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
        ::DeleteCriticalSection( &g_csCookieExtra );

        if (! TerminateUtils()  ||  ! TerminateKeywords())
            return FALSE;
        TRACE("%s shutting down\n", EVENT_MODULE);
        DEBUG_TERM();
    }

    return TRUE;    // ok
}

//
// Required initialization entrypoint for all ISAPI filters
//

BOOL
WINAPI
GetFilterVersion(
    HTTP_FILTER_VERSION* pVer)
{
    EventReport("", "", EVENTLOG_INFORMATION_TYPE, CMFI_LOADED);

    pVer->dwFilterVersion = HTTP_FILTER_REVISION;

    //  Specify the types and order of notification
    pVer->dwFlags = ((SF_NOTIFY_SECURE_PORT  | SF_NOTIFY_NONSECURE_PORT)
                     | SF_NOTIFY_ORDER_MEDIUM
                     | SF_NOTIFY_PREPROC_HEADERS
                     | SF_NOTIFY_URL_MAP
                     | SF_NOTIFY_SEND_RAW_DATA
                     | SF_NOTIFY_END_OF_REQUEST
                     );

    // Set the filter description
    wsprintf(pVer->lpszFilterDesc,
             "Active Server Pages ISAPI filter for munging ASPSESSIONID "
             "cookies, v%d.%02d",
             MAJOR_VERSION, MINOR_VERSION);
    TRACE("%s\n", pVer->lpszFilterDesc);

    return TRUE;
}

//
// Required dispatch entrypoint for all ISAPI filters
//

DWORD
WINAPI
HttpFilterProc(
    HTTP_FILTER_CONTEXT* pfc,
    DWORD                dwNotificationType,
    VOID*                pvData)
{
    // first verify the session ID size
    if ( g_SessionIDSize == -1 )
    {
        SetSessionIDSize( pfc );
    }

 if ( g_mungeMode == MungeMode_Off )
{
// just get out as quick as possible
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

CNotification* pNotify = CNotification::Get(pfc);
if ( pNotify != NULL )
{
if ( pNotify->MungingOff() )
{
// we must've figured out that the browser is
// accepting cookies.
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
}

    switch (dwNotificationType)
    {
    case SF_NOTIFY_READ_RAW_DATA:
        return OnReadRawData(pfc,    (PHTTP_FILTER_RAW_DATA) pvData);

    case SF_NOTIFY_PREPROC_HEADERS:
        return OnPreprocHeaders(pfc, (PHTTP_FILTER_PREPROC_HEADERS) pvData);

    case SF_NOTIFY_URL_MAP:
        return OnUrlMap(pfc,         (PHTTP_FILTER_URL_MAP) pvData);

    case SF_NOTIFY_AUTHENTICATION:
        return OnAuthentication(pfc, (PHTTP_FILTER_AUTHENT) pvData);

    case SF_NOTIFY_ACCESS_DENIED:
        return OnAccessDenied(pfc,   (PHTTP_FILTER_ACCESS_DENIED) pvData);

    case SF_NOTIFY_SEND_RAW_DATA:
        return OnSendRawData(pfc,    (PHTTP_FILTER_RAW_DATA) pvData);

    case SF_NOTIFY_END_OF_REQUEST:
        return OnEndOfRequest(pfc);

    case SF_NOTIFY_LOG:
        return OnLog(pfc,            (PHTTP_FILTER_LOG) pvData);

    case SF_NOTIFY_END_OF_NET_SESSION:
        return OnEndOfNetSession(pfc);

    default:
        TRACE("Unknown notification: %x, context: %p, data: %p\n",
              dwNotificationType, pfc, pvData);
        return SF_STATUS_REQ_NEXT_NOTIFICATION;
    }
}

//
// Read raw data from the client (browser)
//

DWORD
OnReadRawData(
    PHTTP_FILTER_CONTEXT  pfc,
    PHTTP_FILTER_RAW_DATA pRawData)
{
    TRACE("OnReadRawData(%p, %p)\n", pfc, pRawData);

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

//
// Preprocess the headers of the client's request before the server handles
// the request
//

DWORD
OnPreprocHeaders(
    PHTTP_FILTER_CONTEXT         pfc,
    PHTTP_FILTER_PREPROC_HEADERS pHeaders)
{
    TRACE("OnPreprocHeaders(%p)\n", pfc, pHeaders);

    CHAR  szUrl[1024*5];
    DWORD cbUrl = sizeof szUrl;

    // Get the URL for this request
    if (! pHeaders->GetHeader(pfc, "url", szUrl, &cbUrl))
    {
        TRACE("GetHeader(\"url\") failed\n");
        EventReport("OnPreprocHeaders", "url",
                    EVENTLOG_ERROR_TYPE, CMFE_GETHEADER);
        return SF_STATUS_REQ_ERROR;
    }

    CNotification* pNotify = NULL;
    CHAR szSessionID[ MAX_SESSION_ID_SIZE + 1 ];
    *szSessionID = '\0';

    // Does the URL contain an embedded Session ID, such as
    // /foo/bar.asp-ASP=PVZQGHUMEAYAHMFV ?

    if (DecodeURL(szUrl, szSessionID))
    {
        pNotify = CNotification::SetSessionID(pfc, szSessionID);

        // Set the URL to one without the Session ID
        if (!pHeaders->SetHeader(pfc, "url", szUrl))
        {
            TRACE("Failed to set Url header!\n", szUrl);
            EventReport("OnPreprocHeaders", szUrl,
                        EVENTLOG_ERROR_TYPE, CMFE_SETHEADER);
            return SF_STATUS_REQ_ERROR;
        }
    }

    // Look for a "Cookie:" header

    CHAR  szCookie[1024*4];
    DWORD cbCookie = sizeof szCookie;
    BOOL  fCookie = pHeaders->GetHeader(pfc, "Cookie:", szCookie, &cbCookie);

    if (fCookie  &&  cbCookie > 0)
    {
        TRACE("Cookie: %s\n", szCookie);
        // if the Cookie header includes ASPSESSIONID=<whatever>, use that
        pNotify = CNotification::SetSessionID(pfc, szCookie);

// got a header with a cookie, so don't munge anymore
if ( pNotify )
{
TRACE( "Cookies accepted: stop munging\n" );
pNotify->m_fTestCookies = false;
}
    }
    else if (pNotify != NULL  &&  *szSessionID != '\0')
    {
        // No cookie header, so we synthesize an ASPSESSIONID cookie header
        // from the Session ID embedded in the URL
        CHAR sz[ SZ_SESSION_ID_COOKIE_NAME_SIZE + MAX_SESSION_ID_SIZE
                + 1 + COOKIE_NAME_EXTRA_SIZE + 1];
        wsprintf(sz, "%s%s=%s", SZ_SESSION_ID_COOKIE_NAME,
                 g_szCookieExtra, szSessionID);
        TRACE("About to AddHeader(\"%s\")\n", sz);

        if (!pHeaders->AddHeader(pfc, "Cookie:", sz))
        {
            TRACE("Failed to AddHeader(\"Cookie:\", %s)\n", sz);
            EventReport("OnPreprocHeaders", szUrl,
                        EVENTLOG_ERROR_TYPE, CMFE_ADDHEADER);
            return SF_STATUS_REQ_ERROR;
        }

// if we were testing cookies, we now know that the browser isn't
// sending any to us, so start munging
if ( pNotify->m_fTestCookies )
{
TRACE( "Cookies not accepted: continue munging\n" );
pNotify->m_fTestCookies = false;
pNotify->m_fEatCookies = true;
}
    }

    // Kill the "Connection: Keep-alive" header, so that browser will
    // terminate session properly.  If it's present, the server
    // will send back a "Connection: Keep-Alive" header in response to
    // requests for .htm pages (though not for .asp pages).  The
    // browser will think that there's more data to come, when
    // there's not, and it will show an hourglass cursor and eventually
    // put up an error messagebox.

    BOOL fKeepAlive = pHeaders->SetHeader(pfc, "Connection:", "");

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

//
// We have mapped the URL to the corresponding physical file
//

DWORD
OnUrlMap(
    PHTTP_FILTER_CONTEXT pfc,
    PHTTP_FILTER_URL_MAP pMapInfo)
{
    TRACE("OnUrlMap(%p, %p, %s)\n", pfc, pMapInfo, pMapInfo->pszURL);

    // Can we safely ignore this URL based on its MIME type (e.g., image/gif)?
    if (IsIgnorableUrl(pMapInfo->pszURL))
    {
        CNotification::Destroy(pfc);
        TRACE("Ignoring <%s>\n", pMapInfo->pszURL);
    }
    else
    {
        CNotification* pNotify = CNotification::Get(pfc);

        if (pNotify == NULL)
            pNotify = CNotification::Create(pfc, NULL);

        pNotify->m_nState = HN_SEEN_URL;
    }

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

//
// Authenticating the user
//

DWORD
OnAuthentication(
    PHTTP_FILTER_CONTEXT pfc,
    PHTTP_FILTER_AUTHENT pAuthent)
{
    TRACE("OnAuthentication(%p, %p)\n", pfc, pAuthent);

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

//
// Authentication failed
//

DWORD
OnAccessDenied(
    PHTTP_FILTER_CONTEXT       pfc,
    PHTTP_FILTER_ACCESS_DENIED pAccess)
{
    TRACE("OnAccessDenied(%p, %p)\n", pfc, pAccess);

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

//
// Do the hard work of munging the data.  Note that we may be in one of
// three interesting states: HN_SEEN_URL (initially), HN_IN_HEADER (looking
// at the outgoing HTTP headers), and HN_IN_BODY (in the body of the
// response).  If the browser has cached this URL already (not the case
// with .ASP pages, but typically the case with ordinary HTML pages or
// images), no body is sent and we never move into the HN_IN_BODY state.
//
// The data will be sent in one or more packets, and we may need to buffer
// portions of those packets, as tokens may be split across two or more
// packets.  The code assumes that an individual header will not be split
// across packets.
//

DWORD
OnSendRawData(
    PHTTP_FILTER_CONTEXT  pfc,
    PHTTP_FILTER_RAW_DATA pRawData)
{
    TRACE("OnSendRawData(%p, %p)\n", pfc, pRawData);

   CNotification* pNotify = CNotification::Get(pfc);

    if (pNotify == NULL  ||  pNotify->m_nState == HN_UNDEFINED)
        return SF_STATUS_REQ_NEXT_NOTIFICATION;

if ( pNotify->MungingOff() )
{
// either munging has been turned off, or we detected that
// munging isn't necessary
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

    LPSTR   pszData = (LPSTR) pRawData->pvInData;
    int     iStart = 0; // offset of the beginning of the body data

    // first time in OnSendRawData?
    if (pNotify->m_nState == HN_SEEN_URL)
    {
        // Assume Content-Type header is in first packet
        LPCSTR pszContentType = FindHeaderValue("Content-Type:", "text/html",
                                                pRawData, 0);

        if (pszContentType == NULL)
        {
            pNotify->m_nState = HN_UNDEFINED;   // not HTML; ignore
            return SF_STATUS_REQ_NEXT_NOTIFICATION;
        }
        else
        {
            pNotify->m_nState = HN_IN_HEADER;
            pNotify->m_ct = CT_TEXT_HTML;
        }
    }

    if (pNotify->m_nState == HN_IN_HEADER)
    {
        static const char szSetCookie[] = "Set-Cookie:";
        LPSTR pszCookie = FindString(szSetCookie, pRawData, 0);

        // multiple Set-Cookie headers may be present in the header
        while (pszCookie != NULL)
        {
            pNotify->m_nState = HN_IN_HEADER;

            // Header lines are supposed to be terminated by "\r\n"
            LPSTR pszEoln = strchr(pszCookie, '\r');

            if (pszEoln != NULL)
            {
                *pszEoln = '\0';
                TRACE("%s\n", pszCookie);

                // ASP only sends the ASPSESSIONID cookie if a session ID
                // hasn't already been picked, which happens either when
                // Session_OnStart is executed (if it and global.asa are
                // present) or when the Session object is first modified
                // by user code.
                LPCSTR szCookieName;
                if ( ( szCookieName =
                       strstr(pszCookie, SZ_SESSION_ID_COOKIE_NAME) ) != NULL)
                {
                    // need to figure out what's tacked onto the cookie name.
                    if ( !g_fCookieExtraSet )
                    {
                        SetCookieExtra( szCookieName );
                    }

                    // we know this cookie contains ASPSESSIONID, but is it
                    // ours? (the cookie extra parts must match)
                    if ( strstr( szCookieName, g_szCookieExtra ) != NULL )
                    {
                        VERIFY(CNotification::SetSessionID(pfc, pszCookie)
                               == pNotify);
                        TRACE("Update: %s\n", pNotify->SessionID());

                        *pszEoln = '\r';    // restore

                        // Eat outgoing "Set-Cookie: ASPSESSIONIDXXXXXXXX=..."
                        // header?  Benign for cookie-less browsers; will keep
                        // cookie-warning browsers quiet.
                        if (pNotify->m_fEatCookies)
                        {
                            TRACE("Deleting cookie\n");
                            DeleteLine(szSetCookie, pRawData, pszCookie);
                        }
                    }
                }
                else
                {
                    *pszEoln = '\r';    // restore
                }

                pszCookie =
                    FindString(szSetCookie, pRawData,
                            pszEoln - static_cast<LPCSTR>(pRawData->pvInData));
            }
            else
            {
                pszCookie = NULL; // terminate loop
            }
        }

        // If a Content-Length header is present, we need to destroy it
        // because there is no way we can guess a priori how much longer
        // the data will become.  If we don't destroy it, the browser will
        // believe the header and become very confused.

        static const char szContentLength[] = "Content-Length:";
        LPSTR pszCL = FindString(szContentLength, pRawData, 0);

        if (pszCL != NULL)
        {
            char szFmt[ARRAYSIZE(szContentLength) + 10];
            sprintf(szFmt, "%s %%u", szContentLength);
            sscanf(pszCL, szFmt, &pNotify->m_cchContentLength);
            TRACE("%s is %u\n", szContentLength, pNotify->m_cchContentLength);
            DeleteLine(szContentLength, pRawData, pszCL);
        }

        // Is the end-of-headers marker present?
        LPCSTR pszEndHeaderBlock = FindString("\n\r\n", pRawData, 0);

        if (pszEndHeaderBlock != NULL)
        {
            pNotify->m_nState = HN_IN_BODY;
            iStart = pszEndHeaderBlock + 3 - pszData;
        }
    }

    // We're in the body.  Let's do some real work.

    if (pNotify->m_nState == HN_IN_BODY)
    {
        LPSTR pszBuf = pszData;
        int iEnd;

        // Have we got a partial line from the last packet?  If so, it
        // means that the last character in the buffer on that packet was
        // not a token boundary character, such as '\n' or '>'.  Prepend
        // that data to the current batch.

        if (pNotify->m_pbPartialToken != NULL)
        {
            ASSERT(iStart == 0);
            ASSERT(pNotify->m_cbPartialToken > 0);

            pNotify->AppendToken(pfc, pszBuf, pRawData->cbInData);

            pRawData->pvInData = pszBuf = (LPSTR) pNotify->m_pbPartialToken;
            pRawData->cbInData = pRawData->cbInBuffer
                = pNotify->m_cbPartialToken;

            iEnd = g_trie.EndOfBuffer(pRawData, iStart);

            if (iEnd < 0)
            {
                // Don't let IIS send any data on this pass
                pRawData->pvInData = NULL;
                pRawData->cbInData = pRawData->cbInBuffer = 0;

                return SF_STATUS_REQ_NEXT_NOTIFICATION;
            }
            else
            {
                // Have a complete token
                pNotify->m_pbPartialToken = NULL;
                pNotify->m_cbPartialToken = 0;
            }
        }

        ASSERT(pNotify->m_pbPartialToken == NULL
               &&  pNotify->m_cbPartialToken == 0);

        // Is the last token in the block incomplete?
        iEnd = g_trie.EndOfBuffer(pRawData, iStart);

        if (iEnd != pRawData->cbInData)
        {
            LPSTR pszBoln = (iEnd < 0)  ?  pszBuf + iStart  :  pszBuf + iEnd ;

            TRACE("Partial Token: ");
            pNotify->AppendToken(pfc, pszBoln,
                                 (pszBuf + pRawData->cbInData) - pszBoln);
            pRawData->cbInData -= pNotify->m_cbPartialToken;
        }

        pNotify->m_cchContentLength -= pRawData->cbInData;

        // Filter whatever is left
        const int nExtra = Filter(pfc, pRawData, pszBuf,
                                  pRawData->cbInData, iStart,
                                  pNotify->m_szSessionID);
    }

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

//
// The transaction is over.  Pump out any remaining data before the
// connection closes.
//

DWORD
OnEndOfRequest(
    PHTTP_FILTER_CONTEXT pfc)
{
    TRACE("OnEndOfRequest(%p)\n", pfc);

    CNotification* pNotify = CNotification::Get(pfc);

    if (pNotify != NULL  &&  pNotify->m_pbPartialToken != NULL)
    {
        // append a '\n', which is guaranteed to be a token boundary char
        pNotify->m_pbPartialToken[pNotify->m_cbPartialToken] = '\n';
#ifdef _DEBUG
        pNotify->m_pbPartialToken[pNotify->m_cbPartialToken + 1] = '\0';
#endif
        LPBYTE pbTemp = pNotify->m_pbPartialToken;
        DWORD  cb = pNotify->m_cbPartialToken + 1;

        // Note: WriteClient ends up calling OnSendRawData.  Destroy the
        // partial token before it's called.
        pNotify->m_pbPartialToken = NULL;
        pNotify->m_cbPartialToken = 0;

        if (!pfc->WriteClient(pfc, pbTemp, &cb, 0))
            TRACE("WriteClient failed, err %x.\n", GetLastError());
    }

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

//
// Log the details of the transaction
//

DWORD
OnLog(
    PHTTP_FILTER_CONTEXT pfc,
    PHTTP_FILTER_LOG     pLog)
{
    TRACE("OnLog(%p, %p)\n", pfc, pLog);

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

//
// The HTTP session (transaction) is over and the connection has been closed.
//

DWORD
OnEndOfNetSession(
    PHTTP_FILTER_CONTEXT pfc)
{
    TRACE("OnEndOfNetSession(%p)\n", pfc);

    CNotification::Destroy(pfc);

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

void
SetSessionIDSize(
    PHTTP_FILTER_CONTEXT pfc )
{
    static const char szVersion4B2[] = "Microsoft-IIS/4.0 Beta 2";
    static const char szVersion4[]   = "Microsoft-IIS/4.0";
    const long version3Size = 16;
    const long version4Size = MAX_SESSION_ID_SIZE;
    const long version4B2Size = 16;

    long size = version3Size;

    DWORD dwBufferSize = 0;
    pfc->GetServerVariable( pfc, "SERVER_SOFTWARE", NULL, &dwBufferSize );
    if ( ::GetLastError() == ERROR_INSUFFICIENT_BUFFER )
    {
        LPSTR str = (LPSTR)_alloca( ++dwBufferSize );
        if (pfc->GetServerVariable(pfc, "SERVER_SOFTWARE", str, &dwBufferSize))
        {
            TRACE( "Server Software: %s\n", str );
            if ( strcmp( szVersion4B2, str ) == 0 )
            {
                TRACE( "Using version 4 beta 2 session ID size (%d bytes)\n",
                       version4B2Size );
                size = version4B2Size;
            }
            else if ( strncmp( szVersion4, str, strlen( szVersion4 ) ) == 0 )
            {
                TRACE( "Using version 4 session ID size (%d bytes)\n",
                       version4Size );
                size = version4Size;
            }
            else
            {
                TRACE( "Using version 3 session ID size (%d bytes)\n",
                       version3Size );
            }
        }
        else
        {
            TRACE( "Failed to get server variable, error: %d\n",
                   ::GetLastError() );
        }
    }
    else
    {
        TRACE( "Failed to get server variable(SERVER_SOFTWARE), error: %d\n",
               ::GetLastError() );
    }
    ::InterlockedExchange( &g_SessionIDSize, size );
}

void
SetCookieExtra(
    LPCSTR  szCookieName )
{
    ::EnterCriticalSection( &g_csCookieExtra );
    // need to check again in case the cookie extra was set
    // while we were waiting on the critical section
    if ( !g_fCookieExtraSet )
    {
        szCookieName += SZ_SESSION_ID_COOKIE_NAME_SIZE;
        if ( *szCookieName != '=' )
        {
            CHAR szExtra[ COOKIE_NAME_EXTRA_SIZE + 1 ];
            strncpy( szExtra, szCookieName, COOKIE_NAME_EXTRA_SIZE );
            szExtra[ COOKIE_NAME_EXTRA_SIZE ] = 0;

            if ( IsValidCookieExtra( szExtra ) )
            {
                // copy the cookie name extra
                strcpy( g_szCookieExtra, szExtra );
                TRACE("SetCookieExtra(%s)\n", g_szCookieExtra);
                ::InterlockedExchange( (long*)&g_fCookieExtraSet, 1 );
            }
            else
            {
                TRACE( "Cookie extra validation failed\n" );
            }
        }
    }
    ::LeaveCriticalSection( &g_csCookieExtra );
}

// Check to see if this `extra part' is a valid value for this server.
// The extra part is derived from the process ID and then randomized
// slightly.  So we can tell if the extra is reasonable based on the
// process ID.
bool
IsValidCookieExtra(
    LPCSTR  szExtra )
{
    bool rc = true;

    CHAR szProcessID[ COOKIE_NAME_EXTRA_SIZE ];

    // Process ID
    wsprintf(szProcessID, "%08X", GetCurrentProcessId());

    // check based on how we know the process ID is munged to the cookie extra
    static const char *pszDigitsToLetters[2] = {"GHIJKLMNOP","QRSTUVWXYZ"};

    for (int i = 0; i < COOKIE_NAME_EXTRA_SIZE; i++)
    {
        char cp = szProcessID[i];
        char ce = szExtra[i];
        if ( ( cp >= '0' ) && ( cp <= '9' ) )
        {
            int ndx = cp - '0';
            if ( ( pszDigitsToLetters[0][ndx] == ce )
                 || ( pszDigitsToLetters[1][ndx] == ce ) )
            {
                // okay, keep checking
            }
            else
            {
                // no good
                rc = false;
                i = COOKIE_NAME_EXTRA_SIZE;
            }
        }
        else
        {
            if ( cp == ce )
            {
                // okay, keep checking
            }
            else
            {
                // no good
                rc = false;
                i = COOKIE_NAME_EXTRA_SIZE;
            }
        }
    }
#ifdef _DEBUG
    if (!rc)
        TRACE("`%s' is not a valid extra\n", szExtra);
#endif
    return rc;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by version.rc
//

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


STATE.H

#ifndef __STATE_H__
#define __STATE_H__

#include <vector>
#include <stack>

#include "Notify.h"
#include "debug.h"

// Needed for STL on Visual C++ 5.0
#if _MSC_VER>=1100
using namespace std;
#endif

enum PARSESTATE {
INVALID = -1,
TEXT = 1,
COMMENT,  // <!-- ... -->
COMMENT2, // <comment> ... </comment>
ANCHOR,
AREA,
HREF,
};

class CStateStack : public stack< PARSESTATE, vector<PARSESTATE> >
{
public:
CStateStack()
        : m_fInTag(FALSE),
          m_fInComment(FALSE),
          m_fInComment2(FALSE)
    {
        *m_szSessionID = '\0';
        push(TEXT);
    }

CStateStack(
        LPCTSTR ptszSessionID)
        : m_fInTag(FALSE),
          m_fInComment(FALSE),
          m_fInComment2(FALSE)
    {
        strcpy(m_szSessionID, ptszSessionID);
        push(TEXT);
    }

    ~CStateStack()
    {
    }

    BOOL    m_fInTag;
    BOOL    m_fInComment;
    BOOL    m_fInComment2;
    CHAR    m_szSessionID[ MAX_SESSION_ID_SIZE ];
};

#endif // __STATE_H__


TOKEN.CPP

#include "CkyPch.h"

#include "debug.h"
#include "token.h"
#include "utils.h"

// Base class for keywords

CToken::CToken(
    const string& rstr,
    const BOUNDARY bndPrefix /* =IRRELEVANT */,
    const BOUNDARY bndSuffix /* =IRRELEVANT */)
    : m_str(rstr),
      m_bndPrefix(bndPrefix),
      m_bndSuffix(bndSuffix)
{
}

CToken::~CToken()
{
}

BOOL
CToken::MatchesBoundaryClass(
    const TCHAR tch,
    const BOUNDARY bnd)
{
    switch (bnd)
    {
    case IRRELEVANT:
        return TRUE;

    case WHITESPACE:
        return _istspace(tch);

    case ALPHA:
        return _istalpha(tch);

    case NUMERIC:
        return _istdigit(tch);

    case ALPHANUMERIC:
        return _istalnum(tch);

    case NEWLINE:
        return tch == _T('\n')  ||  tch == _T('\r');

    default:
        ASSERT(FALSE);
        return FALSE;
    }
}

// DoFilter: default implementation is to update the state stack
// and copy the text that matched the token

UINT
CToken::DoFilter(
    CStateStack& rss,
    LPCTSTR&     rptszData,
    UINT         cchData,
    LPTSTR&      rptszOutBuf) const
{
    const UINT cb  = CountBytes(rss, rptszData, cchData);
    const UINT cch = m_str.length();

    // for (UINT i = 0; i < cch; ++i)
    //     TRACE("%c", rptszOutBuf[i]);

    memcpy(rptszOutBuf, rptszData, cch);
    rptszData += cch;
    rptszOutBuf += cch;

    return cb;
}

#ifdef _DEBUG

void
CToken::AssertValid() const
{
}

void
CToken::Dump() const
{
    TRACE("\t%d %d", (int) m_bndPrefix, (int) m_bndSuffix);
}

#endif // _DEBUG

//----------------------------------------------------------------

bool
CTokenTrie::AddToken(
    const CToken* ptok)
{
    return CTrie<CToken, true, true>::AddToken(ptok->m_str.c_str(), ptok);
}

inline bool
CTokenTrie::_LastCharPresent(
    CHAR ch) const
{
    ASSERT(CHAR_MIN <= ch  &&  ch <= CHAR_MAX);
    const UINT i = ch - CHAR_MIN;   // CHAR_MIN is -128 for `signed char'

    return m_afLastChar[i >> 3] & (1 << (i & 7))  ?  true  :  false;
}

inline void
CTokenTrie::_SetLastCharPresent(
    CHAR ch,
    bool f)
{
    ASSERT(CHAR_MIN <= ch  &&  ch <= CHAR_MAX);
    const UINT i = ch - CHAR_MIN;

    if (f)
        m_afLastChar[i >> 3] |=  (1 << (i & 7));
    else
        m_afLastChar[i >> 3] &= ~(1 << (i & 7));
}

// ctor

CTokenTrie::CTokenTrie()
{
    memset(m_afCharPresent, 0, sizeof(m_afCharPresent));

    static const CHAR achEndTokens[] = {
        ' ', '\t', '\f', '\b', '\r', '\n', '>',
    };

    for (int i = ARRAYSIZE(achEndTokens);  --i >= 0; )
        _SetLastCharPresent(achEndTokens[i], true);
}

// Returns 1 past the last character which is a valid token-ending char,
// or < 0 if no such char
int
CTokenTrie::EndOfBuffer(
    PHTTP_FILTER_RAW_DATA pRawData,
    int iStart)
{
    LPSTR pszData = (LPSTR) pRawData->pvInData;

    // Empty interval?
    if (pRawData->cbInData == iStart)
        return iStart;

    for (int i = pRawData->cbInData;  --i >= iStart; )
    {
        if (_LastCharPresent(pszData[i]))
            return i+1;
    }

    return -1;
}


TOKEN.H

#ifndef __TOKEN_H__
#define __TOKEN_H__

#include <string>
#include <tchar.h>

#include "state.h"
#include "trie.h"

//+---------------------------------------------------------------------
// CToken:

class CToken
{
public:
    enum BOUNDARY {
        IRRELEVANT = 1,
        WHITESPACE,
        ALPHA,
        NUMERIC,
        ALPHANUMERIC,
        NEWLINE,
    };

    CToken(
        const string&  rstr,
        const BOUNDARY bndPrefix = IRRELEVANT,
        const BOUNDARY bndSuffix = IRRELEVANT);

    static BOOL
    MatchesBoundaryClass(
        const TCHAR    tch,
        const BOUNDARY bnd);

    BOOL
    MatchesPrefixBoundary(
        const TCHAR tch) const
    {return MatchesBoundaryClass(tch, m_bndPrefix);}

    BOOL
    MatchesSuffixBoundary(
        const TCHAR tch) const
    {return MatchesBoundaryClass(tch, m_bndSuffix);}

    virtual UINT
    CountBytes(
        CStateStack& rss,
        LPCTSTR      ptszData,
        UINT         cchData) const = 0;

    virtual UINT
    DoFilter(
        CStateStack& rss,
        LPCTSTR&     rptszData,
        UINT     cchData,
        LPTSTR&      rptszOutBuf) const;

    const string    m_str;
    const BOUNDARY  m_bndPrefix;
    const BOUNDARY  m_bndSuffix;

// Implementation
public:
#ifdef _DEBUG
    virtual void
    AssertValid() const;

    virtual void
    Dump() const;
#endif

    virtual
    ~CToken();
};

//+---------------------------------------------------------------------
// CTokenTrie:

class CTokenTrie : public CTrie<CToken, true, true>
{
public:
    CTokenTrie();

    bool
    AddToken(
        const CToken* ptok);

    int
    EndOfBuffer(
        PHTTP_FILTER_RAW_DATA pRawData,
        int iStart);

private:
    // bit array for last letter of all tokens
    BYTE  m_afLastChar[(CHAR_MAX - CHAR_MIN + 1 + 7) / 8];

    bool
    _LastCharPresent(
        CHAR ch) const;

    void
    _SetLastCharPresent(
        CHAR ch,
        bool f);
};

#endif // __TOKEN_H__


TRIE.H

// A trie is a multiway search tree (aka a radix tree).  See a good
// algorithms text, like Knuth or Sedgewick, for a complete description.
//
// Briefly, given a list of strings such as
//      cab, car, carts, cats, dog, doge, dogs
// you get a trie that looks like this:
//
//                /-[b]
//               /
//        <c>--<a>--[r]--<t>--[s]
//       /       \
//      /         \-<t>--[s]
//     *
//      \              /-[e]
//       \            /
//        <d>--<o>--[g]
//                    \
//                     \-[s]
//
// where `[r]' denotes the end of a word and `<a>', the middle.
//
// A trie has several useful properties:
//  * fast
//  * easily handles longest substring matches
//  * fairly compact, especially when there are many overlapping strings
//
// The multiway tree is implemented as a binary tree with child and sibling
// pointers.
//
// The CTrie template takes three parameters:
//      class _TOKEN:        up to you
//      bool  fIgnoreCase:   case-sensitivity for searches
//      bool  fDeleteTokens: delete _TOKEN* when Flush() called?
// and it exposes three methods:
//      bool AddToken(ptszToken, _TOKEN*)
//      _TOKEN* Search(ptszSearch, pctchMatched = NULL, nMaxLen = 0)
//      void Flush()
//
// Use them like this:
//      CTrie<CToken, true, true> trie;
//      CToken* ptokHello = new CToken(...);
//
//      VERIFY(trie.AddToken(_T("Hello"), ptokHello));
//
//      CToken* ptok = trie.Search(_T("Goodbye"));
//      if (ptok != NULL) {...}
//
//      if (fIniFileChanged)
//      {
//          trie.Flush();   // will delete all tokens
//          AddTokensFromIniFile(trie);
//      }
//
// Note: If you use DUMP(&trie) or ASSERT_VALID(&trie), your _TOKEN class must
// have Dump() or AssertValid() methods, respectively, in its _DEBUG version.
//
//
// TODO:
//  * template really ought to be parameterized on ANSI/Unicode too
//  * STLify it: add iterators, turn it into a container, etc
//  * remove Win32 dependencies (TCHAR)
//  * add operator= and copy ctor
//
//
// George V. Reilly  <gvr@halcyon.com>  Oct 1995  Initial implementation
// George V. Reilly  <gvr@halcyon.com>  Sep 1996  Add CharPresent for ANSI
// George V. Reilly  <gvr@halcyon.com>  Mar 1997  Templatized; removed MFC

#ifndef __TRIE_H__
#define __TRIE_H__

#include <tchar.h>
#include <limits.h>
#include <malloc.h>

#include "debug.h"

// Workaround for bool being a "reserved extension" in Visual C++ 4.x
#if _MSC_VER<1100
# ifndef bool
#  define bool  BOOL
# endif
# ifndef true
#  define true  TRUE
# endif
# ifndef false
#  define false FALSE
# endif
#endif

// forward declaration
template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens> class CTrie;

//+---------------------------------------------------------------------
//  Class:      CTrieNode (tn)
//      one node for each letter

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
class CTrieNode
{
    friend class CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>;
    typedef CTrieNode<_TOKEN, fIgnoreCase, fDeleteTokens> _Node;

public:
    CTrieNode();

    CTrieNode(
        _Node*        pParent,
        const _TOKEN* ptok,
        const TCHAR   tch,
        LPCTSTR       ptszToken);

    bool
    SetData(
        const _TOKEN* ptok,
        LPCTSTR       ptszToken);

    ~CTrieNode();

protected:
    const _Node*  m_pParent;
    _Node*        m_pSibling;
    _Node*        m_pChild;
    const _TOKEN* m_ptok;
#ifdef _DEBUG
    LPTSTR        m_ptszToken;
#endif
    const TCHAR   m_tch;
    TCHAR         m_tchMaxChild;    // Maximum m_tch of child nodes (1 level)

// Diagnostics
public:
#ifdef _DEBUG
    void
    AssertValid() const;

    virtual void
    Dump() const;

protected:
    bool
    CheckNodeToken() const;
#endif

private:
    // private, unimplemented copy ctor and op= to prevent
    // compiler synthesizing them
    CTrieNode(const CTrieNode&);
    CTrieNode& operator=(const CTrieNode&);
};

//+---------------------------------------------------------------------
//  Class:      CTrie (trie)

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
class CTrie
{
    typedef CTrieNode<_TOKEN, fIgnoreCase, fDeleteTokens> _Node;

public:
    CTrie();

    virtual
    ~CTrie();

    virtual bool
    AddToken(
        LPCTSTR             ptszToken,
        const _TOKEN* const ptok);

    virtual const _TOKEN*
    Search(
        LPCTSTR   ptszSearch,
        int*      pctchMatched = NULL,
        const int nMaxLen = 0) const;

    virtual void
    Flush();

protected:
    _Node  m_tnRoot;
    TCHAR  m_tchMinChild;
    TCHAR  m_tchMaxChild;

    void
    _DeleteTrie(
        _Node* ptn);

#ifndef _UNICODE
    // bit array for first letter of all tokens
    BYTE  m_afCharPresent[(CHAR_MAX - CHAR_MIN + 1 + 7) / 8];

    bool
    _CharPresent(
        CHAR ch) const;

    void
    _SetCharPresent(
        CHAR ch,
        bool f);
#endif // !UNICODE

// Diagnostics
public:
#ifdef _DEBUG
    virtual void
    AssertValid() const;

    virtual void
    Dump() const;

protected:
    int   m_ctchMaxTokenLen;    // length of longest token string

    void
    _AssertWalk(
        _Node* ptn,
        LPTSTR ptszName,
        int    iLevel) const;

    void
    _DumpWalk(
        _Node* ptn,
        LPTSTR ptszName,
        int    iLevel,
        int&   rcNodes,
        int&   rcTokens) const;
#endif

private:
    // private, unimplemented copy ctor and op= to prevent
    // compiler synthesizing them
    CTrie(const CTrie&);
    CTrie& operator=(const CTrie&);
};

#ifdef _UNICODE
# define TCHAR_MIN L'\0'
#else // !UNICODE
# define TCHAR_MIN CHAR_MIN
#endif // !UNICODE

//-----------------------------------------------------------------------------
// CTrieNode implementation

// CTrieNode::CTrieNode
//      default ctor (needed for CTrie::m_tnRoot)

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
CTrieNode<_TOKEN, fIgnoreCase, fDeleteTokens>::CTrieNode()
    : m_pParent(NULL),
      m_pSibling(NULL),
      m_pChild(NULL),
      m_ptok(NULL),
#ifdef _DEBUG
      m_ptszToken(NULL),
#endif
      m_tch(TCHAR_MIN),
      m_tchMaxChild(TCHAR_MIN)
{
}

// CTrieNode::CTrieNode
//      ctor

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
CTrieNode<_TOKEN, fIgnoreCase, fDeleteTokens>::CTrieNode(
    _Node*        pParent,
    const _TOKEN* ptok,
    const TCHAR   tch,
    LPCTSTR       ptszToken)
    : m_pParent(pParent),
      m_pSibling(NULL),
      m_pChild(NULL),
      m_ptok(ptok),
#ifdef _DEBUG
      m_ptszToken(NULL),
#endif
      m_tch(tch),
      m_tchMaxChild(TCHAR_MIN)
{
    ASSERT(m_pParent != NULL);
    ASSERT(m_tch > TCHAR_MIN);

    _Node* ptnPrev = NULL;
    _Node* ptn = m_pParent->m_pChild;

    // find where in the list of pParent's children to insert `this'
    while (ptn != NULL  &&  ptn->m_tch < m_tch)
    {
        ptnPrev = ptn;
        ptn = ptn->m_pSibling;
    }

    ASSERT(ptn == NULL  ||  ptn->m_tch != m_tch);

    if (ptnPrev == NULL)
    {
        ASSERT(pParent->m_pChild == ptn);
        pParent->m_pChild = this;
    }
    else
        ptnPrev->m_pSibling = this;

    this->m_pSibling = ptn;

    if (pParent->m_tchMaxChild < m_tch)
        pParent->m_tchMaxChild = m_tch;

#ifdef _DEBUG
    if (ptszToken != NULL)
    {
        ASSERT(m_ptok != NULL);
        m_ptszToken = new TCHAR [_tcslen(ptszToken) + 1];
        _tcscpy(m_ptszToken, ptszToken);
    }
#endif
}

// CTrieNode::SetData
//      sets the data if it's NULL.  Needed if you do
//      AddToken("foobar", &tokFoobar) and then AddToken("foo", &tokFoo)
//      to set the data for tokFoo.

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
bool
CTrieNode<_TOKEN, fIgnoreCase, fDeleteTokens>::SetData(
    const _TOKEN* ptok,
    LPCTSTR       ptszToken)
{
    // Don't set data if ptok is NULL
    if (ptok == NULL)
        return false;

    // overwrite m_ptok only if it is NULL
    if (m_ptok == NULL)
    {
        m_ptok = ptok;
#ifdef _DEBUG
        ASSERT(m_ptszToken == NULL);
        ASSERT(ptszToken != NULL);
        m_ptszToken = new TCHAR [_tcslen(ptszToken) + 1];
        _tcscpy(m_ptszToken, ptszToken);
#endif
    }

    return true;
}

// CTrieNode::~CTrieNode
//      dtor

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
CTrieNode<_TOKEN, fIgnoreCase, fDeleteTokens>::~CTrieNode()
{
#ifdef _DEBUG
    delete [] m_ptszToken;
#endif

    // Is this an auto-delete trie, i.e., do we take care of deleting
    // the _TOKENs?
    if (fDeleteTokens)
    {
        // cast away constness so that delete will work
        delete const_cast<_TOKEN*> (m_ptok);
    }

    ASSERT(m_pChild == NULL);
}

//-----------------------------------------------------------------------------
// CTrieNode diagnostics

#ifdef _DEBUG

// CTrieNode::CheckNodeToken
//      Do the real work of validating a CTrieNode object

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
bool
CTrieNode<_TOKEN, fIgnoreCase, fDeleteTokens>::CheckNodeToken() const
{
    // If there's no m_ptok, it's automatically valid
    if (m_ptok == NULL)
        return true;

    ASSERT(m_ptszToken != NULL);
    const int cLen = _tcslen(m_ptszToken);
    const _Node* ptn = this;

    ASSERT((m_pChild == NULL  &&  m_tchMaxChild == TCHAR_MIN)
           ||  (m_pChild != NULL  &&  m_tchMaxChild > TCHAR_MIN));

    // Walk back up towards CTrie::m_tnRoot
    for (int i = cLen;  --i >= 0;  )
    {
        ASSERT(ptn != NULL);
        ASSERT(ptn->m_tch != TCHAR_MIN);

        const TCHAR tch = (fIgnoreCase
                           ? (TCHAR) _totlower(this->m_ptszToken[i])
                           : this->m_ptszToken[i]);

        if (ptn->m_tch != tch)
            ASSERT(false);

        ASSERT(ptn->m_pParent != NULL  &&  ptn->m_pParent->m_pChild != NULL);

        const _Node* ptn2;

        // check to see if ptn really is a child of its parent
        for (ptn2 = ptn->m_pParent->m_pChild;
             ptn2 != ptn  &&  ptn2 != NULL;
             ptn2 = ptn2->m_pSibling)
        {}
        ASSERT(ptn2 == ptn);

        // check that ptn->m_pParent->m_tchMaxChild is correct
        for (ptn2 = ptn->m_pParent->m_pChild;
             ptn2->m_pSibling != NULL;
             ptn2 = ptn2->m_pSibling)
        {
            ASSERT(ptn2->m_tch > TCHAR_MIN
                   &&  ptn2->m_tch < ptn2->m_pSibling->m_tch);
        }
        ASSERT(ptn->m_pParent->m_tchMaxChild == ptn2->m_tch);

        ptn = ptn->m_pParent;
        ASSERT(ptn->m_ptok != this->m_ptok);
    }

    // check to see if ptn == CTrie::m_tnRoot
    ASSERT(ptn->m_pParent == NULL  &&  ptn->m_pSibling == NULL
           &&  ptn->m_tch == TCHAR_MIN  &&  ptn->m_ptok == NULL);

    return true;
}

// CTrieNode::AssertValid
//      Validate a CTrieNode object

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
void
CTrieNode<_TOKEN, fIgnoreCase, fDeleteTokens>::AssertValid() const
{
    ASSERT(CheckNodeToken());
}

// CTrieNode::Dump
//      Dump a CTrieNode object

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
void
CTrieNode<_TOKEN, fIgnoreCase, fDeleteTokens>::Dump() const
{
    // TODO: flesh out
}

#endif // _DEBUG

//-----------------------------------------------------------------------------
// CTrie implementation

// CTrie::CTrie
//      ctor

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::CTrie()
{
    Flush();
}

// CTrie::~CTrie
//      dtor

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::~CTrie()
{
    Flush();
}

#ifndef _UNICODE

// CTrie::_CharPresent

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
inline bool
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::_CharPresent(
    CHAR ch) const
{
    ASSERT(CHAR_MIN <= ch  &&  ch <= CHAR_MAX);
    const UINT i = ch - CHAR_MIN;   // CHAR_MIN is -128 for `signed char'

    return m_afCharPresent[i >> 3] & (1 << (i & 7))  ?  true  :  false;
}

// CTrie::_SetCharPresent

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
inline void
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::_SetCharPresent(
    CHAR ch,
    bool f)
{
    ASSERT(CHAR_MIN <= ch  &&  ch <= CHAR_MAX);
    const UINT i = ch - CHAR_MIN;

    if (f)
        m_afCharPresent[i >> 3] |=  (1 << (i & 7));
    else
        m_afCharPresent[i >> 3] &= ~(1 << (i & 7));
}

#endif // !UNICODE

// CTrie::AddToken
//      Add search string `ptszToken' to trie, which will return `ptok'
//      if searched for in Search().

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
bool
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::AddToken(
    LPCTSTR             ptszToken,
    const _TOKEN* const ptok)
{
    if (ptok == NULL  ||  ptszToken == NULL  ||  *ptszToken == _T('\0'))
    {
        ASSERT(false);
        return false;
    }

    const int cLen = _tcslen(ptszToken);
    _Node* ptnParent = &m_tnRoot;

    for (int i = 0;  i < cLen;  ++i)
    {
        ASSERT(ptnParent != NULL);

        _Node* ptn = ptnParent->m_pChild;
        const TCHAR tch = (fIgnoreCase
                           ? (TCHAR) _totlower(ptszToken[i])
                           : ptszToken[i]);
        const _TOKEN* ptok2 = (i == cLen - 1)  ?  ptok       :  NULL;
        LPCTSTR ptsz2 =       (i == cLen - 1)  ?  ptszToken  :  NULL;

        while (ptn != NULL  &&  ptn->m_tch < tch)
            ptn = ptn->m_pSibling;

        if (ptn == NULL  ||  ptn->m_tch > tch)
        {
            ptnParent = new _Node(ptnParent, ptok2, tch, ptsz2);
        }
        else
        {
            ASSERT(ptn->m_tch == tch);

            ptn->SetData(ptok2, ptsz2);
            ptnParent = ptn;
        }

        ASSERT(ptnParent->CheckNodeToken());
    }

    m_tchMinChild = m_tnRoot.m_pChild->m_tch;
    m_tchMaxChild = m_tnRoot.m_tchMaxChild;
#ifdef _DEBUG
    m_ctchMaxTokenLen = max(m_ctchMaxTokenLen, cLen);
#endif

    ASSERT(TCHAR_MIN < m_tchMinChild  &&  m_tchMinChild <= m_tchMaxChild);

#ifndef _UNICODE
    // Keep a map of the initial letter of each token, to speed up searches
    if (fIgnoreCase)
    {
        _SetCharPresent(tolower(ptszToken[0]), true);
        _SetCharPresent(toupper(ptszToken[0]), true);
    }
    else
        _SetCharPresent(ptszToken[0], true);
#endif // !UNICODE

#ifdef _DEBUG
    int nTemp;
    const _TOKEN* ptok2 = Search(ptszToken, &nTemp);

    ASSERT(ptok2 == ptok  &&  nTemp == cLen);
#endif // _DEBUG

    return true;
}

// CTrie::Search
//      Search trie for `ptszSearch', returning count of characters
//      matched in `pctchMatched' (if non-NULL), matching at most `nMaxLen'
//      characters, if nMaxLen != 0, or _tcslen(ptszSearch) otherwise.

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
const _TOKEN*
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::Search(
    LPCTSTR   ptszSearch,
    int*      pctchMatched /* = NULL */,
    const int nMaxLen /* = 0 */) const
{
    // Set count of matched characters
    if (pctchMatched != NULL)
        *pctchMatched = 0;

#ifndef _UNICODE
    if (! _CharPresent(ptszSearch[0]))
        return NULL;

    TCHAR tch;
#else    // UNICODE
    TCHAR tch = fIgnoreCase ? (TCHAR) _totlower(ptszSearch[0]) : ptszSearch[0];

    if (tch < m_tchMinChild  ||  m_tchMaxChild < tch)
        return NULL;
#endif // UNICODE

    // For some uses (e.g., ptszSearch is not '\0'-terminated), nMaxLen is
    // specified.  If it's not specified, use the length of the string.
    const int cLen = (nMaxLen != 0)  ?  nMaxLen  :  _tcslen(ptszSearch);
    ASSERT(0 < cLen);

    bool fOvershot = true;
    const _Node* ptnParent = &m_tnRoot;
    const _Node* ptn = NULL;
    int i;

    // Find the longest approximate match.  For example, if we have "foo"
    // and "foobar" in the trie and we're asked to match "fool", we'll work
    // our way down to "foob", then backtrack up to "foo".

    for (i = 0;  i < cLen;  ++i)
    {
        ASSERT(ptnParent != NULL);

        ptn = ptnParent->m_pChild;
        ASSERT(ptn != NULL  &&  ptn->m_pParent == ptnParent);

        tch = fIgnoreCase ? (TCHAR) _totlower(ptszSearch[i]) : ptszSearch[i];
        ASSERT(tch >= TCHAR_MIN);

        if (ptnParent->m_tchMaxChild < tch)
        {
            ASSERT(i > 0);
            break;
        }

        while (ptn != NULL  &&  ptn->m_tch < tch)
            ptn = ptn->m_pSibling;

        // failed to match?
        if (ptn == NULL  ||  ptn->m_tch > tch)
        {
            ASSERT(ptn == NULL  ||  ptn->m_tch <= ptnParent->m_tchMaxChild);

            if (i == 0)
                return NULL;
            break;
        }
        else
        {
            ASSERT(ptn->m_tch == tch);
            ASSERT(ptn->m_pParent->m_tchMaxChild >= tch);

            if (ptn->m_pChild == NULL)
            {
                ASSERT(ptn->m_ptok != NULL);
                fOvershot = false;
                break;
            }

            ptnParent = ptn;
        }
    }

    if (fOvershot)
    {
        --i;  ptn = ptnParent;  // back up one character
    }
    else
        ASSERT(ptn->m_pChild == NULL);

    ASSERT(0 <= i  &&  i < cLen);
    ASSERT(ptn != NULL  &&  ptn != &m_tnRoot);

    // we've found an approximate match; backtrack until we find an exact match
    do
    {
        ASSERT(ptn != NULL);
        ASSERT(ptn->m_tch == (fIgnoreCase
                              ? (TCHAR) _totlower(ptszSearch[i])
                              : ptszSearch[i]));
        ASSERT(ptn->CheckNodeToken());

        const _TOKEN* const ptok = ptn->m_ptok;

        if (ptok != NULL)
        {
            ASSERT(i == (int) _tcslen(ptn->m_ptszToken) - 1);

            if (pctchMatched != NULL)
                *pctchMatched = i+1;

            return ptok;
        }

        ptn = ptn->m_pParent;
    } while (--i >= 0);

    return NULL;
}

// CTrie::Flush
//      flush all nodes leaving an empty trie

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
void
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::Flush()
{
    if (m_tnRoot.m_pChild != NULL)
        _DeleteTrie(m_tnRoot.m_pChild);

    m_tnRoot.m_pChild = NULL;  // or ~CTrieNode will ASSERT
    m_tnRoot.m_tchMaxChild = TCHAR_MIN;

    m_tchMinChild = m_tchMaxChild = TCHAR_MIN;
#ifdef _DEBUG
    m_ctchMaxTokenLen = 0;
#endif
#ifndef _UNICODE
    memset(m_afCharPresent, 0, sizeof(m_afCharPresent));
#endif
}

// CTrie::_DeleteTrie
//      recursively delete a subtrie

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
void
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::_DeleteTrie(
    _Node* ptn)
{
    if (ptn == NULL)
    {
        ASSERT(false);
        return;
    }

    do
    {
        if (ptn->m_pChild != NULL)
        {
            _DeleteTrie(ptn->m_pChild);
            ptn->m_pChild = NULL;   // or ~CTrieNode will ASSERT
        }

        _Node* ptnSibling = ptn->m_pSibling;
        delete ptn;
        ptn = ptnSibling;   // tail recursion
    } while (ptn != NULL);
}

//-----------------------------------------------------------------------------
// CTrie diagnostics

#ifdef _DEBUG

// CTrie::AssertValid

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
void
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::AssertValid() const
{
    TCHAR* ptszName = static_cast<TCHAR*>
                        (_alloca(sizeof(TCHAR) * (m_ctchMaxTokenLen+1)));
    *ptszName = _T('\0');

    ASSERT_VALID(&m_tnRoot);
    ASSERT(m_tnRoot.m_tchMaxChild == m_tchMaxChild);

    if (m_tnRoot.m_pChild != NULL)
    {
        ASSERT(m_tchMinChild == m_tnRoot.m_pChild->m_tch);
        ASSERT(m_ctchMaxTokenLen > 0);
        _AssertWalk(m_tnRoot.m_pChild, ptszName, 0);
    }
    else
    {
        ASSERT(m_tchMinChild == TCHAR_MIN && m_tchMinChild == m_tchMaxChild);
        ASSERT(m_ctchMaxTokenLen == 0);
    }
}

// CTrie::_AssertWalk
//      recursively validate a subtrie

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
void
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::_AssertWalk(
    _Node* ptn,
    LPTSTR ptszName,
    int    iLevel) const
{
    ASSERT(iLevel < m_ctchMaxTokenLen);

    do
    {
        ASSERT_VALID(ptn);

        ptszName[iLevel] = ptn->m_tch;
        ptszName[iLevel+1] = _T('\0');

        if (ptn->m_ptok != NULL)
        {
            ASSERT(ptn->m_ptszToken != NULL);
            if (fIgnoreCase)
                ASSERT(_tcsicmp(ptszName, ptn->m_ptszToken) == 0);
            else
                ASSERT(_tcscmp(ptszName, ptn->m_ptszToken) == 0);
            ASSERT_VALID(ptn->m_ptok);
        }

        if (ptn->m_pChild != NULL)
            _AssertWalk(ptn->m_pChild, ptszName, iLevel+1);

        ptn = ptn->m_pSibling;   // tail recursion
    } while (ptn != NULL);
}

// CTrie::Dump

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
void
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::Dump() const
{
    int cNodes = 0, cTokens = 0;
    TCHAR* ptszName = static_cast<TCHAR*>
                        (_alloca(sizeof(TCHAR) * (m_ctchMaxTokenLen+1)));
    *ptszName = _T('\0');

    TRACE0("Dumping trie...\n");

    if (m_tnRoot.m_pChild != NULL)
        _DumpWalk(m_tnRoot.m_pChild, ptszName, 0, cNodes, cTokens);

    TRACE2("%d nodes, %d tokens\n", cNodes, cTokens);
}

// CTrie::_DumpWalk
//      recursively dump a subtrie

template <class _TOKEN, bool fIgnoreCase, bool fDeleteTokens>
void
CTrie<_TOKEN, fIgnoreCase, fDeleteTokens>::_DumpWalk(
    _Node* ptn,
    LPTSTR ptszName,
    int    iLevel,
    int&   rcNodes,
    int&   rcTokens) const
{
    ASSERT(iLevel < m_ctchMaxTokenLen);

    do
    {
        ASSERT_VALID(ptn);

        ++rcNodes;
        ptszName[iLevel] = ptn->m_tch;
        ptszName[iLevel+1] = _T('\0');

        if (ptn->m_ptok != NULL)
        {
            ++rcTokens;
            ASSERT(ptn->m_ptszToken != NULL);
            TRACE2("\t%s (%s): ", ptszName, ptn->m_ptszToken);
            DUMP(ptn->m_ptok);
            TRACE0("\n");
        }

        if (ptn->m_pChild != NULL)
            _DumpWalk(ptn->m_pChild, ptszName, iLevel+1, rcNodes, rcTokens);

        ptn = ptn->m_pSibling;   // tail recursion
    } while (ptn != NULL);
}

#endif // _DEBUG

#endif // __TRIE_H__


UTILS.CPP

// Miscellaneous utility functions

#include "CkyPch.h"

#include "utils.h"
#include "debug.h"
#include "notify.h"
#include "trie.h"
#include "globals.h"

// A list of MIME extensions that we know can safely ignore.
// BUGBUG: really ought to get this list dynamically

static char* s_aszIgnoredExtensions[] = {
    "evy",     "hqx",     "doc",     "dot",     "bin",
    "oda",     "pdf",     "ai",      "eps",     "ps",
    "rtf",     "hlp",     "bcpio",   "cpio",    "csh",
    "dcr",     "dir",     "dxr",     "dvi",     "gtar",
    "hdf",     "latex",   "mdb",     "crd",     "clp",
    "xla",     "xlc",     "xlm",     "xls",     "xlt",
    "xlw",     "m1d",     "m14",     "wmf",     "mny",
    "ppt",     "mpp",     "pub",     "trm",     "wks",
    "wri",     "cdf",     "nc",      "pma",     "pmc",
    "pml",     "pmr",     "pmw",     "sh",      "shar",
    "sv4cpio", "sv4crc",  "tar",     "tcl",     "tex",
    "texi",    "texinfo", "roff",    "t",       "tr",
    "man",     "me",      "ms",      "ustar",   "src",
    "zip",     "au",      "snd",     "aif",     "aifc",
    "aiff",    "ram",     "wav",     "bmp",     "cod",
    "gif",     "ief",     "jpe",     "jpeg",    "jpg",
    "tif",     "tiff",    "ras",     "cmx",     "pnm",
    "pbm",     "pgm",     "ppm",     "rgb",     "xbm",
    "xpm",     "xwd",     "bas",     "c",       "h",
    "txt",     "rtx",     "tsv",     "etx",     "mpe",
    "mpeg",    "mpg",     "mov",     "qt",      "avi",
    "movie",   "flr",     "wrl",     "wrz",     "xaf",
    "xof",
};

// STL functor for CStrILookup

struct stricomp {
    bool
    operator()(
        const char* s1,
        const char* s2) const
    {
        return stricmp(s1, s2) < 0;
    }
};

typedef set<const char*, stricomp> CStrILookup;

static CStrILookup s_setMimeExtensions;

// Figure out the type of a URL (http:, mailto:, etc)

typedef struct {
    const char* m_pszUrlType;
    URLTYPE     m_ut;

#ifdef _DEBUG
    void
    AssertValid() const
    {}

    void
    Dump() const
    {
        TRACE("\t%d", m_ut);
    }
#endif
} SUrlType;

static const SUrlType s_aUrlTypes[] = {
    {"http:",   UT_HTTP},
    {"https:",  UT_HTTPS},
    {"ftp:",    UT_FTP},
    {"gopher:", UT_GOPHER},
    {"mailto:", UT_MAILTO},
    {"mk:",     UT_MK},
    {"news:",   UT_NEWS},
    {"newsrc:", UT_NEWSRC},
    {"nntp:",   UT_NNTP},
    {"telnet:", UT_TELNET},
    {"wais:",   UT_WAIS},
};

static CTrie<SUrlType, true, false> s_trieUrlType;

//
// Initialize various utility functions
//

BOOL
InitUtils()
{
    TRACE("InitUtils\n");

    // Double-check that we've set up our constants correctly
    ASSERT(strlen(SESSION_ID_PREFIX) == SESSION_ID_PREFIX_SIZE);
    ASSERT(strlen(SESSION_ID_SUFFIX) == SESSION_ID_SUFFIX_SIZE);
    ASSERT(strlen(SZ_SESSION_ID_COOKIE_NAME)== SZ_SESSION_ID_COOKIE_NAME_SIZE);

    // Build the set of ignorable MIME extensions
    s_setMimeExtensions.insert(s_aszIgnoredExtensions,
                               s_aszIgnoredExtensions
                                 + ARRAYSIZE(s_aszIgnoredExtensions));
#ifdef _DEBUG
    int cExt = 0;
    for (CStrILookup::iterator j = s_setMimeExtensions.begin();
         j != s_setMimeExtensions.end();
         ++j)
    {
        const char* psz = *j;
        TRACE("%s%c", psz, (++cExt & 3) ? '\t' : '\n' );
    }
    TRACE("\n");
#endif

    for (int i = 0;  i < ARRAYSIZE(s_aUrlTypes);  ++i)
        s_trieUrlType.AddToken(s_aUrlTypes[i].m_pszUrlType, &s_aUrlTypes[i]);

    DUMP(&s_trieUrlType);

    return InitEventLog();
}

//
// Clean up when terminating
//

BOOL
TerminateUtils()
{
    TRACE("TerminateUtils\n");

    // Best to clean up global objects as much as possible before terminating
    s_setMimeExtensions.clear();

    return TRUE;
}

//
// Can this URL be safely ignored?
//

BOOL
IsIgnorableUrl(
    LPCSTR pszUrl)
{
    // a URL ending in '/' (i.e., a directory name) is ignorable
    if (pszUrl[strlen(pszUrl) - 1] == '/')
        return TRUE;

    LPCSTR pszExtn = strrchr(pszUrl, '.');

    if (pszExtn != NULL)
    {
        return (s_setMimeExtensions.find(pszExtn + 1)
                != s_setMimeExtensions.end());
    }

    return FALSE;
}

//
// "http:" -> UT_HTTP, "ftp:" -> UT_FTP, etc
//

URLTYPE
UrlType(
    LPCTSTR ptszData,
    LPCTSTR ptszEnd,
    int&    rcLen)
{
    const SUrlType* put = s_trieUrlType.Search(ptszData, &rcLen,
                                               ptszEnd - ptszData);

    if (put != NULL)
        return put->m_ut;
    else
        return UT_NONE;
}

//
// Initializes the event log
//

BOOL
InitEventLog()
{
    HKEY hk = NULL;

    // Add the source name as a subkey under the Application
    // key in the EventLog service portion of the registry.

    if (RegCreateKey(HKEY_LOCAL_MACHINE,
                     "SYSTEM\\CurrentControlSet\\Services"
                     "\\EventLog\\Application\\" EVENT_SOURCE,
                     &hk))
    {
        TRACE("could not create registry key\n");
        return FALSE;
    }

    // Get the full path of this DLL

    HMODULE hMod = GetModuleHandle(EVENT_MODULE);

    if (hMod == NULL)
    {
        TRACE("Can't get module handle for %s, error %d\n",
              EVENT_MODULE, GetLastError());
        return FALSE;
    }

    CHAR szModule[MAX_PATH];

    if (GetModuleFileName(hMod, szModule, MAX_PATH) > 0)
        TRACE("Module is `%s'\n", szModule);
    else
    {
        TRACE("No module, error %d\n", GetLastError());
        return FALSE;
    }

    // Add the Event ID message-file name to the subkey.

    if (RegSetValueEx(hk,                       // subkey handle
                      "EventMessageFile",       // value name
                      0,                        // reserved: must be zero
                      REG_EXPAND_SZ,            // value type
                      (LPBYTE) szModule,        // address of value data
                      strlen(szModule) + 1))    // length of value data
    {
        TRACE("could not set EventMessageFile\n");
        return FALSE;
    }

    // Set the supported types flags.

    DWORD dwData = (EVENTLOG_ERROR_TYPE
                    | EVENTLOG_WARNING_TYPE
                    | EVENTLOG_INFORMATION_TYPE);

    if (RegSetValueEx(hk,                // subkey handle
                      "TypesSupported",  // value name
                      0,                 // reserved: must be zero
                      REG_DWORD,         // value type
                      (LPBYTE) &dwData,  // address of value data
                      sizeof(dwData)))   // length of value data
    {
        TRACE("could not set TypesSupported\n");
        return FALSE;
    }

    RegCloseKey(hk);

    return TRUE;
}

//
// Report things in the Event Log
//

VOID
EventReport(
    LPCTSTR string1,
    LPCTSTR string2,
    WORD eventType,
    DWORD eventID)
{
    HANDLE hEvent;
    LPCTSTR pszaStrings[2];
    WORD cStrings;

    cStrings = 0;
    if ((pszaStrings[0] = string1) && (string1[0]))
        cStrings++;
    if ((pszaStrings[1] = string2) && (string2[0]))
        cStrings++;

    hEvent = RegisterEventSource(NULL, EVENT_SOURCE);

    if (hEvent)
    {
        ReportEvent(hEvent,         // handle returned by RegisterEventSource
                    eventType,      // event type to log
                    0,              // event category
                    eventID,        // event identifier
                    NULL,           // user security identifier (optional)
                    cStrings,       // number of strings to merge with message
                    0,              // size of binary data, in bytes
                    pszaStrings,    // array of strings to merge with message
                    NULL);          // address of binary data
        DeregisterEventSource(hEvent);
    }
}

//
// stristr (stolen from fts.c, wickn)
//
// case-insensitive version of strstr
// stristr returns a pointer to the first occurrence of string2 in string1.
// The search does not include terminating nul characters.
//

char*
stristr(const char* string1, const char* string2)
{
    char *cp1 = (char*) string1, *cp2, *cp1a;
    char first;

    // get the first char in string to find
    first = string2[0];

    // first char often won't be alpha
    if (isalpha(first))
    {
        first = tolower(first);
        for ( ; *cp1  != '\0'; cp1++)
        {
            if (tolower(*cp1) == first)
            {
                for (cp1a = &cp1[1], cp2 = (char*) &string2[1];
                     ;
                     cp1a++, cp2++)
                {
                    if (*cp2 == '\0')
                        return cp1;
                    if (tolower(*cp1a) != tolower(*cp2))
                        break;
                }
            }
        }
    }
    else
    {
        for ( ; *cp1 != '\0' ; cp1++)
        {
            if (*cp1 == first)
            {
                for (cp1a = &cp1[1], cp2 = (char*) &string2[1];
                     ;
                     cp1a++, cp2++)
                {
                    if (*cp2 == '\0')
                        return cp1;
                    if (tolower(*cp1a) != tolower(*cp2))
                        break;
                }
            }
        }
    }

    return NULL;
}

//
// Find a string in a block of rawdata (a sort of stristr).
// Can't use stristr because the rawdata is not \0-terminated.
//

LPSTR
FindString(
    LPCSTR psz,
    PHTTP_FILTER_RAW_DATA pRawData,
    int iStart)
{
    LPSTR pszData = iStart + (LPSTR) pRawData->pvInData;
    const int cch = strlen(psz);
    const CHAR ch = tolower(*psz);

    for (int i = pRawData->cbInData - cch + 1 - iStart;  --i >= 0; )
    {
        if (ch == tolower(*pszData)  &&  strnicmp(psz, pszData, cch) == 0)
            return pszData;
        else
            ++pszData;
    }

    return NULL;
}

//
// Find a header-value pair, such as ("Content-Type:", "text/html").  Works
// no matter how many spaces between the pair
//

LPSTR
FindHeaderValue(
    LPCSTR pszHeader,
    LPCSTR pszValue,
    PHTTP_FILTER_RAW_DATA pRawData,
    int iStart)
{
    LPSTR psz = FindString(pszHeader, pRawData, iStart);

    if (psz != NULL)
    {
        LPSTR pszData = iStart + (LPSTR) pRawData->pvInData;
        int   i       = (psz - pszData) + strlen(pszHeader);

        // Skip spaces
        while (i < pRawData->cbInData  &&  isspace(pszData[i]))
            ++i;

        // Check for szValue
        const int cchValue = strlen(pszValue);

        if (i + cchValue <= pRawData->cbInData)
            if (strnicmp(pszData+i, pszValue, cchValue) == 0)
                return psz;
    }

    return NULL;
}

//
// Delete a line such as "Content-Length: 924\r\n"
//

BOOL
DeleteLine(
    LPCSTR psz,
    PHTTP_FILTER_RAW_DATA pRawData,
    LPSTR  pszStart /* = NULL */)
{
    if (pszStart == NULL)
        pszStart = FindString(psz, pRawData, 0);

    if (pszStart == NULL)
        return FALSE;

    LPCSTR pszData = (LPCSTR) pRawData->pvInData;

    for (unsigned i = pszStart - pszData;  i < pRawData->cbInData; )
    {
        if (pszData[i++] == '\n')
        {
            for (unsigned j = 0;  j <  pRawData->cbInData - i;  j++)
                pszStart[j] = pszData[i + j];
            if (j <  pRawData->cbInData)
                pszStart[j] = '\0';
            pRawData->cbInData -= i - (pszStart - pszData);

            return TRUE;
        }
    }

    return FALSE;
}

//
// Extract a Session ID from a Cookie header.  Cookie will be in the form
//     name1=value1; name2=value2; name3=value3; ...
// One of those name-value pairs should be something like:
//     ASPSESSIONIDXXXXXXXX=PVZQGHUMEAYAHMFV or
//     ASPSESSIONID=PVZQGHUMEAYAHMFV
//

BOOL
Cookie2SessionID(
    LPCSTR pszCookie,
    LPSTR  pszSessionID)
{
    if (pszCookie == NULL)
    {
        ASSERT(FALSE);
        return FALSE;
    }

    LPCSTR psz = strstr(pszCookie, SZ_SESSION_ID_COOKIE_NAME);

    if (psz == NULL)
    {
        TRACE("C2SID: failed strstr `%s'\n", pszCookie);
        return FALSE;
    }

    // skip over the "ASPSESSIONIDXXXXXXXX=" or "ASPSESSIONID=" part
    psz += SZ_SESSION_ID_COOKIE_NAME_SIZE;
    if (*(psz) != '=')
    {
        if ( strncmp( psz, g_szCookieExtra, COOKIE_NAME_EXTRA_SIZE ) == 0 )
        {
            psz += COOKIE_NAME_EXTRA_SIZE;
            if ( *(psz) != '=' )
            {
                TRACE("C2SID: no = `%s'\n", psz);
                return FALSE;
            }
        }
        else
        {
            TRACE("C2SID: strncmp(%s) `%s'\n", g_szCookieExtra, psz);
            return FALSE;
        }
    }
    psz++;

    return CopySessionID(psz, pszSessionID);
}

//
// Extract a Session ID from psz, placing it into pszSessionID
//

BOOL
CopySessionID(
    LPCSTR psz,
    LPSTR  pszSessionID)
{
    ASSERT( g_SessionIDSize != -1 );

    strncpy(pszSessionID, psz, g_SessionIDSize);
    pszSessionID[g_SessionIDSize] = '\0';

#ifdef _DEBUG
    TRACE("SessionID='%s'\n", pszSessionID);
    for (psz = pszSessionID;  *psz;  ++psz)
        ASSERT('A' <= *psz  &&  *psz <= 'Z');
#endif

    return TRUE;
}

//
// Remove an encoded Session ID from a URL, returning the Session ID
// e.g., /foo/bar.asp-ASP=PVZQGHUMEAYAHMFV?quux=5 ->
//           /foo/bar.asp?quux=5 + PVZQGHUMEAYAHMFV
//

BOOL
DecodeURL(
    LPSTR pszUrl,
    LPSTR pszSessionID)
{
    ASSERT( g_SessionIDSize != -1 );

    TRACE("Decode(%s)", pszUrl);

    LPSTR pszQuery = strchr(pszUrl, '?');

    if (pszQuery != NULL)
        *pszQuery++ = '\0';

    LPSTR pszLastSlash = strrchr(pszUrl, *SESSION_ID_PREFIX);

    if (pszLastSlash == NULL)
    {
        TRACE(" (no `" SESSION_ID_PREFIX "')\n");
        return FALSE;
    }

    const int cchEncodedID = strlen(pszLastSlash);

    if (strncmp(pszLastSlash, SESSION_ID_PREFIX, SESSION_ID_PREFIX_SIZE) != 0
        || cchEncodedID !=
            (SESSION_ID_PREFIX_SIZE + g_SessionIDSize + SESSION_ID_SUFFIX_SIZE)
        || strcmp(pszLastSlash + cchEncodedID - SESSION_ID_SUFFIX_SIZE,
                  SESSION_ID_SUFFIX) != 0)
    {
        if (pszQuery != NULL)
            *pszQuery = '?';
        TRACE(": not encoded\n");
        return FALSE;
    }
    else
    {
        strncpy(pszSessionID, pszLastSlash + SESSION_ID_PREFIX_SIZE,
                g_SessionIDSize);
        pszSessionID[g_SessionIDSize] = '\0';

        *pszLastSlash = '\0';

        if (pszQuery != NULL)
        {
            *pszLastSlash++ = '?';

            while ((*pszLastSlash++ = *pszQuery++) != '\0')
                ;
        }

        TRACE(" -> %s, %s\n", pszUrl, pszSessionID);

        return TRUE;
    }
}

//
// Wrapper for pfc->AllocMem.
//

VOID*
AllocMem(
    PHTTP_FILTER_CONTEXT  pfc,
    DWORD                 cbSize)
{
    PVOID pv = pfc->AllocMem(pfc, cbSize, 0);
#ifdef _DEBUG
    memset(pv, 0, cbSize);
#endif
    TRACE("Allocated %d (%x) bytes at %x\n", cbSize, cbSize, pv);
    return pv;
}


UTILS.H

#ifndef __UTILS_H__
#define __UTILS_H__

// disable warning messages about truncating extremly long identifiers
#pragma warning (disable : 4786)
#include <string>

// Needed for STL on Visual C++ 5.0
#if _MSC_VER>=1100
using namespace std;
#endif

#include "messages.h"

#define EVENT_SOURCE "CkyMunge"
#define EVENT_MODULE "CkyMunge.dll"

#define ARRAYSIZE(a)  (sizeof(a)/sizeof(*(a)))

BOOL
InitUtils();

BOOL
TerminateUtils();

BOOL
InitEventLog();

VOID
EventReport(
    LPCTSTR string1,
    LPCTSTR string2,
    WORD eventType,
    DWORD eventID);

char*
stristr(const char*, const char*);

LPSTR
FindString(
    LPCSTR psz,
    PHTTP_FILTER_RAW_DATA pRawData,
    int iStart);

LPSTR
FindHeaderValue(
    LPCSTR pszHeader,
    LPCSTR pszValue,
    PHTTP_FILTER_RAW_DATA pRawData,
    int iStart);

BOOL
DeleteLine(
    LPCSTR psz,
    PHTTP_FILTER_RAW_DATA pRawData,
    LPSTR  pszStart = NULL);

BOOL
Cookie2SessionID(
    LPCSTR pszCookie,
    LPSTR  pszSessionID);

BOOL
CopySessionID(
    LPCSTR psz,
    LPSTR  pszSessionID);

BOOL
IsIgnorableUrl(
    LPCSTR pszUrl);

BOOL
DecodeURL(
    LPSTR pszUrl,
    LPSTR pszSessionID);

VOID*
AllocMem(
    PHTTP_FILTER_CONTEXT  pfc,
    DWORD                 cbSize);

enum URLTYPE {
    UT_NONE,
    UT_UNKNOWN,
    UT_HTTP,
    UT_HTTPS,
    UT_FTP,
    UT_GOPHER,
    UT_MAILTO,
    UT_NEWS,
    UT_NEWSRC,
    UT_NNTP,
    UT_TELNET,
    UT_WAIS,
    UT_MK,
};

URLTYPE
UrlType(
    LPCTSTR ptszData,
    LPCTSTR ptszEnd,
    int&    rcLen);

#endif // __UTILS_H__


UPCASE.C

/*++

Copyright (c) 1995-1996  Microsoft Corporation

Module Name:

    upcase.c

Abstract:

    This filter converts HTML response data to upper case if requested
    by the client. The request is made by specifying an extra
    subdirectory in the URL, which doesnt actually exist on the server,
    but is used by the filter to indicate that upper case is desired.
    The extra subdirectory is removed from the request before the HTTP
    server sees the request.

    When a request is received, the filter inspects the
    subdirectories specified in the URL. If a subdirectory is 'UC'
    or 'uc', the subdirectory is removed from the request, and the
    filter saves in its pFilterContext field, a value indicating that
    the response data should be converted to upper case.

    When the filter entrypoint is later called for the response, it
    checks the pFilterContext field and converts the data if
    the mime-type of the response indicates that its an html file.
    This avoids conversions on binary data.

    An example URL entered by a user might be:

        http://www.myweb.com/sales/uc/projections.htm

    While the functionality of this filter is somewhat contrived, this
    filter does a good job of demonstrating the following features of
    filters:
        - parsing/modifying HTTP headers (in the request)
        - modifying data following HTTP headers (in the response)
        - saving state to be used by the filter later
        - adding request/response level functionality to the server
            (instead of using a mechanism like this to convert to
             uppercase, you may use it to do on-the-fly customized
             translations of HTML pages (English -> French perhaps?)

Author:

    Kerry Schwartz (kerrys) 05-Nov-1995

--*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <httpfilt.h>

BOOL
WINAPI __stdcall
GetFilterVersion(
    HTTP_FILTER_VERSION * pVer
    )
{
    //
    //  Specify the types and order of notification
    //

    pVer->dwFlags = (SF_NOTIFY_NONSECURE_PORT     |
                     SF_NOTIFY_URL_MAP            |
                     SF_NOTIFY_SEND_RAW_DATA      |
                     SF_NOTIFY_ORDER_DEFAULT);

    pVer->dwFilterVersion = HTTP_FILTER_REVISION;

    strcpy( pVer->lpszFilterDesc,
            "Upper case conversion filter, Version 1.0");

    return TRUE;
}

DWORD
WINAPI __stdcall
HttpFilterProc(
    HTTP_FILTER_CONTEXT *      pfc,
    DWORD                      NotificationType,
    VOID *                     pvData )
{
    CHAR *pchIn, *pPhysPath;
    DWORD cbBuffer, cbtemp;
    PHTTP_FILTER_URL_MAP pURLMap;
    PHTTP_FILTER_RAW_DATA pRawData;

    switch ( NotificationType )
    {
    case SF_NOTIFY_URL_MAP:

        pURLMap = (PHTTP_FILTER_URL_MAP) pvData;
        pPhysPath = pURLMap->pszPhysicalPath;
        pfc->pFilterContext = 0;

        while (*pPhysPath)
        {
            if (*pPhysPath == '\\' &&
               (*(pPhysPath+1) == 'u' || *(pPhysPath+1) == 'U') &&
               (*(pPhysPath+2) == 'c' || *(pPhysPath+2) == 'C') &&
                *(pPhysPath+3) == '\\')
            {
                while (*(pPhysPath+3))
                {
                    *pPhysPath = *(pPhysPath+3);
                    pPhysPath++;
                }
                *pPhysPath = '\0';
                pfc->pFilterContext = (VOID *) 1;
                break;
            }
            pPhysPath++;
        }

        break;

    case SF_NOTIFY_SEND_RAW_DATA:

        pRawData = (PHTTP_FILTER_RAW_DATA) pvData;

        if (pfc->pFilterContext)
        {
            pchIn = (BYTE *) pRawData->pvInData;
            cbBuffer = 0;
            cbtemp = 0;

            if (pfc->pFilterContext == (VOID *) 1)      // first block?
            {
                while (cbBuffer < pRawData->cbInData)
                {
                    if (pchIn[cbBuffer] == '\n' &&
                        pchIn[cbBuffer+2] == '\n')
                    {
                        cbBuffer +=3;
                        break;
                    }
                    cbBuffer++;
                }

                while (cbtemp < cbBuffer)
                {
                    if (pchIn[cbtemp] == '/' && pchIn[cbtemp+1] == 'h' &&
                        pchIn[cbtemp+2] == 't' && pchIn[cbtemp+3] == 'm')
                    {
                        pfc->pFilterContext = (VOID *) 2;
                        break;
                    }
                    cbtemp++;
                }
                if (cbtemp == cbBuffer)
                    pfc->pFilterContext = 0;        // not an html file
            }

            if (pfc->pFilterContext)
            {
                while (cbBuffer < pRawData->cbInData)
                {
                    pchIn[cbBuffer] =
                        (pchIn[cbBuffer]>='a' && pchIn[cbBuffer]<='z') ?
                (pchIn[cbBuffer]-'a'+'A') : pchIn[cbBuffer];
                    cbBuffer++;
                }

            }
        }
        break;

    default:
        break;
    }

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}


README.TXT

Index Server sample code
------------------------

Content Indexing is a service in Windows NT version 5.  It provides very fast
full text search over material that has been indexed (typically files or web
pages) at the expense of disk space (for storing the catalogs).  For more
information, see the "Index Server" section in the SDK documentation.

The programmatic interface to the index server is via OLEDB.  The following
samples demonstrate this interface:

  SIMPLE - create a query, submit it, and print the results.
  ADVQUERY - allow a more complex query and avoid the use of CICreateCommand().
  CATQ - GUI tool for searching the SDK; will start the index server if needed.

The index server relies on "filters" (registered implementations of the
IFilter interface) to parse different file formats.  The following sample
shows how such a filter DLL is built.

  IFILTER - implementation of the IFilter interface for HTML files.


README.TXT

Demonstration of issuing Microsoft Index Server queries

SUMMARY
=======

ADVQUERY is a more advanced sample query program than QSAMPLE.
It shows everything needed to execute a Microsft Index Server query.

MORE INFORMATION
================

usage: ADVQUERY query [/c:catalog] [/m:machine]
    query        a Microsoft Index Server query
    /c:catalog   name of the catalog, default is SYSTEM
    /m:machine   name of the machine, default is local machine
    /s:scope     root path, default is entire catalog
    /d           displays the DBCOMMANDTREE, default is off

The "query" paramater can be a word or phrase.  Files that contain the
word or phrase are listed in the result. To include a space in a query,
enclose the query in quotes.

The "catalog" parameter is the name of the catalog to be queried.  The
default is "SYSTEM", which is the default catalog installed with Windows
NT 5.0.  Additional catalogs can be created with the Microsoft Index Server
MMC snapin.

The "machine" parameter is the name of the machine on which the query
will be executed.  The default is ".", which is the local machine.  Machine
names should not be preceeded with two backslashes.

The "scope" parameter is the file path under which files must exist
to be included in the results.  The default is "\", which includes all
scopes in the catalog.

The "/d" parameter displays the DBCOMMANDTREE built to execute the query.

For each file that matches the query, the file's rank, size, and path
are printed.

The query results are sorted by rank (how well the file matches the query).

Examples:

    advquery foo
    Finds all files in the SYSTEM catalog on the local machine containing
    the word "foo"

    advquery "foo bar" "/s:c:\my directory"
    Finds all files in the SYSTEM catalog on the local machine containing
    the phrase "foo bar" that are in the specified directory or any
    subdirectory.

    advquery foo /m:SERVERNAME
    Finds all files in the SYSTEM catalog on machine "servername" containing
    the word foo


DISPTREE.CXX

//+-------------------------------------------------------------------------
//
// 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.
//
// Copyright 1997 Microsoft Corporation.  All Rights Reserved.
//
// FILE:     disptree.cxx
//
// PURPOSE:  Displays a command tree, for debugging.
//
// PLATFORM: Windows NT
//
//--------------------------------------------------------------------------

#define UNICODE

#include <stdio.h>
#include <windows.h>

#define OLEDBVER 0x0200 // need the command tree definitions

#include <oledberr.h>
#include <oledb.h>

//+-------------------------------------------------------------------------
//
//  Function:   PrintVectorItems
//
//  Synopsis:   Prints items in a PROPVARIANT vector
//
//  Arguments:  [pVal]  - The array of values
//              [cVals] - The count of values
//              [pcFmt] - The format string
//
//--------------------------------------------------------------------------

template<class T> void PrintVectorItems(
    T *     pVal,
    ULONG   cVals,
    char *  pcFmt )
{
    printf( "{ " );

    for( ULONG iVal = 0; iVal < cVals; iVal++ )
    {
        if ( 0 != iVal )
            printf( "," );
        printf( pcFmt, *pVal++ );
    }

    printf( " }" );
} //PrintVectorItems

//+-------------------------------------------------------------------------
//
//  Function:   DisplayVariant
//
//  Synopsis:   Displays a PROPVARIANT.  This is not a complete
//              implementation; many VT_ types are not complete.
//
//  Arguments:  [pNode]  - The command tree node
//              [iLevel] - The 0-based level in the tree
//
//--------------------------------------------------------------------------

void DisplayVariant( PROPVARIANT * pVar )
{
    if ( 0 == pVar )
    {
        printf( "NULL" );
        return;
    }

    PROPVARIANT & v = *pVar;

    switch ( v.vt )
    {
        case VT_I4 : printf( "VT_I4 %d", v.lVal ); break;
        case VT_UI1 : printf( "VT_UI1 %d", v.bVal ); break;
        case VT_I2 : printf( "VT_I2 %d", v.iVal ); break;
        case VT_R4 : printf( "VT_R4 %f", v.fltVal ); break;
        case VT_R8 : printf( "VT_R8 %lf", v.dblVal ); break;
        case VT_BOOL : printf( "VT_BOOL %d", v.boolVal ); break;
        case VT_ERROR : printf( "VT_ERROR %#x", v.scode ); break;
        case VT_CY : printf( "VT_CY " ); break;
        case VT_DATE : printf( "VT_DATE " ); break;
        case VT_BSTR : printf( "VT_BSTR '%ws'", v.bstrVal ); break;
        case VT_UNKNOWN : printf( "VT_UNKNOWN " ); break;
        case VT_DISPATCH : printf( "VT_DISPATCH " ); break;
        case VT_BYREF|VT_UI1 : printf( "VT_BYREF|VT_UI1 " ); break;
        case VT_BYREF|VT_I2 : printf( "VT_BYREF|VT_I2 " ); break;
        case VT_BYREF|VT_I4 : printf( "VT_BYREF|VT_I4 " ); break;
        case VT_BYREF|VT_R4 : printf( "VT_BYREF|VT_R4 " ); break;
        case VT_BYREF|VT_R8 : printf( "VT_BYREF|VT_R8 " ); break;
        case VT_BYREF|VT_BOOL : printf( "VT_BYREF|VT_BOOL " ); break;
        case VT_BYREF|VT_ERROR : printf( "VT_BYREF|VT_ERROR " ); break;
        case VT_BYREF|VT_CY : printf( "VT_BYREF|VT_CY " ); break;
        case VT_BYREF|VT_DATE : printf( "VT_BYREF|VT_DATE " ); break;
        case VT_BYREF|VT_BSTR : printf( "VT_BYREF|VT_BSTR " ); break;
        case VT_BYREF|VT_UNKNOWN : printf( "VT_BYREF|VT_UNKNOWN " ); break;
        case VT_BYREF|VT_DISPATCH : printf( "VT_BYREF|VT_DISPATCH " ); break;
        case VT_BYREF|VT_ARRAY : printf( "VT_BYREF|VT_ARRAY " ); break;
        case VT_BYREF|VT_VARIANT : printf( "VT_BYREF|VT_VARIANT " ); break;
        case VT_I1 : printf( "VT_I1 %d", v.bVal ); break;
        case VT_UI2 : printf( "VT_UI2 %u", v.uiVal ); break;
        case VT_UI4 : printf( "VT_UI4 %u", v.ulVal ); break;
        case VT_INT : printf( "VT_INT %d", v.lVal ); break;
        case VT_UINT : printf( "VT_UINT %u", v.ulVal ); break;
        case VT_BYREF|VT_DECIMAL : printf( "VT_BYREF|VT_DECIMAL " ); break;
        case VT_BYREF|VT_I1 : printf( "VT_BYREF|VT_I1 " ); break;
        case VT_BYREF|VT_UI2 : printf( "VT_BYREF|VT_UI2 " ); break;
        case VT_BYREF|VT_UI4 : printf( "VT_BYREF|VT_UI4 " ); break;
        case VT_BYREF|VT_INT : printf( "VT_BYREF|VT_INT " ); break;
        case VT_BYREF|VT_UINT : printf( "VT_BYREF|VT_UINT " ); break;
        case VT_LPSTR : printf( "VT_LPSTR '%s'", v.pszVal ); break;
        case VT_LPWSTR : printf( "VT_LPWSTR '%ws'", v.pwszVal ); break;
        case VT_I8 : printf( "VT_I8 %I64d", v.hVal ); break;
        case VT_UI8 : printf( "VT_I8 %I64u", v.hVal ); break;
        case VT_VECTOR | VT_I1:
            printf( "VT_VECTOR | VT_I1 " );
            PrintVectorItems( v.caub.pElems, v.caub.cElems, "%d" );
            break;
        case VT_VECTOR | VT_I2:
            printf( "VT_VECTOR | VT_I2 " );
            PrintVectorItems( v.cai.pElems, v.cai.cElems, "%d" );
            break;
        case VT_VECTOR | VT_I4:
            printf( "VT_VECTOR | VT_I4 " );
            PrintVectorItems( v.cal.pElems, v.cal.cElems, "%d" );
            break;
        case VT_VECTOR | VT_I8:
            printf( "VT_VECTOR | VT_I8 " );
            PrintVectorItems( v.cah.pElems, v.cah.cElems, "%I64d" );
            break;
        case VT_VECTOR | VT_UI1:
            printf( "VT_VECTOR | VT_UI1 " );
            PrintVectorItems( v.caub.pElems, v.caub.cElems, "%u" );
            break;
        case VT_VECTOR | VT_UI2:
            printf( "VT_VECTOR | VT_UI2 " );
            PrintVectorItems( v.caui.pElems, v.caui.cElems, "%u" );
            break;
        case VT_VECTOR | VT_UI4:
            printf( "VT_VECTOR | VT_UI4 " );
            PrintVectorItems( v.caul.pElems, v.caul.cElems, "%u" );
            break;
        case VT_VECTOR | VT_UI8:
            printf( "VT_VECTOR | VT_UI8 " );
            PrintVectorItems( v.cauh.pElems, v.cauh.cElems, "%I64u" );
            break;
        case VT_VECTOR | VT_BSTR:
            printf( "VT_VECTOR | VT_BSTR " );
            PrintVectorItems( v.cabstr.pElems, v.cabstr.cElems, "%ws" );
            break;
        case VT_VECTOR | VT_LPSTR:
            printf( "VT_VECTOR | VT_LPSTR " );
            PrintVectorItems( v.calpstr.pElems, v.calpstr.cElems, "%s" );
            break;
        case VT_VECTOR | VT_LPWSTR:
            printf( "VT_VECTOR | VT_LPWSTR " );
            PrintVectorItems( v.calpwstr.pElems, v.calpwstr.cElems, "%ws" );
            break;
        case VT_VECTOR | VT_R4:
            printf( "VT_VECTOR | VT_R4 " );
            PrintVectorItems( v.caflt.pElems, v.caflt.cElems, "%f" );
            break;
        case VT_VECTOR | VT_R8:
            printf( "VT_VECTOR | VT_R8 " );
            PrintVectorItems( v.cadbl.pElems, v.cadbl.cElems, "%lf" );
            break;
        default : printf( "unknown vt %#x", v.vt );
    }
} //DisplayVariant

//+-------------------------------------------------------------------------
//
//  Function:   PrintSpace
//
//  Synopsis:   Prints white space.
//
//  Arguments:  [cPlaces] - Number of levels to print
//
//--------------------------------------------------------------------------

void PrintSpace( ULONG cPlaces )
{
    for( ULONG iPlace = 0; iPlace < cPlaces; iPlace++ )
        printf( "  " );
} //PrintSpace

//+-------------------------------------------------------------------------
//
//  Function:   DisplayCommandTree
//
//  Synopsis:   Displays the command tree, useful for debugging.  This is
//              not a complete implementation; many DBVALUEKINDs are not
//              complete.
//
//  Arguments:  [pNode]  - The command tree node
//              [iLevel] - The 0-based level in the tree
//
//--------------------------------------------------------------------------

void DisplayCommandTree(
    DBCOMMANDTREE * pNode,
    ULONG           iLevel )
{
    PrintSpace( iLevel );
    printf( "op: (%d) ", pNode->op );
    switch ( pNode->op )
    {
        case DBOP_scalar_constant : printf( "DBOP_scalar_constant" ); break;
        case DBOP_DEFAULT : printf( "DBOP_DEFAULT" ); break;
        case DBOP_NULL : printf( "DBOP_NULL" ); break;
        case DBOP_bookmark_name : printf( "DBOP_bookmark_name" ); break;
        case DBOP_catalog_name : printf( "DBOP_catalog_name" ); break;
        case DBOP_column_name : printf( "DBOP_column_name" ); break;
        case DBOP_schema_name : printf( "DBOP_schema_name" ); break;
        case DBOP_outall_name : printf( "DBOP_outall_name" ); break;
        case DBOP_qualifier_name : printf( "DBOP_qualifier_name" ); break;
        case DBOP_qualified_column_name : printf( "DBOP_qualified_column_name" ); break;
        case DBOP_table_name : printf( "DBOP_table_name" ); break;
        case DBOP_nested_table_name : printf( "DBOP_nested_table_name" ); break;
        case DBOP_nested_column_name : printf( "DBOP_nested_column_name" ); break;
        case DBOP_row : printf( "DBOP_row" ); break;
        case DBOP_table : printf( "DBOP_table" ); break;
        case DBOP_sort : printf( "DBOP_sort" ); break;
        case DBOP_distinct : printf( "DBOP_distinct" ); break;
        case DBOP_distinct_order_preserving : printf( "DBOP_distinct_order_preserving" ); break;
        case DBOP_alias : printf( "DBOP_alias" ); break;
        case DBOP_cross_join : printf( "DBOP_cross_join" ); break;
        case DBOP_union_join : printf( "DBOP_union_join" ); break;
        case DBOP_inner_join : printf( "DBOP_inner_join" ); break;
        case DBOP_left_semi_join : printf( "DBOP_left_semi_join" ); break;
        case DBOP_right_semi_join : printf( "DBOP_right_semi_join" ); break;
        case DBOP_left_anti_semi_join : printf( "DBOP_left_anti_semi_join" ); break;
        case DBOP_right_anti_semi_join : printf( "DBOP_right_anti_semi_join" ); break;
        case DBOP_left_outer_join : printf( "DBOP_left_outer_join" ); break;
        case DBOP_right_outer_join : printf( "DBOP_right_outer_join" ); break;
        case DBOP_full_outer_join : printf( "DBOP_full_outer_join" ); break;
        case DBOP_natural_join : printf( "DBOP_natural_join" ); break;
        case DBOP_natural_left_outer_join : printf( "DBOP_natural_left_outer_join" ); break;
        case DBOP_natural_right_outer_join : printf( "DBOP_natural_right_outer_join" ); break;
        case DBOP_natural_full_outer_join : printf( "DBOP_natural_full_outer_join" ); break;
        case DBOP_set_intersection : printf( "DBOP_set_intersection" ); break;
        case DBOP_set_union : printf( "DBOP_set_union" ); break;
        case DBOP_set_left_difference : printf( "DBOP_set_left_difference" ); break;
        case DBOP_set_right_difference : printf( "DBOP_set_right_difference" ); break;
        case DBOP_set_anti_difference : printf( "DBOP_set_anti_difference" ); break;
        case DBOP_bag_intersection : printf( "DBOP_bag_intersection" ); break;
        case DBOP_bag_union : printf( "DBOP_bag_union" ); break;
        case DBOP_bag_left_difference : printf( "DBOP_bag_left_difference" ); break;
        case DBOP_bag_right_difference : printf( "DBOP_bag_right_difference" ); break;
        case DBOP_bag_anti_difference : printf( "DBOP_bag_anti_difference" ); break;
        case DBOP_division : printf( "DBOP_division" ); break;
        case DBOP_relative_sampling : printf( "DBOP_relative_sampling" ); break;
        case DBOP_absolute_sampling : printf( "DBOP_absolute_sampling" ); break;
        case DBOP_transitive_closure : printf( "DBOP_transitive_closure" ); break;
        case DBOP_recursive_union : printf( "DBOP_recursive_union" ); break;
        case DBOP_aggregate : printf( "DBOP_aggregate" ); break;
        case DBOP_remote_table : printf( "DBOP_remote_table" ); break;
        case DBOP_select : printf( "DBOP_select" ); break;
        case DBOP_order_preserving_select : printf( "DBOP_order_preserving_select" ); break;
        case DBOP_project : printf( "DBOP_project" ); break;
        case DBOP_project_order_preserving : printf( "DBOP_project_order_preserving" ); break;
        case DBOP_top : printf( "DBOP_top" ); break;
        case DBOP_top_percent : printf( "DBOP_top_percent" ); break;
        case DBOP_top_plus_ties : printf( "DBOP_top_plus_ties" ); break;
        case DBOP_top_percent_plus_ties : printf( "DBOP_top_percent_plus_ties" ); break;
        case DBOP_rank : printf( "DBOP_rank" ); break;
        case DBOP_rank_ties_equally : printf( "DBOP_rank_ties_equally" ); break;
        case DBOP_rank_ties_equally_and_skip : printf( "DBOP_rank_ties_equally_and_skip" ); break;
        case DBOP_navigate : printf( "DBOP_navigate" ); break;
        case DBOP_nesting : printf( "DBOP_nesting" ); break;
        case DBOP_unnesting : printf( "DBOP_unnesting" ); break;
        case DBOP_nested_apply : printf( "DBOP_nested_apply" ); break;
        case DBOP_cross_tab : printf( "DBOP_cross_tab" ); break;
        case DBOP_is_NULL : printf( "DBOP_is_NULL" ); break;
        case DBOP_is_NOT_NULL : printf( "DBOP_is_NOT_NULL" ); break;
        case DBOP_equal : printf( "DBOP_equal" ); break;
        case DBOP_not_equal : printf( "DBOP_not_equal" ); break;
        case DBOP_less : printf( "DBOP_less" ); break;
        case DBOP_less_equal : printf( "DBOP_less_equal" ); break;
        case DBOP_greater : printf( "DBOP_greater" ); break;
        case DBOP_greater_equal : printf( "DBOP_greater_equal" ); break;
        case DBOP_equal_all : printf( "DBOP_equal_all" ); break;
        case DBOP_not_equal_all : printf( "DBOP_not_equal_all" ); break;
        case DBOP_less_all : printf( "DBOP_less_all" ); break;
        case DBOP_less_equal_all : printf( "DBOP_less_equal_all" ); break;
        case DBOP_greater_all : printf( "DBOP_greater_all" ); break;
        case DBOP_greater_equal_all : printf( "DBOP_greater_equal_all" ); break;
        case DBOP_equal_any : printf( "DBOP_equal_any" ); break;
        case DBOP_not_equal_any : printf( "DBOP_not_equal_any" ); break;
        case DBOP_less_any : printf( "DBOP_less_any" ); break;
        case DBOP_less_equal_any : printf( "DBOP_less_equal_any" ); break;
        case DBOP_greater_any : printf( "DBOP_greater_any" ); break;
        case DBOP_greater_equal_any : printf( "DBOP_greater_equal_any" ); break;
        case DBOP_anybits : printf( "DBOP_anybits" ); break;
        case DBOP_allbits : printf( "DBOP_allbits" ); break;
        case DBOP_anybits_any : printf( "DBOP_anybits_any" ); break;
        case DBOP_allbits_any : printf( "DBOP_allbits_any" ); break;
        case DBOP_anybits_all : printf( "DBOP_anybits_all" ); break;
        case DBOP_allbits_all : printf( "DBOP_allbits_all" ); break;
        case DBOP_between : printf( "DBOP_between" ); break;
        case DBOP_between_unordered : printf( "DBOP_between_unordered" ); break;
        case DBOP_match : printf( "DBOP_match" ); break;
        case DBOP_match_unique : printf( "DBOP_match_unique" ); break;
        case DBOP_match_partial : printf( "DBOP_match_partial" ); break;
        case DBOP_match_partial_unique : printf( "DBOP_match_partial_unique" ); break;
        case DBOP_match_full : printf( "DBOP_match_full" ); break;
        case DBOP_match_full_unique : printf( "DBOP_match_full_unique" ); break;
        case DBOP_scalar_parameter : printf( "DBOP_scalar_parameter" ); break;
        case DBOP_scalar_function : printf( "DBOP_scalar_function" ); break;
        case DBOP_plus : printf( "DBOP_plus" ); break;
        case DBOP_minus : printf( "DBOP_minus" ); break;
        case DBOP_times : printf( "DBOP_times" ); break;
        case DBOP_over : printf( "DBOP_over" ); break;
        case DBOP_div : printf( "DBOP_div" ); break;
        case DBOP_modulo : printf( "DBOP_modulo" ); break;
        case DBOP_power : printf( "DBOP_power" ); break;
        case DBOP_like : printf( "DBOP_like" ); break;
        case DBOP_sounds_like : printf( "DBOP_sounds_like" ); break;
        case DBOP_like_any : printf( "DBOP_like_any" ); break;
        case DBOP_like_all : printf( "DBOP_like_all" ); break;
        case DBOP_is_INVALID : printf( "DBOP_is_INVALID" ); break;
        case DBOP_is_TRUE : printf( "DBOP_is_TRUE" ); break;
        case DBOP_is_FALSE : printf( "DBOP_is_FALSE" ); break;
        case DBOP_and : printf( "DBOP_and" ); break;
        case DBOP_or : printf( "DBOP_or" ); break;
        case DBOP_xor : printf( "DBOP_xor" ); break;
        case DBOP_equivalent : printf( "DBOP_equivalent" ); break;
        case DBOP_not : printf( "DBOP_not" ); break;
        case DBOP_implies : printf( "DBOP_implies" ); break;
        case DBOP_overlaps : printf( "DBOP_overlaps" ); break;
        case DBOP_case_condition : printf( "DBOP_case_condition" ); break;
        case DBOP_case_value : printf( "DBOP_case_value" ); break;
        case DBOP_nullif : printf( "DBOP_nullif" ); break;
        case DBOP_cast : printf( "DBOP_cast" ); break;
        case DBOP_coalesce : printf( "DBOP_coalesce" ); break;
        case DBOP_position : printf( "DBOP_position" ); break;
        case DBOP_extract : printf( "DBOP_extract" ); break;
        case DBOP_char_length : printf( "DBOP_char_length" ); break;
        case DBOP_octet_length : printf( "DBOP_octet_length" ); break;
        case DBOP_bit_length : printf( "DBOP_bit_length" ); break;
        case DBOP_substring : printf( "DBOP_substring" ); break;
        case DBOP_upper : printf( "DBOP_upper" ); break;
        case DBOP_lower : printf( "DBOP_lower" ); break;
        case DBOP_trim : printf( "DBOP_trim" ); break;
        case DBOP_translate : printf( "DBOP_translate" ); break;
        case DBOP_convert : printf( "DBOP_convert" ); break;
        case DBOP_string_concat : printf( "DBOP_string_concat" ); break;
        case DBOP_current_date : printf( "DBOP_current_date" ); break;
        case DBOP_current_time : printf( "DBOP_current_time" ); break;
        case DBOP_current_timestamp : printf( "DBOP_current_timestamp" ); break;
        case DBOP_content_select : printf( "DBOP_content_select" ); break;
        case DBOP_content : printf( "DBOP_content" ); break;
        case DBOP_content_freetext : printf( "DBOP_content_freetext" ); break;
        case DBOP_content_proximity : printf( "DBOP_content_proximity" ); break;
        case DBOP_content_vector_or : printf( "DBOP_content_vector_or" ); break;
        case DBOP_delete : printf( "DBOP_delete" ); break;
        case DBOP_update : printf( "DBOP_update" ); break;
        case DBOP_insert : printf( "DBOP_insert" ); break;
        case DBOP_min : printf( "DBOP_min" ); break;
        case DBOP_max : printf( "DBOP_max" ); break;
        case DBOP_count : printf( "DBOP_count" ); break;
        case DBOP_sum : printf( "DBOP_sum" ); break;
        case DBOP_avg : printf( "DBOP_avg" ); break;
        case DBOP_any_sample : printf( "DBOP_any_sample" ); break;
        case DBOP_stddev : printf( "DBOP_stddev" ); break;
        case DBOP_stddev_pop : printf( "DBOP_stddev_pop" ); break;
        case DBOP_var : printf( "DBOP_var" ); break;
        case DBOP_var_pop : printf( "DBOP_var_pop" ); break;
        case DBOP_first : printf( "DBOP_first" ); break;
        case DBOP_last : printf( "DBOP_last" ); break;
        case DBOP_in : printf( "DBOP_in" ); break;
        case DBOP_exists : printf( "DBOP_exists" ); break;
        case DBOP_unique : printf( "DBOP_unique" ); break;
        case DBOP_subset : printf( "DBOP_subset" ); break;
        case DBOP_proper_subset : printf( "DBOP_proper_subset" ); break;
        case DBOP_superset : printf( "DBOP_superset" ); break;
        case DBOP_proper_superset : printf( "DBOP_proper_superset" ); break;
        case DBOP_disjoint : printf( "DBOP_disjoint" ); break;
        case DBOP_pass_through : printf( "DBOP_pass_through" ); break;
        case DBOP_defined_by_GUID : printf( "DBOP_defined_by_GUID" ); break;
        case DBOP_text_command : printf( "DBOP_text_command" ); break;
        case DBOP_SQL_select : printf( "DBOP_SQL_select" ); break;
        case DBOP_prior_command_tree : printf( "DBOP_prior_command_tree" ); break;
        case DBOP_add_columns : printf( "DBOP_add_columns" ); break;
        case DBOP_column_list_anchor : printf( "DBOP_column_list_anchor" ); break;
        case DBOP_column_list_element : printf( "DBOP_column_list_element" ); break;
        case DBOP_command_list_anchor : printf( "DBOP_command_list_anchor" ); break;
        case DBOP_command_list_element : printf( "DBOP_command_list_element" ); break;
        case DBOP_from_list_anchor : printf( "DBOP_from_list_anchor" ); break;
        case DBOP_from_list_element : printf( "DBOP_from_list_element" ); break;
        case DBOP_project_list_anchor : printf( "DBOP_project_list_anchor" ); break;
        case DBOP_project_list_element : printf( "DBOP_project_list_element" ); break;
        case DBOP_row_list_anchor : printf( "DBOP_row_list_anchor" ); break;
        case DBOP_row_list_element : printf( "DBOP_row_list_element" ); break;
        case DBOP_scalar_list_anchor : printf( "DBOP_scalar_list_anchor" ); break;
        case DBOP_scalar_list_element : printf( "DBOP_scalar_list_element" ); break;
        case DBOP_set_list_anchor : printf( "DBOP_set_list_anchor" ); break;
        case DBOP_set_list_element : printf( "DBOP_set_list_element" ); break;
        case DBOP_sort_list_anchor : printf( "DBOP_sort_list_anchor" ); break;
        case DBOP_sort_list_element : printf( "DBOP_sort_list_element" ); break;
        case DBOP_alter_character_set : printf( "DBOP_alter_character_set" ); break;
        case DBOP_alter_collation : printf( "DBOP_alter_collation" ); break;
        case DBOP_alter_domain : printf( "DBOP_alter_domain" ); break;
        case DBOP_alter_index : printf( "DBOP_alter_index" ); break;
        case DBOP_alter_procedure : printf( "DBOP_alter_procedure" ); break;
        case DBOP_alter_schema : printf( "DBOP_alter_schema" ); break;
        case DBOP_alter_table : printf( "DBOP_alter_table" ); break;
        case DBOP_alter_trigger : printf( "DBOP_alter_trigger" ); break;
        case DBOP_alter_view : printf( "DBOP_alter_view" ); break;
        case DBOP_coldef_list_anchor : printf( "DBOP_coldef_list_anchor" ); break;
        case DBOP_coldef_list_element : printf( "DBOP_coldef_list_element" ); break;
        case DBOP_create_assertion : printf( "DBOP_create_assertion" ); break;
        case DBOP_create_character_set : printf( "DBOP_create_character_set" ); break;
        case DBOP_create_collation : printf( "DBOP_create_collation" ); break;
        case DBOP_create_domain : printf( "DBOP_create_domain" ); break;
        case DBOP_create_index : printf( "DBOP_create_index" ); break;
        case DBOP_create_procedure : printf( "DBOP_create_procedure" ); break;
        case DBOP_create_schema : printf( "DBOP_create_schema" ); break;
        case DBOP_create_synonym : printf( "DBOP_create_synonym" ); break;
        case DBOP_create_table : printf( "DBOP_create_table" ); break;
        case DBOP_create_temporary_table : printf( "DBOP_create_temporary_table" ); break;
        case DBOP_create_translation : printf( "DBOP_create_translation" ); break;
        case DBOP_create_trigger : printf( "DBOP_create_trigger" ); break;
        case DBOP_create_view : printf( "DBOP_create_view" ); break;
        case DBOP_drop_assertion : printf( "DBOP_drop_assertion" ); break;
        case DBOP_drop_character_set : printf( "DBOP_drop_character_set" ); break;
        case DBOP_drop_collation : printf( "DBOP_drop_collation" ); break;
        case DBOP_drop_domain : printf( "DBOP_drop_domain" ); break;
        case DBOP_drop_index : printf( "DBOP_drop_index" ); break;
        case DBOP_drop_procedure : printf( "DBOP_drop_procedure" ); break;
        case DBOP_drop_schema : printf( "DBOP_drop_schema" ); break;
        case DBOP_drop_synonym : printf( "DBOP_drop_synonym" ); break;
        case DBOP_drop_table : printf( "DBOP_drop_table" ); break;
        case DBOP_drop_translation : printf( "DBOP_drop_translation" ); break;
        case DBOP_drop_trigger : printf( "DBOP_drop_trigger" ); break;
        case DBOP_drop_view : printf( "DBOP_drop_view" ); break;
        case DBOP_foreign_key : printf( "DBOP_foreign_key" ); break;
        case DBOP_grant_privileges : printf( "DBOP_grant_privileges" ); break;
        case DBOP_index_list_anchor : printf( "DBOP_index_list_anchor" ); break;
        case DBOP_index_list_element : printf( "DBOP_index_list_element" ); break;
        case DBOP_primary_key : printf( "DBOP_primary_key" ); break;
        case DBOP_property_list_anchor : printf( "DBOP_property_list_anchor" ); break;
        case DBOP_property_list_element : printf( "DBOP_property_list_element" ); break;
        case DBOP_referenced_table : printf( "DBOP_referenced_table" ); break;
        case DBOP_rename_object : printf( "DBOP_rename_object" ); break;
        case DBOP_revoke_privileges : printf( "DBOP_revoke_privileges" ); break;
        case DBOP_schema_authorization : printf( "DBOP_schema_authorization" ); break;
        case DBOP_unique_key : printf( "DBOP_unique_key" ); break;
        default : printf( "unknown DBOP" ); break;
    }

    printf( "\n" );

    PrintSpace( iLevel );
    printf( "wKind: (%d) ", pNode->wKind );
    switch( pNode->wKind )
    {
        case DBVALUEKIND_BYGUID :
        {
            printf( "DBVALUEKIND_BYGUID " );
            break;
        }
        case DBVALUEKIND_COLDESC :
        {
            printf( "DBVALUEKIND_COLDESC " );
            break;
        }
        case DBVALUEKIND_ID :
        {
            printf( "DBVALUEKIND_ID: " );
            if ( 0 == pNode->value.pdbidValue )
                printf( "NULL" );
            else
            {
                DBKIND kind = pNode->value.pdbidValue->eKind;

                if ( DBKIND_GUID        == kind ||
                     DBKIND_GUID_NAME   == kind ||
                     DBKIND_GUID_PROPID == kind )
                {
                    GUID &g = pNode->value.pdbidValue->uGuid.guid;
                    printf( "%8.8x-%4.4x-%4.4x-%2.2x%2.2x-"
                            "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
                            g.Data1,g.Data2,g.Data3,g.Data4[0],
                            g.Data4[1],g.Data4[2],g.Data4[3],g.Data4[4],
                            g.Data4[5],g.Data4[6],g.Data4[7] );
                }
                if ( DBKIND_GUID_PROPID == kind ||
                     DBKIND_PROPID      == kind )
                    printf( " %d", pNode->value.pdbidValue->uName.ulPropid );
                if ( DBKIND_GUID_NAME == kind ||
                     DBKIND_NAME      == kind )
                    printf( " '%ws'", pNode->value.pdbidValue->uName.pwszName );
            }
            break;
        }
        case DBVALUEKIND_CONTENT :
        {
            printf( "DBVALUEKIND_CONTENT: " );
            if ( 0 == pNode->value.pdbcntntValue )
                printf( "NULL" );
            else
                printf( "'%ws', method %d, weight %d, lcid %#x",
                        pNode->value.pdbcntntValue->pwszPhrase,
                        pNode->value.pdbcntntValue->dwGenerateMethod,
                        pNode->value.pdbcntntValue->lWeight,
                        pNode->value.pdbcntntValue->lcid );
            break;
        }
        case DBVALUEKIND_CONTENTVECTOR :
        {
            printf( "DBVALUEKIND_CONTENTVECTOR: " );
            DBCONTENTVECTOR *p = pNode->value.pdbcntntvcValue;
            if ( 0 == p )
                printf( "NULL" );
            else
            {
                printf( "method %d, weight %d ",
                         p->dwRankingMethod,
                         p->lWeight );
            }
            break;
        }
        case DBVALUEKIND_GROUPINFO :
        {
            printf( "DBVALUEKIND_GROUPINFO " );
            break;
        }
        case DBVALUEKIND_PARAMETER :
        {
            printf( "DBVALUEKIND_PARAMETER " );
            break;
        }
        case DBVALUEKIND_PROPERTY :
        {
            printf( "DBVALUEKIND_PROPERTY " );
            break;
        }
        case DBVALUEKIND_SETFUNC :
        {
            printf( "DBVALUEKIND_SETFUNC " );
            break;
        }
        case DBVALUEKIND_SORTINFO :
        {
            printf( "DBVALUEKIND_SORTINFO: " );
            if ( 0 == pNode->value.pdbsrtinfValue )
                printf( "NULL" );
            else
                printf( "fDesc %d, lcid %#x",
                        pNode->value.pdbsrtinfValue->fDesc,
                        pNode->value.pdbsrtinfValue->lcid );
            break;
        }
        case DBVALUEKIND_TEXT :
        {
            printf( "DBVALUEKIND_TEXT " );
            break;
        }
        case DBVALUEKIND_COMMAND :
        {
            printf( "DBVALUEKIND_COMMAND " );
            break;
        }
        case DBVALUEKIND_MONIKER :
        {
            printf( "DBVALUEKIND_MONIKER " );
            break;
        }
        case DBVALUEKIND_ROWSET :
        {
            printf( "DBVALUEKIND_ROWSET " );
            break;
        }
        case DBVALUEKIND_IDISPATCH :
        {
            printf( "DBVALUEKIND_IDISPATCH " );
            break;
        }
        case DBVALUEKIND_IUNKNOWN :
        {
            printf( "DBVALUEKIND_IUNKNOWN " );
            break;
        }
        case DBVALUEKIND_EMPTY :
        {
            printf( "DBVALUEKIND_EMPTY " );
            break;
        }
        case DBVALUEKIND_NULL :
        {
            printf( "DBVALUEKIND_NULL " );

break;
        }
        case DBVALUEKIND_I2 :
        {
            printf( "DBVALUEKIND_I2: %d", pNode->value.sValue );
            break;
        }
        case DBVALUEKIND_I4 :
        {
            printf( "DBVALUEKIND_I4: %d", pNode->value.lValue );
            break;
        }
        case DBVALUEKIND_R4 :
        {
            printf( "DBVALUEKIND_R4: %f", pNode->value.flValue );
            break;
        }
        case DBVALUEKIND_R8 :
        {
            printf( "DBVALUEKIND_R8: %lf", pNode->value.dblValue );
            break;
        }
        case DBVALUEKIND_CY :
        {
            printf( "DBVALUEKIND_CY " );
            break;
        }
        case DBVALUEKIND_DATE :
        {
            printf( "DBVALUEKIND_DATE " );
            break;
        }
        case DBVALUEKIND_BSTR :
        {
            printf( "DBVALUEKIND_BSTR: '%ws'", pNode->value.pbstrValue );
            break;
        }
        case DBVALUEKIND_ERROR :
        {
            printf( "DBVALUEKIND_ERROR: %d", pNode->value.scodeValue );
            break;
        }
        case DBVALUEKIND_BOOL :
        {
            printf( "DBVALUEKIND_BOOL: %d", pNode->value.fValue );
            break;
        }
        case DBVALUEKIND_VARIANT :
        {
            printf( "DBVALUEKIND_VARIANT " );

            // Index Server uses PROPVARIANTs (extended VARIANTs)

            DisplayVariant( (PROPVARIANT *) pNode->value.pvarValue );
            break;
        }
        case DBVALUEKIND_I1 :
        {
            printf( "DBVALUEKIND_I1: %d", pNode->value.schValue );
            break;
        }
        case DBVALUEKIND_UI1 :
        {
            printf( "DBVALUEKIND_UI1: %ud", pNode->value.uchValue );
            break;
        }
        case DBVALUEKIND_UI2 :
        {
            printf( "DBVALUEKIND_UI2: %ud", pNode->value.usValue );
            break;
        }
        case DBVALUEKIND_UI4 :
        {
            printf( "DBVALUEKIND_UI4: %ud", pNode->value.ulValue );
            break;
        }
        case DBVALUEKIND_I8 :
        {
            printf( "DBVALUEKIND_I8: %I64d", pNode->value.llValue );
            break;
        }
        case DBVALUEKIND_UI8 :
        {
            printf( "DBVALUEKIND_UI8: %I64u", pNode->value.llValue );
            break;
        }
        case DBVALUEKIND_GUID :
        {
            printf( "DBVALUEKIND_GUID " );
            break;
        }
        case DBVALUEKIND_BYTES :
        {
            printf( "DBVALUEKIND_BYTES " );
            break;
        }
        case DBVALUEKIND_STR :
        {
            printf( "DBVALUEKIND_STR: '%s'", pNode->value.pzValue );
            break;
        }
        case DBVALUEKIND_WSTR :
        {
            printf( "DBVALUEKIND_WSTR: '%ws'", pNode->value.pwszValue );
            break;
        }
        case DBVALUEKIND_NUMERIC :
        {
            printf( "DBVALUEKIND_NUMERIC " );
            break;
        }
        case DBVALUEKIND_DBDATE :
        {
            printf( "DBVALUEKIND_DBDATE " );
            break;
        }
        case DBVALUEKIND_DBTIME :
        {
            printf( "DBVALUEKIND_DBTIME " );
            break;
        }
        case DBVALUEKIND_DBTIMESTAMP :
        {
            printf( "DBVALUEKIND_DBTIMESTAMP " );
            break;
        }
        default :
        {
            printf( "unknown DBVALUEKIND " );
            break;
        }
    }

    printf( "\n" );

    if ( pNode->pctFirstChild )
    {
        PrintSpace( iLevel );
        printf( "first child:\n" );
        DisplayCommandTree( pNode->pctFirstChild, iLevel + 1 );
    }

    if ( pNode->pctNextSibling )
    {
        PrintSpace( iLevel );
        printf( "next sibling:\n" );
        DisplayCommandTree( pNode->pctNextSibling, iLevel + 1 );
    }

    if ( 0 == iLevel )
        printf( "\n" );
} //DisplayCommandTree


ADVQUERY.CXX

//+-------------------------------------------------------------------------
//
// 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.
//
// Copyright 1997 Microsoft Corporation.  All Rights Reserved.
//
// PROGRAM:  advquery.cxx
//
// PURPOSE:  Illustrates a minimal query using Microsoft Index Server.
//           Uses native OLE DB, not Index Server helper functions.
//
// PLATFORM: Windows NT
//
//--------------------------------------------------------------------------

#define UNICODE

#include <stdio.h>
#include <windows.h>

#define OLEDBVER 0x0200 // need the command tree definitions
#define DBINITCONSTANTS

#include <oledberr.h>
#include <oledb.h>

#include <ntquery.h>

// This is found in disptree.cxx

extern void DisplayCommandTree( DBCOMMANDTREE * pNode, ULONG iLevel = 0 );

//+-------------------------------------------------------------------------
//
//  Template:   XInterface
//
//  Synopsis:   Template for managing ownership of interfaces
//
//--------------------------------------------------------------------------

template<class T> class XInterface
{
public:
    XInterface( T * p = 0 ) : _p( p ) {}
    ~XInterface() { if ( 0 != _p ) _p->Release(); }
    T * operator->() { return _p; }
    T * GetPointer() const { return _p; }
    IUnknown ** GetIUPointer() { return (IUnknown **) &_p; }
    T ** GetPPointer() { return &_p; }
    void ** GetQIPointer() { return (void **) &_p; }
    T * Acquire() { T * p = _p; _p = 0; return p; }

private:
    T * _p;
};

//+-------------------------------------------------------------------------
//
//  Function:   CreateICommand
//
//  Synopsis:   Creates an ICommand.
//
//  Arguments:  [ppICommand] - Where the ICommand is returned on success
//
//  Returns:    HRESULT result
//
//--------------------------------------------------------------------------

HRESULT CreateICommand( ICommand ** ppICommand )
{
    // Instantiate the data source object

    const GUID CLSID_IndexServerDSO = CLSID_INDEX_SERVER_DSO;
    XInterface<IDBInitialize> xIDBInit;
    HRESULT hr = CoCreateInstance( CLSID_IndexServerDSO,
                                 0,
                                 CLSCTX_INPROC_SERVER,
                                 IID_IDBInitialize,
                                 xIDBInit.GetQIPointer() );

    if ( FAILED(hr) )
        return hr;

    // Initialize, verifying that we supplied the right variables

    hr = xIDBInit->Initialize();
    if ( FAILED(hr) )
        return hr;

    // Get a session object

    XInterface<IDBCreateSession> xIDBSess;
    hr = xIDBInit->QueryInterface( IID_IDBCreateSession,
                                   xIDBSess.GetQIPointer() );
    if ( FAILED(hr) )
        return hr;

    // Get a Create Command object

    XInterface<IDBCreateCommand> xICreateCommand;
    hr = xIDBSess->CreateSession( 0,
                                  IID_IDBCreateCommand,
                                  xICreateCommand.GetIUPointer() );
    if ( FAILED(hr) )
        return hr;

    // Create the ICommand

    XInterface<ICommand> xICommand;
    hr = xICreateCommand->CreateCommand( 0,
                                         IID_ICommand,
                                         xICommand.GetIUPointer() );
    if ( FAILED(hr) )
        return hr;

    *ppICommand = xICommand.Acquire();

    return hr;
} //CreateICommand

//+-------------------------------------------------------------------------
//
//  Function:   SetCommandProperties
//
//  Synopsis:   Sets the DBPROP_USEEXTENDEDDBTYPES property to TRUE, so
//              data is returned in PROPVARIANTs, as opposed to the
//              default, which is OLE automation VARIANTs.  PROPVARIANTS
//              allow a superset of VARIANT data types.  Use of these
//              types avoids costly coercions.
//
//              Also sets the DBPROP_USECONTENTINDEX property to TRUE, so
//              the index will always be used to resolve the query (as
//              opposed to enumerating all the files on the disk), even
//              if the index is out of date.
//
//              Both of these properties are unique to Index Server's OLE DB
//              implementation.
//
//  Arguments:  [pICommand] - The ICommand used to set the property
//
//  Returns:    HRESULT result of setting the properties
//
//--------------------------------------------------------------------------

HRESULT SetCommandProperties( ICommand * pICommand )
{
    static const DBID dbcolNull = { { 0,0,0, { 0,0,0,0,0,0,0,0 } },
                                    DBKIND_GUID_PROPID, 0 };
    static const GUID guidQueryExt = DBPROPSET_QUERYEXT;

    DBPROP aProp[2];

    aProp[0].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
    aProp[0].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
    aProp[0].dwStatus = 0;
    aProp[0].colid = dbcolNull;
    aProp[0].vValue.vt = VT_BOOL;
    aProp[0].vValue.boolVal = VARIANT_TRUE;

    aProp[1] = aProp[0];
    aProp[1].dwPropertyID = DBPROP_USECONTENTINDEX;

    DBPROPSET aPropSet[1];

    aPropSet[0].rgProperties = &aProp[0];
    aPropSet[0].cProperties = 2;
    aPropSet[0].guidPropertySet = guidQueryExt;

    XInterface<ICommandProperties> xICommandProperties;
    HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
                                            xICommandProperties.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    return xICommandProperties->SetProperties( 1,          // 1 property set
                                               aPropSet ); // the properties
} //SetCommandProperties

//+-------------------------------------------------------------------------
//
//  Function:   SetScopeCatalogAndMachine
//
//  Synopsis:   Sets the catalog and machine properties in the ICommand.
//              Also sets a default scope.
//
//  Arguments:  [pICommand]       - ICommand to set props on
//              [pwcQueryScope]   - Scope for the query
//              [pwcQueryCatalog] - Catalog name over which query is run
//              [pwcQueryMachine] - Machine name on which query is run
//
//  Returns:    HRESULT result of the operation
//
//--------------------------------------------------------------------------

HRESULT SetScopeCatalogAndMachine(
    ICommand *    pICommand,
    WCHAR const * pwcQueryScope,
    WCHAR const * pwcQueryCatalog,
    WCHAR const * pwcQueryMachine )
{
    // Get an ICommandProperties so we can set the properties

    XInterface<ICommandProperties> xICommandProperties;
    HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
                                            xICommandProperties.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    // note: SysAllocString, SafeArrayCreate, and SafeArrayPutElement can
    // fail, but this isn't checked here for brevity.

    SAFEARRAYBOUND rgBound[1];
    rgBound[0].lLbound = 0;
    rgBound[0].cElements = 1;
    SAFEARRAY * pMachines = SafeArrayCreate( VT_BSTR, 1, rgBound );
    long i = 0;
    SafeArrayPutElement( pMachines, &i, SysAllocString( pwcQueryMachine ) );

    SAFEARRAY * pCatalogs = SafeArrayCreate( VT_BSTR, 1, rgBound );
    SafeArrayPutElement( pCatalogs, &i, SysAllocString( pwcQueryCatalog ) );

    SAFEARRAY * pScopes = SafeArrayCreate( VT_BSTR, 1, rgBound );
    SafeArrayPutElement( pScopes, &i, SysAllocString( pwcQueryScope ) );

    LONG lFlags = QUERY_DEEP;
    SAFEARRAY * pFlags = SafeArrayCreate( VT_I4, 1, rgBound );
    SafeArrayPutElement( pFlags, &i, &lFlags );

    DBPROP aScopeProperties[2];
    memset( aScopeProperties, 0, sizeof aScopeProperties );
    aScopeProperties[0].dwPropertyID = DBPROP_CI_INCLUDE_SCOPES;
    aScopeProperties[0].vValue.vt = VT_BSTR | VT_ARRAY;
    aScopeProperties[0].vValue.parray = pScopes;

    aScopeProperties[1].dwPropertyID = DBPROP_CI_SCOPE_FLAGS;
    aScopeProperties[1].vValue.vt = VT_I4 | VT_ARRAY;
    aScopeProperties[1].vValue.parray = pFlags;

    DBPROP aCatalogProperties[1];
    memset( aCatalogProperties, 0, sizeof aCatalogProperties );
    aCatalogProperties[0].dwPropertyID = DBPROP_CI_CATALOG_NAME;
    aCatalogProperties[0].vValue.vt = VT_BSTR | VT_ARRAY;
    aCatalogProperties[0].vValue.parray = pCatalogs;

    DBPROP aMachineProperties[1];
    memset( aMachineProperties, 0, sizeof aMachineProperties );
    aMachineProperties[0].dwPropertyID = DBPROP_MACHINE;
    aMachineProperties[0].vValue.vt = VT_BSTR | VT_ARRAY;
    aMachineProperties[0].vValue.parray = pMachines;

    const GUID guidFSCI = DBPROPSET_FSCIFRMWRK_EXT;
    DBPROPSET aAllPropsets[3];
    aAllPropsets[0].rgProperties = aScopeProperties;
    aAllPropsets[0].cProperties = 2;
    aAllPropsets[0].guidPropertySet = guidFSCI;

    aAllPropsets[1].rgProperties = aCatalogProperties;
    aAllPropsets[1].cProperties = 1;
    aAllPropsets[1].guidPropertySet = guidFSCI;

    const GUID guidCI = DBPROPSET_CIFRMWRKCORE_EXT;
    aAllPropsets[2].rgProperties = aMachineProperties;
    aAllPropsets[2].cProperties = 1;
    aAllPropsets[2].guidPropertySet = guidCI;

    const ULONG cPropertySets = sizeof aAllPropsets / sizeof aAllPropsets[0];

    hr = xICommandProperties->SetProperties( cPropertySets,  // # of propsets
                                             aAllPropsets ); // the propsets

    SafeArrayDestroy( pScopes );
    SafeArrayDestroy( pFlags );
    SafeArrayDestroy( pCatalogs );
    SafeArrayDestroy( pMachines );

    return hr;
} //SetScopeCatalogAndMachine

//+-------------------------------------------------------------------------
//
//  Function:   AllocAndCopy
//
//  Synopsis:   Allocates and duplicates a string.
//
//  Arguments:  [pwcIn]  - The string to copy
//
//  Returns:    A string
//
//--------------------------------------------------------------------------

WCHAR * AllocAndCopy( WCHAR const * pwcIn )
{
    ULONG cwc = wcslen( pwcIn ) + 1;

    // note: CoTaskMemAlloc can return 0 if out of memory, not checked

    WCHAR * pwc = (WCHAR *) CoTaskMemAlloc( cwc * sizeof WCHAR );
    wcscpy( pwc, pwcIn );
    return pwc;
} //AllocAndCopy

//+-------------------------------------------------------------------------
//
//  Function:   NewTreeNode
//
//  Synopsis:   Allocates and initializes a DBCOMMANDTREE object
//
//  Arguments:  [op]     - The node's operator
//              [wKind]  - The kind of node
//
//  Returns:    an initialized DBCOMMANDTREE object
//
//--------------------------------------------------------------------------

DBCOMMANDTREE * NewTreeNode(
    DBCOMMANDOP op,
    WORD        wKind )
{
    DBCOMMANDTREE * pTree = (DBCOMMANDTREE *)
                             CoTaskMemAlloc( sizeof DBCOMMANDTREE );
    memset( pTree, 0, sizeof DBCOMMANDTREE );
    pTree->op = op;
    pTree->wKind = wKind;
    return pTree;
} //NewTreeNode

//+-------------------------------------------------------------------------
//
//  Function:   CreateQueryTree
//
//  Synopsis:   Creates a DBCOMMANDTREE for the query
//
//  Arguments:  [pwcQueryRestrition] - The actual query string
//              [ppTree]             - Resulting query tree
//
//  Returns:    HRESULT result of the operation
//
//  Notes:      The query tree has a string restriction, a list of
//              columns to return (rank, size, and path), and a sort
//              order (rank).
//              Here are two views of the query tree
//
//                child: project
//                  sibling: sort_list_anchor
//                    child: sort_list_element
//                      value: SORTINFO (descending, lcid)
//                      child: column_name
//                        value: DBID rank
//                  child: select
//                    sibling: project_list_anchor
//                      child: project_list_element
//                        child: column_name
//                          value: DBID rank
//                        sibling: project_list_element
//                          child: column_name
//                            value: DBID size
//                          sibling: project_list_element
//                            child: column_name
//                              value: DBID path
//                    child: table_name
//                      value: WSTR: "Table"
//                      sibling: content
//                        value: DBCONTENT (restriction, weight, lcid)
//                        child: column_name
//                          value: DBID contents
//
// +---------------------------+
// | DBOP_sort                 |
// | DBVALUEKIND_EMPTY         |
// +---------------------------+
// |
// |child
// |                            sibling
// +---------------------------+-------+---------------------------+
// | DBOP_project              |       | DBOP_sort_list_anchor     |
// | DBVALUEKIND_EMPTY         |       | DBVALUEKIND_EMPTY         |
// +---------------------------+       +---------------------------+
// |                                   |
// |child                              |child
// |                                   |
// |                                   +---------------------------+
// |                                   | DBOP_sort_list_element    |
// |                                   | DBVALUEKIND_SORTINFO      |
// |                                   +---------------------------+
// |                                   |            |
// |                                   |child       | pdbsrtinfValue
// |                                   |            |
// |                                   |     +-------------+
// |                                   |     | DBSORTIFO   |
// |                                   |     | fDesc TRUE  |
// |                                   |     | lcid system |
// |                                   |     +-------------+
// |                                   |
// |                                   |
// |                                   |
// |                                   +---------------------------+
// |                                   | DBOP_column_name          |
// |                                   | DBVALUEKIND_ID            |
// |                                   +---------------------------+
// |                                                |
// |                                                | pdbidValue
// |                                                |
// |                                             +------+
// |                                             | DBID |
// |                                             | rank |
// |                                             +------+
// |
// |                            sibling
// +---------------------------+-------+---------------------------+-------+
// | DBOP_select               |       | DBOP_project_list_anchor  |       | s
// | DBVALUEKIND_EMPTY         |       | DBVALUEKIND_EMPTY         |       | i
// +---------------------------+       +---------------------------+       | b
// |                                   |                                   | l
// |child                              |child                              | i
// |                                   |                                   | n
// |                                   +---------------------------+       | g
// |                                   | DBOP_project_list_element |       |
// |                                   | DBVALUEKIND_EMPTY         |       |
// |                                   +---------------------------+       |
// |                                   |                                   |
// |                                   |child                              |
// |                                   |                                   |
// |                                   +---------------------------+       |
// |                                   | DBOP_column_name          |       |
// |                                   | DBVALUEKIND_ID            |       |
// |                                   +---------------------------+       |
// |                                                |                      |
// |                                                | pdbidValue           |
// |                                                |                      |
// |                                             +------+                  |
// |                                             | DBID |                  |
// |                                             | rank |                  |
// |                                             +------+                  |
// |                                                                       |
// |                                   +-----------------------------------+
// |                                   |
// |                                   +---------------------------+-------+
// |                                   | DBOP_project_list_element |       | s
// |                                   | DBVALUEKIND_EMPTY         |       | i
// |                                   +---------------------------+       | b
// |                                   |                                   | l
// |                                   |child                              | i
// |                                   |                                   | n
// |                                   +---------------------------+       | g
// |                                   | DBOP_column_name          |       |
// |                                   | DBVALUEKIND_ID            |       |
// |                                   +---------------------------+       |
// |                                                |                      |
// |                                                | pdbidValue           |
// |                                                |                      |
// |                                             +------+                  |
// |                                             | DBID |                  |
// |                                             | size |                  |
// |                                             +------+                  |
// |                                                                       |
// |                                   +-----------------------------------+
// |                                   |
// |                                   +---------------------------+
// |                                   | DBOP_project_list_element |
// |                                   | DBVALUEKIND_EMPTY         |
// |                                   +---------------------------+
// |                                   |
// |                                   |child
// |                                   |
// |                                   +---------------------------+
// |                                   | DBOP_column_name          |
// |                                   | DBVALUEKIND_ID            |
// |                                   +---------------------------+
// |                                                |
// |                                                | pdbidValue
// |                                                |
// |                                             +------+
// |                                             | DBID |
// |                                             | path |
// |                                             +------+
// |
// |                            sibling
// +---------------------------+-------+---------------------------+
// | DBOP_table_name           |       | DBOP_content              |
// | DBVALUEKIND_WSTR: 'Table' |       | DBVALUEKIND_CONTENT       |
// +---------------------------+       +---------------------------+
//                                     |            |
//                                     |child       | pdbcntntValue
//                                     |            |
//                                     |  +---------------------------+
//                                     |  | DBCONTENT                 |
//                                     |  | dwGenerateMethod: GENERATE_METHOD_EXACT |
//                                     |  | lWeight: 1000             |
//                                     |  | lcid: system              |
//                                     |  | pwszPhrase: the query     |
//                                     |  +---------------------------+
//                                     |
//                                     |
//                                     |
//                                     +---------------------------+
//                                     | DBOP_column_name          |
//                                     | DBVALUEKIND_ID            |
//                                     +---------------------------+
//                                                  |
//                                                  | pdbidValue
//                                                  |
//                                             +----------+
//                                             | DBID     |
//                                             | contents |
//                                             +----------+
//
//--------------------------------------------------------------------------

HRESULT CreateQueryTree(
    WCHAR const *    pwcQueryRestriction,
    DBCOMMANDTREE ** ppTree )
{
    // These are the properties that'll be referenced below

    const DBID dbidContents = { PSGUID_STORAGE, DBKIND_GUID_PROPID,
                                (LPOLESTR) PID_STG_CONTENTS };
    const DBID dbidPath =     { PSGUID_STORAGE, DBKIND_GUID_PROPID,
                                (LPOLESTR) PID_STG_PATH };
    const DBID dbidSize =     { PSGUID_STORAGE, DBKIND_GUID_PROPID,
                                (LPOLESTR) PID_STG_SIZE };
    DBID dbidRank;
    dbidRank.uGuid.guid = PSGUID_QUERY;
    dbidRank.eKind = DBKIND_GUID_PROPID;
    dbidRank.uName.ulPropid = PROPID_QUERY_RANK ;

    // The restriction is a content node with either a word or a phrase.
    // This is the most simple possible query.  Other types of nodes include
    // AND, OR, NOT, etc.
    // The CITextToFullTree function is available for building more complex
    // queries given a text string.

    DBCOMMANDTREE *pRestriction = NewTreeNode( DBOP_content,
                                               DBVALUEKIND_CONTENT );
    DBCONTENT * pDBContent = (DBCONTENT *) CoTaskMemAlloc( sizeof DBCONTENT );
    memset( pDBContent, 0, sizeof DBCONTENT );
    pRestriction->value.pdbcntntValue = pDBContent;
    pDBContent->dwGenerateMethod = GENERATE_METHOD_EXACT;
    pDBContent->lWeight = 1000; // maximum possible weight
    pDBContent->lcid = GetSystemDefaultLCID();
    pDBContent->pwszPhrase = AllocAndCopy( pwcQueryRestriction );

    // This identifies "file contents" as the property for the restrition

    DBCOMMANDTREE *pPropID = NewTreeNode( DBOP_column_name, DBVALUEKIND_ID );
    pRestriction->pctFirstChild = pPropID;
    DBID *pDBID = (DBID *) CoTaskMemAlloc( sizeof DBID );
    *pDBID = dbidContents;
    pPropID->value.pdbidValue = pDBID;

    DBCOMMANDTREE *pSelect = NewTreeNode( DBOP_select, DBVALUEKIND_EMPTY );
    DBCOMMANDTREE *pTableId = NewTreeNode( DBOP_table_name, DBVALUEKIND_WSTR );
    pSelect->pctFirstChild = pTableId;
    pTableId->value.pwszValue = AllocAndCopy( L"Table" );
    pTableId->pctNextSibling = pRestriction;

    DBCOMMANDTREE *pProject = NewTreeNode( DBOP_project, DBVALUEKIND_EMPTY );
    pProject->pctFirstChild = pSelect;

    // The project anchor holds the list of columns that are retrieved

    DBCOMMANDTREE * pProjectAnchor = NewTreeNode( DBOP_project_list_anchor,
                                                  DBVALUEKIND_EMPTY );
    pSelect->pctNextSibling = pProjectAnchor;

    // Retrieve rank as column 1

    DBCOMMANDTREE * pProjectRank = NewTreeNode( DBOP_project_list_element,
                                                  DBVALUEKIND_EMPTY );
    pProjectAnchor->pctFirstChild = pProjectRank;

    DBCOMMANDTREE * pColumnRank = NewTreeNode( DBOP_column_name,
                                               DBVALUEKIND_ID );
    pProjectRank->pctFirstChild = pColumnRank;
    DBID *pDBIDRank = (DBID *) CoTaskMemAlloc( sizeof DBID );
    pColumnRank->value.pdbidValue = pDBIDRank;
    *pDBIDRank = dbidRank;

    // Retrieve file size as column 2

    DBCOMMANDTREE * pProjectSize = NewTreeNode( DBOP_project_list_element,
                                                DBVALUEKIND_EMPTY );
    pProjectRank->pctNextSibling = pProjectSize;

    DBCOMMANDTREE * pColumnSize = NewTreeNode( DBOP_column_name,
                                               DBVALUEKIND_ID );
    pProjectSize->pctFirstChild = pColumnSize;
    DBID *pDBIDSize = (DBID *) CoTaskMemAlloc( sizeof DBID );
    pColumnSize->value.pdbidValue = pDBIDSize;
    *pDBIDSize = dbidSize;

    // Retrieve file path as column 3

    DBCOMMANDTREE * pProjectPath = NewTreeNode( DBOP_project_list_element,
                                                DBVALUEKIND_EMPTY );
    pProjectSize->pctNextSibling = pProjectPath;

    DBCOMMANDTREE * pColumnPath = NewTreeNode( DBOP_column_name,
                                               DBVALUEKIND_ID );
    pProjectPath->pctFirstChild = pColumnPath;
    DBID *pDBIDPath = (DBID *) CoTaskMemAlloc( sizeof DBID );
    pColumnPath->value.pdbidValue = pDBIDPath;
    *pDBIDPath = dbidPath;

    // The sort node specifies the sort order for the results

    DBCOMMANDTREE * pSort = NewTreeNode( DBOP_sort, DBVALUEKIND_EMPTY );
    pSort->pctFirstChild = pProject;

    // The sort anchor is the start of the list of sort properties

    DBCOMMANDTREE * pSortAnchor = NewTreeNode( DBOP_sort_list_anchor,
                                               DBVALUEKIND_EMPTY );
    pProject->pctNextSibling = pSortAnchor;

    // The sort order is rank

    DBCOMMANDTREE * pSortRank = NewTreeNode( DBOP_sort_list_element,
                                             DBVALUEKIND_SORTINFO );
    pSortAnchor->pctFirstChild = pSortRank;

    DBSORTINFO * pSortInfo = (DBSORTINFO *) CoTaskMemAlloc( sizeof DBSORTINFO );
    memset( pSortInfo, 0, sizeof DBSORTINFO );
    pSortRank->value.pdbsrtinfValue = pSortInfo;
    pSortInfo->fDesc = TRUE; // descending, not ascending
    pSortInfo->lcid = GetSystemDefaultLCID();

    DBCOMMANDTREE * pSortColumnRank = NewTreeNode( DBOP_column_name,
                                                   DBVALUEKIND_ID );
    pSortRank->pctFirstChild = pSortColumnRank;
    DBID *pDBIDSortRank = (DBID *) CoTaskMemAlloc( sizeof DBID );
    pSortColumnRank->value.pdbidValue = pDBIDSortRank;
    *pDBIDSortRank = dbidRank;

    // The sort node is the head of the tree

    *ppTree = pSort;

    return S_OK;
} //CreateQueryTree

//+-------------------------------------------------------------------------
//
//  Function:   DoQuery
//
//  Synopsis:   Creates and executes a query, then displays the results.
//
//  Arguments:  [pwcQueryScope]      - Root path for all results
//              [pwcQueryCatalog]    - Catalog name over which query is run
//              [pwcQueryMachine]    - Machine name on which query is run
//              [pwcQueryRestrition] - The actual query string
//              [fDisplayTree]       - TRUE to display the command tree
//
//  Returns:    HRESULT result of the query
//
//--------------------------------------------------------------------------

HRESULT DoQuery(
    WCHAR const * pwcQueryScope,
    WCHAR const * pwcQueryCatalog,
    WCHAR const * pwcQueryMachine,
    WCHAR const * pwcQueryRestriction,
    BOOL          fDisplayTree )
{
    // Create an ICommand object.  The default scope for the query is the
    // entire catalog.

    XInterface<ICommand> xICommand;
    HRESULT hr = CreateICommand( xICommand.GetPPointer() );
    if ( FAILED( hr ) )
        return hr;

    // Set the scope, catalog, and machine in the ICommand

    hr = SetScopeCatalogAndMachine( xICommand.GetPointer(),
                                    pwcQueryScope,
                                    pwcQueryCatalog,
                                    pwcQueryMachine );
    if ( FAILED( hr ) )
        return hr;

    // Set required properties on the ICommand

    hr = SetCommandProperties( xICommand.GetPointer() );

if ( FAILED( hr ) )
        return hr;

    // Create an OLE DB query tree from a text restriction

    DBCOMMANDTREE * pTree;
    hr = CreateQueryTree( pwcQueryRestriction, // the input query
                          &pTree );            // the output tree
    if ( FAILED( hr ) )
        return hr;

    // If directed, display the command tree

    if ( fDisplayTree )
        DisplayCommandTree( pTree );

    // Set the tree in the ICommandTree

    XInterface<ICommandTree> xICommandTree;
    hr = xICommand->QueryInterface( IID_ICommandTree,
                                    xICommandTree.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    hr = xICommandTree->SetCommandTree( &pTree,
                                        DBCOMMANDREUSE_NONE,
                                        FALSE );
    if ( FAILED( hr ) )
        return hr;

    // Execute the query.  The query is complete when Execute() returns

    XInterface<IRowset> xIRowset;
    hr = xICommand->Execute( 0,            // no aggregating IUnknown
                             IID_IRowset,  // IID for interface to return
                             0,            // no DBPARAMs
                             0,            // no rows affected
                             xIRowset.GetIUPointer() ); // result
    if ( FAILED( hr ) )
        return hr;

    // Create an accessor, so data can be retrieved from the rowset

    XInterface<IAccessor> xIAccessor;
    hr = xIRowset->QueryInterface( IID_IAccessor,
                                   xIAccessor.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    // Column iOrdinals are parallel with those passed to CiTextToFullTree,
    // so MapColumnIDs isn't necessary.  These binding values for dwPart,
    // dwMemOwner, and wType are the most optimal bindings for Index Server.

    const ULONG cColumns = 3; // 3 for Rank, Size, and Path
    DBBINDING aColumns[ cColumns ];
    memset( aColumns, 0, sizeof aColumns );

    aColumns[0].iOrdinal   = 1; // first column specified above (rank)
    aColumns[0].obValue    = 0; // offset where value is written in GetData
    aColumns[0].dwPart     = DBPART_VALUE;  // retrieve value, not status
    aColumns[0].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // Index Server owned
    aColumns[0].wType      = DBTYPE_VARIANT | DBTYPE_BYREF; // VARIANT *

    aColumns[1] = aColumns[0];
    aColumns[1].iOrdinal   = 2; // second column specified above (size)
    aColumns[1].obValue    = sizeof (PROPVARIANT *); // offset for value

    aColumns[2] = aColumns[0];
    aColumns[2].iOrdinal   = 3; // third column specified above (path)
    aColumns[2].obValue    = 2 * sizeof (PROPVARIANT *); // offset for value

    HACCESSOR hAccessor;
    hr = xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, // rowdata accessor
                                     cColumns,           // # of columns
                                     aColumns,           // columns
                                     0,                  // ignored
                                     &hAccessor,         // result
                                     0 );                // no status
    if ( FAILED( hr ) )
        return hr;

    // Display the results of the query.  Print file size and file path.

    printf( " Rank       Size  Path\n" );

    ULONG cRowsSoFar = 0;

    do
    {
        ULONG cRowsReturned = 0;
        const ULONG cRowsAtATime = 10;
        HROW aHRow[cRowsAtATime];
        HROW * pgrHRows = aHRow;
        hr = xIRowset->GetNextRows( 0,              // no chapter
                                    0,              // no rows to skip
                                    cRowsAtATime,   // # rows to get
                                    &cRowsReturned, // # rows returned
                                    &pgrHRows);     // resulting hrows

        if ( FAILED( hr ) )
            break;

        for ( ULONG iRow = 0; iRow < cRowsReturned; iRow++ )
        {
            PROPVARIANT * aData[cColumns];
            hr = xIRowset->GetData( aHRow[iRow],  // hrow being accessed
                                    hAccessor,    // accessor to use
                                    &aData );     // resulting data
            if ( FAILED( hr ) )
                break;

            if ( VT_I4 ==     aData[0]->vt &&
                 VT_I8 ==     aData[1]->vt &&
                 VT_LPWSTR == aData[2]->vt )
                printf( "%5d %10I64d  %ws\n",
                        aData[0]->lVal,
                        aData[1]->hVal,
                        aData[2]->pwszVal );
            else
                printf( "could not retrieve a file's values\n" );
        }

        if ( 0 != cRowsReturned )
            xIRowset->ReleaseRows( cRowsReturned, // # of rows to release
                                   aHRow,         // rows to release
                                   0,             // no options
                                   0,             // no refcounts
                                   0 );           // no status

        if ( DB_S_ENDOFROWSET == hr )
        {
            hr = S_OK; // succeeded, return S_OK from DoQuery
            break;
        }

        if ( FAILED( hr ) )
            break;

        cRowsSoFar += cRowsReturned;
    } while ( TRUE );

    printf( "%d files matched the query '%ws'\n",
            cRowsSoFar,
            pwcQueryRestriction );

    xIAccessor->ReleaseAccessor( hAccessor, 0 );

    return hr;
} //DoQuery

//+-------------------------------------------------------------------------
//
//  Function:   Usage
//
//  Synopsis:   Displays information about how to use the app and exits
//
//--------------------------------------------------------------------------

void Usage()
{
    printf( "usage: ADVQUERY query [/c:catalog] [/m:machine] [/s:scope] [/d]\n\n" );
    printf( "    query        word or phrase used for the search\n" );
    printf( "    /c:catalog   name of the catalog, default is SYSTEM\n" );
    printf( "    /m:machine   name of the machine, default is local machine\n" );
    printf( "    /s:scope     root path, default is entire catalog (\\) \n" );
    printf( "    /d           display the DBCOMMANDTREE, default is off\n" );
    exit( -1 );
} //Usage

//+-------------------------------------------------------------------------
//
//  Function:   wmain
//
//  Synopsis:   Entry point for the app.  Parses command line arguments
//              and issues a query.
//
//  Arguments:  [argc]     - Argument count
//              [argv]     - Arguments
//
//--------------------------------------------------------------------------

extern "C" int __cdecl wmain( int argc, WCHAR * argv[] )
{
    WCHAR const * pwcScope       = L"\\";     // default scope: entire catalog
    WCHAR const * pwcCatalog     = L"system"; // default: system catalog
    WCHAR const * pwcMachine     = L".";      // default: local machine
    WCHAR const * pwcRestriction = 0;         // no default restriction
    BOOL fDisplayTree            = FALSE;     // don't display the tree

    // Parse command line parameters

    for ( int i = 1; i < argc; i++ )
    {
        if ( L'-' == argv[i][0] || L'/' == argv[i][0] )
        {
            WCHAR wc = toupper( argv[i][1] );

            if ( ':' != argv[i][2] && 'D' != wc )
                Usage();

            if ( 'C' == wc )
                pwcCatalog = argv[i] + 3;
            else if ( 'M' == wc )
                pwcMachine = argv[i] + 3;
            else if ( 'S' == wc )
                pwcScope = argv[i] + 3;
            else if ( 'D' == wc )
                fDisplayTree = TRUE;
            else
                Usage();
        }
        else if ( 0 != pwcRestriction )
            Usage();
        else
            pwcRestriction = argv[i];
    }

    // A query restriction is necessary.  Fail if none is given.

    if ( 0 == pwcRestriction )
        Usage();

    // Initialize COM

    HRESULT hr = CoInitialize( 0 );

    if ( SUCCEEDED( hr ) )
    {
        // Run the query

        hr = DoQuery( pwcScope,
                      pwcCatalog,
                      pwcMachine,
                      pwcRestriction,
                      fDisplayTree );

        CoUninitialize();
    }

    if ( FAILED( hr ) )
    {
        printf( "the query '%ws' failed with error %#x\n",
                pwcRestriction, hr );
        return -1;
    }

    return 0;
} //wmain


README.TXT

SUMMARY
=======

The CATQ sample shows how to run a query against a catalog generated by
the Microsoft Index Server.  By default, in this example, the catalog is
for files on the local machine and the query is restricted to the scope
of the SDK.  As such, this sample provides fast full text search over all
text-based files in the SDK.

MORE INFORMATION
================

Content indexing (CISVC) is a service included in all versions of Windows NT 5,
but it is not running by default.  When the CATQ sample is started, it checks
the status of CISVC, and starts it up if it is not running.

In the status window at the bottom of the CATQ sample, the progress of the
indexing effort is displayed as a fraction and as a blue "progress bar."  The
numerator and denominator of this fraction are read as performance registry
counters, which are written out by CISVC.

The denominator in the progress display is the total number of files that
CISVC must index in order for the catalog to be up-to-date.  Unfortunately,
this value is not known instantly at the time of CISVC startup, and in fact,
it typically grows over time as the numerator grows.  For this reason, the
progress percentage can decrease even as the content indexer is making
progress.  I.e. it is finding new files to index as fast as it is indexing
files in its existing list.

As a result of running the CATQ sample, the CISVC service is started.  This
will place a small load on your CPU and on your disk controler.  The resulting
catalog will also take up some amount of disk space.

If you would like to stop the CISVC service, use the service control manager
built into the system, or the SC.EXE utility distributed with the SDK.  In the
case of the later tool, the appropriate command line is:

    SC stop CISVC

If you wish to reclaim disk space occupied by the catalog, after the service
has been stopped, delete all of the files in %SystemRoot%\system32\catalog.wci.

By default, most of the text file formats present in the SDK are indexed.
Additional file formats can also be indexed and searched by providing custom
filters.  See IFilter in the documentation for more information.


CIQUERY.CXX

/*++

Copyright 1992 - 1998 Microsoft Corporation, All rights reserved.

    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.

Module Name:

    ciquery.cxx

Abstract:

    Perform queries against the content index catalog generated by
     the Microsoft Index Server.

Platform:

    Windows NT

Revision History:

    David Lee                      Wrote it.
    Steve Firebaugh                Modified slightly to work in GUI context.

--*/

#include <stdio.h>
#include <windows.h>

#define OLEDBVER 0x0200 // need the command tree definitions
#define DBINITCONSTANTS

#include <oledberr.h>
#include <oledb.h>

#include <ntquery.h>

#include "catq.h"

//+-------------------------------------------------------------------------
//
//  Template:   XInterface
//
//  Synopsis:   Template for managing ownership of interfaces
//
//--------------------------------------------------------------------------

template<class T> class XInterface
{
public:
    XInterface( T * p = 0 ) : _p( p ) {}
    ~XInterface() { if ( 0 != _p ) _p->Release(); }
    T * operator->() { return _p; }
    T * GetPointer() const { return _p; }
    IUnknown ** GetIUPointer() { return (IUnknown **) &_p; }
    T ** GetPPointer() { return &_p; }
    void ** GetQIPointer() { return (void **) &_p; }
    T * Acquire() { T * p = _p; _p = 0; return p; }

private:
    T * _p;
};

//+-------------------------------------------------------------------------
//
//  Function:   SetCommandProperties
//
//  Synopsis:   Sets the DBPROP_USEEXTENDEDDBTYPES property to TRUE, so
//              data is returned in PROPVARIANTs, as opposed to the
//              default, which is OLE automation VARIANTs.  PROPVARIANTS
//              allow a superset of VARIANT data types.  Use of these
//              types avoids costly coercions.
//
//              Also sets the DBPROP_USECONTENTINDEX property to TRUE, so
//              the index will always be used to resolve the query (as
//              opposed to enumerating all the files on the disk), even
//              if the index is out of date.
//
//              Both of these properties are unique to Index Server's OLE DB
//              implementation.
//
//  Arguments:  [pICommand] - The ICommand used to set the property
//
//  Returns:    HRESULT result of setting the properties
//
//--------------------------------------------------------------------------

HRESULT SetCommandProperties( ICommand * pICommand, WCHAR const * pwcQueryScope )
{
    static const DBID dbcolNull = { { 0,0,0, { 0,0,0,0,0,0,0,0 } },
                                    DBKIND_GUID_PROPID, 0 };
    static const GUID guidQueryExt = DBPROPSET_QUERYEXT;
    static const GUID guidFSCIFRMWRK = DBPROPSET_FSCIFRMWRK_EXT;

    DBPROP aProp[2];

    aProp[0].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
    aProp[0].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
    aProp[0].dwStatus = 0;
    aProp[0].colid = dbcolNull;
    aProp[0].vValue.vt = VT_BOOL;
    aProp[0].vValue.boolVal = VARIANT_TRUE;

    aProp[1] = aProp[0];
    aProp[1].dwPropertyID = DBPROP_USECONTENTINDEX;

    SAFEARRAYBOUND rgBound[1];
    rgBound[0].lLbound = 0;
    rgBound[0].cElements = 1;
    DBPROP aScopeProperties[2];
    LONG lFlags = QUERY_DEEP;
    long i = 0;

    SAFEARRAY * pScopes = SafeArrayCreate( VT_BSTR, 1, rgBound );
    if (!pScopes) return GetLastError();
    SafeArrayPutElement( pScopes, &i, SysAllocString( pwcQueryScope ) );

    SAFEARRAY * pFlags = SafeArrayCreate( VT_I4, 1, rgBound );
    if (!pFlags) return GetLastError();
    SafeArrayPutElement( pFlags, &i, &lFlags );

    memset( aScopeProperties, 0, sizeof aScopeProperties );
    aScopeProperties[0].dwPropertyID = DBPROP_CI_INCLUDE_SCOPES;
    aScopeProperties[0].vValue.vt = VT_BSTR | VT_ARRAY;
    aScopeProperties[0].vValue.parray = pScopes;

    aScopeProperties[1].dwPropertyID = DBPROP_CI_SCOPE_FLAGS;
    aScopeProperties[1].vValue.vt = VT_I4 | VT_ARRAY;
    aScopeProperties[1].vValue.parray = pFlags;

    DBPROPSET aPropSet[2];

    aPropSet[1].rgProperties = &aProp[0];
    aPropSet[1].cProperties = 2;
    aPropSet[1].guidPropertySet = guidQueryExt;

    aPropSet[0].rgProperties = &aScopeProperties[0];
    aPropSet[0].cProperties = 2;
    aPropSet[0].guidPropertySet = guidFSCIFRMWRK;

    XInterface<ICommandProperties> xICommandProperties;
    HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
                                            xICommandProperties.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    hr = xICommandProperties->SetProperties( 2,          // 2 properties set
                                             aPropSet ); // the properties

    SafeArrayDestroy( pScopes );
    SafeArrayDestroy( pFlags );

    return hr;

} //SetCommandProperties

//+-------------------------------------------------------------------------
//
//  Function:   DoQuery
//
//  Synopsis:   Creates and executes a query, then displays the results.
//
//  Arguments:  [pwcQueryCatalog]    - Catalog name over which query is run
//              [pwcQueryMachine]    - Machine name on which query is run
//              [pwcQueryScope]      - Directory scope to constrain the search
//              [pwcQueryRestrition] - The actual query string
//
//  Returns:    HRESULT result of the query
//
//--------------------------------------------------------------------------

HRESULT DoQuery(
    WCHAR const * pwcQueryCatalog,
    WCHAR const * pwcQueryMachine,
    WCHAR const * pwcQueryScope,
    WCHAR const * pwcQueryRestriction)
{
    // Create an ICommand object.  The default scope for the query is the
    // entire catalog.  CICreateCommand is a shortcut for making an
    // ICommand.  The ADVQUERY sample shows the OLE DB equivalent.

    XInterface<ICommand> xICommand;
    HRESULT hr = CICreateCommand( xICommand.GetIUPointer(), // result
                                  0,                  // controlling unknown
                                  IID_ICommand,       // IID requested
                                  pwcQueryCatalog,    // catalog name
                                  pwcQueryMachine );  // machine name

    if ( FAILED( hr ) )
        return hr;

    // Set required properties on the ICommand

    hr = SetCommandProperties( xICommand.GetPointer(), pwcQueryScope );
    if ( FAILED( hr ) )
        return hr;

    // Create an OLE DB query tree from a text restriction, column
    // set, and sort order.

    DBCOMMANDTREE * pTree;
    hr = CITextToFullTree( pwcQueryRestriction,      // the query itself
                           L"Size,Path",             // columns to return
                           L"Rank[d]",               // sort rank-descending
                           0,                        // reserved
                           &pTree,                   // resulting tree
                           0,                        // no custom properties
                           0,                        // no custom properties
                           GetSystemDefaultLCID() ); // default locale
    if ( FAILED( hr ) )
        return hr;

    // Set the tree in the ICommandTree

    XInterface<ICommandTree> xICommandTree;
    hr = xICommand->QueryInterface( IID_ICommandTree,
                                    xICommandTree.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    hr = xICommandTree->SetCommandTree( &pTree,
                                        DBCOMMANDREUSE_NONE,
                                        FALSE );
    if ( FAILED( hr ) )
        return hr;

    // Execute the query.  The query is complete when Execute() returns

    XInterface<IRowset> xIRowset;
    hr = xICommand->Execute( 0,            // no aggregating IUnknown
                             IID_IRowset,  // IID for interface to return
                             0,            // no DBPARAMs
                             0,            // no rows affected
                             xIRowset.GetIUPointer() ); // result
    if ( FAILED( hr ) )
        return hr;

    // Create an accessor, so data can be retrieved from the rowset

    XInterface<IAccessor> xIAccessor;
    hr = xIRowset->QueryInterface( IID_IAccessor,
                                   xIAccessor.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    // Column iOrdinals are parallel with those passed to CiTextToFullTree,
    // so MapColumnIDs isn't necessary.  These binding values for dwPart,
    // dwMemOwner, and wType are the most optimal bindings for Index Server.

    const ULONG cColumns = 2; // 2 for Size and Path
    DBBINDING aColumns[ cColumns ];
    memset( aColumns, 0, sizeof aColumns );

    aColumns[0].iOrdinal   = 1; // first column specified above (size)
    aColumns[0].obValue    = 0; // offset where value is written in GetData
    aColumns[0].dwPart     = DBPART_VALUE;  // retrieve value, not status
    aColumns[0].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // Index Server owned
    aColumns[0].wType      = DBTYPE_VARIANT | DBTYPE_BYREF; // VARIANT *

    aColumns[1] = aColumns[0];
    aColumns[1].iOrdinal   = 2; // second column specified above (path)
    aColumns[1].obValue    = sizeof (PROPVARIANT *); // offset for value

    HACCESSOR hAccessor;
    hr = xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, // rowdata accessor
                                     cColumns,           // # of columns
                                     aColumns,           // columns
                                     0,                  // ignored
                                     &hAccessor,         // result
                                     0 );                // no status
    if ( FAILED( hr ) )
        return hr;

    // Display the results of the query.  Print file size and file path.

    ULONG cRowsSoFar = 0;

    do
    {
        ULONG cRowsReturned = 0;
        const ULONG cRowsAtATime = 10;
        HROW aHRow[cRowsAtATime];
        HROW * pgrHRows = aHRow;
        hr = xIRowset->GetNextRows( 0,              // no chapter
                                    0,              // no rows to skip
                                    cRowsAtATime,   // # rows to get
                                    &cRowsReturned, // # rows returned
                                    &pgrHRows);     // resulting hrows

        if ( FAILED( hr ) )
            break;

        for ( ULONG iRow = 0; iRow < cRowsReturned; iRow++ )
        {
            PROPVARIANT * aData[cColumns];
            hr = xIRowset->GetData( aHRow[iRow],  // hrow being accessed
                                    hAccessor,    // accessor to use
                                    &aData );     // resulting data
            if ( FAILED( hr ) )
                break;

            if ( VT_I8 ==     aData[0]->vt &&
                 VT_LPWSTR == aData[1]->vt )
                catqReportResultsVariable(
                        TEXT("%s"),
                        aData[1]->pwszVal );
            else
                catqLogComment( TEXT("could not retrieve a file's values" ));
        }

        if ( 0 != cRowsReturned )
            xIRowset->ReleaseRows( cRowsReturned, // # of rows to release
                                   aHRow,         // rows to release
                                   0,             // no options
                                   0,             // no refcounts
                                   0 );           // no status

        if ( DB_S_ENDOFROWSET == hr )
        {
            hr = S_OK; // succeeded, return S_OK from DoQuery
            break;
        }

        if ( FAILED( hr ) )
            break;

        cRowsSoFar += cRowsReturned;
    } while ( TRUE );

    catqLogCommentVariable(
            TEXT("%d files matched the query '%s'"),
            cRowsSoFar,
            pwcQueryRestriction );

    xIAccessor->ReleaseAccessor( hAccessor, 0 );

    return hr;
} //DoQuery


MAIN.CXX

/*++

Copyright 1992 - 1998 Microsoft Corporation, All rights reserved.

    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.

Module Name:

    main.cxx

Abstract:

    WinMain() and other functions required to interact with Windows.

Revision History:

    Steve Firebaugh     9-97          Created from previous samples

--*/

#include <windows.h>
#include <commctrl.h>
#include <ntquery.h>
#include "catq.h"

//
// Declare a global HWND for this module.  It will contain the window
//  handle for the main dialog.  Make it global so that the comment
//  and result reporting functions in this module do not require HWND
//  parameters (since they are called from other modules that don't
//  need to know anything about HWND).
//

HWND hwndGlobalDlg = NULL;

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
/*++

  required for all WIN32 applications
  input parameters:  c.f. generic sample

--*/
{
    int iRet;

    UNREFERENCED_PARAMETER( hPrevInstance );
    UNREFERENCED_PARAMETER( lpCmdLine );
    UNREFERENCED_PARAMETER( nCmdShow);

    //
    // Detect platform and exit gracefully if app will not work correctly.
    //  notice that this may need to be updated over time if/when Index Server
    //  is expanded to run on additional platforms.
    //
    // Here we check first for Windows NT, and then for version 5 or greater.
    //

    {
      OSVERSIONINFO osvi;
      osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
      GetVersionEx (&osvi);

      if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
        MessageBox (NULL,
                    TEXT ("Application requires Windows NT"),
                    SZ_APP_TITLE,
                    MB_OK | MB_ICONSTOP);
        return 0;
      }

      if (osvi.dwMajorVersion < 5) {
        MessageBox (NULL,
                    TEXT ("Application requires at least Windows NT version 5"),
                    SZ_APP_TITLE,
                    MB_OK | MB_ICONSTOP);
        return 0;
      }
    }  // end version checking

    //
    // Just put up a dialog box that does all of the work.  Interesting code
    //  is in the MainDlgProc() below.
    //
    // Here we use DialogBoxParam() rather than just DialogBox(), because we
    //  want to pass the hInstance to the WM_INITDIALOG message.
    //

    iRet = DialogBoxParam (hInstance,
                           TEXT("mainDlg"),
                           NULL,
                           (DLGPROC)MainDlgProc,
                           (LONG) hInstance);
    return iRet;
}

LRESULT CALLBACK MainDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
/*++

  standard WIN32 dialog procedure for the main window

  input parameters:  c.f. generic sample

--*/
{

  switch (message) {

    case WM_INITDIALOG: {

      //
      // Store the hwnd in the global variable for use later.
      //

      hwndGlobalDlg = hwnd;

      //
      // The dialog template in the .rc file specifies the location and size
      //  of most of the "child controls."  However, we also want a status bar
      //  on the bottom of the main window.  Create the status bar here, but
      //  before doing that, call the Windows function to initialize the
      //  common controls.  StatusBars and ProgressBars are "common controls"
      //  and will not work until InitCommonControls is called.
      //

      InitCommonControls ();
      catqCreateStatusBar (hwnd);

      //
      // Set the font for the text display windows to be a fixed width font
      //

      SendDlgItemMessage (hwnd,
                          DID_OUTRESULTS,
                          WM_SETFONT,
                          (WPARAM) GetStockObject (ANSI_FIXED_FONT),
                          (LPARAM) 0 );

      //
      // Set initial values into the input controls.  For the machine this
      //  is just "."  For the catalog, we call the appropriate query API.
      //  For the directory scope, we try to locate the SDK.  For the search
      //  text we start with some friendly initial string.
      //

      { WCHAR szbuffer[MAX_PATH];

      SetWindowText (GetDlgItem (hwnd, DID_MACHINE), TEXT("."));

      catqInitializeSDKPath (GetDlgItem (hwnd, DID_SCOPE), szbuffer);

      catqInitializeCatalogs (GetDlgItem (hwnd, DID_CATALOG), szbuffer);

      SetWindowText (GetDlgItem (hwnd, DID_CONTAIN), TEXT("<search text here>"));
      }

      //
      // Select all of the text in the search-string edit control so that
      //  anything the user types will replace what is there.
      //

      SendMessage (GetDlgItem (hwnd, DID_CONTAIN), EM_SETSEL, 0, MAX_PATH);
      SetFocus ( GetDlgItem (hwnd, DID_CONTAIN));

      //
      // Since main window is a dialog it does not get an app defined icon
      //  by default... so, load one out of the built in resources, and
      //  associate it with this window.  Notice hInstance passed in as
      //  lParam ala DialogBoxParam().
      //

      SendMessage (hwnd, WM_SETICON, ICON_BIG, (LPARAM) LoadIcon ((HINSTANCE)lParam, TEXT("catqIcon")));

      //
      // Start a window timer to post us a messsage every one second.
      //  Use this later to keep fields in the status bar up-to-date.
      //

      SetTimer (hwnd, TID_PROGRESS, 1000, NULL);

      //
      // Content indexing is only available if the correct service is running.
      //  Ensure that it is running, or start it if it is not.
      //

      catqStartCIService ( hwnd );

      return FALSE;
    } break;  // WM_INITDIALOG

    case WM_TIMER:

      //
      // Timer messages with ID of TID_PROGRESS indicate that we should
      //  query the progress of the indexing effort.
      //

      if ((int) wParam == TID_PROGRESS) {
        LONG lTotal, lFinished;
        int iProgress;

        iProgress = catqPDHQueryProgress (&lFinished, &lTotal);

        catqUpdateProgress (GetDlgItem (hwnd, DID_STATUSBAR),
                            iProgress,
                            lFinished, lTotal);

      }
    break;

    case WM_COMMAND:

    //
    // Handle the events caused by the buttons on the main page
    //

      switch (LOWORD(wParam)) {

        //
        // When one of the files in the listbox is double clicked, launch
        //  the file that has been selected.  If ShellExecute() fails, try
        //  to load the file into notepad.  This will work for all "text files"
        //  but will look ugly for "binary files" that are indexed.
        //

        case DID_OUTRESULTS      :
          if (HIWORD (wParam) == LBN_DBLCLK) {
            HWND hwndLB;
            LRESULT lRet;
            TCHAR pszBuffer [ MAX_PATH ] ;

            hwndLB = GetDlgItem (hwnd, DID_OUTRESULTS);
            lRet = SendMessage (hwndLB, LB_GETCURSEL, 0,0);
            lRet = SendMessage (hwndLB, LB_GETTEXT, (WPARAM)lRet, (LPARAM) pszBuffer);

            lRet = (LRESULT) ShellExecute (NULL,
                                           TEXT("open"),
                                           pszBuffer,
                                           NULL,
                                           NULL,
                                           SW_SHOW);

            if (lRet <= 32 ) { // magic number, see docs
              catqLogCommentVariable (TEXT ("ShellExecute (%s) failed (%d).  Loading into notepad." ),
                                      pszBuffer, lRet);
              lRet = (LRESULT) ShellExecute (NULL,
                                             TEXT("open"),
                                             TEXT("notepad"),
                                             pszBuffer,
                                             NULL,
                                             SW_SHOW);
            }
          }
        break;

        //
        // When the "find now" button is hit, take appropriate action.
        //

        case DID_FINDNOW      : {
           TCHAR szCatalog[MAX_PATH]     = TEXT("");
           TCHAR szMachine[MAX_PATH]     = TEXT("");
           TCHAR szScope[MAX_PATH]     = TEXT("");
           TCHAR szRestriction[MAX_PATH] = TEXT("");

           HRESULT hr;

           //
           // Clear out the old results list from the listbox,
           //

           SendMessage( GetDlgItem (hwnd, DID_OUTRESULTS),
                        LB_RESETCONTENT, 0, 0);

           //
           // Query the edit controls for the user specified parameters
           //

           GetWindowText (GetDlgItem (hwnd, DID_CATALOG),
                         szCatalog,
                         MAX_PATH);

           GetWindowText (GetDlgItem (hwnd, DID_MACHINE),
                         szMachine,
                         MAX_PATH);

           GetWindowText (GetDlgItem (hwnd, DID_SCOPE),
                         szScope,
                         MAX_PATH);

           GetWindowText (GetDlgItem (hwnd, DID_CONTAIN),
                         szRestriction,
                         MAX_PATH);

           //
           // And finally, with these parameters, call our own function
           //  to perform the query (and display the results).
           //

           hr = DoQuery( szCatalog,
                         szMachine,
                         szScope,
                         szRestriction);

           if ( FAILED( hr ) )
           {
               catqReportError ( hr );
               return 0;
           }

        } break;

      } // end switch (LOWORD())

    break; // WM_COMMAND

    case WM_SIZE: {

      //
      // Pass the WM_SIZE along to the status bar window procedure (provided
      //  by windows, not part of this application code)...  it will resize
      //  itself to fit in the bottom of the window... once this is done,
      //  re-space the different sections of  the status bar with our function.
      //

      SendMessage (GetDlgItem (hwnd, DID_STATUSBAR),
                   WM_SIZE, wParam, lParam);

      catqPartitionStatusBar (GetDlgItem (hwnd, DID_STATUSBAR));

      //
      // Adjust the large list box to fill the remainder of the window.
      //

#define NICEBORDER 5
#define BELOWINPUTCONTROLS 100

      int x, y, cx, cy;
      int nWidth, nHeight;
      RECT rect;

      nWidth = LOWORD (lParam);
      nHeight = HIWORD (lParam);

      SendMessage (GetDlgItem (hwnd, DID_STATUSBAR), SB_GETRECT, 0, (LPARAM) &rect);

      x = NICEBORDER;
      cx = nWidth - (2* NICEBORDER);

      nHeight -= BELOWINPUTCONTROLS;
      nHeight -= rect.bottom;
      nHeight -= 2* NICEBORDER;

      cy = (nHeight > 0) ? nHeight : 0;

      y = BELOWINPUTCONTROLS + NICEBORDER;
      SetWindowPos ( GetDlgItem (hwnd, DID_OUTRESULTS),
                    NULL,
                    x,
                    y,
                    cx,
                    cy,
                    SWP_NOZORDER);
      return 0;

    } break; // WM_SIZE

    case WMAPP_ADDRESULT:

      //
      // Application defined message.
      //
      // Append a new file name to the bottom of the results listbox.
      //
      // wParam is ignored
      // lParam is pointer to the string to add to the listbox.
      //

      SendMessage (GetDlgItem (hwnd, DID_OUTRESULTS),
                     LB_ADDSTRING, 0, lParam);

    break;

    case WM_SYSCOMMAND:

    //
    //  ignore all syscommand messages, except for SC_CLOSE.  On this one,
    //   log one final comment for the debugger output and call EndDialog().
    //

      if (wParam == SC_CLOSE) {

        catqLogComment (TEXT("bye."));

        EndDialog (hwnd, TRUE);
        return TRUE;
      } else
        return FALSE;
    break;

    default: return FALSE;
  } // end switch(message)
  return TRUE;
}

int catqInitializeCatalogs ( HWND hwndComboBox, LPTSTR pszScope )
/*++

    Fill the combobox indicated by the first parameter with catalogs
     as returned by the WIN32 API LocateCatalogs.  The second parameter
     is the "scope" that we are checking catalogs for.  We expect this
     to be the directory location of the SDK.

--*/
{
#define PSZ_SYSTEMCATALOG TEXT("System")

    HRESULT hr = S_OK;
    ULONG iBmk;
    ULONG iCountSuccess = 0;

    //
    // Loop through calls to LocateCatalogs until the return value is not
    //  S_OK.  For any catalog that we get, verify that the strings were
    //  big enough, and add the catalog name to the combobox.
    //
    // This code is taken from the LocateCatalogs documentation.
    //

    for ( iBmk = 0; S_OK == hr; iBmk++ ) {

      TCHAR atcMachine[ MAX_COMPUTERNAME_LENGTH + 1 ];
      const ULONG ctcMachineBuffer = sizeof atcMachine / sizeof TCHAR;
      ULONG ctcMachine = ctcMachineBuffer;
      TCHAR atcCatalog[ MAX_PATH + 1 ];
      const ULONG ctcCatalogBuffer = sizeof atcCatalog / sizeof TCHAR;
      ULONG ctcCatalog = ctcCatalogBuffer;

      hr = LocateCatalogs( pszScope,
                           iBmk,
                           atcMachine,
                           &ctcMachine,
                           atcCatalog,
                           &ctcCatalog );

      if ( ( hr == S_OK ) &&
           ( ctcMachine <= ctcMachineBuffer ) &&
           ( ctcCatalog <= ctcCatalogBuffer ) )    {
          iCountSuccess++;
          SendMessage ( hwndComboBox,
                        CB_ADDSTRING,
                        0,
                        (LPARAM) atcCatalog);

      }
    }

    //
    // In some cases (the scope parameter is "\" for example) LocateCatalogs
    //  will not return any useful catalogs.  We detect this case by comparing
    //  iCountSuccess with 0.  In this case, add the system catalog anyway.
    //
    // The failure of LocateCatalogs("\" ... ) may change in a later release.
    //

    if (iCountSuccess == 0)
        SendMessage ( hwndComboBox,
                      CB_ADDSTRING,
                      0,
                      (LPARAM) PSZ_SYSTEMCATALOG);

    //
    // Select the initial value to be the system catalog.
    //

    SendMessage ( hwndComboBox,
                  CB_SELECTSTRING,
                  0,
                  (LPARAM) PSZ_SYSTEMCATALOG);
    return TRUE;
}

int catqInitializeSDKPath (HWND hwndEdit, LPTSTR pszPath )
/*++

    This routine is called one time at window creation time.  It initializes
     the value in the passed in edit control to be equal to the directory
     location of the SDK.  Separate this out as a function only because
     the logic is rather involved and would clutter up the main window proc.

    hwndEdit -- the edit control that this function fills.
    pszPath -- buffer to return the path in to the calling routine.

--*/
{
    DWORD dwRet;
    TCHAR pszSDKEnvVar[MAX_PATH];

    //
    // Query the environment for the location where the SDK is installed.
    //

    dwRet = GetEnvironmentVariable (TEXT("MSSDK"), pszSDKEnvVar, MAX_PATH);

    if (dwRet == 0) {
      catqLogComment (TEXT("Can't locate SDK.  Defaulting to \\."));
      lstrcpy (pszSDKEnvVar, TEXT("\\"));
    }

    SetWindowText (hwndEdit, pszSDKEnvVar);
    lstrcpy (pszPath, pszSDKEnvVar);

  return TRUE;
}

int catqReportError ( int iError )
/*++

    Call WIN32 API GetLastError, format it for human consumption, and pass
     it along to our application output routine.

--*/
{
    DWORD  dwRet;
    TCHAR  szBuffer[MAX_PATH], szCommentStr[MAX_PATH];

    dwRet = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM,
                           NULL,
                           iError,
                           LANG_NEUTRAL,
                           szBuffer,
                           MAX_PATH,
                           NULL);

    //
    // If formatmessage succeeds, glue the numeric value on the end just
    //  for fun.  If it fails, report that along with the numeric value.
    //

    if (dwRet)
        wsprintf (szCommentStr,
                  TEXT("%s (0x%x)"),
                  szBuffer,
                  iError);

    else
        wsprintf (szCommentStr,
                  TEXT("FormatMessage failed on 0x%x"),
                  iError);

    catqLogComment ( szCommentStr );

    return TRUE;
}

void
_cdecl
catqReportResultsVariable(
    PTCH Format,
    ...
    )
/*++

    Format the variable number of arguments passed in based on the first
     argument (the format string), and display the message to the user.

    Uses GLOBAL hwndGlobalDlg

--*/
{
    static TCHAR buffer[MAX_PATH];

    va_list marker;
    va_start (marker,Format);
    wvsprintf (buffer,Format, marker);

    if (IsWindow(hwndGlobalDlg))
        SendMessage (hwndGlobalDlg,
                     WMAPP_ADDRESULT,
                     0,
                     (LPARAM) buffer);
    else
        MessageBox (NULL,
                    buffer,
                    SZ_APP_TITLE,
                    MB_OK );
    return;
}

void
_cdecl
catqLogCommentVariable(
    PTCH Format,
    ...
    )
/*++

    Format the variable number of arguments passed in based on the first
     argument (the format string), and call the routine to log the comment.

--*/
{
    static TCHAR buffer[MAX_PATH];

    va_list marker;
    va_start (marker,Format);
    wvsprintf (buffer,Format, marker);

    catqLogComment ( buffer );
    return;
}

int catqLogComment ( PTCHAR ptString )
/*++

    If the main window has been created already (i.e. hwnd is valid), then
     display the message (second input parameter) in the statusbar.
     If the main window does not exist, put up a message box.

    Uses GLOBAL hwndGlobalDlg

--*/
{
    if (IsWindow(hwndGlobalDlg))
        catqUpdateStatusComment(GetDlgItem(hwndGlobalDlg, DID_STATUSBAR),
                                ptString);
    else
        MessageBox (NULL,
                    ptString,
                    SZ_APP_TITLE,
                    MB_OK | MB_ICONSTOP);

    //
    // Also write to the debug output stream for the sake of anyone running
    //  under a debugger.  Or, notice that DBMON.EXE from the SDK will allow
    //  one to monitor output sent to OutputDebugString ().
    //

    OutputDebugString ( TEXT("catq: "));
    OutputDebugString ( ptString );
    OutputDebugString ( TEXT("\n"));

    return TRUE;
}


PROGRESS.CXX

/*++

Copyright 1992 - 1998 Microsoft Corporation, All rights reserved.

    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.

Module Name:

    pdhprog.cxx

Abstract:

    functions to query the performance data helper (PDH) DLL in order to
     get the current status of the content indexing effort.

Revision History:

    Steve Firebaugh     9-97          Created from previous samples

--*/

#include <windows.h>
#include <pdh.h>
#include "catq.h"

//
// Here are the names of two of the performance counters that the CISVC
//  service logs to the performance registry.  See Content Indexing doc.
//  (or perfmon) for the others.
//

#define PSZ_COUNTER_TOBEFILTERED TEXT ("\\\\.\\Content Index(System)\\Files to be filtered")
#define PSZ_COUNTER_TOTALNUMDOCS TEXT ("\\\\.\\Content Index(System)\\Total # documents")

int catqPDHQueryProgress ( LPLONG lpToBeFiltered, LPLONG lpTotal)
/*++

    Query the current status of the content indexing service by
     querying performance data via the "performance data helper" DLL
     (first introduced in NT4 and now a part of the operating system).
     The return value is the approximate percentage complete, the two
     long parameters are the numerator and denominator of that ratio.

    Return value in [0,100] if success, else CATQ_PROGRESS_ERROR

--*/
{
    int        iReturn = CATQ_PROGRESS_ERROR;
    HQUERY     hQuery;
    HCOUNTER   arCounter[2] = {0,0};
    PDH_STATUS pdhStatus;
    PDH_FMT_COUNTERVALUE   arFmtValue[2];
    DWORD           ctrType;

    //
    // First step in using PDH is to open a "query"...
    //

    pdhStatus = PdhOpenQuery (0, 0, &hQuery);
    if (pdhStatus != ERROR_SUCCESS) {
      catqLogComment (TEXT("PdhOpenQuery failed"));
      goto ClosePDHAndReturn;
    }

    //
    // With the query handle, we now add two "counters"...
    //

    pdhStatus = PdhAddCounter (hQuery,
                               PSZ_COUNTER_TOBEFILTERED,
                               0,
                               &arCounter[0]);
    if (pdhStatus != ERROR_SUCCESS) {
      catqLogComment (TEXT("PdhAddCounter failed"));
      goto ClosePDHAndReturn;
    }

    pdhStatus = PdhAddCounter (hQuery,
                               PSZ_COUNTER_TOTALNUMDOCS,
                               0,
                               &arCounter[1]);
    if (pdhStatus != ERROR_SUCCESS) {
      catqLogComment (TEXT("PdhAddCounter failed"));
      goto ClosePDHAndReturn;
    }

    //
    // Now that everything is setup, actually do the query to bind
    //  values to the counters...
    //

    pdhStatus = PdhCollectQueryData (hQuery);
    if (pdhStatus != ERROR_SUCCESS) {
      catqLogComment (TEXT("PdhCollectQueryData failed"));
      goto ClosePDHAndReturn;
    }

    //
    // And finally, get the formatted values...
    //

    pdhStatus = PdhGetFormattedCounterValue (arCounter[0],
                                             PDH_FMT_LONG,
                                             &ctrType,
                                             &arFmtValue[0]);

    if (pdhStatus != ERROR_SUCCESS) {
      catqLogComment (TEXT("PdhGetFormattedCounterValue failed"));
      goto ClosePDHAndReturn;
    }

    *lpToBeFiltered = arFmtValue[0].longValue;

    pdhStatus = PdhGetFormattedCounterValue (arCounter[1],
                                             PDH_FMT_LONG,
                                             &ctrType,
                                             &arFmtValue[1]);

    if (pdhStatus != ERROR_SUCCESS) {
      catqLogComment (TEXT("PdhGetFormattedCounterValue failed"));
      goto ClosePDHAndReturn;
    }

    *lpTotal = arFmtValue[1].longValue;

    //
    // We succeeded in getting formatted performance data.
    //  Compute the return value, clip it to the spec.ed bounds...
    //

    if (*lpTotal != 0)
      iReturn = (int) (((*lpTotal - *lpToBeFiltered) *100 ) / *lpTotal);

    if (iReturn <0 )   iReturn = 0;
    if (iReturn >100 ) iReturn = 100;

    //
    // Close the handle and return.
    //

ClosePDHAndReturn:

    PdhRemoveCounter( arCounter[0] );
    PdhRemoveCounter( arCounter[1] );

    pdhStatus = PdhCloseQuery (hQuery);
    return iReturn;

}


CATQ.H

//
// catq.h - common defines and function prototypes for the catq sample.
//

//
// function prototypes for the window procedures.
//

LRESULT CALLBACK MainDlgProc(HWND, UINT, WPARAM, LPARAM);

//
// forward declare other helper functions
//

int catqReportError ( int );
int catqLogComment ( PTCHAR );
void _cdecl catqLogCommentVariable ( PTCH, ... );
void _cdecl catqReportResultsVariable( PTCH, ... );
int catqInitializeSDKPath ( HWND, LPTSTR );
int catqInitializeCatalogs ( HWND, LPTSTR );

int catqStartCIService ( HWND );

int catqCreateStatusBar ( HWND );
int catqPartitionStatusBar ( HWND );
int catqUpdateProgress( HWND, int, LONG, LONG );
int catqUpdateStatusComment(HWND hwndStatusBar, LPTSTR lpszStatusString);

int catqPDHQueryProgress ( LPLONG, LPLONG  );

HRESULT DoQuery(
    WCHAR const * pwcQueryCatalog,
    WCHAR const * pwcQueryMachine,
    WCHAR const * pwcQueryScope,
    WCHAR const * pwcQueryRestriction);

//
// Possible return value from catqPDHQueryProgress()
//

#define CATQ_PROGRESS_ERROR -1

//
// IDs for the controls on the main dialog
//

#define DID_MACHINE      1001
#define DID_CATALOG      1002
#define DID_CONTAIN      1003
#define DID_SCOPE        1004
#define DID_OUTRESULTS   1020
#define DID_FINDNOW      1030
#define DID_STATUSBAR    1040
#define DID_PROGRESSBAR  1041

//
// Timer ID
//

#define TID_PROGRESS     2057

//
// Application defined window messages for "message based" communication
//  to/from the main window.
//

#define WMAPP_ADDRESULT         WM_APP + 10

#define SZ_APP_TITLE TEXT("Content Index Query Tool")

//
// The following string is the formal name of the content indexing service.
//  It is defined here for use in starting the service if necessary.
//

#define SZ_CISERVICE_NAME  TEXT("CISVC")


SERVICE.CXX

/*++

Copyright 1992 - 1998 Microsoft Corporation, All rights reserved.

    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.

Module Name:

    service.cxx

Abstract:

    Start the content indexing service (CISVC)

Revision History:

    Steve Firebaugh     9-97          Created from previous samples

--*/

#include <windows.h>
#include "catq.h"

int catqStartCIService ( HWND hwnd )
/*++

    Query the state of the content indexing service.  If it is not
     running, then attempt to start it up.  Access to the service
     is via the Windows NT "service control manager."

--*/
{

    SC_HANDLE   schCIService = NULL;
    SC_HANDLE   schSCManager = NULL;
    int iFunctionRet = TRUE;     // store function return value, assume success.
    int iLocalRet;
    SERVICE_STATUS ssStatus;
    TCHAR szBuffer[MAX_PATH];

    //
    // Service control manager works by first "opening" it...
    //

    schSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS); // access required

    if (!schSCManager) {
        catqReportError ( GetLastError() );
        iFunctionRet = FALSE;
        goto CloseSCAndReturn;
    }

    //
    // With the service control manager handle, now try and open the
    //  specific service that we are interested in.
    //

    schCIService = OpenService (schSCManager,
                              SZ_CISERVICE_NAME,  // known name from .h file
                              SERVICE_ALL_ACCESS);

    if (!schCIService) {
        catqReportError ( GetLastError() );
        iFunctionRet = FALSE;
        goto CloseSCAndReturn;
    }

    //
    // With the CI service handle, now query its status
    //

    iLocalRet = QueryServiceStatus (schCIService, &ssStatus);

    if (!iLocalRet) {
        catqReportError ( GetLastError() );
        iFunctionRet = FALSE;
        goto CloseCIAndReturn;
    }

    //
    // Report to the user the status of the service
    //

    wsprintf (szBuffer, TEXT("The service %s "), SZ_CISERVICE_NAME);
    switch ( ssStatus.dwCurrentState ) {
        case SERVICE_RUNNING: lstrcat (szBuffer, TEXT("is running.")); break;
        case SERVICE_PAUSED:  lstrcat (szBuffer, TEXT("was paused."));  break;
        case SERVICE_STOPPED: lstrcat (szBuffer, TEXT("was stopped.")); break;
        default:              lstrcat (szBuffer, TEXT("is pending.")); break;
    } // end switch

    //
    // If it is stopped or paused, then try to start it.
    //

    if ((ssStatus.dwCurrentState == SERVICE_STOPPED) ||
        (ssStatus.dwCurrentState == SERVICE_PAUSED)) {
            lstrcat (szBuffer, TEXT(" ... Attempting to start it."));

            iLocalRet = StartService (schCIService, 0, NULL);
            if (!iLocalRet) {
                catqReportError ( GetLastError() );
                iFunctionRet = FALSE;
                goto CloseCIAndReturn;
            }
    }

    //
    // Exit this function here at the end to ensure that the SC Manager
    //  handle is freed correctly.
    //

CloseCIAndReturn:

    catqLogComment (szBuffer);

    CloseServiceHandle(schCIService);

CloseSCAndReturn:

    CloseServiceHandle(schSCManager);

    return iFunctionRet;
}


STATBAR.CXX

/*++

Copyright 1992 - 1998 Microsoft Corporation, All rights reserved.

    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.

Module Name:

    statbar.cxx

Abstract:

    Functions required to support the status bar on the bottom of the window

Revision History:

    Steve Firebaugh     9-97          Created from previous samples

--*/

#include <windows.h>            // required for all Windows applications
#include <commctrl.h>           // prototypes and defs for common controls
#include "catq.h"

int catqCreateStatusBar(HWND hwnd)
/*++

    When the main window is created, call this function to create a status
     bar on the bottom of it.  Here, we also create a progress bar as a
     child of the status bar, and then size it in the partition routine.

    Notice that the Win32 API InitCommonControls() must be called before
     this function in order for CreateWindowEx() to succeed with the
     window classes that we are using here.

--*/
{
    HWND hwndStatusBar, hwndProgressBar;

    hwndStatusBar = CreateWindowEx ( NULL,
                                     STATUSCLASSNAME,
                                     TEXT("Comments"),
                                     WS_CHILD | WS_VISIBLE | WS_BORDER,
                                     0,0,0,0,
                                     hwnd,
                                     (HMENU)DID_STATUSBAR,
                                     NULL,
                                     NULL);

    hwndProgressBar = CreateWindowEx ( NULL,
                                       PROGRESS_CLASS,
                                       TEXT(""),
                                       WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
                                       0,0,20, 30,
                                       hwndStatusBar,
                                       (HMENU)DID_PROGRESSBAR,
                                       NULL,
                                       NULL);

    catqPartitionStatusBar (hwndStatusBar);
    catqUpdateProgress (hwndStatusBar, CATQ_PROGRESS_ERROR, 0, 0) ;

    return TRUE;
}

int catqPartitionStatusBar(HWND hwndStatusBar)
/*++

    Divide the status bar up into separate "partitions."

    hwndStatusBar - the window handle to the status bar

--*/
{
    int   ptArray[3];
    RECT  rect;
    HDC   hDC;
    int   iQuantum;

    //
    // Fill in the ptArray values with the spacing of the different partitions.
    //

    hDC = GetDC(hwndStatusBar);
    GetClientRect(hwndStatusBar, &rect);
    ReleaseDC(hwndStatusBar, hDC);

    iQuantum = rect.right;
    iQuantum -= rect.bottom;
    iQuantum /= 4;

    ptArray[0] = iQuantum *2;
    ptArray[1] = ptArray[0] + iQuantum;
    ptArray[2] = ptArray[1] + iQuantum;

    //
    // Send this array to the status bar window...
    //

    SendMessage(hwndStatusBar,
                SB_SETPARTS,
                sizeof(ptArray)/sizeof(ptArray[0]),
                (LPARAM)(LPINT)ptArray);

    //
    // Now that the status bar is partitioned correctly, position the
    //  progress bar in the right most partition.
    //

    SetWindowPos ( GetDlgItem (hwndStatusBar, DID_PROGRESSBAR),
                    NULL,
                    ptArray[1]+2,
                    2,
                    iQuantum,
                    rect.bottom-2,
                    SWP_NOZORDER);

    return TRUE;

}

int catqUpdateProgress( HWND hwndStatusBar,
                        int iProgress,
                        LONG lToBeFiltered,
                        LONG lTotal )
/*++

    hwndStatusBar - the window handle to the status bar

--*/
{
#define SBTID_PROGRESS 1

    TCHAR lpszProgressString[MAX_PATH];

    if (iProgress == CATQ_PROGRESS_ERROR)
      wsprintf (lpszProgressString, TEXT("no CISVC status available"));
    else
      wsprintf (lpszProgressString,
                TEXT("To filter: %d / %d.  Done: %d %%"),
                lToBeFiltered, lTotal,
                iProgress);

    SendMessage(hwndStatusBar,
                SB_SETTEXT,
                SBTID_PROGRESS,
                (LPARAM)lpszProgressString);

    SendMessage (GetDlgItem (hwndStatusBar, DID_PROGRESSBAR), PBM_SETPOS, iProgress, 0);

    return TRUE;
}

int catqUpdateStatusComment(HWND hwndStatusBar, LPTSTR lpszStatusString)
{
/*++

    Set the second parameter string into the text of the status bar in
     the first partition.  This is where we report "comments" to the user.

--*/

    SendMessage(hwndStatusBar,
                SB_SETTEXT,
                0,
                (LPARAM)lpszStatusString);

    return TRUE;
}


README.TXT

The IFilter interface was designed primarily to provide a uniform mechanism to extract character streams from formatted data. The goal was to provide ISVs with an interface that extracts text as the initial step in content indexing document data. IFilter can be implemented over any document format and the ISV can choose any API or interface to read the data format.  For example, a content filter can be written that reads data using the Win32 file APIs or uses the OLE storage interfaces.

Any software author who stores textual data should consider implementing a content filter for the document format to allow content indexing systems to extract text.

The sample filter in this directory will extract text and properties from HTML pages.  In addition to raw content, headings (level 1 to 6), title and anchors are emitted as pseudo-properties.  Title is also published as a full property available via IFilter::GetValue.

To compile and link the filter, i.e., htmlfilt.dll:

1) Make sure the Lib and Include environment variables for libraries and include files are set correctly.

2) Make sure the CPU environment variable is set correctly.

3) Enter the command 'nmake'.


ASSERT.CXX

//+---------------------------------------------------------------------------
//  Copyright 1996 - 1998 Microsoft Corporation.
//
//  File:       assert.cxx
//
//  Contents:   Debug assert function
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

//+---------------------------------------------------------------------------
//
//  Function:   Win4Assert
//
//  Synopsis:   Display assertion information
//
//----------------------------------------------------------------------------

void Win4AssertEx( char const * szFile,
                   int iLine,
                   char const * szMessage)
{
     char acsString[200];

     sprintf( acsString, "%s, File: %s Line: %u\n", szMessage, szFile, iLine );
     OutputDebugStringA( acsString );

     DebugBreak();
}


BAG.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       bag.cxx
//
//  Contents:   Bag for Html parsing elements
//
//  Classes:    CHtmlElementBag
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <bag.hxx>
#include <htmlelem.hxx>

//+-------------------------------------------------------------------------
//
//  Class:      CHtmlElemBagEntry::CHtmlElemBagEntry
//
//  Purpose:    Constructor
//
//--------------------------------------------------------------------------

CHtmlElemBagEntry::CHtmlElemBagEntry()
    : _pHtmlElement(0),
      _eTokType(GenericToken)
{
}

//+-------------------------------------------------------------------------
//
//  Class:      CHtmlElemBagEntry::~CHtmlElemBagEntry
//
//  Purpose:    Destructor
//
//--------------------------------------------------------------------------

CHtmlElemBagEntry::~CHtmlElemBagEntry()
{
    delete _pHtmlElement;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlElementBag::CHtmlElementBag
//
//  Synopsis:   Constructor
//
//  Arguments:  [cElems]  -- Count of Html elements in bag
//
//--------------------------------------------------------------------------

CHtmlElementBag::CHtmlElementBag( unsigned cElems )
    : _uMaxSize(cElems),
      _uCurSize(0)
{
    _aBagEntry = newk(mtNewX, NULL) CHtmlElemBagEntry[cElems];
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlElementBag::~CHtmlElementBag
//
//  Synopsis:   Destructor
//
//--------------------------------------------------------------------------

CHtmlElementBag::~CHtmlElementBag()
{
    delete[] _aBagEntry;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlElementBag::AddElement
//
//  Synopsis:   Add a new mapping the bag
//
//  Arguments:  [eHtmlToken]  -- token type
//              [pHtmlElem] -- Html element corresponding to eHtmlToken
//
//--------------------------------------------------------------------------

void CHtmlElementBag::AddElement( HtmlTokenType eTokType,
                                  CHtmlElement *pHtmlElement )
{
    Win4Assert( _uCurSize < _uMaxSize );

    _aBagEntry[_uCurSize].SetTokenType( eTokType );
    _aBagEntry[_uCurSize].SetHtmlElement( pHtmlElement );
    _uCurSize++;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlElementBag::QueryElement
//
//  Synopsis:   Retrieve the Html element corresponding to given token type
//
//  Arguments:  [eHtmlToken]  -- token type
//
//--------------------------------------------------------------------------

CHtmlElement *CHtmlElementBag::QueryElement( HtmlTokenType eTokType )
{
    for (unsigned i=0; i<_uCurSize; i++)
    {
        if ( _aBagEntry[i].GetTokenType() == eTokType )
            return _aBagEntry[i].GetHtmlElement();
    }

    htmlDebugOut(( DEB_ERROR,
                 "Unknown Html token type 0x%x in CHtmlElementBag::QueryElement\n",
                 eTokType ));
    Win4Assert( !"Cannot map Html token type" );
    return 0;
}


CHARHASH.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       charhash.cxx
//
//  Contents:   Hash table that maps special characters to Unicode
//
//  Classes:    CSpecialCharHashTable
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <charhash.hxx>
#include <htmlfilt.hxx>

//+-------------------------------------------------------------------------
//
//  Method:     CSpecialCharHashEntry::CSpecialCharHashEntry
//
//  Synopsis:   Constructor
//
//--------------------------------------------------------------------------

CSpecialCharHashEntry::CSpecialCharHashEntry( WCHAR *pwszName, WCHAR wch )
    : _wch(wch),
      _pHashEntryNext(0)
{
    Win4Assert( wcslen(pwszName) + 1 < MAX_TAG_LENGTH );
    wcscpy( _wszName, pwszName );
}

//+-------------------------------------------------------------------------
//
//  Method:     CSpecialCharHashTable::~CSpecialCharHashTable
//
//  Synopsis:   Destructor
//
//--------------------------------------------------------------------------

CSpecialCharHashTable::~CSpecialCharHashTable()
{
    for ( unsigned i=0; i<SPECIAL_CHAR_HASH_TABLE_SIZE; i++)
    {
        CSpecialCharHashEntry *pHashEntry = _aHashTable[i];
        while ( pHashEntry != 0 )
        {
            CSpecialCharHashEntry *pHashEntryNext = pHashEntry->GetNextHashEntry();
            delete pHashEntry;
            pHashEntry = pHashEntryNext;
        }
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CSpecialCharHashTable::Add
//
//  Synopsis:   Add a special char -> Unicode char mapping
//
//  Arguments:  [pwszName] -- the special char
//              [wch]      -- the corresponding Unicode char
//
//--------------------------------------------------------------------------

void CSpecialCharHashTable::Add( WCHAR *pwszName, WCHAR wch )
{
#if DBG == 1
    //
    // Check for duplicate entries
    //
    WCHAR wchExisting;

    BOOL fFound = Lookup( pwszName, wcslen(pwszName), wchExisting );
    Win4Assert( !fFound );
#endif

    CSpecialCharHashEntry *pHashEntry = newk(mtNewX, NULL) CSpecialCharHashEntry( pwszName,
                                                                                wch );
    unsigned uHashValue = Hash( pwszName, wcslen(pwszName) );
    pHashEntry->SetNextHashEntry( _aHashTable[uHashValue] );
    _aHashTable[uHashValue] = pHashEntry;
}

//+-------------------------------------------------------------------------
//
//  Method:     CSpecialCharHashTable::Lookup
//
//  Synopsis:   Return the Unicode mapping corresponding to given special char
//
//  Arguments:  [pwcInputBuf] -- input buffer
//              [uLen]      -- Length of input (not \0 terminated)
//              [wch]       -- Unicode mapping returned here
//
//  Returns:    True if a mapping was found in the hash table
//
//--------------------------------------------------------------------------

BOOL CSpecialCharHashTable::Lookup( WCHAR *pwcInputBuf,
                                    unsigned uLen,
                                    WCHAR& wch )
{
    unsigned uHashValue = Hash( pwcInputBuf, uLen );

    Win4Assert( uHashValue < SPECIAL_CHAR_HASH_TABLE_SIZE );

    for ( CSpecialCharHashEntry *pHashEntry = _aHashTable[uHashValue];
          pHashEntry != 0;
          pHashEntry = pHashEntry->GetNextHashEntry() )
    {
        if ( wcsncmp( pwcInputBuf, pHashEntry->GetName(), uLen ) == 0 )
        {
            wch  = pHashEntry->GetWideChar();
            return TRUE;
        }
    }

    return FALSE;
}

//+-------------------------------------------------------------------------
//
//  Method:     CSpecialCharHashTable::Hash
//
//  Synopsis:   Implements the hash function
//
//  Arguments:  [pwszName]  -- name to hash
//              [cLen]     -- length of pszName (it is not null terminated)
//
//  Returns:    Position of chained list in hash table
//
//--------------------------------------------------------------------------

unsigned CSpecialCharHashTable::Hash( WCHAR *pwszName, unsigned cLen )
{
    for ( ULONG uHashValue=0; cLen>0; pwszName++ )
    {
        uHashValue = *pwszName + 31 * uHashValue;
        cLen--;
    }

    return uHashValue % SPECIAL_CHAR_HASH_TABLE_SIZE;
}

//+-------------------------------------------------------------------------
//
//  Method:     CSpecialCharHashTable::CSpecialCharHashTable
//
//  Synopsis:   Constructor
//
//--------------------------------------------------------------------------

CSpecialCharHashTable::CSpecialCharHashTable()
{
    for (unsigned i=0; i<SPECIAL_CHAR_HASH_TABLE_SIZE; i++)
        _aHashTable[i] = 0;

    //
    // Initialize the table with various Ascii string->Unicode mappings
    //

    //
    // For lt and gt, use Unicode chars from private use area to avoid
    // collision with '<' and '>' chars in Html tags. These will be
    // mapped back to '<' and '>' by the scanner.
    //
    Add(L"lt",      PRIVATE_USE_MAPPING_FOR_LT);
    Add(L"gt",      PRIVATE_USE_MAPPING_FOR_GT);

    Add(L"amp",     0x26);
    Add(L"quot",    0x22);
    Add(L"nbsp",    0xa0);
    Add(L"shy",     0xad);
    Add(L"Agrave",  0xc0);
    Add(L"agrave",  0xe0);
    Add(L"Aacute",  0xc1);
    Add(L"aacute",  0xe1);
    Add(L"Acirc",   0xc2);
    Add(L"acirc",   0xe2);
    Add(L"Atilde",  0xc3);
    Add(L"atilde",  0xe3);
    Add(L"Auml",    0xc4);
    Add(L"auml",    0xe4);
    Add(L"Aring",   0xc5);
    Add(L"aring",   0xe5);
    Add(L"AElig",   0xc6);
    Add(L"aelig",   0xe6);
    Add(L"Ccedil",  0xc7);
    Add(L"ccedil",  0xe7);
    Add(L"Egrave",  0xc8);
    Add(L"egrave",  0xe8);
    Add(L"Eacute",  0xc9);
    Add(L"eacute",  0xe9);
    Add(L"Ecirc",   0xca);
    Add(L"ecirc",   0xea);
    Add(L"Euml",    0xcb);
    Add(L"euml",    0xeb);
    Add(L"Igrave",  0xcc);
    Add(L"igrave",  0xec);
    Add(L"Iacute",  0xcd);
    Add(L"iacute",  0xed);
    Add(L"Icirc",   0xce);
    Add(L"icirc",   0xee);
    Add(L"Iuml",    0xcf);
    Add(L"iuml",    0xef);
    Add(L"ETH",     0xd0);
    Add(L"eth",     0xf0);
    Add(L"Ntilde",  0xd1);
    Add(L"ntilde",  0xf1);
    Add(L"Ograve",  0xd2);
    Add(L"ograve",  0xf2);
    Add(L"Oacute",  0xd3);
    Add(L"oacute",  0xf3);
    Add(L"Ocirc",   0xd4);
    Add(L"ocirc",   0xf4);
    Add(L"Otilde",  0xd5);
    Add(L"otilde",  0xf5);
    Add(L"Ouml",    0xd6);
    Add(L"ouml",    0xf6);
    Add(L"Oslash",  0xd8);
    Add(L"oslash",  0xf8);
    Add(L"Ugrave",  0xd9);
    Add(L"ugrave",  0xf9);
    Add(L"Uacute",  0xda);
    Add(L"uacute",  0xfa);
    Add(L"Ucirc",   0xdb);
    Add(L"ucirc",   0xfb);
    Add(L"Uuml",    0xdc);
    Add(L"uuml",    0xfc);
    Add(L"Yacute",  0xdd);
    Add(L"yacute",  0xfd);
    Add(L"THORN",   0xde);
    Add(L"thorn",   0xfe);
    Add(L"szlig",   0xdf);
    Add(L"yuml",    0xff);
    Add(L"iexcl",   0xa1);
    Add(L"cent",    0xa2);
    Add(L"pound",   0xa3);
    Add(L"curren",  0xa4);
    Add(L"yen",     0xa5);
    Add(L"brvbar",  0xa6);
    Add(L"sect",    0xa7);
    Add(L"die",     0xa8);
    Add(L"copy",    0xa9);
    Add(L"laquo",   0xab);
    Add(L"reg",     0xae);
    Add(L"macron",  0xaf);
    Add(L"deg",     0xb0);
    Add(L"plusmn",  0xb1);
    Add(L"sup2",    0xb2);
    Add(L"sup3",    0xb3);
    Add(L"acute",   0xb4);
    Add(L"micro",   0xb5);
    Add(L"para",    0xb6);
    Add(L"middot",  0xb7);
    Add(L"cedil",   0xb8);
    Add(L"supl",    0xb9);
    Add(L"raquo",   0xbb);
    Add(L"frac14",  0xbc);
    Add(L"frac12",  0xbd);
    Add(L"frac34",  0xbe);
    Add(L"iquest",  0xbf);
    Add(L"times",   0xd7);
    Add(L"divide",  0xf7);
}


CODEPAGE.CXX

//+---------------------------------------------------------------------------
//
//  Copyright (C) Microsoft Corporation, 1991 - 1996
//
//  File:   codepage.hxx
//
//  Contents:   Locale to codepage
//
//-----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <osv.hxx>
#include <codepage.hxx>

//+---------------------------------------------------------------------------
//
//  Function:   LocaleToCodepage
//
//  Purpose:    Returns a codepage from a locale
//
//  Arguments:  [lcid]  --  Locale
//
//  History:    08-Jul-96   SitaramR    Created
//
//----------------------------------------------------------------------------

ULONG LocaleToCodepage( LCID lcid )
{
    const BUFFER_LENGTH = 10;
    WCHAR wcsCodePage[BUFFER_LENGTH];
    int cwc;

    if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
    {
        cwc = GetLocaleInfoW( lcid, LOCALE_IDEFAULTANSICODEPAGE, wcsCodePage, BUFFER_LENGTH );
    }
    else // win95
    {
        char *pszCodePage = new char[BUFFER_LENGTH*2];

        if ( pszCodePage == NULL )
        {
            cwc = 0;
            SetLastErrorEx( ERROR_INVALID_PARAMETER, SLE_ERROR );
        }
        else
        {
            cwc = GetLocaleInfoA( lcid,
                                  LOCALE_IDEFAULTANSICODEPAGE,
                                  pszCodePage,
                                  BUFFER_LENGTH );

            if ( cwc && mbstowcs( wcsCodePage, pszCodePage, cwc ) == -1 )
            {
                cwc = 0;
                SetLastErrorEx( ERROR_INVALID_PARAMETER, SLE_ERROR );
            }

            delete [] pszCodePage;
            pszCodePage = NULL;
        }
    }

    //
    // If error, return Ansi code page
    //
    if ( cwc == 0 )
    {
         htmlDebugOut(( DEB_ERROR, "GetLocaleInfoW for lcid %d returned %d\n", lcid, GetLastError() ));

         return CP_ACP;
    }

    return wcstoul( wcsCodePage, 0 , 10 );
}


EXCEPT.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1991-1998 Microsoft Corporation.
//
//  File:       Except.cxx
//
//  Contents:   Conversion to Ole errors
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

typedef LONG NTSTATUS;

//+-------------------------------------------------------------------------
//
//  Method:     IsOleError, public
//
//  Synopsis:   return TRUE if sc looks like an OLE SCODE.
//
//--------------------------------------------------------------------------

inline BOOL IsOleError (NTSTATUS sc)
{
    return ((sc & 0xFFF00000) == 0x80000000) ||
           (SUCCEEDED(sc) && (sc & 0xFFFFF000) != 0);
}

//+-------------------------------------------------------------------------
//
//  Method:     GetOleError, public
//
//  Purpose:    Converts a status code to an Ole error code, because interfaces
//              return Ole error code only.
//
//--------------------------------------------------------------------------

SCODE GetOleError(CException & e)
{
    NTSTATUS scError = e.GetErrorCode();

    if (IsOleError(scError))
    {
        return scError;
    }
    else if ( STATUS_NO_MEMORY == scError )
    {
        return E_OUTOFMEMORY;
    }
    else
    {
        return E_FAIL;
    }
}

void _cdecl SystemExceptionTranslator( unsigned int uiWhat,
                                       struct _EXCEPTION_POINTERS * pexcept )
{
    throw CException( uiWhat ) ;
}


FILTER.H

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 3.00.44 */
/* at Fri Jul 12 18:12:51 1996
 */
/* Compiler settings for filter.idl:
    Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext
    error checks: none
*/
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __filter_h__
#define __filter_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __IFilter_FWD_DEFINED__
#define __IFilter_FWD_DEFINED__
typedef interface IFilter IFilter;
#endif /* __IFilter_FWD_DEFINED__ */

/* header files for imported files */
#include "unknwn.h"
#include "objidl.h"

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

/****************************************
 * Generated header for interface: __MIDL__intf_0000
 * at Fri Jul 12 18:12:51 1996
 * using MIDL 3.00.44
 ****************************************/
/* [local] */

#if !defined(_TAGFULLPROPSPEC_DEFINED_)
#define _TAGFULLPROPSPEC_DEFINED_
typedef struct  tagFULLPROPSPEC
    {
    GUID guidPropSet;
    PROPSPEC psProperty;
    }FULLPROPSPEC;

#endif // #if !defined(_TAGFULLPROPSPEC_DEFINED_)

extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_s_ifspec;

#ifndef __IFilter_INTERFACE_DEFINED__
#define __IFilter_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IFilter
 * at Fri Jul 12 18:12:51 1996
 * using MIDL 3.00.44
 ****************************************/
/* [unique][uuid][object][local] */

#ifndef _tagIFILTER_INIT_DEFINED
typedef
enum tagIFILTER_INIT
    {IFILTER_INIT_CANON_PARAGRAPHS= 1,
IFILTER_INIT_HARD_LINE_BREAKS= 2,
IFILTER_INIT_CANON_HYPHENS= 4,
IFILTER_INIT_CANON_SPACES= 8,
IFILTER_INIT_APPLY_INDEX_ATTRIBUTES= 16,
IFILTER_INIT_APPLY_OTHER_ATTRIBUTES= 32,
IFILTER_INIT_INDEXING_ONLY= 64,
IFILTER_INIT_SEARCH_LINKS= 128
    }IFILTER_INIT;

#define _tagIFILTER_INIT_DEFINED
#define _IFILTER_INIT_DEFINED
#endif
#ifndef _tagIFILTER_FLAGS_DEFINED
typedef
enum tagIFILTER_FLAGS
    {IFILTER_FLAGS_OLE_PROPERTIES= 1
    }IFILTER_FLAGS;

#define _tagIFILTER_FLAGS_DEFINED
#define _IFILTER_FLAGS_DEFINED
#endif
#ifndef _tagCHUNKSTATE_DEFINED
typedef
enum tagCHUNKSTATE
    {CHUNK_TEXT= 0x1,
CHUNK_VALUE= 0x2
    }CHUNKSTATE;

#define _tagCHUNKSTATE_DEFINED
#define _CHUNKSTATE_DEFINED
#endif
#ifndef _tagCHUNK_BREAKTYPE_DEFINED
typedef
enum tagCHUNK_BREAKTYPE
    {CHUNK_NO_BREAK= 0,
CHUNK_EOW= 1,
CHUNK_EOS= 2,
CHUNK_EOP= 3,
CHUNK_EOC= 4
    }CHUNK_BREAKTYPE;

#define _tagCHUNK_BREAKTYPE_DEFINED
#define _CHUNK_BREAKTYPE_DEFINED
#endif
#ifndef _tagFILTERREGION_DEFINED
typedef struct  tagFILTERREGION
    {
    ULONG idChunk;
    ULONG cwcStart;
    ULONG cwcExtent;
    }FILTERREGION;

#define _tagFILTERREGION_DEFINED
#define _FILTERREGION_DEFINED
#endif
#ifndef _tagSTAT_CHUNK_DEFINED
typedef struct  tagSTAT_CHUNK
    {
    ULONG idChunk;
    CHUNK_BREAKTYPE breakType;
    CHUNKSTATE flags;
    LCID locale;
    FULLPROPSPEC attribute;
    ULONG idChunkSource;
    ULONG cwcStartSource;
    ULONG cwcLenSource;
    }STAT_CHUNK;

#define _tagSTAT_CHUNK_DEFINED
#define _STAT_CHUNK_DEFINED
#endif

EXTERN_C const IID IID_IFilter;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface IFilter : public IUnknown
    {
    public:
        virtual SCODE STDMETHODCALLTYPE Init(
            /* [in] */ ULONG grfFlags,
            /* [in] */ ULONG cAttributes,
            /* [size_is][in] */ const FULLPROPSPEC __RPC_FAR *aAttributes,
            /* [out] */ ULONG __RPC_FAR *pFlags) = 0;

        virtual SCODE STDMETHODCALLTYPE GetChunk(
            /* [out] */ STAT_CHUNK __RPC_FAR *pStat) = 0;

        virtual SCODE STDMETHODCALLTYPE GetText(
            /* [out][in] */ ULONG __RPC_FAR *pcwcBuffer,
            /* [size_is][out] */ WCHAR __RPC_FAR *awcBuffer) = 0;

        virtual SCODE STDMETHODCALLTYPE GetValue(
            /* [out] */ PROPVARIANT __RPC_FAR *__RPC_FAR *ppPropValue) = 0;

        virtual SCODE STDMETHODCALLTYPE BindRegion(
            /* [in] */ FILTERREGION origPos,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppunk) = 0;

    };

#else /* C style interface */

    typedef struct IFilterVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
            IFilter __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
            IFilter __RPC_FAR * This);

        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
            IFilter __RPC_FAR * This);

        SCODE ( STDMETHODCALLTYPE __RPC_FAR *Init )(
            IFilter __RPC_FAR * This,
            /* [in] */ ULONG grfFlags,
            /* [in] */ ULONG cAttributes,
            /* [size_is][in] */ const FULLPROPSPEC __RPC_FAR *aAttributes,
            /* [out] */ ULONG __RPC_FAR *pFlags);

        SCODE ( STDMETHODCALLTYPE __RPC_FAR *GetChunk )(
            IFilter __RPC_FAR * This,
            /* [out] */ STAT_CHUNK __RPC_FAR *pStat);

        SCODE ( STDMETHODCALLTYPE __RPC_FAR *GetText )(
            IFilter __RPC_FAR * This,
            /* [out][in] */ ULONG __RPC_FAR *pcwcBuffer,
            /* [size_is][out] */ WCHAR __RPC_FAR *awcBuffer);

        SCODE ( STDMETHODCALLTYPE __RPC_FAR *GetValue )(
            IFilter __RPC_FAR * This,
            /* [out] */ PROPVARIANT __RPC_FAR *__RPC_FAR *ppPropValue);

        SCODE ( STDMETHODCALLTYPE __RPC_FAR *BindRegion )(
            IFilter __RPC_FAR * This,
            /* [in] */ FILTERREGION origPos,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppunk);

        END_INTERFACE
    } IFilterVtbl;

    interface IFilter
    {
        CONST_VTBL struct IFilterVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IFilter_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IFilter_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IFilter_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IFilter_Init(This,grfFlags,cAttributes,aAttributes,pFlags)\
    (This)->lpVtbl -> Init(This,grfFlags,cAttributes,aAttributes,pFlags)

#define IFilter_GetChunk(This,pStat)\
    (This)->lpVtbl -> GetChunk(This,pStat)

#define IFilter_GetText(This,pcwcBuffer,awcBuffer)\
    (This)->lpVtbl -> GetText(This,pcwcBuffer,awcBuffer)

#define IFilter_GetValue(This,ppPropValue)\
    (This)->lpVtbl -> GetValue(This,ppPropValue)

#define IFilter_BindRegion(This,origPos,riid,ppunk)\
    (This)->lpVtbl -> BindRegion(This,origPos,riid,ppunk)

#endif /* COBJMACROS */

#endif /* C style interface */

SCODE STDMETHODCALLTYPE IFilter_Init_Proxy(
    IFilter __RPC_FAR * This,
    /* [in] */ ULONG grfFlags,
    /* [in] */ ULONG cAttributes,
    /* [size_is][in] */ const FULLPROPSPEC __RPC_FAR *aAttributes,
    /* [out] */ ULONG __RPC_FAR *pFlags);

void __RPC_STUB IFilter_Init_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

SCODE STDMETHODCALLTYPE IFilter_GetChunk_Proxy(
    IFilter __RPC_FAR * This,
    /* [out] */ STAT_CHUNK __RPC_FAR *pStat);

void __RPC_STUB IFilter_GetChunk_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

SCODE STDMETHODCALLTYPE IFilter_GetText_Proxy(
    IFilter __RPC_FAR * This,
    /* [out][in] */ ULONG __RPC_FAR *pcwcBuffer,
    /* [size_is][out] */ WCHAR __RPC_FAR *awcBuffer);

void __RPC_STUB IFilter_GetText_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

SCODE STDMETHODCALLTYPE IFilter_GetValue_Proxy(
    IFilter __RPC_FAR * This,
    /* [out] */ PROPVARIANT __RPC_FAR *__RPC_FAR *ppPropValue);

void __RPC_STUB IFilter_GetValue_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

SCODE STDMETHODCALLTYPE IFilter_BindRegion_Proxy(
    IFilter __RPC_FAR * This,
    /* [in] */ FILTERREGION origPos,
    /* [in] */ REFIID riid,
    /* [out] */ void __RPC_FAR *__RPC_FAR *ppunk);

void __RPC_STUB IFilter_BindRegion_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IFilter_INTERFACE_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif


FILTERR.H

#ifndef _FILTERR_H_
#define _FILTERR_H_
#ifndef FACILITY_WINDOWS
//
//  Values are 32 bit values layed out as follows:
//
//   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
//   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//  +---+-+-+-----------------------+-------------------------------+
//  |Sev|C|R|     Facility          |               Code            |
//  +---+-+-+-----------------------+-------------------------------+
//
//  where
//
//      Sev - is the severity code
//
//          00 - Success
//          01 - Informational
//          10 - Warning
//          11 - Error
//
//      C - is the Customer code flag
//
//      R - is a reserved bit
//
//      Facility - is the facility code
//
//      Code - is the facility's status code
//
//
// Define the facility codes
//
#define FACILITY_WINDOWS                 0x8
#define FACILITY_ITF                     0x4

//
// Define the severity codes
//
#define STATUS_SEVERITY_SUCCESS          0x0
#define STATUS_SEVERITY_COFAIL           0x3
#define STATUS_SEVERITY_COERROR          0x2

//
// MessageId: NOT_AN_ERROR
//
// MessageText:
//
//  NOTE:  This dummy error message is necessary to force MC to output
//         the above defines inside the FACILITY_WINDOWS guard instead
//         of leaving it empty.
//
#define NOT_AN_ERROR                     ((HRESULT)0x00080000L)

#endif // FACILITY_WINDOWS
//
// Codes 0x1700-0x172F are reserved for FILTER
//
//
// MessageId: FILTER_E_END_OF_CHUNKS
//
// MessageText:
//
//  No more chunks of text available in object.
//
#define FILTER_E_END_OF_CHUNKS           ((HRESULT)0x80041700L)

//
// MessageId: FILTER_E_NO_MORE_TEXT
//
// MessageText:
//
//  No more text available in chunk.
//
#define FILTER_E_NO_MORE_TEXT            ((HRESULT)0x80041701L)

//
// MessageId: FILTER_E_NO_MORE_VALUES
//
// MessageText:
//
//  No more property values available in chunk.
//
#define FILTER_E_NO_MORE_VALUES          ((HRESULT)0x80041702L)

//
// MessageId: FILTER_E_ACCESS
//
// MessageText:
//
//  Unable to access object.
//
#define FILTER_E_ACCESS                  ((HRESULT)0x80041703L)

//
// MessageId: FILTER_W_MONIKER_CLIPPED
//
// MessageText:
//
//  Moniker doesn't cover entire region.
//
#define FILTER_W_MONIKER_CLIPPED         ((HRESULT)0x00041704L)

//
// MessageId: FILTER_E_NO_TEXT
//
// MessageText:
//
//  No text in current chunk.
//
#define FILTER_E_NO_TEXT                 ((HRESULT)0x80041705L)

//
// MessageId: FILTER_E_NO_VALUES
//
// MessageText:
//
//  No values in current chunk.
//
#define FILTER_E_NO_VALUES               ((HRESULT)0x80041706L)

//
// MessageId: FILTER_E_EMBEDDING_UNAVAILABLE
//
// MessageText:
//
//  Unable to bind IFilter for embedded object.
//
#define FILTER_E_EMBEDDING_UNAVAILABLE   ((HRESULT)0x80041707L)

//
// MessageId: FILTER_E_LINK_UNAVAILABLE
//
// MessageText:
//
//  Unable to bind IFilter for linked object.
//
#define FILTER_E_LINK_UNAVAILABLE        ((HRESULT)0x80041708L)

//
// MessageId: FILTER_S_LAST_TEXT
//
// MessageText:
//
//  This is the last text in the current chunk.
//
#define FILTER_S_LAST_TEXT               ((HRESULT)0x00041709L)

//
// MessageId: FILTER_S_LAST_VALUES
//
// MessageText:
//
//  This is the last value in the current chunk.
//
#define FILTER_S_LAST_VALUES             ((HRESULT)0x0004170AL)

//
// MessageId: FILTER_E_PASSWORD
//
// MessageText:
//
//  File was not filtered due to password protection.
//
#define FILTER_E_PASSWORD                ((HRESULT)0x8004170BL)

#endif // _FILTERR_H_


HTMLELEM.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       htmlelem.cxx
//
//  Contents:   Base class for Html element
//
//  Classes:    CHtmlElement
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <htmlelem.hxx>

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlElement::CHtmlElement
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]     -- Reference to IFilter
//              [mmSerialStream]  -- Reference to input stream
//
//--------------------------------------------------------------------------

CHtmlElement::CHtmlElement( CHtmlIFilter& htmlIFilter,
                            CSerialStream& serialStream )
    : _htmlIFilter(htmlIFilter),
      _serialStream(serialStream),
      _scanner(htmlIFilter, serialStream)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlElement::GetValue
//
//  Synopsis:   Default implementation for returning value in current chunk
//
//  Arguments:  [ppPropValue]  -- Value returned here
//
//--------------------------------------------------------------------------
SCODE CHtmlElement::GetValue( VARIANT ** ppPropValue )
{
    return FILTER_E_NO_VALUES;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlElement::InitFilterRegion
//
//  Synopsis:   Initializes the filter region of a chunk to default value
//
//  Arguments:  [idChunkSource]  -- Id of source chunk
//              [cwcStartSource] -- Offset of source text in chunk
//              [cwcLenSource]   -- Length of source text in chunk
//
//--------------------------------------------------------------------------

void CHtmlElement::InitFilterRegion( ULONG& idChunkSource,
                                     ULONG& cwcStartSource,
                                     ULONG& cwcLenSource )
{
    idChunkSource = 0;
    cwcStartSource = 0;
    cwcLenSource = 0;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlElement::SwitchToNextHtmlElement
//
//  Synopsis:   Find the next Html element and switch to it's parsing algorithm
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CHtmlElement::SwitchToNextHtmlElement( STAT_CHUNK *pStat )
{
    CToken token;

    _scanner.SkipCharsUntilNextRelevantToken( token );
    HtmlTokenType eTokType = token.GetTokenType();
    if ( eTokType == EofToken )
        return FILTER_E_END_OF_CHUNKS;

    CHtmlElement *pHtmlElemNext = _htmlIFilter.QueryHtmlElement( eTokType );
    Win4Assert( pHtmlElemNext );
    pHtmlElemNext->InitStatChunk( pStat );
    _htmlIFilter.ChangeState( pHtmlElemNext );

    return S_OK;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlElement::SkipRemainingTextAndGotoNextChunk
//
//  Synopsis:   Move to the next chunk
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CHtmlElement::SkipRemainingTextAndGotoNextChunk( STAT_CHUNK *pStat )
{
    ULONG ulBufSize  = TEMP_BUFFER_SIZE;

    //
    // Loop until text in current chunk has been exhausted
    //
    SCODE sc = GetText( &ulBufSize, _aTempBuffer );
    while ( SUCCEEDED(sc) )
    {
        sc = GetText( &ulBufSize, _aTempBuffer );
    }

    sc = GetChunk( pStat );

    return sc;
}


HTMLFILT.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       htmlfilt.cxx
//
//  Contents:   Html filter
//
//  Classes:    CHtmlIFilter
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <eh.h>
#include <htmlfilt.hxx>
#include <textelem.hxx>
#include <proptag.hxx>
#include <anchor.hxx>
#include <inputtag.hxx>
#include <start.hxx>
#include <titletag.hxx>
#include <imagetag.hxx>
#include <metatag.hxx>
#include <scriptag.hxx>
#include <htmlguid.hxx>
#include <stgprop.h>
#include <limits.h>
#include <regacc32.hxx>

DECLARE_INFOLEVEL(html)

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::CHtmlIFilter
//
//  Synopsis:   Constructor
//
//--------------------------------------------------------------------------

CHtmlIFilter::CHtmlIFilter()
    : _pHtmlElement(0),
      _fNonHtmlFile(FALSE),
      _fFilterContent(FALSE),
      _fFilterMetaTag(FALSE),
      _fFilterScriptTag(FALSE),
      _cAttributes(0),
      _pAttributes(0),
      _pwszFileName(0),
      _ulChunkID(0),
      _locale(GetSystemDefaultLCID()),
      _htmlElementBag( COUNT_OF_HTML_ELEMENTS )
{
    //
    // Setup various Html elements
    //
    _htmlElementBag.AddElement( TextToken, newk(mtNewX, NULL) CTextElement( *this, _serialStream ) );

    _htmlElementBag.AddElement( StartOfFileToken, newk(mtNewX, NULL) CStartOfFileElement( *this, _serialStream ) );

    CFullPropSpec psTitle( CLSID_SummaryInformation, PID_TITLE );
    _htmlElementBag.AddElement( TitleToken, newk(mtNewX, NULL) CTitleTag( *this,
                                                                          _serialStream,
                                                                          psTitle,
                                                                          TitleToken ) );

    CFullPropSpec psHeading1( CLSID_HtmlInformation, PID_HEADING_1 );
    _htmlElementBag.AddElement( Heading1Token, newk(mtNewX, NULL) CPropertyTag( *this,
                                                                                _serialStream,
                                                                                psHeading1,
                                                                                Heading1Token ) );

    CFullPropSpec psHeading2( CLSID_HtmlInformation, PID_HEADING_2 );
    _htmlElementBag.AddElement( Heading2Token, newk(mtNewX, NULL) CPropertyTag( *this,
                                                                                _serialStream,
                                                                                psHeading2,
                                                                                Heading2Token ) );

    CFullPropSpec psHeading3( CLSID_HtmlInformation, PID_HEADING_3 );
    _htmlElementBag.AddElement( Heading3Token, newk(mtNewX, NULL) CPropertyTag( *this,
                                                                                _serialStream,
                                                                                psHeading3,
                                                                                Heading3Token ) );

    CFullPropSpec psHeading4( CLSID_HtmlInformation, PID_HEADING_4 );
    _htmlElementBag.AddElement( Heading4Token, newk(mtNewX, NULL) CPropertyTag( *this,
                                                                                _serialStream,
                                                                                psHeading4,
                                                                                Heading4Token ) );

    CFullPropSpec psHeading5( CLSID_HtmlInformation, PID_HEADING_5 );
    _htmlElementBag.AddElement( Heading5Token, newk(mtNewX, NULL) CPropertyTag( *this,
                                                                                _serialStream,
                                                                                psHeading5,
                                                                                Heading5Token ) );

    CFullPropSpec psHeading6( CLSID_HtmlInformation, PID_HEADING_6 );
    _htmlElementBag.AddElement( Heading6Token, newk(mtNewX, NULL) CPropertyTag( *this,
                                                                                _serialStream,
                                                                                psHeading6,
                                                                                Heading6Token ) );

    _htmlElementBag.AddElement( AnchorToken, newk(mtNewX, NULL) CAnchorTag( *this, _serialStream ) );

    _htmlElementBag.AddElement( InputToken, newk(mtNewX, NULL) CInputTag( *this, _serialStream ) );

    _htmlElementBag.AddElement( ImageToken, newk(mtNewX, NULL) CImageTag( *this, _serialStream ) );

    //
    // Read classid of meta information from registry
    //
    const WCHAR *pszKey = L"system\\currentcontrolset\\control\\HtmlFilter";
    CRegAccess regMetaInfo( HKEY_LOCAL_MACHINE, pszKey );

    const MAX_VALUE_LENGTH = 100;
    WCHAR wszValue[MAX_VALUE_LENGTH];
    _fFilterMetaTag = regMetaInfo.Get( L"MetaTagClsid", wszValue, MAX_VALUE_LENGTH );

    if ( _fFilterMetaTag )
    {
        GUID ClsidMetaInformation;

        StringToClsid( wszValue, ClsidMetaInformation );
        _htmlElementBag.AddElement( MetaToken, newk(mtNewX, NULL) CMetaTag( *this,
                                                                            _serialStream,
                                                                            ClsidMetaInformation ) );
    }

    //
    // Read classid of scripts from registry
    //
    CRegAccess regScriptInfo( HKEY_LOCAL_MACHINE, pszKey );

    _fFilterScriptTag = regScriptInfo.Get( L"ScriptTagClsid", wszValue, MAX_VALUE_LENGTH );

    if ( _fFilterScriptTag )
    {
        GUID ClsidScriptInformation;

        StringToClsid( wszValue, ClsidScriptInformation );
        _htmlElementBag.AddElement( ScriptToken, newk(mtNewX, NULL) CScriptTag( *this,
                                                                                _serialStream,
                                                                                ClsidScriptInformation ) );
    }

    //
    // Set the start state to StartOfFile mode
    //
    _pHtmlElement = _htmlElementBag.QueryElement( StartOfFileToken );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::~CHtmlIFilter
//
//  Synopsis:   Destructor
//
//--------------------------------------------------------------------------

CHtmlIFilter::~CHtmlIFilter()
{
    delete[] _pAttributes;
    delete[] _pwszFileName;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHtmlIFilter::BindRegion
//
//  Synopsis:   Creates moniker or other interface for text indicated
//
//  Arguments:  [origPos] -- the region of text to be mapped to a moniker
//              [riid]    -- Interface id
//              [ppunk]   -- the interface
//
//----------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::BindRegion( FILTERREGION origPos,
                                                  REFIID riid,
                                                  void ** ppunk )
{
    return E_NOTIMPL;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHtmlIFilter::GetClassID, public
//
//  Synopsis:   Returns the class id of this class.
//
//  Arguments:  [pClassID] -- the class id
//
//----------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::GetClassID( CLSID * pClassID )
{
    *pClassID = CLSID_HtmlIFilter;
    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHtmlIFilter::IsDirty, public
//
//  Synopsis:   Always returns S_FALSE since the filter is read-only
//
//----------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::IsDirty()
{
    return S_FALSE;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHtmlIFilter::Load, public
//
//  Synopsis:   Loads the indicated file
//
//  Arguments:  [pszFileName] -- the file name
//              [dwMode] -- the mode to load the file in
//
//----------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::Load( LPCWSTR psszFileName, DWORD dwMode )
{
    if ( _pwszFileName != 0 )
    {
        delete _pwszFileName;
        _pwszFileName = 0;
    }

    _se_translator_function tf = _set_se_translator( SystemExceptionTranslator );

    SCODE sc = S_OK;
    try
    {
        unsigned cLen = wcslen( psszFileName ) + 1;
        _pwszFileName = newk(mtNewX, NULL) WCHAR[cLen];
        wcscpy( _pwszFileName, psszFileName );

        _fNonHtmlFile = IsNonHtmlFile();
    }
    catch( CException& e )
    {
        htmlDebugOut(( DEB_ERROR, "Exception 0x%x caught in CHtmlIFilter::Load", e.GetErrorCode() ));

        sc = GetOleError( e );
    }

    _set_se_translator( tf );

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHtmlIFilter::Save, public
//
//  Synopsis:   Always returns E_FAIL, since the file is opened read-only
//
//----------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::Save( LPCWSTR pszFileName, BOOL fRemember )
{
    return E_FAIL;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHtmlIFilter::SaveCompleted, public
//
//  Synopsis:   Always returns S_OK since the file is opened read-only
//
//----------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::SaveCompleted( LPCWSTR pszFileName )
{
    return E_FAIL;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHtmlIFilter::GetCurFile, public
//
//  Synopsis:   Returns a copy of the current file name
//
//  Arguments:  [ppszFileName] -- where the copied string is returned
//
//----------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::GetCurFile( LPWSTR * ppwszFileName )
{
    if ( _pwszFileName == 0 )
        return E_FAIL;

    unsigned cLen = wcslen( _pwszFileName ) + 1;
    *ppwszFileName = (WCHAR *)CoTaskMemAlloc( cLen*sizeof(WCHAR) );

    SCODE sc = S_OK;
    if ( *ppwszFileName )
        wcscpy( *ppwszFileName, _pwszFileName );
    else
        sc = E_OUTOFMEMORY;

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::Init
//
//  Synopsis:   Initializes instance of Html filter
//
//  Arguments:  [grfFlags] -- flags for filter behavior
//              [cAttributes] -- number of attributes in array pAttributes
//              [pAttributes] -- array of attributes
//              [pFlags]      -- Set to 0 version 1
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::Init( ULONG grfFlags,
                                            ULONG cAttributes,
                                            FULLPROPSPEC const * pAttributes,
                                            ULONG * pFlags )
{
    SCODE sc = S_OK;

    *pFlags = 0;  // There are no OLE docfile properties for .htm files.

    //
    // IE workaround, don't filter non-Html files such as .gif
    //
    if ( _fNonHtmlFile )
        return S_OK;

    _se_translator_function tf = _set_se_translator( SystemExceptionTranslator );

    try
    {

        _fFilterContent = FALSE;

        if ( _cAttributes > 0 )
        {
            delete[] _pAttributes;
            _pAttributes = 0;
            _cAttributes = 0;
        }

        if ( cAttributes > 0 )
        {
            //
            // Filter properties specified in pAttributes
            //
            _pAttributes = newk(mtNewX, NULL) CFullPropSpec[cAttributes];
            _cAttributes = cAttributes;

            CFullPropSpec *pAttrib = (CFullPropSpec *) pAttributes;

            for (unsigned i=0; i<cAttributes; i++)
            {
                if ( pAttrib[i].IsPropertyPropid()
                     && pAttrib[i].GetPropertyPropid() == PID_STG_CONTENTS
                     && pAttrib[i].GetPropSet() == CLSID_Storage )
                {
                    _fFilterContent = TRUE;
                }

                _pAttributes[i] = pAttrib[i];
            }
        }
        else if ( grfFlags & IFILTER_INIT_APPLY_INDEX_ATTRIBUTES )
        {
            //
            // Filter contents and all pseudo-properties
            //
            _fFilterContent = TRUE;

            const COUNT_ATTRIBUTES = 8;
            _pAttributes = newk(mtNewX, NULL) CFullPropSpec[COUNT_ATTRIBUTES];
            _cAttributes = COUNT_ATTRIBUTES;

            _pAttributes[0].SetPropSet( CLSID_SummaryInformation );
            _pAttributes[0].SetProperty( PID_TITLE );

            _pAttributes[1].SetPropSet( CLSID_HtmlInformation );
            _pAttributes[1].SetProperty( PID_HREF );

            _pAttributes[2].SetPropSet( CLSID_HtmlInformation );
            _pAttributes[2].SetProperty( PID_HEADING_1 );

            _pAttributes[3].SetPropSet( CLSID_HtmlInformation );
            _pAttributes[3].SetProperty( PID_HEADING_2 );

            _pAttributes[4].SetPropSet( CLSID_HtmlInformation );
            _pAttributes[4].SetProperty( PID_HEADING_3 );

            _pAttributes[5].SetPropSet( CLSID_HtmlInformation );
            _pAttributes[5].SetProperty( PID_HEADING_4 );

            _pAttributes[6].SetPropSet( CLSID_HtmlInformation );
            _pAttributes[6].SetProperty( PID_HEADING_5 );

            _pAttributes[7].SetPropSet( CLSID_HtmlInformation );
            _pAttributes[7].SetProperty( PID_HEADING_6 );
        }
        else if ( grfFlags == 0 )
        {
            //
            // Filter only contents
            //
            _fFilterContent = TRUE;
        }

        _pHtmlElement = _htmlElementBag.QueryElement( StartOfFileToken );
        _ulChunkID = 0;
        _serialStream.Init( _pwszFileName );
    }
    catch( CException& e)
    {
        htmlDebugOut(( DEB_ERROR,
                       "Exception 0x%x caught in CHtmlIFilter::Init\n",
                       e.GetErrorCode() ));
        sc = GetOleError( e );
    }

    _set_se_translator( tf );

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::ChangeState
//
//  Synopsis:   Change the current Html element and hence the parsing algorithm
//
//  Arguments:  [pHtmlElemNewState]  -- New Html element to change to
//
//--------------------------------------------------------------------------

void CHtmlIFilter::ChangeState( CHtmlElement *pHtmlElemNewState )
{
    _pHtmlElement = pHtmlElemNewState;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::QueryElement
//
//  Synopsis:   Return HtmlElement corresponding to given token type
//
//  Arguments:  [eTokenType]  -- token type
//
//--------------------------------------------------------------------------

CHtmlElement *CHtmlIFilter::QueryHtmlElement( HtmlTokenType eTokType )
{
    return _htmlElementBag.QueryElement( eTokType );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::GetNextChunkId
//
//  Synopsis:   Return a brand new chunk id
//
//--------------------------------------------------------------------------

ULONG CHtmlIFilter::GetNextChunkId()
{
    Win4Assert( _ulChunkID != ULONG_MAX );

    return ++_ulChunkID;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in ppStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::GetChunk( STAT_CHUNK * pStat )
{
    //
    // IE workaround, don't filter non-Html files such as .gif
    //
    if ( _fNonHtmlFile )
        return FILTER_E_END_OF_CHUNKS;

    SCODE sc = S_OK;

    _se_translator_function tf = _set_se_translator( SystemExceptionTranslator );

    try
    {
        sc = _pHtmlElement->GetChunk( pStat );
    }
    catch( CException& e )
    {
        htmlDebugOut(( DEB_ERROR,
                       "Caught exception 0x%x in CHtmlIFilter::GetChunk\n",
                       e.GetErrorCode() ));

        sc = GetOleError( e );
    }

    _set_se_translator( tf );

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcOutput] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::GetText( ULONG * pcwcOutput, WCHAR * awcOutput )
{
    //
    // IE workaround, don't filter non-Html files such as .gif
    //
    if ( _fNonHtmlFile )
        return FILTER_E_NO_TEXT;

    SCODE sc = S_OK;

    _se_translator_function tf = _set_se_translator( SystemExceptionTranslator );

    try
    {
        sc = _pHtmlElement->GetText( pcwcOutput, awcOutput );
    }
    catch( CException& e )
    {
        htmlDebugOut(( DEB_ERROR,
                       "Caught exception 0x%x in CHtmlIFilter::GetText\n",
                       e.GetErrorCode() ));

        sc = GetOleError( e );
    }

    _set_se_translator( tf );

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHtmlIFilter::GetValue
//
//  Synopsis:   Retrieves value from current chunk
//
//----------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilter::GetValue( VARIANT ** ppPropValue )
{
    //
    // IE workaround, don't filter non-Html files such as .gif
    //
    if ( _fNonHtmlFile )
        return FILTER_E_NO_VALUES;

    SCODE sc = S_OK;

    _se_translator_function tf = _set_se_translator( SystemExceptionTranslator );

    try
    {
        sc = _pHtmlElement->GetValue( ppPropValue );
    }
    catch( CException& e )
    {
        htmlDebugOut(( DEB_ERROR,
                       "Caught exception 0x%x in CHtmlIFilter::GetValue\n",
                       e.GetErrorCode() ));

        sc = GetOleError( e );
    }

    _set_se_translator( tf );

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::IsStopToken
//
//  Synopsis:   Check if given token is a token that should stop further
//              scanning
//
//  Arguments:  [token]  -- Given token
//
//--------------------------------------------------------------------------

BOOL CHtmlIFilter::IsStopToken( CToken& token )
{
    HtmlTokenType tokType = token.GetTokenType();
    if ( ( tokType == InputToken || tokType == ImageToken )  && _fFilterContent
         || tokType == MetaToken && _fFilterMetaTag
         || tokType == ScriptToken && _fFilterScriptTag )
    {
        return TRUE;
    }

    if ( token.GetTokenType() == GenericToken
         || token.GetTokenType() == BreakToken )
        return FALSE;

    for ( unsigned i=0; i<_cAttributes; i++ )
    {
        if ( token.IsMatchProperty( _pAttributes[i] ) )
            return TRUE;
    }

    return FALSE;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilter::IsNonHtmlFile
//
//  Synopsis:   Check if file being filtered has an extension such as .gif,
//              which means that the file is not an Html file
//
//--------------------------------------------------------------------------

BOOL CHtmlIFilter::IsNonHtmlFile()
{
    WCHAR *pwszExtension;

    for ( int i=wcslen(_pwszFileName)-1; i>=0; i-- )
    {
        if ( _pwszFileName[i] == L'.' )
        {
            pwszExtension = &_pwszFileName[i+1];
            break;
        }
    }

    //
    // No extension, then assume it's an html file
    //
    if ( i < 0 )
        return FALSE;

    switch( pwszExtension[0] )
    {
    case L'a':
    case L'A':
        if ( _wcsicmp( pwszExtension, L"aif" ) == 0
             || _wcsicmp( pwszExtension, L"aifc" ) == 0
             || _wcsicmp( pwszExtension, L"aiff" ) == 0
             || _wcsicmp( pwszExtension, L"au" ) == 0 )
        {
            return TRUE;
        }

        break;

    case L'g':
    case L'G':
        if ( _wcsicmp( pwszExtension, L"gif" ) == 0 )
            return TRUE;

        break;

    case L'j':
    case L'J':
        if ( _wcsicmp( pwszExtension, L"jfif" ) == 0
             || _wcsicmp( pwszExtension, L"jpe" ) == 0
             || _wcsicmp( pwszExtension, L"jpeg" ) == 0
             || _wcsicmp( pwszExtension, L"jpg" ) == 0 )
        {
            return TRUE;
        }

        break;

    case L's':
    case L'S':
        if ( _wcsicmp( pwszExtension, L"snd" ) == 0 )
            return TRUE;

        break;

    case L'x':
    case L'X':
        if ( _wcsicmp( pwszExtension, L"xbm" ) == 0 )
            return TRUE;

        break;

    default:
        return FALSE;
    }
}


HTMLGUID.CXX

//+-------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation
//
//  File:       htmlguid.cxx
//
//  Contents:   Definitions of Html properties
//
//--------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <htmlguid.hxx>
#include <stgprop.h>

GUID CLSID_Storage = PSGUID_STORAGE;

GUID CLSID_SummaryInformation = {
    0xF29F85E0,
    0x4FF9,
    0x1068,
    { 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9 }
};

GUID CLSID_HtmlInformation = {
    0x70eb7a10,
    0x55d9,
    0x11cf,
    { 0xb7, 0x5b, 0x00, 0xaa, 0x00, 0x51, 0xfe, 0x20 }
};


HTMLIFLT.CXX

//+-------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation
//
//  File:       htmliflt.cxx
//
//  Contents:   Html IFilter
//
//--------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

class CHtmlElement;

#include <eh.h>

#include <htmliflt.hxx>
#include <serstrm.hxx>

#include <bag.hxx>
#include <charhash.hxx>
#include <htmlfilt.hxx>

long gulcInstances = 0;

extern "C" GUID CLSID_HtmlIFilter = {
    0xE0CA5340,
    0x4534,
    0x11CF,
    { 0xB9, 0x52, 0x00, 0xAA, 0x00, 0x51, 0xFE, 0x20 }
};

//
// BUGBUG:  CLSID_HtmlClass is only necessary because real OLE doesn't
//          yet support persistent handlers as KyleOLE does.
//          When DllGetClassObject is called, real OLE asks for CLSID_HtmlClass,
//          whereas under the persistent handler model, CLSID_HtmlIFilter
//          should be used.
//

extern "C" GUID CLSID_HtmlClass = {
    0x6A7A7550,
    0x4535,
    0x11CF,
    { 0xB9, 0x52, 0x00, 0xAA, 0x00, 0x51, 0xFE, 0x20 }
};

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterBase::CHtmlIFilterBase
//
//  Synopsis:   Base constructor
//
//  Effects:    Manages global refcount
//
//--------------------------------------------------------------------------

CHtmlIFilterBase::CHtmlIFilterBase()
{
    _uRefs = 1;
    InterlockedIncrement( &gulcInstances );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterBase::~CHtmlIFilterBase
//
//  Synopsis:   Base destructor
//
//  Effects:    Manages global refcount
//
//--------------------------------------------------------------------------

CHtmlIFilterBase::~CHtmlIFilterBase()
{
    InterlockedDecrement( &gulcInstances );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterBase::QueryInterface
//
//  Synopsis:   Rebind to other interface
//
//  Arguments:  [riid]      -- IID of new interface
//              [ppvObject] -- New interface * returned here
//
//  Returns:    S_OK if bind succeeded, E_NOINTERFACE if bind failed
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilterBase::QueryInterface( REFIID riid,
                                                          void  ** ppvObject)
{
    //
    // Optimize QueryInterface by only checking minimal number of bytes.
    //
    // IID_IUnknown     = 00000000-0000-0000-C000-000000000046
    // IID_IFilter      = 89BCB740-6119-101A-BCB7-00DD010655AF
    // IID_IPersist     = 0000010c-0000-0000-C000-000000000046
    // IID_IPersistFile = 0000010b-0000-0000-C000-000000000046
    //                          --
    //                           |
    //                           +--- Unique!
    //

    Win4Assert( (IID_IUnknown.Data1     & 0x000000FF) == 0x00 );
    Win4Assert( (IID_IFilter.Data1      & 0x000000FF) == 0x40 );
    Win4Assert( (IID_IPersist.Data1     & 0x000000FF) == 0x0c );
    Win4Assert( (IID_IPersistFile.Data1 & 0x000000FF) == 0x0b );

    IUnknown *pUnkTemp;
    SCODE sc = S_OK;

    switch( riid.Data1 & 0x000000FF )
    {
    case 0x00:
        if ( IID_IUnknown == riid )
            pUnkTemp = (IUnknown *)(IPersist *)(IPersistFile *)this;
        else
            sc = E_NOINTERFACE;
        break;

    case 0x40:
        if ( IID_IFilter == riid )
            pUnkTemp = (IUnknown *)(IFilter *)this;
        else
            sc = E_NOINTERFACE;
        break;

    case 0x0c:
        if ( IID_IPersist == riid )
            pUnkTemp = (IUnknown *)(IPersist *)(IPersistFile *)this;
        else
            sc = E_NOINTERFACE;
        break;

    case 0x0b:
        if ( IID_IPersistFile == riid )
            pUnkTemp = (IUnknown *)(IPersistFile *)this;
        else
            sc = E_NOINTERFACE;
        break;

    default:
        pUnkTemp = 0;
        sc = E_NOINTERFACE;
        break;
    }

    if( 0 != pUnkTemp )
    {
        *ppvObject = (void  * )pUnkTemp;
        pUnkTemp->AddRef();
    }
    return(sc);
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterBase::AddRef
//
//  Synopsis:   Increments refcount
//
//--------------------------------------------------------------------------

ULONG STDMETHODCALLTYPE CHtmlIFilterBase::AddRef()
{
    return InterlockedIncrement( &_uRefs );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterBase::Release
//
//  Synopsis:   Decrement refcount.  Delete if necessary.
//
//--------------------------------------------------------------------------

ULONG STDMETHODCALLTYPE CHtmlIFilterBase::Release()
{
    unsigned long uTmp = InterlockedDecrement( &_uRefs );

    if ( 0 == uTmp )
        delete this;

    return(uTmp);
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterCF::CHtmlIFilterCF
//
//  Synopsis:   Html IFilter class factory constructor
//
//+-------------------------------------------------------------------------

CHtmlIFilterCF::CHtmlIFilterCF()
{
    _uRefs = 1;
    InterlockedIncrement( &gulcInstances );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterCF::~CHtmlIFilterCF
//
//  Synopsis:   Html IFilter class factory destructor
//
//  History:    1-Jan-1996    SitaramR    Created
//
//--------------------------------------------------------------------------

CHtmlIFilterCF::~CHtmlIFilterCF()
{
    InterlockedDecrement( &gulcInstances );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterCF::QueryInterface
//
//  Synopsis:   Rebind to other interface
//
//  Arguments:  [riid]      -- IID of new interface
//              [ppvObject] -- New interface * returned here
//
//  Returns:    S_OK if bind succeeded, E_NOINTERFACE if bind failed
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilterCF::QueryInterface( REFIID riid,
                                                        void  ** ppvObject )
{
    //
    // Optimize QueryInterface by only checking minimal number of bytes.
    //
    // IID_IUnknown      = 00000000-0000-0000-C000-000000000046
    // IID_IClassFactory = 00000001-0000-0000-C000-000000000046
    //                           --
    //                           |
    //                           +--- Unique!
    //

    Win4Assert( (IID_IUnknown.Data1      & 0x000000FF) == 0x00 );
    Win4Assert( (IID_IClassFactory.Data1 & 0x000000FF) == 0x01 );

    IUnknown *pUnkTemp;
    SCODE sc = S_OK;

    switch( riid.Data1 & 0x000000FF )
    {
    case 0x00:
        if ( IID_IUnknown == riid )
            pUnkTemp = (IUnknown *)(IPersist *)(IPersistFile *)this;
        else
            sc = E_NOINTERFACE;
        break;

    case 0x01:
        if ( IID_IClassFactory == riid )
            pUnkTemp = (IUnknown *)(IClassFactory *)this;
        else
            sc = E_NOINTERFACE;
        break;

    default:
        pUnkTemp = 0;
        sc = E_NOINTERFACE;
        break;
    }

    if( 0 != pUnkTemp )
    {
        *ppvObject = (void  * )pUnkTemp;
        pUnkTemp->AddRef();
    }

    return(sc);
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterCF::AddRef
//
//  Synopsis:   Increments refcount
//
//--------------------------------------------------------------------------

ULONG STDMETHODCALLTYPE CHtmlIFilterCF::AddRef()
{
    return InterlockedIncrement( &_uRefs );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterCF::Release
//
//  Synopsis:   Decrement refcount.  Delete if necessary.
//
//--------------------------------------------------------------------------

ULONG STDMETHODCALLTYPE CHtmlIFilterCF::Release()
{
    unsigned long uTmp = InterlockedDecrement( &_uRefs );

    if ( 0 == uTmp )
        delete this;

    return(uTmp);
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterCF::CreateInstance
//
//  Synopsis:   Creates new HtmlIFilter object
//
//  Arguments:  [pUnkOuter] -- 'Outer' IUnknown
//              [riid]      -- Interface to bind
//              [ppvObject] -- Interface returned here
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilterCF::CreateInstance( IUnknown * pUnkOuter,
                                                        REFIID riid,
                                                        void  * * ppvObject )
{
    CHtmlIFilter *  pIUnk = 0;
    SCODE sc = S_OK;

    _se_translator_function tf = _set_se_translator( SystemExceptionTranslator );

    try
    {
        pIUnk = newk(mtNewX, NULL) CHtmlIFilter();
        sc = pIUnk->QueryInterface(  riid , ppvObject );

        if( SUCCEEDED(sc) )
            pIUnk->Release();  // Release extra refcount from QueryInterface
    }
    catch(CException& e)
    {
        Win4Assert( 0 == pIUnk );

        htmlDebugOut(( DEB_ERROR,
                       "Exception 0x%x caught in CHtmlIFilterCF::CreateInstance",
                       e.GetErrorCode() ));
        sc = GetOleError( e );
    }

    _set_se_translator( tf );

    return (sc);
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlIFilterCF::LockServer
//
//  Synopsis:   Force class factory to remain loaded
//
//  Arguments:  [fLock] -- TRUE if locking, FALSE if unlocking
//
//  Returns:    S_OK
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CHtmlIFilterCF::LockServer(BOOL fLock)
{
    if(fLock)
        InterlockedIncrement( &gulcInstances );
    else
        InterlockedDecrement( &gulcInstances );

    return(S_OK);
}

//+-------------------------------------------------------------------------
//
//  Function:   DllGetClassObject
//
//  Synopsis:   Ole DLL load class routine
//
//  Arguments:  [cid]    -- Class to load
//              [iid]    -- Interface to bind to on class object
//              [ppvObj] -- Interface pointer returned here
//
//  Returns:    Html filter class factory
//
//--------------------------------------------------------------------------

extern "C" SCODE STDMETHODCALLTYPE DllGetClassObject( REFCLSID   cid,
                                                      REFIID     iid,
                                                      void **    ppvObj )
{
    IUnknown *  pResult = 0;
    SCODE       sc      = S_OK;

    _se_translator_function tf = _set_se_translator( SystemExceptionTranslator );

    try
    {
        if ( cid == CLSID_HtmlIFilter || cid == CLSID_HtmlClass )
            pResult = (IUnknown *) newk(mtNewX, NULL) CHtmlIFilterCF;
        else
            sc = E_NOINTERFACE;

        if ( pResult )
        {
            sc = pResult->QueryInterface( iid, ppvObj );
            pResult->Release();     // Release extra refcount from QueryInterface
        }
    }
    catch(CException& e)
    {
        if ( pResult )
            pResult->Release();

        htmlDebugOut(( DEB_ERROR,
                       "Exception 0x%x caught in CHtmlIFilterCF::CreateInstance",
                       e.GetErrorCode() ));
        sc = GetOleError( e );
    }

    _set_se_translator( tf );

    return (sc);
}

//+-------------------------------------------------------------------------
//
//  Method:     DllCanUnloadNow
//
//  Synopsis:   Notifies DLL to unload (cleanup global resources)
//
//  Returns:    S_OK if it is acceptable for caller to unload DLL.
//
//--------------------------------------------------------------------------

extern "C" SCODE STDMETHODCALLTYPE DllCanUnloadNow( void )
{
    if ( 0 == gulcInstances )
        return( S_OK );
    else
        return( S_FALSE );
}


HTMLSCAN.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       htmlscan.cxx
//
//  Contents:   Scanner for html files
//
//  Classes:    CHtmlScanner
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <htmlguid.hxx>
#include <charhash.hxx>
#include <htmlfilt.hxx>

//+-------------------------------------------------------------------------
//
//  Method:     CToken::IsMatchProperty
//
//  Synopsis:   Does the token's property match the given property ?
//
//  Arguments:  [propSpec]   -- Property to match
//
//--------------------------------------------------------------------------

BOOL CToken::IsMatchProperty( CFullPropSpec& propSpec )
{
    if ( propSpec.IsPropertyPropid()
         && propSpec.GetPropSet() == _guidPropset
         && propSpec.GetPropertyPropid() == _propid )
    {
        return TRUE;
    }
    else
        return FALSE;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::CHtmlScanner
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]    -- Reference to Html filter
//              [serialStream]   -- Reference to input stream to scan
//
//--------------------------------------------------------------------------

CHtmlScanner::CHtmlScanner( CHtmlIFilter& htmlIFilter,
                            CSerialStream& serialStream )
    : _htmlIFilter(htmlIFilter),
      _serialStream(serialStream),
      _uLenTagBuf(TAG_BUFFER_SIZE),
      _cTagCharsRead(0)
{
    _pwcTagBuf = newk(mtNewX, NULL) WCHAR[ TAG_BUFFER_SIZE ];
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::~CHtmlScanner
//
//  Synopsis:   Destructor
//
//--------------------------------------------------------------------------

CHtmlScanner::~CHtmlScanner()
{
    delete[] _pwcTagBuf;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::GetBlockOfChars
//
//  Synopsis:   Returns a block of chars upto the size requested by user. If
//              any Html tag is encountered, it stops scanning, and returns the
//              token found.
//
//  Arguments:  [cCharsNeeded]   -- Maximum # chars to scan
//              [awcBuffer]      -- Buffer to fill with scanned chars
//              [cCharsScanned]  -- # chars actually scanned
//              [token]          -- Token found (if any)
//
//--------------------------------------------------------------------------

void CHtmlScanner::GetBlockOfChars( ULONG cCharsNeeded,
                                    WCHAR *awcBuffer,
                                    ULONG& cCharsScanned,
                                    CToken& token )
{
    cCharsScanned = 0;

    while ( cCharsNeeded > 0 )
    {
        if ( _serialStream.Eof() )
        {
            token.SetTokenType( EofToken );
            return;
        }

        WCHAR wch = _serialStream.GetChar();
        if ( wch == L'<' )
        {
            //
            // Html tag encountered
            //
            ScanTag( token );
            return;
        }
        else
        {
            //
            // &lt; and &gt; were mapped to Unicode chars from private use area
            // to avoid collision with '<' and '>' chars in Html tags. Map them
            // back to '<' and '>'.
            //
            if ( wch == PRIVATE_USE_MAPPING_FOR_LT )
                wch = L'<';
            else if ( wch == PRIVATE_USE_MAPPING_FOR_GT )
                wch = L'>';

            awcBuffer[cCharsScanned++] = wch;
            cCharsNeeded--;
        }
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::SkipCharsUntilNextRelevantToken
//
//  Synopsis:   Skips characters in input until EOF or an interesting token
//              is found. The list of properties that were asked to be filtered
//              as part of the IFilter::Init call determines whether a token is
//              interesting or not.
//
//  Arguments:  [fFilterContents]     -- Are contents filtered ?
//              [fFilterProperties]   -- Are properties filtered ?
//              [cAttributes]          -- Count of properties
//              [pAttributes]         -- List of properties to be filtered
//
//--------------------------------------------------------------------------

void CHtmlScanner::SkipCharsUntilNextRelevantToken( CToken& token )
{
    //
    // Loop until we find a stop token or end of file
    //
    for (;;)
    {
        if ( _serialStream.Eof() )
        {
            token.SetTokenType( EofToken );
            return;
        }

        WCHAR wch = _serialStream.GetChar();
        if ( wch == L'<' )
        {
            ScanTag( token );

            if ( token.GetTokenType() == EofToken
                 || _htmlIFilter.IsStopToken( token ) )
            {
                return;
            }
            else
            {
                //
                // Uninteresting tag, hence skip tag
                //
                EatTag();
            }
        }
        else
        {
            //
            // Vanilla text
            //
            if ( _htmlIFilter.FFilterContent() )
            {
                _serialStream.UnGetChar( wch );
                token.SetTokenType( TextToken );
                return;
            }
            else
                EatText();
        }
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::ScanTag
//
//  Synopsis:   Scans a Html tag from input
//
//  Arguments:  [token]  -- Token info returned here
//
//--------------------------------------------------------------------------

void CHtmlScanner::ScanTag( CToken& token )
{

    EatBlanks();

    if ( _serialStream.Eof() )
    {
        token.SetTokenType( EofToken );
        return;
    }
    WCHAR wch = _serialStream.GetChar();

    token.SetStartTokenFlag( TRUE );
    if ( wch == L'/' )
    {
        //
        // This is an end tag
        //
        token.SetStartTokenFlag( FALSE );
        EatBlanks();

        if ( _serialStream.Eof() )
        {
            token.SetTokenType( EofToken );
            return;
        }
        wch = _serialStream.GetChar();
    }

    WCHAR awcTagName[MAX_TAG_LENGTH+1];
    unsigned uLenTag = 0;

    //
    // Scan the tag name into szTagName. We scan MAX_TAG_LENGTH
    // characters only, because anything longer is most probably
    // a bogus tag.
    //
    while ( !iswspace(wch)
            && wch != L'>'
            && uLenTag < MAX_TAG_LENGTH )
    {
        awcTagName[uLenTag++] = wch;

        if ( _serialStream.Eof() )
            break;
        wch = _serialStream.GetChar();
    }
    awcTagName[uLenTag] = 0;

    if ( _serialStream.Eof() )
    {
        token.SetTokenType( EofToken );
        return;
    }
    else if ( wch == L'>' || uLenTag == MAX_TAG_LENGTH )
    {
        //
        // Push char back into input stream because a subsequent GetChar()
        // will be expecting to see the char in the input
        //
        _serialStream.UnGetChar( wch );
    }

    TagNameToToken( awcTagName, token );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::ReadTagIntoBuffer
//
//  Synopsis:   Reads the rest of Html tag into the internal buffer
//
//--------------------------------------------------------------------------

void CHtmlScanner::ReadTagIntoBuffer()
{
    _cTagCharsRead = 0;

    if ( _serialStream.Eof() )
        return;

    WCHAR wch = _serialStream.GetChar();
    while ( wch != L'>' )
    {
        if ( _cTagCharsRead >= _uLenTagBuf )
            GrowTagBuffer();
        Win4Assert( _cTagCharsRead < _uLenTagBuf );

        _pwcTagBuf[_cTagCharsRead++] = wch;

        if ( _serialStream.Eof() )
            return;
        wch = _serialStream.GetChar();
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::ScanTagBuffer
//
//  Synopsis:   Scans the internal tag buffer for a given name, and returns the
//              corresponding value
//
//  Arguments:  [awcName]   -- Pattern to match
//              [pwcValue]  -- Start position of value returned here
//              [uLenValue  -- Length of value field
//
//--------------------------------------------------------------------------

void CHtmlScanner::ScanTagBuffer( WCHAR *awcName,
                                  WCHAR * & pwcValue,
                                  unsigned& uLenValue )
{
    unsigned uLenName = wcslen( awcName );

    if ( _cTagCharsRead <= uLenName )
    {
        //
        // Pattern to match is longer than scanned tag
        //
        pwcValue = 0;
        uLenValue = 0;

        return;
    }

    for ( unsigned i=0; i<_cTagCharsRead-uLenName; i++ )
    {
        BOOL fMatch = TRUE;
        for ( unsigned j=0; j<uLenName; j++ )
        {
            //
            // Case insensitive match
            //
            if ( towlower(awcName[j]) != towlower(_pwcTagBuf[i+j]) )
            {
                fMatch = FALSE;
                break;
            }
        }

        if ( fMatch )
        {
            unsigned k = i + uLenName;
            while ( _pwcTagBuf[k] != L'"' && k < _cTagCharsRead )
                k++;

            uLenValue = k - (i + uLenName);
            pwcValue = &_pwcTagBuf[i+uLenName];

            return;
        }
    }

    uLenValue = 0;
    pwcValue = 0;
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::EatTag
//
//  Synopsis:   Skips characters in input until the '>' char, which demarcates
//              the end of the tag
//
//--------------------------------------------------------------------------

void CHtmlScanner::EatTag()
{
    if ( _serialStream.Eof() )
        return;

    WCHAR wch = _serialStream.GetChar();
    while ( wch != L'>' &&  !_serialStream.Eof() )
        wch = _serialStream.GetChar();
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::EatText
//
//  Synopsis:   Skips characters in input until a '<', ie a tag is encountered
//
//--------------------------------------------------------------------------

void CHtmlScanner::EatText()
{
    if ( _serialStream.Eof() )
        return;

    WCHAR wch = _serialStream.GetChar();
    while ( wch != L'<' && !_serialStream.Eof() )
        wch = _serialStream.GetChar();

    if ( wch == L'<' )
        _serialStream.UnGetChar( wch );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::EatBlanks
//
//  Synopsis:   Skips generic white space characters in input
//
//--------------------------------------------------------------------------

void CHtmlScanner::EatBlanks()
{
    if ( _serialStream.Eof() )
        return;

    WCHAR wch = _serialStream.GetChar();
    while ( iswspace(wch) && !_serialStream.Eof() )
        wch = _serialStream.GetChar();

    if ( !iswspace(wch) )
        _serialStream.UnGetChar( wch );
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::TagNameToToken
//
//  Synopsis:   Maps a tag name to token information
//
//  Arguments:  [awcTagName]  -- Tag name to map
//              [token]       -- Token information returned here
//
//--------------------------------------------------------------------------

void CHtmlScanner::TagNameToToken( WCHAR *awcTagName, CToken& token )
{
    //
    // The number of interesting Html tags will be small, hence no need for
    // a table lookup
    //
    switch( awcTagName[0] )
    {
    case L'a':
    case L'A':
        if ( _wcsicmp( awcTagName, L"a" ) == 0 )
        {
            token.SetTokenType( AnchorToken );
            token.SetPropset( CLSID_HtmlInformation );
            token.SetPropid( PID_HREF );
        }
        else if ( _wcsicmp( awcTagName, L"address" ) == 0 )
            token.SetTokenType( BreakToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L'b':
    case L'B':
        if ( _wcsicmp( awcTagName, L"br" ) == 0
             || _wcsicmp( awcTagName, L"blockquote" ) == 0 )
        {
            token.SetTokenType( BreakToken );
        }
        else
            token.SetTokenType( GenericToken );

        break;

    case L'd':
    case L'D':
        if ( _wcsicmp( awcTagName, L"dd" ) == 0
             || _wcsicmp( awcTagName, L"dl" ) == 0
             || _wcsicmp( awcTagName, L"dt" ) == 0 )
        {
            token.SetTokenType( BreakToken );
        }
        else
            token.SetTokenType( GenericToken );

        break;

    case L'f':
    case L'F':
        if ( _wcsicmp( awcTagName, L"form" ) == 0 )
            token.SetTokenType( BreakToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L'h':
    case L'H':
        if ( _wcsicmp( awcTagName, L"h1" ) == 0 )
        {
            token.SetTokenType( Heading1Token );
            token.SetPropset( CLSID_HtmlInformation );
            token.SetPropid( PID_HEADING_1 );
        }
        else if ( _wcsicmp( awcTagName, L"h2" ) == 0 )
        {
            token.SetTokenType( Heading2Token );
            token.SetPropset( CLSID_HtmlInformation );
            token.SetPropid( PID_HEADING_2 );
        }
        else if ( _wcsicmp( awcTagName, L"h3" ) == 0 )
        {
            token.SetTokenType( Heading3Token );
            token.SetPropset( CLSID_HtmlInformation );
            token.SetPropid( PID_HEADING_3 );
        }
        else if ( _wcsicmp( awcTagName, L"h4" ) == 0 )
        {
            token.SetTokenType( Heading4Token );
            token.SetPropset( CLSID_HtmlInformation );
            token.SetPropid( PID_HEADING_4 );
        }
        else if ( _wcsicmp( awcTagName, L"h5" ) == 0 )
        {
            token.SetTokenType( Heading5Token );
            token.SetPropset( CLSID_HtmlInformation );
            token.SetPropid( PID_HEADING_5 );
        }
        else if ( _wcsicmp( awcTagName, L"h6" ) == 0 )
        {
            token.SetTokenType( Heading6Token );
            token.SetPropset( CLSID_HtmlInformation );
            token.SetPropid( PID_HEADING_6 );
        }
        else
            token.SetTokenType( GenericToken );

        break;

    case L'i':
    case L'I':
        if ( _wcsicmp( awcTagName, L"input" ) == 0 )
            token.SetTokenType( InputToken );
        else if ( _wcsicmp( awcTagName, L"img" ) == 0 )
            token.SetTokenType( ImageToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L'l':
    case L'L':
        if ( _wcsicmp( awcTagName, L"li" ) == 0 )
            token.SetTokenType( BreakToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L'm':
    case L'M':
        if ( _wcsicmp( awcTagName, L"math" ) == 0 )
            token.SetTokenType( BreakToken );
        else if (  _wcsicmp( awcTagName, L"meta" ) == 0 )
            token.SetTokenType( MetaToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L'o':
    case L'O':
        if ( _wcsicmp( awcTagName, L"ol" ) == 0 )
            token.SetTokenType( BreakToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L'p':
    case L'P':
        if ( _wcsicmp( awcTagName, L"p" ) == 0 )
            token.SetTokenType( BreakToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L's':
    case L'S':
        if ( _wcsicmp( awcTagName, L"script" ) == 0 )
            token.SetTokenType( ScriptToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L't':
    case L'T':
        if ( _wcsicmp( awcTagName, L"title" ) == 0 )
        {
            token.SetTokenType( TitleToken );
            token.SetPropset( CLSID_SummaryInformation );
            token.SetPropid( PID_TITLE );
        }
        else if ( _wcsicmp( awcTagName, L"table" ) == 0
                  || _wcsicmp( awcTagName, L"th" ) == 0
                  || _wcsicmp( awcTagName, L"tr" ) == 0
                  || _wcsicmp( awcTagName, L"td" ) == 0 )
            token.SetTokenType( BreakToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L'u':
    case L'U':
        if ( _wcsicmp( awcTagName, L"ul" ) == 0 )
            token.SetTokenType( BreakToken );
        else
            token.SetTokenType( GenericToken );

        break;

    case L'!':
        if  ( _wcsicmp( awcTagName, L"!--" ) == 0 )
            token.SetTokenType( CommentToken );
        else
            token.SetTokenType( GenericToken );

        break;

    default:
        //
        // It's an uninteresting tag
        //
        token.SetTokenType( GenericToken );
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CHtmlScanner::GrowTagBuffer
//
//  Synopsis:   Grow internal tag buffer to twice its current size
//
//--------------------------------------------------------------------------

void CHtmlScanner::GrowTagBuffer()
{
    WCHAR *pwcNewTagBuf = newk(mtNewX, NULL) WCHAR[2 * _uLenTagBuf];
    RtlCopyMemory( pwcNewTagBuf,
                   _pwcTagBuf,
                   _uLenTagBuf * sizeof(WCHAR) );

    delete[] _pwcTagBuf;
    _uLenTagBuf *= 2;
    _pwcTagBuf = pwcNewTagBuf;
}


IMAGETAG.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       imagetag.cxx
//
//  Contents:   Parsing algorithm for image tag in Html
//
//  Classes:    CImageTag
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <imagetag.hxx>
#include <htmlguid.hxx>
#include <stgprop.h>

//+-------------------------------------------------------------------------
//
//  Method:     CImageTag::CImageTag
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]    -- Html IFilter
//              [serialStream]   -- Input stream
//
//--------------------------------------------------------------------------

CImageTag::CImageTag( CHtmlIFilter& htmlIFilter,
                      CSerialStream& serialStream )
    : CHtmlElement(htmlIFilter, serialStream),
      _pwcValueBuf(0),
      _cValueChars(0),
      _cValueCharsFiltered(0)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CImageTag::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in pStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CImageTag::GetChunk( STAT_CHUNK * pStat )
{
    SCODE sc = SwitchToNextHtmlElement( pStat );

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Method:     CImageTag::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcOutput] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE CImageTag::GetText( ULONG *pcwcOutput, WCHAR *awcBuffer )
{
    ULONG cCharsRemaining = _cValueChars - _cValueCharsFiltered;

    if ( cCharsRemaining == 0 )
    {
        *pcwcOutput = 0;
        return FILTER_E_NO_MORE_TEXT;
    }

    if ( *pcwcOutput < cCharsRemaining )
    {
        RtlCopyMemory( awcBuffer,
                       _pwcValueBuf + _cValueCharsFiltered,
                       *pcwcOutput * sizeof(WCHAR) );
        _cValueCharsFiltered += *pcwcOutput;

        return S_OK;
    }
    else
    {
        RtlCopyMemory( awcBuffer,
                       _pwcValueBuf + _cValueCharsFiltered,
                       cCharsRemaining * sizeof(WCHAR) );
        _cValueCharsFiltered += cCharsRemaining;
        *pcwcOutput = cCharsRemaining;

        return FILTER_S_LAST_TEXT;
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CImageTag::InitStatChunk
//
//  Synopsis:   Initializes the STAT_CHUNK
//
//  Arguments:  [pStat] -- STAT_CHUNK to initialize
//
//--------------------------------------------------------------------------

void CImageTag::InitStatChunk( STAT_CHUNK *pStat )
{
    //
    // Read the alt field of image tag
    //
    _scanner.ReadTagIntoBuffer();
    _scanner.ScanTagBuffer( L"alt=\"", _pwcValueBuf, _cValueChars );

    _cValueCharsFiltered = 0;

    pStat->idChunk = _htmlIFilter.GetNextChunkId();
    pStat->flags = CHUNK_TEXT;
    pStat->locale = _htmlIFilter.GetLocale();
    pStat->attribute.guidPropSet = CLSID_Storage;
    pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
    pStat->attribute.psProperty.propid = PID_STG_CONTENTS;
    pStat->breakType = CHUNK_EOS;
    pStat->idChunkSource = pStat->idChunk;
    pStat->cwcStartSource = 0;
    pStat->cwcLenSource = 0;
}


INPSTRM.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       inpstrm.cxx
//
//  Contents:   Memory mapped input stream
//
//  Classes:    CMemoryMappedSerialStream
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <htmlfilt.hxx>
#include <codepage.hxx>

//+-------------------------------------------------------------------------
//
//  Method:     CMemoryMappedInputStream::CMemoryMappedInputStream
//
//  Synopsis:   Constructor
//
//--------------------------------------------------------------------------

CMemoryMappedInputStream::CMemoryMappedInputStream()
    : _bytesReadFromMMBuffer(0),
      _charsReadFromTranslatedBuffer(0),
      _cwcTranslatedBuffer(0),
      _ulCodePage(LocaleToCodepage(GetSystemDefaultLCID())),
      _fUnGotChar(FALSE),
      _wch(0)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CMemoryMappedInputStream::~CMemoryMappedInputStream
//
//  Synopsis:   Destructor
//
//--------------------------------------------------------------------------

CMemoryMappedInputStream::~CMemoryMappedInputStream()
{
    if ( _mmStream.Ok() && _mmStreamBuf.Get() != 0 )
        _mmStream.Unmap( _mmStreamBuf );
}

//+-------------------------------------------------------------------------
//
//  Method:     CMemoryMappedInputStream::Init
//
//  Synopsis:   Initialize the memory mapped stream
//
//  Arguments:  [pwszFileName] -- File to be mapped
//
//--------------------------------------------------------------------------

void CMemoryMappedInputStream::Init( WCHAR *pwszFileName )
{
    Win4Assert( pwszFileName );

    _bytesReadFromMMBuffer = 0;

    if ( _mmStream.Ok() )
    {
        _mmStreamBuf.Rewind();
        _mmStream.Close();
    }

    _mmStream.Open( pwszFileName,
                    GENERIC_READ,
                    FILE_SHARE_READ,
                    OPEN_EXISTING );

    if ( _mmStream.Ok() )
    {
        _mmStreamBuf.Init( &_mmStream );
        if ( !_mmStream.isEmpty() )
            _mmStreamBuf.Map( HTML_FILTER_CHUNK_SIZE );
    }
    else
    {
        htmlDebugOut(( DEB_ERROR, "Throwing FILTER_E_ACCESS in CMemoryMappedInputStream::Init\n" ));
        throw( CException( FILTER_E_ACCESS ) );
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CMemoryMappedInputStream::GetChar
//
//  Synopsis:   Read the next consecutive character from the input file
//
//--------------------------------------------------------------------------

WCHAR CMemoryMappedInputStream::GetChar()
{
    if ( !_mmStream.Ok() || !_mmStreamBuf.Get() )
        throw( CException( FILTER_E_ACCESS ) );

    if ( _fUnGotChar )
    {
        _fUnGotChar = FALSE;
        return _wch;
    }

    if ( _charsReadFromTranslatedBuffer == _cwcTranslatedBuffer )
    {
        if ( _bytesReadFromMMBuffer == _mmStreamBuf.Size() )
        {
            if ( _mmStreamBuf.Eof() )
                return WEOF;

            //
            // Try to map in next chunk of file
            //
            _mmStreamBuf.Map( HTML_FILTER_CHUNK_SIZE );
            _bytesReadFromMMBuffer = 0;

            Win4Assert( _mmStreamBuf.Size() > _bytesReadFromMMBuffer );
        }

        ULONG cChUnread = _mmStreamBuf.Size() - _bytesReadFromMMBuffer;

        //
        // Since, the wide chars are precomposed, assume 1:1 translation mapping
        //
        ULONG cChIn = TRANSLATED_CHAR_BUFFER_LENGTH;
        if ( cChIn > cChUnread )
        {
            //
            // Not enough chars in input
            //
            cChIn = cChUnread;
        }

        ULONG cwcActual = 0;
        do
        {
            cwcActual = MultiByteToWideChar( _ulCodePage,
                                             MB_PRECOMPOSED,
                                             (char *) _mmStreamBuf.Get() + _bytesReadFromMMBuffer,
                                             cChIn,
                                             _awcTranslatedBuffer,
                                             TRANSLATED_CHAR_BUFFER_LENGTH );
            if ( cwcActual == 0 )
            {
                if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER && cChIn >= 2 )
                    cChIn /= 2;
                else
                {
                    Win4Assert( !"CMemoryMappedInputStream::GetChar, cannot translate single char" );

                    throw( CException( GetLastError() ) );
                }
            }
        } while( cwcActual == 0 );

        Win4Assert( _bytesReadFromMMBuffer + cChIn <= _mmStreamBuf.Size() );
        Win4Assert( cwcActual <= TRANSLATED_CHAR_BUFFER_LENGTH );

        _bytesReadFromMMBuffer += cChIn;
        _charsReadFromTranslatedBuffer = 0;
        _cwcTranslatedBuffer = cwcActual;
    }

    return _awcTranslatedBuffer[_charsReadFromTranslatedBuffer++];
}

//+-------------------------------------------------------------------------
//
//  Method:     CMemoryMappedInputStream::UnGetChar
//
//  Synopsis:   Pushes(logically) a character back into the input stream
//
//  Arguments:  [wch] -- Char to be pushed back
//
//--------------------------------------------------------------------------

void CMemoryMappedInputStream::UnGetChar( WCHAR wch )
{
    //
    // We can unget only one char at a time
    //
    Win4Assert( !_fUnGotChar );

    _fUnGotChar = TRUE;
    _wch = wch;
}

//+-------------------------------------------------------------------------
//
//  Method:     CMemoryMappedInputStream::Eof
//
//  Synopsis:   Is this the end of input file ?
//
//--------------------------------------------------------------------------

BOOL CMemoryMappedInputStream::Eof()
{
    if ( !_mmStream.Ok() )
        throw( CException( FILTER_E_ACCESS ) );

    if ( _fUnGotChar )
        return FALSE;

    if ( _charsReadFromTranslatedBuffer < _cwcTranslatedBuffer )
        return FALSE;

    if ( _bytesReadFromMMBuffer == _mmStreamBuf.Size() )
        return _mmStreamBuf.Eof();
    else
        return FALSE;
}


INPUTTAG.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       inputtag.cxx
//
//  Contents:   Parsing algorithm for input tag in Html
//
//  Classes:    CInputTag
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <inputtag.hxx>
#include <htmlguid.hxx>
#include <stgprop.h>

//+-------------------------------------------------------------------------
//
//  Method:     CInputTag::CInputTag
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]    -- Html IFilter
//              [serialStream]   -- Input stream
//
//--------------------------------------------------------------------------

CInputTag::CInputTag( CHtmlIFilter& htmlIFilter,
                      CSerialStream& serialStream )
    : CHtmlElement(htmlIFilter, serialStream),
      _pwcValueBuf(0),
      _cValueChars(0),
      _cValueCharsFiltered(0)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CInputTag::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in pStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CInputTag::GetChunk( STAT_CHUNK * pStat )
{
    SCODE sc = SwitchToNextHtmlElement( pStat );

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Method:     CInputTag::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcOutput] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE CInputTag::GetText( ULONG *pcwcOutput, WCHAR *awcBuffer )
{
    ULONG cCharsRemaining = _cValueChars - _cValueCharsFiltered;

    if ( cCharsRemaining == 0 )
    {
        *pcwcOutput = 0;
        return FILTER_E_NO_MORE_TEXT;
    }

    if ( *pcwcOutput < cCharsRemaining )
    {
        RtlCopyMemory( awcBuffer,
                       _pwcValueBuf + _cValueCharsFiltered,
                       *pcwcOutput * sizeof(WCHAR) );
        _cValueCharsFiltered += *pcwcOutput;

        return S_OK;
    }
    else
    {
        RtlCopyMemory( awcBuffer,
                       _pwcValueBuf + _cValueCharsFiltered,
                       cCharsRemaining * sizeof(WCHAR) );
        _cValueCharsFiltered += cCharsRemaining;
        *pcwcOutput = cCharsRemaining;

        return FILTER_S_LAST_TEXT;
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CInputTag::InitStatChunk
//
//  Synopsis:   Initializes the STAT_CHUNK
//
//  Arguments:  [pStat] -- STAT_CHUNK to initialize
//
//--------------------------------------------------------------------------

void CInputTag::InitStatChunk( STAT_CHUNK *pStat )
{
    _cValueCharsFiltered = 0;

    pStat->idChunk = _htmlIFilter.GetNextChunkId();
    pStat->flags = CHUNK_TEXT;
    pStat->locale = _htmlIFilter.GetLocale();
    pStat->attribute.guidPropSet = CLSID_Storage;
    pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
    pStat->attribute.psProperty.propid = PID_STG_CONTENTS;
    pStat->breakType = CHUNK_EOS;
    pStat->idChunkSource = pStat->idChunk;
    pStat->cwcStartSource = 0;
    pStat->cwcLenSource = 0;

    _scanner.ReadTagIntoBuffer();

    //
    // Check input type
    //
    WCHAR *pwcType;
    unsigned cwcType;
    _scanner.ScanTagBuffer( L"type=\"", pwcType, cwcType );

    if ( cwcType == 6           // 6 == wcslen( L"hidden" )
         && _wcsnicmp( pwcType, L"hidden", 6 ) == 0 )
    {
        //
        // Input is hidden, so don't output value field
        //
        _pwcValueBuf = 0;
        _cValueChars = 0;

        return;
    }

    //
    // Read the value field
    //
    _scanner.ScanTagBuffer( L"value=\"", _pwcValueBuf, _cValueChars );
}


MAIN.CXX

//+---------------------------------------------------------------------------
//
// Copyright (C) 1996, Microsoft Corporation.
//
// File:        main.cxx
//
// Contents:    DLL entry point for query.dll
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#define _DECL_DLLMAIN 1
#include <process.h>

#include <osv.hxx>

// global function to find OS platform
#define UNINITIALIZED -1
int g_nOSWinNT = UNINITIALIZED;

//+-------------------------------------------------------------------------
//
//  Function:   GetOSVersion
//
//  Synopsis:   Determine OS version
//
//--------------------------------------------------------------------------

void InitOSVersion()
{
    if ( g_nOSWinNT == UNINITIALIZED )
    {
        OSVERSIONINFOA OSVersionInfo;
        OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFOA );

        if ( GetVersionExA( &OSVersionInfo ) )
        {
            g_nOSWinNT = OSVersionInfo.dwPlatformId;
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   DllMain
//
//  Synopsis:   Called from C-Runtime on process/thread attach/detach
//
//  Arguments:  [hInstance]  --
//              [dwReason]   --
//              [lpReserved] --
//
//----------------------------------------------------------------------------

BOOL WINAPI DllMain( HANDLE hInstance, DWORD dwReason , void * lpReserved )
{
    if ( DLL_PROCESS_ATTACH == dwReason )
    {
        DisableThreadLibraryCalls( (HINSTANCE)hInstance );

        InitOSVersion();
    }

    return TRUE;
}


METATAG.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       metatag.cxx
//
//  Contents:   Parsing algorithm for meta tag in Html
//
//  Classes:    CMetaTag
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <metatag.hxx>
#include <htmlguid.hxx>

//+-------------------------------------------------------------------------
//
//  Method:     CMetaTag::CMetaTag
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]    -- Html IFilter
//              [serialStream]   -- Input stream
//              [clsidMetaInfo]  -- Clsid for meta chunk
//
//--------------------------------------------------------------------------

CMetaTag::CMetaTag( CHtmlIFilter& htmlIFilter,
                    CSerialStream& serialStream,
                    GUID clsidMetaInfo )
    : CHtmlElement(htmlIFilter, serialStream),
      _clsidMetaInfo(clsidMetaInfo),
      _pwcValueBuf(0),
      _cValueChars(0),
      _eState(FilteringValue)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CMetaTag::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in pStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CMetaTag::GetChunk( STAT_CHUNK * pStat )
{
    SCODE sc = SwitchToNextHtmlElement( pStat );

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Method:     CMetaTag::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcOutput] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE CMetaTag::GetText( ULONG *pcwcOutput, WCHAR *awcBuffer )
{
    return FILTER_E_NO_TEXT;
}

//+-------------------------------------------------------------------------
//
//  Method:     CMetaTag::GetValue
//
//  Synopsis:   Retrieves value from current chunk
//
//  Arguments:  [ppPropValue] -- Value returned here
//
//--------------------------------------------------------------------------

SCODE CMetaTag::GetValue( VARIANT **ppPropValue )
{
    switch ( _eState )
    {
    case FilteringValue:
    {
        PROPVARIANT *pPropVar = (PROPVARIANT *) CoTaskMemAlloc( sizeof PROPVARIANT );
        if ( pPropVar == 0 )
            return E_OUTOFMEMORY;

        pPropVar->vt = VT_LPWSTR;
        pPropVar->pwszVal = (WCHAR *) CoTaskMemAlloc( ( _cValueChars + 1 ) * sizeof( WCHAR ) );
        if ( pPropVar->pwszVal == 0 )
        {
            CoTaskMemFree( (void *) pPropVar );
            return E_OUTOFMEMORY;
        }

        RtlCopyMemory( pPropVar->pwszVal, _pwcValueBuf, _cValueChars * sizeof(WCHAR) );
        pPropVar->pwszVal[_cValueChars] = 0;

        *ppPropValue = pPropVar;

        _eState = NoMoreValue;
        return S_OK;
    }

    case NoMoreValue:

        return FILTER_E_NO_MORE_VALUES;

    default:
        Win4Assert( !"CMetaTag::GetValue, unknown state" );
        return E_FAIL;
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CMetaTag::InitStatChunk
//
//  Synopsis:   Initializes the STAT_CHUNK
//
//  Arguments:  [pStat] -- STAT_CHUNK to initialize
//
//--------------------------------------------------------------------------

void CMetaTag::InitStatChunk( STAT_CHUNK *pStat )
{
    WCHAR *pwcName;
    unsigned cwcName;

    //
    // Read the names field
    //
    _scanner.ReadTagIntoBuffer();
    _scanner.ScanTagBuffer( L"name=\"", pwcName, cwcName );

    if ( cwcName == 0 )
    {
        //
        // Try to read http-equiv instead
        //
        _scanner.ScanTagBuffer( L"http-equiv=\"", pwcName, cwcName );
    }

    if ( cwcName == 0 )
    {
        //
        // Chunk of unknown type
        //
        _cValueChars = 0;
        pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
        pStat->attribute.psProperty.propid = 3;
    }
    else
    {
        _scanner.ScanTagBuffer( L"content=\"", _pwcValueBuf, _cValueChars );

        pStat->attribute.psProperty.ulKind = PRSPEC_LPWSTR;

        if ( cwcName > MAX_PROPSPEC_STRING_LENGTH )
            cwcName = MAX_PROPSPEC_STRING_LENGTH;     // Truncate to max length permitted

        RtlCopyMemory( _awszPropSpec, pwcName, cwcName * sizeof(WCHAR) );
        _awszPropSpec[cwcName] = 0;

        pStat->attribute.psProperty.lpwstr = _awszPropSpec;
    }

    _eState = FilteringValue;

    pStat->idChunk = _htmlIFilter.GetNextChunkId();
    pStat->flags = CHUNK_VALUE;
    pStat->locale = _htmlIFilter.GetLocale();
    pStat->attribute.guidPropSet = _clsidMetaInfo;
    pStat->breakType = CHUNK_EOS;
    pStat->idChunkSource = pStat->idChunk;
    pStat->cwcStartSource = 0;
    pStat->cwcLenSource = 0;
}


MMBUF.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       mmstrm.cxx
//
//  Contents:   Memory Mapped Buffer, Safe ptr to stream
//
//  Classes:    CMmStreamBuf
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <pmmstrm.hxx>

CMmStreamBuf::~CMmStreamBuf()
{
    if (_buf && _pStream )
        _pStream->Unmap(*this);
}

SMmStream::~SMmStream()
{
    delete _pStream;
}


MMSCBUF.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       mmscbuf.cxx
//
//  Contents:   Memory Mapped Stream buffer for consecutive buffer mapping
//
//  Classes:    CMmStreamConsecBuf
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include "mmscbuf.hxx"

//+-------------------------------------------------------------------------
//
//  Member:     CMmStreamConsecBuf::CMmStreamConsecBuf, public
//
//  Synopsis:   Constructor
//
//--------------------------------------------------------------------------

CMmStreamConsecBuf::CMmStreamConsecBuf()
: _pMmStream(0)
{
    LARGE_INTEGER liOffset = { 0, 0 };
    _liOffset = liOffset;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStreamConsecBuf::Map, public
//
//  Synopsis:   Map next consecutive part of file
//
//  Arguments:  [cb] -- size of the mapped area
//
//--------------------------------------------------------------------------

void CMmStreamConsecBuf::Map( ULONG cb )
{
    Win4Assert( 0 != _pMmStream );

    if ( Get() != 0 )
        _pMmStream->Unmap( *this );

    LARGE_INTEGER liNewOffset;
    LARGE_INTEGER liStreamSize={_pMmStream->SizeLow(), _pMmStream->SizeHigh()};

     liNewOffset.QuadPart = cb + _liOffset.QuadPart;

    if ( liNewOffset.QuadPart > liStreamSize.QuadPart )
    {
        cb = (ULONG)(liStreamSize.QuadPart - _liOffset.QuadPart);
        liNewOffset = liStreamSize;
    }

    _pMmStream->Map( *this,
                     cb,
                     _liOffset.LowPart,
                     _liOffset.HighPart );

    _liOffset = liNewOffset;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStreamConsecBuf::Init, public
//
//  Synopsis:   Initizializes CMmStreamConsecBuf
//
//  Arguments:  [pMmStream] -- pointer to the CMmStream from which to fill
//                             the buffer
//
//--------------------------------------------------------------------------

void CMmStreamConsecBuf::Init( PMmStream * pMmStream )
{
    _pMmStream = pMmStream;
    LARGE_INTEGER liOffset = { 0, 0 };
    _liOffset = liOffset;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStreamConsecBuf::Rewind, public
//
//  Synopsis:   Rewind file to beginning.
//
//--------------------------------------------------------------------------

void CMmStreamConsecBuf::Rewind()
{

    Win4Assert( 0 != _pMmStream );

    if ( Get() != 0 )
        _pMmStream->Unmap( *this );

    _liOffset.LowPart = 0;
    _liOffset.HighPart = 0;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStreamConsecBuf::Eof, public
//
//  Synopsis:   Returns whether end of file has been hit
//
//  Returns:    FALSE if there is still more file to be mapped.  TRUE
//              otherwise.
//
//--------------------------------------------------------------------------

BOOL CMmStreamConsecBuf::Eof()
{
    Win4Assert( 0 != _pMmStream );
    return( ( (ULONG) _liOffset.HighPart == _pMmStream->SizeHigh() ) &&
            ( _liOffset.LowPart == _pMmStream->SizeLow() ) );
}


MMSTRM.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corp.
//
//  File:       mmstrm.cxx
//
//  Contents:   Memory Mapped Stream using win32 API
//
//  Classes:    CMmStream, CMmStreamBuf
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <mmstrm.hxx>
#include <osv.hxx>

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::CMmStream
//
//  Synopsis:   constructor
//
//  Arguments:  [wcsPath] - file path
//
//--------------------------------------------------------------------------

CMmStream::CMmStream ( WCHAR* wcsPath )
    : _hMap(0)
{
    OpenRorW ( wcsPath );
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::CMmStream
//
//  Synopsis:   constructor
//
//--------------------------------------------------------------------------
CMmStream::CMmStream()
        : _hFile(INVALID_HANDLE_VALUE),
          _hMap(0),
          _fWrite(FALSE)
{
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::~CMmStream
//
//  Synopsis:   destructor
//
//
//--------------------------------------------------------------------------
CMmStream::~CMmStream()
{
    if (Ok())
    {
        if(_hMap )
        {
            CloseHandle(_hMap);
            _hMap = 0;
        }

        CloseHandle ( _hFile );
        _hFile = INVALID_HANDLE_VALUE;
    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::OpenRorW, public
//
//  Synopsis:   Open stream
//
//  Arguments:  [path] - file path
//
//  Notes:      Tries to open read/write, create if necessary.
//              If it fails, try opening read only. If read/write,
//              regions are mapped in COMMON_PAGE_SIZE increments.
//
//--------------------------------------------------------------------------

void CMmStream::OpenRorW ( WCHAR* wcsPath )
{
    // Try read write access (create if possible)

    if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
    {
        _hFile = CreateFile( wcsPath,
                             GENERIC_READ | GENERIC_WRITE,
                             FILE_SHARE_READ,
                             0, // security
                             OPEN_ALWAYS,
                             FILE_ATTRIBUTE_NORMAL,
                             0 ); // template
    }
    else // win95
    {
        // Worst case - size of multibyte == size of Unicode
        int nLen = wcslen( wcsPath ) * 2;
        char *pszPath = new char[nLen];

        if ( pszPath == NULL )
        {
            _hFile = INVALID_HANDLE_VALUE;
        }
        else
        {
            if ( wcstombs( pszPath, wcsPath, nLen ) == -1 )
            {
                _hFile = INVALID_HANDLE_VALUE;
            }
            else
            {
                _hFile = CreateFileA( pszPath,
                                      GENERIC_READ | GENERIC_WRITE,
                                      FILE_SHARE_READ,
                                      0, // security
                                      OPEN_ALWAYS,
                                      FILE_ATTRIBUTE_NORMAL,
                                      0 ); // template
            }
            delete [] pszPath;
            pszPath = NULL;
        }
    }

    if ( Ok() )
    {
        _fWrite = TRUE;
    }
    else
    {
        _fWrite = FALSE;
        // Try read only access

        if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
        {
            _hFile = CreateFile( wcsPath,
                                 GENERIC_READ,
                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
                                 0, // security
                                 OPEN_EXISTING,
                                 FILE_ATTRIBUTE_NORMAL,
                                 0 ); // template
        }
        else // win95
        {
            // Worst case - size of multibyte == size of Unicode
            int nLen = wcslen( wcsPath ) * 2;
            char *pszPath = new char[nLen];

            if ( pszPath == NULL )
            {
                _hFile = INVALID_HANDLE_VALUE;
            }
            else
            {
                if ( wcstombs( pszPath, wcsPath, nLen ) == -1 )
                {
                    _hFile = INVALID_HANDLE_VALUE;
                }
                else
                {
                    _hFile = CreateFileA( pszPath,
                                          GENERIC_READ,
                                          FILE_SHARE_READ | FILE_SHARE_WRITE,
                                          0, // security
                                          OPEN_EXISTING,
                                          FILE_ATTRIBUTE_NORMAL,
                                          0 ); // template
                }
                delete [] pszPath;
                pszPath = NULL;
            }
        }
    }

    if (Ok())
    {
        _sizeLow = GetFileSize ( _hFile, &_sizeHigh );

        if (_sizeLow == 0xffffffff && GetLastError() != NO_ERROR)
        {
            Close();
            throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
        }

        if (_fWrite )
        {
            CommonPageRound(_sizeLow, _sizeHigh);
        }

        if ( _sizeLow != 0 || _sizeHigh != 0 )
        {
            if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
            {
                _hMap = CreateFileMapping( _hFile,
                                           0, // security
                                           _fWrite ? PAGE_READWRITE : PAGE_READONLY,
                                           _sizeHigh,
                                           _sizeLow,
                                           0 ); // name
            }
            else // windows 95
            {
                _hMap = CreateFileMappingA( _hFile,
                                            0, // security
                                            _fWrite ? PAGE_READWRITE : PAGE_READONLY,
                                            _sizeHigh,
                                            _sizeLow,
                                            0 ); // name
            }

            if (_hMap == NULL)
            {
                Close();
                throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
            }
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   IsCreateExisting
//
//  Synopsis:   Tests if the error was a result of trying to create a
//              file that already exists.
//
//  Arguments:  [Error]      --  Error returned by the system.
//              [modeAccess] --  access mode specified to CreateFile.
//              [modeCreate] --  create mode specified to CreateFile.
//
//  Returns:    TRUE if the failure is because of trying to create a "New"
//              file with the same name as the one already existing.
//              FALSE otherwise.
//
//----------------------------------------------------------------------------

BOOL IsCreateExisting( ULONG Error, ULONG modeAccess, ULONG modeCreate )
{
    if ( (Error == ERROR_ALREADY_EXISTS || Error == ERROR_FILE_EXISTS) &&
         (modeAccess & GENERIC_WRITE) &&
         (modeCreate & CREATE_NEW) )
    {
       return TRUE;
    }
    else
    {
        return FALSE;
    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::Open, public
//
//  Synopsis:   Open stream
//
//  Arguments:  [path] - file path
//              [modeAccess] -- access mode
//              [modeShare] -- sharing mode
//              [modeCreate] -- create mode
//
//--------------------------------------------------------------------------

void CMmStream::Open(
    const WCHAR* wcsPath,
    ULONG modeAccess,
    ULONG modeShare,
    ULONG modeCreate,
    ULONG modeAttribute)
{
    _fWrite = FALSE;

    if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
    {
        _hFile = CreateFile( wcsPath,
                             modeAccess,
                             modeShare,
                             0, // security
                             modeCreate,
                             modeAttribute,
                             0 ); // template
    }
    else // win95
    {
        // Worst case - size of multibyte == size of Unicode
        int nLen = wcslen( wcsPath ) * 2;
        char *pszPath = new char[nLen];

        if ( pszPath == NULL )
        {
            _hFile = INVALID_HANDLE_VALUE;
        }
        else
        {
            if ( wcstombs( pszPath, wcsPath, nLen ) == -1 )
            {
                _hFile = INVALID_HANDLE_VALUE;
            }
            else
            {
                _hFile = CreateFileA( pszPath,
                                      modeAccess,
                                      modeShare,
                                      0, // security
                                      modeCreate,
                                      modeAttribute,
                                      0 ); // template
            }

            delete [] pszPath;
            pszPath = NULL;
        }
    }

    if ( !Ok() )
    {
        //
        // If applicable, delete a zomby and re-create it.
        //
        if ( IsCreateExisting( GetLastError(), modeAccess, modeCreate ) )
        {
            htmlDebugOut(( DEB_ERROR, "CreateNew on %ws - already exists\n",
                         wcsPath ));
            modeCreate &= ~CREATE_NEW;   // Turn off the Create new flag
            modeCreate |= CREATE_ALWAYS; // Enable the CREATE_ALWAYS flag.

            if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
            {
                _hFile = CreateFile( wcsPath,
                                     modeAccess,
                                     modeShare,
                                     0,          // security
                                     modeCreate,
                                     FILE_ATTRIBUTE_NORMAL,
                                     0 );        // template
            }
            else // win95
            {
                // Worst case - size of multibyte == size of Unicode
                int nLen = wcslen( wcsPath );
                char *pszPath = new char[nLen];

                if ( pszPath == NULL )
                {
                    _hFile = INVALID_HANDLE_VALUE;
                }
                else
                {
                    if ( wcstombs( pszPath, wcsPath, nLen ) == -1 )
                    {
                        _hFile = INVALID_HANDLE_VALUE;
                    }
                    else
                    {
                        _hFile = CreateFileA( pszPath,
                                              modeAccess,
                                              modeShare,
                                              0,          // security
                                              modeCreate,
                                              FILE_ATTRIBUTE_NORMAL,
                                              0 );        // template
                    }
                    delete [] pszPath;
                    pszPath = NULL;
                }
            }
        }
    }

    if ( Ok() )
    {
        _sizeLow = GetFileSize ( _hFile, &_sizeHigh );

        if (_sizeLow == 0xffffffff && GetLastError() != NO_ERROR)
        {
            Close();
            htmlDebugOut (( DEB_ERROR, "Open stream %ws failed\n", wcsPath ));
            throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
        }

        if ( modeAccess & GENERIC_WRITE )
        {
            _fWrite = TRUE;
            CommonPageRound(_sizeLow, _sizeHigh);
            // BUGBUG OFS zero size files
            if (_sizeLow == 0 && _sizeHigh == 0)
            {
                if ( SetFilePointer ( _hFile,
                                      COMMON_PAGE_SIZE,
                                      0,
                                      FILE_BEGIN ) == 0xFFFFFFFF  &&
                     GetLastError() != NO_ERROR )
                {
                    Close();
                    htmlDebugOut(( DEB_ERROR,
                                 "CMmStream::Open -- SetFilePointer returned %d\n",
                                 GetLastError() ));
                    throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
                }

                if ( !SetEndOfFile( _hFile ) )
                {
                    Close();
                    htmlDebugOut(( DEB_ERROR,
                                 "CMmStream::Open -- SetEndOfFile returned %d\n",
                                 GetLastError() ));
                    throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
                }
                _sizeLow = COMMON_PAGE_SIZE;
            }
        }

        if ( _sizeLow != 0 || _sizeHigh != 0 )
        {
            if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
            {
                _hMap = CreateFileMapping( _hFile,
                                           0, // security
                                           _fWrite ? PAGE_READWRITE : PAGE_READONLY,
                                           _sizeHigh,
                                           _sizeLow,
                                           0 ); // name
            }
            else // win95
            {
                _hMap = CreateFileMappingA( _hFile,
                                            0, // security
                                            _fWrite ? PAGE_READWRITE : PAGE_READONLY,
                                            _sizeHigh,
                                            _sizeLow,
                                            0 ); // name
            }

            if (_hMap == NULL)
            {
                Close();
                htmlDebugOut (( DEB_ERROR, "File mapping failed\n" ));
                throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
            }
        }
    }
    else
    {
        htmlDebugOut (( DEB_ITRACE, "Open failed on MM Stream; GetLastError()=0x%x\n",
                      GetLastError() ));
    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::Close, public
//
//  Synopsis:   Create all handles
//
//--------------------------------------------------------------------------

void CMmStream::Close()
{
    if (Ok())
    {
        if(_hMap )
        {
            if ( !CloseHandle(_hMap))
            {
                htmlDebugOut (( DEB_ERROR, "Closing file mapping failed\n" ));
                throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
            }
            _hMap = 0;
        }
        if ( !CloseHandle ( _hFile ))
        {

            htmlDebugOut (( DEB_ERROR, "Closing file handle failed\n" ));
            throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
        }
        _hFile = INVALID_HANDLE_VALUE;
    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::SetSize, public
//
//  Synopsis:   Increase the size of the (writable) file
//
//  Arguments:  [storage] -- storage (not used)
//              [newSizeLow]  -- Low 32 bits of filesize
//              [newSizeHigh] -- High 32 bits of filesize
//
//--------------------------------------------------------------------------

void CMmStream::SetSize ( PStorage& storage,
    ULONG newSizeLow, ULONG newSizeHigh )
{
    if (_hMap != 0)
        CloseHandle(_hMap);

    LARGE_INTEGER sizeOld = { _sizeLow, _sizeHigh };
    LARGE_INTEGER sizeNew = { newSizeLow, newSizeHigh };

    if (sizeNew.QuadPart < sizeOld.QuadPart)
    {
        if ( SetFilePointer ( _hFile,
                              newSizeLow,
                              (long *)&newSizeHigh,   // BUGBUG: Win32
                              FILE_BEGIN ) == 0xFFFFFFFF  &&
             GetLastError() != NO_ERROR )
        {
            htmlDebugOut(( DEB_ERROR,
                         "CMmStream::Close -- SetFilePointer returned %d\n",
                         GetLastError() ));
            throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
        }

        if ( !SetEndOfFile( _hFile ) )
        {
            htmlDebugOut(( DEB_ERROR,
                         "CMmStream::Close -- SetEndOfFile returned %d\n",
                         GetLastError() ));
            throw ( CException(HRESULT_FROM_WIN32(GetLastError())));
        }

    }

    if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
    {
        _hMap = CreateFileMapping( _hFile,
                                   0, // security
                                   PAGE_READWRITE,
                                   newSizeHigh,
                                   newSizeLow,
                                   0 ); // name
    }
    else // win95
    {
        _hMap = CreateFileMappingA( _hFile,
                                    0, // security
                                    PAGE_READWRITE,
                                    newSizeHigh,
                                    newSizeLow,
                                    0 ); // name
    }

    if (_hMap == 0)
        throw ( CException(HRESULT_FROM_WIN32(GetLastError())));

    _sizeLow = newSizeLow;
    _sizeHigh = newSizeHigh;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::MapAll, public
//
//  Synopsis:   Create file mapping
//
//--------------------------------------------------------------------------
void CMmStream::MapAll ( CMmStreamBuf& sbuf )
{
    Win4Assert ( SizeHigh() == 0 );
    Map ( sbuf, SizeLow(), 0, 0 );
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::Map, private
//
//  Synopsis:   Create file mapping
//
//  Arguments:  [cb] -- size of the mapped area
//              [offLow] -- low part of file offset
//              [offHigh] -- high part of file offset
//
//--------------------------------------------------------------------------

void CMmStream::Map ( CMmStreamBuf& sbuf,
                      ULONG cb, ULONG offLow, ULONG offHigh,
                      BOOL
                    )
{
    Win4Assert ( _hMap != 0 );

    if ( !_fWrite )
    {
        //
        // Adjust size to be min( cb, sizeoffile - off )
        //

        LARGE_INTEGER size = { _sizeLow, _sizeHigh };
        LARGE_INTEGER off  = { offLow, offHigh };
        LARGE_INTEGER licb = { cb, 0 };
        LARGE_INTEGER diff;

        diff.QuadPart = size.QuadPart - off.QuadPart;

        if ( diff.QuadPart < licb.QuadPart )
        {
            cb = diff.LowPart;
            htmlDebugOut(( DEB_ITRACE,
                         "CMmStream::Map -- reducing map to 0x%x bytes\n",
                         cb ));
        }
    }

    void* buf = MapViewOfFile (
            _hMap,
            _fWrite? FILE_MAP_WRITE: FILE_MAP_READ,
            offHigh,
            offLow,
            cb );

    if ( 0 == buf )
    {
        htmlDebugOut(( DEB_ERROR,
                     "CMmStream::Map -- MapViewOfFile returned %d\n",
                     GetLastError() ));
        throw( CException( HRESULT_FROM_WIN32( GetLastError() ) ) );
    }

    sbuf.SetBuf( buf );
    sbuf.SetSize ( cb );
    sbuf.SetStream ( this );
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::Unmap, public
//
//  Synopsis:   Unmap the view of file
//
//  History:    10-Mar-93 BartoszM  Created
//
//--------------------------------------------------------------------------
void CMmStream::Unmap( CMmStreamBuf& sbuf )
{
     if ( !UnmapViewOfFile(sbuf.Get()) )
     {
         htmlDebugOut(( DEB_ERROR, "UnmapViewOfFile returned %d\n",
                      GetLastError() ));
         throw( CException( HRESULT_FROM_WIN32( GetLastError() ) ) );
     }

     sbuf.SetBuf ( 0 );
}

//+-------------------------------------------------------------------------
//
//  Member:     CMmStream::Flush, public
//
//  Synopsis:   Flush the view back to disk
//
//  History:    10-Mar-93 BartoszM  Created
//
//--------------------------------------------------------------------------

void CMmStream::Flush( CMmStreamBuf& sbuf, ULONG cb )
{
    FlushViewOfFile ( sbuf.Get(), cb );
}


PCH.CXX

//+-------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation
//
//  File:       pch.cxx
//
//  Contents:   Pre-compiled header
//
//--------------------------------------------------------------------------

#include <windows.h>

#define VARIANT PROPVARIANT

#include <filter.h>
#include <global.hxx>
#include <filterr.h>
#include <htmlscan.hxx>

#pragma hdrstop


PROPSPEC.CXX

//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation
//
//  File:       propspec.cxx
//
//  Contents:   C++ wrappers for FULLPROPSPEC
//
//--------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <propspec.hxx>

//+-------------------------------------------------------------------------
//
//  Member:     CFullPropSpec::CFullPropSpec, public
//
//  Synopsis:   Default constructor
//
//  Effects:    Defines property with null guid and propid 0
//
//--------------------------------------------------------------------------

CFullPropSpec::CFullPropSpec()
{
    memset( &_guidPropSet, 0, sizeof( _guidPropSet ) );
    _psProperty.ulKind = PRSPEC_PROPID;
    _psProperty.propid = 0;
}

//+-------------------------------------------------------------------------
//
//  Member:     CFullPropSpec::CFullPropSpec, public
//
//  Synopsis:   Construct propid based propspec
//
//  Arguments:  [guidPropSet]  -- Property set
//              [pidProperty] -- Property
//
//--------------------------------------------------------------------------

CFullPropSpec::CFullPropSpec( GUID const & guidPropSet, PROPID pidProperty )
        : _guidPropSet( guidPropSet )
{
    _psProperty.ulKind = PRSPEC_PROPID;
    _psProperty.propid = pidProperty;
}

//+-------------------------------------------------------------------------
//
//  Member:     CFullPropSpec::CFullPropSpec, public
//
//  Synopsis:   Construct name based propspec
//
//  Arguments:  [guidPropSet] -- Property set
//              [wcsProperty] -- Property
//
//--------------------------------------------------------------------------

CFullPropSpec::CFullPropSpec( GUID const & guidPropSet,
                              WCHAR const * wcsProperty )
        : _guidPropSet( guidPropSet )
{
    _psProperty.ulKind = PRSPEC_PROPID;
    SetProperty( wcsProperty );
}

//+-------------------------------------------------------------------------
//
//  Member:     CFullPropSpec::CFullPropSpec, public
//
//  Synopsis:   Copy constructor
//
//  Arguments:  [src] -- Source property spec
//
//--------------------------------------------------------------------------

CFullPropSpec::CFullPropSpec( CFullPropSpec const & src )
        : _guidPropSet( src._guidPropSet )
{
    _psProperty.ulKind = src._psProperty.ulKind;

    if ( _psProperty.ulKind == PRSPEC_LPWSTR )
    {
        if ( src._psProperty.lpwstr )
        {
            _psProperty.ulKind = PRSPEC_PROPID;
            SetProperty( src._psProperty.lpwstr );
        }
        else
            _psProperty.lpwstr = 0;

    }
    else
    {
        _psProperty.propid = src._psProperty.propid;
    }
}

inline void * operator new( size_t size, void * p )
{
    return( p );
}

//+-------------------------------------------------------------------------
//
//  Member:     CFullPropSpec::operator=, public
//
//  Synopsis:   Assignment operator
//
//  Arguments:  [Property] -- Source property
//
//--------------------------------------------------------------------------

CFullPropSpec & CFullPropSpec::operator=( CFullPropSpec const & Property )
{
    //
    // Clean up.
    //

    CFullPropSpec::~CFullPropSpec();

    new (this) CFullPropSpec( Property );

    return *this;
}

CFullPropSpec::~CFullPropSpec()
{
    if ( _psProperty.ulKind == PRSPEC_LPWSTR &&
         _psProperty.lpwstr )
    {
        CoTaskMemFree( _psProperty.lpwstr );
    }
}

void CFullPropSpec::SetProperty( PROPID pidProperty )
{
    if ( _psProperty.ulKind == PRSPEC_LPWSTR &&
         0 != _psProperty.lpwstr )
    {
        CoTaskMemFree( _psProperty.lpwstr );
    }

    _psProperty.ulKind = PRSPEC_PROPID;
    _psProperty.propid = pidProperty;
}

BOOL CFullPropSpec::SetProperty( WCHAR const * wcsProperty )
{
    if ( _psProperty.ulKind == PRSPEC_LPWSTR &&
         0 != _psProperty.lpwstr )
    {
        CoTaskMemFree( _psProperty.lpwstr );
    }

    _psProperty.ulKind = PRSPEC_LPWSTR;

    int len = (wcslen( wcsProperty ) + 1) * sizeof( WCHAR );

    _psProperty.lpwstr = (WCHAR *)CoTaskMemAlloc( len );

    if ( 0 != _psProperty.lpwstr )
    {
        memcpy( _psProperty.lpwstr,
                wcsProperty,
                len );
        return( TRUE );
    }
    else
    {
        _psProperty.lpwstr = 0;
        return( FALSE );
    }
}

int CFullPropSpec::operator==( CFullPropSpec const & prop ) const
{
    if ( memcmp( &prop._guidPropSet,
                 &_guidPropSet,
                 sizeof( _guidPropSet ) ) != 0 ||
         prop._psProperty.ulKind != _psProperty.ulKind )
    {
        return( 0 );
    }

    switch( _psProperty.ulKind )
    {
    case PRSPEC_LPWSTR:
        return( _wcsicmp( GetPropertyName(), prop.GetPropertyName() ) == 0 );
        break;

    case PRSPEC_PROPID:
        return( GetPropertyPropid() == prop.GetPropertyPropid() );
        break;

    default:
        return( 0 );
        break;
    }
}

int CFullPropSpec::operator!=( CFullPropSpec const & prop ) const
{
    if (*this == prop)
        return( 0 );
    else
        return( 1 );
}


PROPTAG.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       proptag.cxx
//
//  Contents:   Generic parsing algorithm for property tags, such as title and headings
//
//  Classes:    CPropertyTag
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <proptag.hxx>
#include <htmlguid.hxx>
#include <stgprop.h>

//+-------------------------------------------------------------------------
//
//  Method:     CPropertyTag::CPropertyTag
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]  -- Reference to Html filter
//              [serialStream] -- Reference to input stream
//              [propSpec]     -- Property spec
//              [eTokType]     -- Token corresponding to this property
//
//--------------------------------------------------------------------------

CPropertyTag::CPropertyTag( CHtmlIFilter& htmlIFilter,
                            CSerialStream& serialStream,
                            CFullPropSpec& propSpec,
                            HtmlTokenType eTokType )
    : CHtmlElement(htmlIFilter, serialStream),
      _eState(NoMoreProperty),
      _ulIdContentChunk(0),
      _pwcPropBuf(0),
      _uLenPropBuf(0),
      _cPropChars(0),
      _cPropCharsFiltered(0),
      _propSpec(propSpec),
      _eTokType(eTokType)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CPropertyTag::~CPropertyTag
//
//  Synopsis:   Destructor
//
//--------------------------------------------------------------------------

CPropertyTag::~CPropertyTag()
{
    delete[] _pwcPropBuf;
}

//+-------------------------------------------------------------------------
//
//  Method:     CPropertyTag::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in pStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CPropertyTag::GetChunk( STAT_CHUNK * pStat )
{
    switch ( _eState )
    {
    case FilteringContent:
    case FilteringProperty:
    case FilteringPropertyButContentNotFiltered:
    {
        SCODE sc = SkipRemainingTextAndGotoNextChunk( pStat );

        return sc;
    }

    case NoMoreContent:
        _eState = FilteringProperty;

        pStat->idChunk = _htmlIFilter.GetNextChunkId();
        pStat->locale = _htmlIFilter.GetLocale();
        pStat->cwcStartSource = 0;
        pStat->cwcLenSource = 0;
        pStat->flags = CHUNK_TEXT;
        pStat->attribute.guidPropSet = _propSpec.GetPropSet();

        Win4Assert( _propSpec.IsPropertyPropid() );

        pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
        pStat->attribute.psProperty.propid = _propSpec.GetPropertyPropid();
        pStat->idChunkSource = _ulIdContentChunk;
        pStat->breakType = CHUNK_EOS;

        break;

    case NoMoreProperty:
    {
        //
        // Skip over the end property tag
        //
        _scanner.EatTag();

        SCODE sc = SwitchToNextHtmlElement( pStat );

        return sc;
    }

    default:
        Win4Assert( !"Unknown _eState in CPropertyTag::GetChunk" );
        htmlDebugOut(( DEB_ERROR, "CPropertyTag::GetChunk, unkown property tag state: %d\n", _eState ));
    }

    return S_OK;
}

//+-------------------------------------------------------------------------
//
//  Method:     CPropertyTag::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcBuffer] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE CPropertyTag::GetText( ULONG *pcwcOutput, WCHAR *awcBuffer )
{
    switch ( _eState )
    {
    case NoMoreContent:
    case NoMoreProperty:
         return FILTER_E_NO_MORE_TEXT;

    case FilteringContent:
    case FilteringPropertyButContentNotFiltered:
    {
        ULONG cCharsRead = 0;
        while ( cCharsRead < *pcwcOutput )
        {
            ULONG cCharsScanned;
            ULONG cCharsNeeded = *pcwcOutput - cCharsRead;
            CToken token;
            _scanner.GetBlockOfChars( cCharsNeeded,
                                      awcBuffer + cCharsRead,
                                      cCharsScanned,
                                      token );

            cCharsRead += cCharsScanned;
            if ( cCharsScanned == cCharsNeeded )
            {
                //
                // We've read the #chars requested by user
                //
                break;
            }

            HtmlTokenType eTokType = token.GetTokenType();
            if ( eTokType == EofToken || eTokType == _eTokType )
            {
                //
                // End of file or end property tag
                //
                if ( _eState == FilteringContent )
                    _eState = NoMoreContent;
                else
                    _eState = NoMoreProperty;

                break;
            }
            else if ( eTokType == BreakToken )
            {
                //
                // Insert a newline char
                //
                Win4Assert( cCharsRead < *pcwcOutput );
                awcBuffer[cCharsRead++] = L'\n';
                _scanner.EatTag();
            }
            else
            {
                //
                // Uninteresting tag, so skip over tag and continue processing
                //
                _scanner.EatTag();
            }
        }

        //
        // Keep a copy of property in our internal buffer
        //
        if ( cCharsRead > 0 )
            ConcatenateProperty( awcBuffer, cCharsRead );

        *pcwcOutput = cCharsRead;

        if ( _eState == NoMoreContent || _eState == NoMoreProperty )
            return FILTER_S_LAST_TEXT;
        else
            return S_OK;

        break;
    }

    case FilteringProperty:
    {
        ULONG cCharsRemaining = _cPropChars - _cPropCharsFiltered;

        if ( cCharsRemaining == 0 )
        {
            _eState = NoMoreProperty;
            return FILTER_E_NO_MORE_TEXT;
        }

        if ( *pcwcOutput < cCharsRemaining )
        {
            RtlCopyMemory( awcBuffer,
                           _pwcPropBuf + _cPropCharsFiltered,
                           *pcwcOutput * sizeof(WCHAR) );
            _cPropCharsFiltered += *pcwcOutput;

            return S_OK;
        }
        else
        {
            RtlCopyMemory( awcBuffer,
                           _pwcPropBuf + _cPropCharsFiltered,
                           cCharsRemaining * sizeof(WCHAR) );
            _cPropCharsFiltered += cCharsRemaining;
            *pcwcOutput = cCharsRemaining;
            _eState = NoMoreProperty;

            return FILTER_S_LAST_TEXT;
        }

        break;
    }

    default:
        Win4Assert( !"Unknown value of _eState" );
        htmlDebugOut(( DEB_ERROR,
                       "CPropertyTag::GetText, unknown value of _eState: %d\n",
                       _eState ));
        return S_OK;
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CPropertyTag::InitStatChunk
//
//  Synopsis:   Initializes the STAT_CHUNK as part of a GetChunk call
//
//  Arguments:  [pStat] -- STAT_CHUNK to initialize
//
//--------------------------------------------------------------------------

void CPropertyTag::InitStatChunk( STAT_CHUNK *pStat )
{
    //
    // Skip over the rest of property tag
    //
    _scanner.EatTag();

    _cPropChars = 0;
    _cPropCharsFiltered = 0;

    pStat->idChunk = _htmlIFilter.GetNextChunkId();
    pStat->flags = CHUNK_TEXT;
    pStat->breakType = CHUNK_EOS;
    pStat->locale = _htmlIFilter.GetLocale();
    pStat->cwcStartSource = 0;
    pStat->cwcLenSource = 0;

    if ( _htmlIFilter.FFilterContent() )
    {
        //
        // Store the idChunk so that it can be retrieved later to set the value of
        // idChunkSource when initializing the pseudo-property
        //
        _ulIdContentChunk = pStat->idChunk;
        pStat->attribute.guidPropSet = CLSID_Storage;
        pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
        pStat->attribute.psProperty.propid = PID_STG_CONTENTS;
        pStat->idChunkSource = pStat->idChunk;

        _eState = FilteringContent;
    }
    else
    {
        //
        // We've been asked to filter only the pseudo-property, so skip content filtering
        //
        _ulIdContentChunk = 0;
        pStat->attribute.guidPropSet = _propSpec.GetPropSet();

        Win4Assert( _propSpec.IsPropertyPropid() );

        pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
        pStat->attribute.psProperty.propid = _propSpec.GetPropertyPropid();
        pStat->idChunkSource = 0;

        _eState = FilteringPropertyButContentNotFiltered;
    }
 }

 //+-------------------------------------------------------------------------
//
//  Method:     CPropertyTag::ConcatenateProperty
//
//  Synopsis:   Append the Property suffix to Property stored in internal buffer
//
//  Arguments:  [awcPropertySuffix] -- Suffix of Property
//              [uLen]           -- Length of Property
//
//--------------------------------------------------------------------------

void CPropertyTag::ConcatenateProperty( WCHAR *awcPropSuffix, unsigned uLen )
{
    //
    // Need to grow Property buffer ?
    //
    if ( _cPropChars + uLen > _uLenPropBuf )
    {
        WCHAR *pwcNewPropBuf = newk(mtNewX, NULL) WCHAR[_cPropChars + uLen + PROP_BUFFER_SLOP];
        RtlCopyMemory( pwcNewPropBuf, _pwcPropBuf, _cPropChars * sizeof(WCHAR) );

        delete[] _pwcPropBuf;
        _uLenPropBuf = _cPropChars + uLen + PROP_BUFFER_SLOP;

        _pwcPropBuf = pwcNewPropBuf;
    }

    RtlCopyMemory( _pwcPropBuf + _cPropChars, awcPropSuffix, uLen * sizeof(WCHAR) );
    _cPropChars += uLen;
}


ANCHOR.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       anchor.cxx
//
//  Contents:   Parsing algorithm for anchor tag in Html
//
//  Classes:    CAnchorTag
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <anchor.hxx>
#include <htmlguid.hxx>

//+-------------------------------------------------------------------------
//
//  Method:     CAnchorTag::CAnchorTag
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]    -- Html IFilter
//              [serialStream]   -- Input stream
//
//--------------------------------------------------------------------------

CAnchorTag::CAnchorTag( CHtmlIFilter& htmlIFilter,
                        CSerialStream& serialStream )
    : CHtmlElement(htmlIFilter, serialStream),
      _eState(StartAnchor),
      _pwcHrefBuf(0),
      _uLenHrefBuf(0),
      _cHrefChars(0),
      _cHrefCharsFiltered(0)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CAnchorTag::~CAnchorTag
//
//  Synopsis:   Destructor
//
//  Arguments:  [htmlIFilter]    -- Html IFilter
//              [serialStream]   -- Input stream
//
//--------------------------------------------------------------------------

CAnchorTag::~CAnchorTag()
{
    delete[] _pwcHrefBuf;
}

//+-------------------------------------------------------------------------
//
//  Method:     CAnchorTag::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in pStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CAnchorTag::GetChunk( STAT_CHUNK * pStat )
{
    //
    // Toggle state
    //
    if ( _eState == StartAnchor )
        _eState = EndAnchor;
    else
        _eState = StartAnchor;

    SCODE sc = SwitchToNextHtmlElement( pStat );

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Method:     CAnchorTag::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcOutput] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE CAnchorTag::GetText( ULONG *pcwcOutput, WCHAR *awcBuffer )
{
    if ( _eState == StartAnchor )
    {
        *pcwcOutput = 0;
        return FILTER_E_NO_MORE_TEXT;
    }

    ULONG cCharsRemaining = _cHrefChars - _cHrefCharsFiltered;

    if ( cCharsRemaining == 0 )
    {
        *pcwcOutput = 0;
        return FILTER_E_NO_MORE_TEXT;
    }

    if ( *pcwcOutput < cCharsRemaining )
    {
        RtlCopyMemory( awcBuffer,
                       _pwcHrefBuf + _cHrefCharsFiltered,
                       *pcwcOutput * sizeof(WCHAR) );
        _cHrefCharsFiltered += *pcwcOutput;

        return S_OK;
    }
    else
    {
        RtlCopyMemory( awcBuffer,
                       _pwcHrefBuf + _cHrefCharsFiltered,
                       cCharsRemaining * sizeof(WCHAR) );
        _cHrefCharsFiltered += cCharsRemaining;
        *pcwcOutput = cCharsRemaining;

        return FILTER_S_LAST_TEXT;
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CAnchorTag::InitStatChunk
//
//  Synopsis:   Initializes the STAT_CHUNK
//
//  Arguments:  [pStat] -- STAT_CHUNK to initialize
//
//--------------------------------------------------------------------------

void CAnchorTag::InitStatChunk( STAT_CHUNK *pStat )
{
    pStat->idChunk = _htmlIFilter.GetNextChunkId();
    pStat->flags = CHUNK_TEXT;
    pStat->locale = _htmlIFilter.GetLocale();
    pStat->attribute.guidPropSet = CLSID_HtmlInformation;
    pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
    pStat->attribute.psProperty.propid = PID_HREF;
    pStat->breakType = CHUNK_EOS;

    if ( _eState == StartAnchor)
    {
        //
        // Start tag, hence read href field into local buffer
        //
        _scanner.ReadTagIntoBuffer();

        WCHAR *pwcHrefBuf;
        unsigned cHrefChars;
        _scanner.ScanTagBuffer( L"href=\"", pwcHrefBuf, cHrefChars );

        //
        // Need to grow internal buffer ?
        //
        if ( cHrefChars > _uLenHrefBuf )
        {
            delete[] _pwcHrefBuf;
            _pwcHrefBuf = 0;
            _uLenHrefBuf = 0;

            _pwcHrefBuf = newk(mtNewX, NULL) WCHAR[cHrefChars];
            _uLenHrefBuf = cHrefChars;
        }

        RtlCopyMemory( _pwcHrefBuf, pwcHrefBuf, cHrefChars*sizeof(WCHAR) );
        _cHrefChars = cHrefChars;
        _cHrefCharsFiltered = 0;

        pStat->idChunkSource = 0;
        pStat->cwcStartSource = 0;
        pStat->cwcLenSource = 0;
    }
    else
    {
        Win4Assert( _eState == EndAnchor );

        _scanner.EatTag();

        //
        // Set up the filter region to be the one between the start and end
        // anchor tags, i.e. the region belonging to the current Html element,
        // because we haven't changed state yet.
        //
        _htmlIFilter.GetCurHtmlElement()->InitFilterRegion( pStat->idChunkSource,
                                                            pStat->cwcStartSource,
                                                            pStat->cwcLenSource );
    }
}


REGACC32.CXX

//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright 1992 - 1998 Microsoft Corporation
//
//  File:       RegAcc.hxx
//
//  Contents:   'Simple' registry access
//
//
//--------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <osv.hxx>
#include <regacc32.hxx>

//+-------------------------------------------------------------------------
//
//  Member:     CRegAccess::CRegAccess, public
//
//  Synopsis:   Initialize registry access object
//
//  Arguments:  [ulRelative] -- Position in registry from which [pwcsRegPath]
//                              begins.
//              [pwcsRegPath] -- Path to node.
//
//--------------------------------------------------------------------------

CRegAccess::CRegAccess( HKEY keyRelative, WCHAR const * pwcsRegPath )
        : _hKey( (HKEY)0xFFFFFFFF ),
          _wcsPath( 0 )
{
    if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
    {
        RegOpenKey( keyRelative, pwcsRegPath, &_hKey );
    }
    else // win95
    {
        int nPathLen = wcslen( pwcsRegPath ) * 2;  // some multibyte chars are 2 bytes long

        char * pszPath = new char [nPathLen];

        if ( NULL != pszPath )
        {
            wcstombs( pszPath, pwcsRegPath, nPathLen );

            RegOpenKeyA( keyRelative, pszPath, &_hKey );
        }

        delete [] pszPath;
    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CRegAccess::~CRegAccess, public
//
//  Synopsis:   Destructor
//
//--------------------------------------------------------------------------

CRegAccess::~CRegAccess()
{
    RegCloseKey( _hKey );
    delete [] _wcsPath;
}

//+-------------------------------------------------------------------------
//
//  Member:     CRegAccess::Get, public
//
//  Synopsis:   Retrive value of specified key from registry.
//
//  Arguments:  [pwcsKey] -- Key to retrieve value of.
//              [wcsVal]  -- String stored here.
//              [cc]      -- Size (in characters) of [wcsVal]
//
//  Notes:      Key must be string for successful retrieval.
//
//--------------------------------------------------------------------------

BOOL CRegAccess::Get( WCHAR const * pwcsKey, WCHAR * wcsVal, unsigned cc )
{
    if ( _hKey == (HKEY)0xFFFFFFFF )
        return FALSE;

    DWORD dwType;
    DWORD cb = cc * sizeof(WCHAR);
    wcsVal[0] = 0;

    BOOL fOk;

    if ( GetOSVersion() == VER_PLATFORM_WIN32_NT )
    {
        fOk = ( ERROR_SUCCESS == RegQueryValueEx( _hKey,
                                                  pwcsKey,
                                                  0,
                                                  &dwType,
                                                  (BYTE *)wcsVal,
                                                  &cb ) ) &&
              ( wcsVal[0] != 0 );
    }
    else // win95
    {
        // Worst case - size of multibyte == size of Unicode
        int nKeyLen = wcslen( pwcsKey ) * 2;

        char * pszKey = new char [nKeyLen];

        if ( NULL != pszKey )
        {
            char * pszVal = new char [cc*2];

            if ( NULL != pszVal )
            {
                wcstombs( pszKey, pwcsKey, nKeyLen );

                fOk = (ERROR_SUCCESS == RegQueryValueExA( _hKey,
                                                          pszKey,
                                                          0,
                                                          &dwType,
                                                         (BYTE *)pszVal,
                                                         &cb ) );

                mbstowcs( wcsVal, pszVal, cb );     // cb since it will convert fewer when it hits terminator

                delete [] pszVal;
            }
            else
            {
                fOk = FALSE;
            }

            delete [] pszKey;
        }
        else
        {
            fOk = FALSE;
        }
    }

    return fOk;
}

//+-------------------------------------------------------------------------
//
//  Method:     StringToClsid
//
//  Synopsis:   Convert string containing CLSID to CLSID
//
//  Arguments:  [wszClass] -- string containg CLSID
//              [guidClass] -- output guid
//
//--------------------------------------------------------------------------

void StringToClsid( WCHAR *wszClass, GUID& guidClass )
{
    wszClass[9] = 0;
    guidClass.Data1 = wcstoul( &wszClass[1], 0, 16 );
    wszClass[14] = 0;
    guidClass.Data2 = (USHORT)wcstoul( &wszClass[10], 0, 16 );
    wszClass[19] = 0;
    guidClass.Data3 = (USHORT)wcstoul( &wszClass[15], 0, 16 );

    WCHAR wc = wszClass[22];
    wszClass[22] = 0;
    guidClass.Data4[0] = (unsigned char)wcstoul( &wszClass[20], 0, 16 );
    wszClass[22] = wc;
    wszClass[24] = 0;
    guidClass.Data4[1] = (unsigned char)wcstoul( &wszClass[22], 0, 16 );

    for ( int i = 0; i < 6; i++ )
    {
       wc = wszClass[27+i*2];
       wszClass[27+i*2] = 0;
       guidClass.Data4[2+i] = (unsigned char)wcstoul( &wszClass[25+i*2], 0, 16 );
       wszClass[27+i*2] = wc;
    }
}


SCRIPTAG.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       scripttag.cxx
//
//  Contents:   Parsing algorithm for script tag in Html
//
//  Classes:    CScriptTag
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <scriptag.hxx>
#include <htmlguid.hxx>

//+-------------------------------------------------------------------------
//
//  Method:     CScriptTag::CScriptTag
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]    -- Html IFilter
//              [serialStream]   -- Input stream
//              [clsidScriptInfo]  -- Clsid for script chunk
//
//--------------------------------------------------------------------------

CScriptTag::CScriptTag( CHtmlIFilter& htmlIFilter,
                    CSerialStream& serialStream,
                    GUID clsidScriptInfo )
    : CHtmlElement(htmlIFilter, serialStream),
      _clsidScriptInfo(clsidScriptInfo),
      _eState(NoMoreContent)
{
    _awszPropSpec[0] = 0;
}

//+-------------------------------------------------------------------------
//
//  Method:     CScriptTag::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in pStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CScriptTag::GetChunk( STAT_CHUNK * pStat )
{
    switch ( _eState )
    {

    case  FilteringContent:
    {
        SCODE sc = SkipRemainingTextAndGotoNextChunk( pStat );

        return sc;
    }

    case NoMoreContent:
    {
            SCODE sc = SwitchToNextHtmlElement( pStat );

            return sc;
    }

    default:
        Win4Assert( !"Unknown _eState in CScriptTag::GetChunk" );
        htmlDebugOut(( DEB_ERROR, "CScriptTag::GetChunk, unkown tag state: %d\n", _eState ));

        return S_OK;
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CScriptTag::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcOutput] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE CScriptTag::GetText( ULONG *pcwcOutput, WCHAR *awcBuffer )
{
    switch( _eState )
    {
    case NoMoreContent:
        return FILTER_E_NO_MORE_TEXT;

    case FilteringContent:
    {
        ULONG cCharsRead = 0;
        while ( cCharsRead < *pcwcOutput )
        {
            ULONG cCharsScanned;
            ULONG cCharsNeeded = *pcwcOutput - cCharsRead;
            CToken token;
            _scanner.GetBlockOfChars( cCharsNeeded,
                                      awcBuffer + cCharsRead,
                                      cCharsScanned,
                                      token );

            cCharsRead += cCharsScanned;
            if ( cCharsScanned == cCharsNeeded )
            {
                //
                // We've read the #chars requested by user
                //
                break;
            }

            HtmlTokenType eTokType = token.GetTokenType();
            if ( eTokType == EofToken || eTokType == ScriptToken )
            {
                //
                // End of file or end script tag
                //
                _eState = NoMoreContent;

                break;
            }
            else if ( eTokType == BreakToken )
            {
                //
                // Insert a newline char
                //
                Win4Assert( cCharsRead < *pcwcOutput );
                awcBuffer[cCharsRead++] = L'\n';
                _scanner.EatTag();
            }
            else if ( eTokType == CommentToken )
            {
                //
                // Scripts are often enclosed in comments for backward
                // compatibilty, so do nothing
                //
            }
            else
            {
                //
                // Uninteresting tag, so skip over tag and continue processing
                //
                _scanner.EatTag();
            }
        }

        *pcwcOutput = cCharsRead;

        if ( _eState == NoMoreContent )
            return FILTER_S_LAST_TEXT;
        else
            return S_OK;

        break;
    }

    default:
        Win4Assert( !"Unknown value of _eState" );
        htmlDebugOut(( DEB_ERROR,
                       "CScriptTag::GetText, unknown value of _eState: %d\n",
                       _eState ));
        return S_OK;
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CScriptTag::GetValue
//
//  Synopsis:   Retrieves value from current chunk
//
//  Arguments:  [ppPropValue] -- Value returned here
//
//--------------------------------------------------------------------------

SCODE CScriptTag::GetValue( VARIANT **ppPropValue )
{
    return FILTER_E_NO_VALUES;
}

//+-------------------------------------------------------------------------
//
//  Method:     CScriptTag::InitStatChunk
//
//  Synopsis:   Initializes the STAT_CHUNK
//
//  Arguments:  [pStat] -- STAT_CHUNK to initialize
//
//--------------------------------------------------------------------------

void CScriptTag::InitStatChunk( STAT_CHUNK *pStat )
{
    WCHAR *pwcLang;
    unsigned cwcLang;

    //
    // Read the Language field
    //
    _scanner.ReadTagIntoBuffer();
    _scanner.ScanTagBuffer( L"language=\"", pwcLang, cwcLang );

    if ( cwcLang == 0 )
    {
        //
        // Use whatever was specified in an earlier script tag, if none, the default is
        // Javascript
        //
        if ( _awszPropSpec[0] == 0 )
            RtlCopyMemory( _awszPropSpec, L"javascript", (wcslen(L"javascript") + 1) * sizeof( WCHAR) );
    }
    else
    {
        if ( cwcLang > MAX_PROPSPEC_STRING_LENGTH )
            cwcLang = MAX_PROPSPEC_STRING_LENGTH;     // Truncate to max length permitted

        RtlCopyMemory( _awszPropSpec, pwcLang, cwcLang * sizeof(WCHAR) );
        _awszPropSpec[cwcLang] = 0;
    }

    _eState = FilteringContent;

    pStat->attribute.guidPropSet = _clsidScriptInfo;
    pStat->attribute.psProperty.ulKind = PRSPEC_LPWSTR;
    pStat->attribute.psProperty.lpwstr = _awszPropSpec;

    pStat->idChunk = _htmlIFilter.GetNextChunkId();
    pStat->flags = CHUNK_TEXT;
    pStat->locale = _htmlIFilter.GetLocale();
    pStat->breakType = CHUNK_EOS;
    pStat->idChunkSource = pStat->idChunk;
    pStat->cwcStartSource = 0;
    pStat->cwcLenSource = 0;
}


SERSTRM.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       serstrm.cxx
//
//  Contents:   Serial stream
//
//  Classes:    CSerialStream
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <htmlfilt.hxx>

//
// Special char hash table is static so that there is only one hash table
// per htmlfilt dll
//
CSpecialCharHashTable CSerialStream::_specialCharHash;

//
// aControlCodeMap is the mapping for chars in the control code region (0x80 to 0x9f)
// as mapped by the web browser
//
static WCHAR aControlCodeMap[] = { 0x20,     // space
                                   0x20,     // space
                                   0x2c,     // comma
                                   0x192,    // script f
                                   0x201e,   // low double comma
                                   0x2026,   // horizontal ellipsis
                                   0x2020,   // dagger
                                   0x2021,   // double dagger
                                   0x5e,     // circumflex
                                   0x2030,   // per mille
                                   0x160,    // S haeck
                                   0x2039,   // left guillemet
                                   0x152,    // OE
                                   0x20,     // space
                                   0x20,     // space
                                   0x20,     // space
                                   0x20,     // space
                                   0x2018,   // single turned comma
                                   0x2019,   // single turned comma
                                   0x201c,   // double turned comma
                                   0x201d,   // double turned comma
                                   0x2022,   // bullet
                                   0x2014,   // em dash
                                   0x2015,   // quotation dash
                                   0x2dc,    // tilde
                                   0x2122,   // trademark
                                   0x161,    // s haeck
                                   0x203a,   // right guillemet
                                   0x153,    // oe
                                   0x20,     // space
                                   0x20,     // space
                                   0x178     // y diaresis
                                 };

//+-------------------------------------------------------------------------
//
//  Method:     CSerialStream::CSerialStream
//
//  Synopsis:   Constructor
//
//--------------------------------------------------------------------------

CSerialStream::CSerialStream()
    : _wch(0),
      _fUnGotChar(FALSE),
      _cCharsReadAhead(0),
      _pCurChar(_awcReadAheadBuf)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CSerialStream::Init
//
//  Synopsis:   Initialize the memory mapped stream
//
//  Arguments:  [pwszFileName] -- File to be mapped
//
//--------------------------------------------------------------------------

void CSerialStream::Init( WCHAR *pwszFileName )
{
    _mmInputStream.Init( pwszFileName );
}

//+-------------------------------------------------------------------------
//
//  Method:     CSerialStream::GetChar
//
//  Synopsis:   Read the next Unicode character from the input file, accounting
//              for special chars, chars push backs and read ahead
//
//  Returns:    Next Unicode char
//
//--------------------------------------------------------------------------

WCHAR CSerialStream::GetChar()
{
    //
    // Was a char pushed back into input ?
    //
    if ( _fUnGotChar )
    {
        _fUnGotChar = FALSE;
        return _wch;
    }

    //
    // Are there any chars in our read-ahead buffer ?
    //
    WCHAR wch;
    if ( _pCurChar < _awcReadAheadBuf + _cCharsReadAhead )
    {
        wch = *_pCurChar;
        _pCurChar++;

        return wch;
    }

    if ( _mmInputStream.Eof() )
        return WEOF;

    //
    // Read from input stream
    //
    wch = _mmInputStream.GetChar();
    if ( wch == 0xa || wch == 0xd )         // Replace newlines with spaces
        wch = L' ';

    if ( wch == L'&' )
    {
        //
        // Read in the special char until ';' or '&', or EOF, or until _awcReadAhead buffer is full
        //
        _awcReadAheadBuf[0] = wch;
        for ( unsigned i=1; i<MAX_SPECIAL_CHAR_LENGTH; i++)
        {
            if ( _mmInputStream.Eof() )
                break;

            wch = _mmInputStream.GetChar();
            if ( wch == 0xa || wch == 0xd )         // Replace newlines with spaces
                wch = L' ';

            _awcReadAheadBuf[i] = wch;
            if ( wch == L';' || wch == L'&' )
                break;
        }

        if ( wch == L';' )
        {
            if ( _awcReadAheadBuf[1] == L'#' && IsUnicodeNumber( &_awcReadAheadBuf[2], i-2, wch ) )
            {
                //
                // Return converted Unicode number, e.g. for &#160, return 160
                //
                return wch;
            }
            else if ( _specialCharHash.Lookup( &_awcReadAheadBuf[1], i-1, wch ) )
            {
                //
                // Corresponding Unicode char found
                //
                return wch;
            }
            else
            {
                _cCharsReadAhead = i+1;
                _pCurChar = &_awcReadAheadBuf[1];

                return L'&';
            }
        }
        else if ( wch == L'&' )
        {
            //
            // For example, &acu&aacute; . So keep &acu in the read ahead buffer,
            // but push the second '&' back into the input stream, so that &aacute;
            // can be recognized as a special char during a subsequent call to GetChar()
            //
            _mmInputStream.UnGetChar( L'&' );
            _cCharsReadAhead = i;
            _pCurChar = &_awcReadAheadBuf[1];

            return L'&';
        }
        else
        {
            _cCharsReadAhead = i;
            _pCurChar = &_awcReadAheadBuf[1];

            return L'&';
        }
    }
    else
        return wch;
}

//+-------------------------------------------------------------------------
//
//  Method:     CSerialStream::UnGetChar
//
//  Synopsis:   Pushes(logically) a character back into the input stream
//
//  Arguments:  [wch]  -- Char to be pushed back
//
//--------------------------------------------------------------------------

void CSerialStream::UnGetChar( WCHAR wch )
{
    //
    // We can unget only one char at a time
    //
    Win4Assert( ! _fUnGotChar );

    _fUnGotChar = TRUE;
    _wch = wch;
}

//+-------------------------------------------------------------------------
//
//  Method:     CSerialStream::Eof
//
//  Synopsis:   Is this the end of input file ?
//
//--------------------------------------------------------------------------

BOOL CSerialStream::Eof()
{
    if ( _fUnGotChar )
        return FALSE;

    if ( _pCurChar < _awcReadAheadBuf + _cCharsReadAhead )
        return FALSE;

    return _mmInputStream.Eof();
}

//+-------------------------------------------------------------------------
//
//  Method:     CSerialStream::IsUnicodeNumber
//
//  Synopsis:   Is the special char a Unicode number ?
//
//  Arguments:  [pwcInputBuf] -- input buffer
//              [uLen]        -- Length of input (not \0 terminated)
//              [wch]         -- Unicode char returned here
//
//  Returns:    True if we've managed to convert to a Unicode number
//
//--------------------------------------------------------------------------

BOOL CSerialStream::IsUnicodeNumber( WCHAR *pwcInputBuf,
                                     unsigned uLen,
                                     WCHAR& wch )
{
    wch = 0;
    for ( unsigned i=0; i<uLen; i++ )
    {
        //
        // Check that the result will be less than max Unicode value, namely 0xffff
        //
        if ( iswdigit( pwcInputBuf[i] )
             && wch < (0xffff - (pwcInputBuf[i]-L'0'))/10 )
        {
            wch = 10 * wch + pwcInputBuf[i] - L'0';
        }
        else
            return FALSE;
    }

    if ( wch >= 0x80 && wch <= 0x9f )
    {
        //
        // Chars in the control code region are mapped by the browser to valid chars
        //

        wch = aControlCodeMap[wch-0x80];
    }

    return ( wch > 0 );
}


START.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       start.cxx
//
//  Contents:   Parsing algorithm at the beginning of an Html file
//
//  Classes:    CStartOfFileElement
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <start.hxx>

//+-------------------------------------------------------------------------
//
//  Method:     CStartOfFileElement::CStartOfFileElement
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]    -- Html IFilter
//              [serialStream]   -- Input stream
//
//--------------------------------------------------------------------------

CStartOfFileElement::CStartOfFileElement( CHtmlIFilter& htmlIFilter,
                                          CSerialStream& serialStream )
    : CHtmlElement(htmlIFilter, serialStream)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CStartOfFileElement::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in pStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CStartOfFileElement::GetChunk( STAT_CHUNK * pStat )
{
    //
    // This is the first GetChunk call ever, hence setup the correct
    // Html Element
    //
    SCODE sc = SwitchToNextHtmlElement( pStat );

    return sc;
}

//+-------------------------------------------------------------------------
//
//  Method:     CStartOfFileElement::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcOutput] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE CStartOfFileElement::GetText( ULONG *pcwcOutput, WCHAR *awcBuffer )
{
    //
    // CStartOfFile is used to set up the first chunk correctly, and the first
    // chunk cannot be of type CStartOfFile
    //
    Win4Assert( !"CStartOfFileElement::GetText() call unexpected" );

    return E_FAIL;
}

//+-------------------------------------------------------------------------
//
//  Method:     CStartOfFileElement::InitStatChunk
//
//  Synopsis:   Initializes the STAT_CHUNK
//
//  Arguments:  [pStat] -- STAT_CHUNK to initialize
//
//--------------------------------------------------------------------------

void CStartOfFileElement::InitStatChunk( STAT_CHUNK *pStat )
{
    Win4Assert( !"CStartOfFileElement::InitStatChunk() call unexpected" );
}


STGPROP.H

//+---------------------------------------------------------------------------
//
//  File:       stgprop.h
//
//  Contents:   Standard storage provider property definitions;
//
//  History:    Jul-9-93       robertfe
//
//----------------------------------------------------------------------------

#ifndef _STGPROP_H_
#define _STGPROP_H_

#define PSGUID_STORAGE  { 0xb725f130,           \
                          0x47ef, 0x101a,       \
                          { 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac } }

//#define PID_STG_DICTIONARY            ((PROPID) 0x00000000) //reserved
//#define PID_STG_CODEPAGE              ((PROPID) 0x00000001) //reserved

#define PID_STG_DIRECTORY               ((PROPID) 0x00000002)

#define PID_STG_CLASSID                 ((PROPID) 0x00000003)
#define PID_STG_STORAGETYPE             ((PROPID) 0x00000004)

// unused #define PID_STG_              ((PROPID) 0x00000005)
// unused #define PID_STG_              ((PROPID) 0x00000006)
// unused #define PID_STG_              ((PROPID) 0x00000007)

#define PID_STG_FILEINDEX               ((PROPID) 0x00000008)
#define PID_STG_LASTCHANGEUSN           ((PROPID) 0x00000009)
#define PID_STG_NAME                    ((PROPID) 0x0000000a)
#define PID_STG_PATH                    ((PROPID) 0x0000000b)

#define PID_STG_SIZE                    ((PROPID) 0x0000000c)
#define PID_STG_ATTRIBUTES              ((PROPID) 0x0000000d)
#define PID_STG_WRITETIME               ((PROPID) 0x0000000e)
#define PID_STG_CREATETIME              ((PROPID) 0x0000000f)
#define PID_STG_ACCESSTIME              ((PROPID) 0x00000010)

// unused #define PID_STG_              ((PROPID) 0x00000011)

#define PID_STG_ALLOCSIZE               ((PROPID) 0x00000012)
#define PID_STG_CONTENTS                ((PROPID) 0x00000013)
#define PID_STG_SHORTNAME               ((PROPID) 0x00000014)

#define PID_STG_MAX                     PID_STG_SHORTNAME

#define CSTORAGEPROPERTY            0x15

#endif _STGPROP_H_


TEXTELEM.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       textelem.cxx
//
//  Contents:   Parsing algorithm for vanilla text in Html
//
//  Classes:    CTextElement
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <textelem.hxx>
#include <htmlguid.hxx>
#include <stgprop.h>

//+-------------------------------------------------------------------------
//
//  Method:     CTextElement::CTextElement
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]    -- Html IFilter
//              [serialStream]   -- Input stream
//
//--------------------------------------------------------------------------

CTextElement::CTextElement( CHtmlIFilter& htmlIFilter,
                            CSerialStream& serialStream )
    : CHtmlElement(htmlIFilter, serialStream),
      _fNoMoreText(TRUE),
      _eTokTypeNext(GenericToken),
      _idChunk(0),
      _cTextChars(0)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CTextElement::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in pStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CTextElement::GetChunk( STAT_CHUNK * pStat )
{
    if ( _serialStream.Eof() )
        return FILTER_E_END_OF_CHUNKS;

    if ( _fNoMoreText )
    {
        //
        // This is the GetChunk call after we've returned FILTER_E_NO_MORE_TEXT
        // for the previous chunk
        //
        CHtmlElement *pHtmlElemNext = _htmlIFilter.QueryHtmlElement( _eTokTypeNext );
        Win4Assert( pHtmlElemNext );
        pHtmlElemNext->InitStatChunk( pStat );
        _htmlIFilter.ChangeState( pHtmlElemNext );
    }
    else
    {
        //
        // GetChunk was called even though we had not returned
        // FILTER_E_NO_MORE_TEXT.
        //
        SCODE sc = SkipRemainingTextAndGotoNextChunk( pStat );

        return sc;
    }

    return S_OK;
}

//+-------------------------------------------------------------------------
//
//  Method:     CTextElement::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcOutput] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE CTextElement::GetText( ULONG *pcwcOutput, WCHAR *awcBuffer )
{
    if ( _fNoMoreText || _serialStream.Eof() )
    {
        *pcwcOutput = 0;
        return FILTER_E_NO_MORE_TEXT;
    }

    ULONG cCharsRead = 0;  // count of chars read from input
    while ( cCharsRead < *pcwcOutput )
    {
        CToken token;
        ULONG cCharsScanned;
        ULONG cCharsNeeded = *pcwcOutput - cCharsRead;

        _scanner.GetBlockOfChars( cCharsNeeded,
                                  awcBuffer + cCharsRead,
                                  cCharsScanned,
                                  token );

        cCharsRead += cCharsScanned;
        if ( cCharsScanned == cCharsNeeded )
        {
            //
            // We've read the #chars requested by the user
            //
            break;
        }

        HtmlTokenType eTokType = token.GetTokenType();
        if ( eTokType == EofToken || _htmlIFilter.IsStopToken( token ) )
        {
            //
            // End of file, or we've hit an interesting token
            //
            _fNoMoreText = TRUE;
            _eTokTypeNext = eTokType;         // Need the token type to set up the next chunk

            break;
        }
        else if ( eTokType == BreakToken )
        {
            //
            // Insert a newline char
            //
            Win4Assert( cCharsRead < *pcwcOutput );
            awcBuffer[cCharsRead++] = L'\n';
            _scanner.EatTag();
        }
        else
        {
            //
            // Skip over uninteresting tag and continue processing
            //
            _scanner.EatTag();
        }
    }

    *pcwcOutput = cCharsRead;

    _cTextChars += cCharsRead;

    if ( _fNoMoreText )
        return FILTER_S_LAST_TEXT;
    else
        return S_OK;
}

//+-------------------------------------------------------------------------
//
//  Method:     CTextElement::InitStatChunk
//
//  Synopsis:   Initializes the STAT_CHUNK
//
//  Arguments:  [pStat] -- STAT_CHUNK to initialize
//
//--------------------------------------------------------------------------

void CTextElement::InitStatChunk( STAT_CHUNK *pStat )
{
    //
    // There is more text to be returned
    //
    _fNoMoreText = FALSE;

    pStat->idChunk = _htmlIFilter.GetNextChunkId();
    pStat->flags = CHUNK_TEXT;
    pStat->locale = _htmlIFilter.GetLocale();
    pStat->attribute.guidPropSet = CLSID_Storage;
    pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
    pStat->attribute.psProperty.propid = PID_STG_CONTENTS;
    pStat->breakType = CHUNK_EOS;
    pStat->idChunkSource = pStat->idChunk;
    pStat->cwcStartSource = 0;
    pStat->cwcLenSource = 0;

    _idChunk = pStat->idChunk;
    _cTextChars = 0;
}

//+-------------------------------------------------------------------------
//
//  Method:     CTextElement::InitFilterRegion
//
//  Synopsis:   Initializes the filter region corresponding to this chunk
//
//  Arguments:  [idChunkSource]  -- Id of source chunk
//              [cwcStartSource] -- Offset of source text in chunk
//              [cwcLenSource]   -- Length of source text in chunk
//
//--------------------------------------------------------------------------

void CTextElement::InitFilterRegion( ULONG& idChunkSource,
                                     ULONG& cwcStartSource,
                                     ULONG& cwcLenSource )
{
    idChunkSource = _idChunk;
    cwcStartSource = 0;
    cwcLenSource = _cTextChars;
}


TITLETAG.CXX

//+---------------------------------------------------------------------------
//
//  Copyright 1992 - 1998 Microsoft Corporation.
//
//  File:       titletag.cxx
//
//  Contents:   Parsing algorithm for title tag in Html
//
//  Classes:    CTitleTag
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <titletag.hxx>
#include <htmlguid.hxx>

//+-------------------------------------------------------------------------
//
//  Method:     CTitleTag::CTitleTag
//
//  Synopsis:   Constructor
//
//  Arguments:  [htmlIFilter]  -- Reference to Html filter
//              [serialStream] -- Reference to input stream
//              [propSpec]     -- Property spec
//              [eTokType]     -- Token corresponding to this property
//
//--------------------------------------------------------------------------

CTitleTag::CTitleTag( CHtmlIFilter& htmlIFilter,
                      CSerialStream& serialStream,
                      CFullPropSpec& propSpec,
                      HtmlTokenType eTokType )
    : CPropertyTag(htmlIFilter, serialStream, propSpec, eTokType)
{
}

//+-------------------------------------------------------------------------
//
//  Method:     CTitleTag::GetChunk
//
//  Synopsis:   Gets the next chunk and returns chunk information in pStat
//
//  Arguments:  [pStat] -- chunk information returned here
//
//--------------------------------------------------------------------------

SCODE CTitleTag::GetChunk( STAT_CHUNK * pStat )
{
    switch ( _eState )
    {
    case NoMoreProperty:
        _eState = FilteringValueProperty;

        pStat->idChunk = _htmlIFilter.GetNextChunkId();
        pStat->breakType = CHUNK_EOS;
        pStat->flags = CHUNK_VALUE;
        pStat->locale = _htmlIFilter.GetLocale();
        pStat->attribute.guidPropSet = _propSpec.GetPropSet();

        Win4Assert( _propSpec.IsPropertyPropid() );

        pStat->attribute.psProperty.ulKind = PRSPEC_PROPID;
        pStat->attribute.psProperty.propid = _propSpec.GetPropertyPropid();
        pStat->idChunkSource = _ulIdContentChunk;
        pStat->cwcStartSource = 0;
        pStat->cwcLenSource = 0;

        return S_OK;

    case FilteringValueProperty:
    case NoMoreValueProperty:
        //
        // Skip over the end property tag
        //
        _scanner.EatTag();

        return SwitchToNextHtmlElement( pStat );

    default:
        return CPropertyTag::GetChunk( pStat );
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CTitleTag::GetText
//
//  Synopsis:   Retrieves text from current chunk
//
//  Arguments:  [pcwcBuffer] -- count of UniCode characters in buffer
//              [awcBuffer]  -- buffer for text
//
//--------------------------------------------------------------------------

SCODE CTitleTag::GetText( ULONG *pcwcOutput, WCHAR *awcBuffer )
{
    switch ( _eState )
    {
    case FilteringValueProperty:
    case NoMoreValueProperty:
        return FILTER_E_NO_TEXT;

    default:
        return CPropertyTag::GetText( pcwcOutput, awcBuffer );
    }
}

//+-------------------------------------------------------------------------
//
//  Method:     CTitleTag::GetValue
//
//  Synopsis:   Retrieves value from current chunk
//
//  Arguments:  [ppPropValue] -- Value returned here
//
//--------------------------------------------------------------------------

SCODE CTitleTag::GetValue( VARIANT **ppPropValue )
{
    switch (_eState)
    {
    case FilteringContent:
    case NoMoreContent:
    case FilteringProperty:
    case FilteringPropertyButContentNotFiltered:
    case NoMoreProperty:
        return FILTER_E_NO_VALUES;

    case FilteringValueProperty:
    {
        ConcatenateProperty( L"\0", 1 );     // Null terminate for SetLPWSTR

        PROPVARIANT *pPropVar = (PROPVARIANT *) CoTaskMemAlloc( sizeof PROPVARIANT );
        if ( pPropVar == 0 )
            return E_OUTOFMEMORY;

        pPropVar->vt = VT_LPWSTR;
        int cb = ( wcslen(_pwcPropBuf) + 1 ) * sizeof( WCHAR );
        pPropVar->pwszVal = (WCHAR *) CoTaskMemAlloc( cb );

        if ( pPropVar->pwszVal == 0 )
        {
            CoTaskMemFree( (void *) pPropVar );
            return E_OUTOFMEMORY;
        }

        RtlCopyMemory( pPropVar->pwszVal, _pwcPropBuf, cb );

        *ppPropValue = pPropVar;

        _eState = NoMoreValueProperty;
        return S_OK;
    }

    case NoMoreValueProperty:
        return FILTER_E_NO_MORE_VALUES;

    default:
        Win4Assert( !"Unknown value of _eState" );
        htmlDebugOut(( DEB_ERROR,
                       "CTitleTag::GetValue, unknown value of _eState: %d\n",
                       _eState ));
        return E_FAIL;
    }
}


README.TXT

Demonstration of issuing Microsoft Index Server queries

SUMMARY
=======

QSAMPLE is a command line utility that issues Microsoft Index Server queries.
It uses the helper functions CCreateICommand and CITextToFullTree to
simplify the code needed to create a query.

MORE INFORMATION
================

usage: QSAMPLE query [/c:catalog] [/m:machine]
    query        a Microsoft Index Server query
    /c:catalog   name of the catalog, default is SYSTEM
    /m:machine   name of the machine, default is local machine
    /d           displays the DBCOMMANDTREE, default is off

The syntax for the "query" parameter is described in the Microsoft Index
Server documentation.  Typically this is a single word.  To include a
space in a query, enclose the query in quotes.

The "catalog" parameter is the name of the catalog to be queried.  The
default is "SYSTEM", which is the default catalog installed with Windows
NT 5.0.  Additional catalogs can be created with the Microsoft Index Server
MMC snapin.

The "machine" parameter is the name of the machine on which the query
will be executed.  The default is ".", which is the local machine.  Machine
names should not be preceeded with two backslashes.

The "/d" parameter displays the DBCOMMANDTREE built to execute the query.

For each file that matches the query, the file's size and path
are printed.

The query results are not sorted.

How this example differs from the ADVQUERY sample:

  QSAMPLE uses Index Server's helper functions CICreateCommand and
  CITextToFullTree as opposed to creating an ICommand via ole and
  the command tree from scratch.

Examples:

    qsample foo
    Finds all files in the SYSTEM catalog on the local machine containing
    the word "foo"

    qsample "foo bar"
    Finds all files in the SYSTEM catalog on the local machine containing
    the phrase "foo bar"

    qsample "foo and bar"
    Finds all files in the SYSTEM catalog on the local machine containing
    both "foo" and "bar"

    qsample "foo or bar"
    Finds all files in the SYSTEM catalog on the local machine containing
    either "foo" or "bar"

    qsample foo /m:SERVERNAME
    Finds all files in the SYSTEM catalog on machine "servername" containing
    the word foo

    qsample "@size > 1000000"
    Finds all files in the SYSTEM catalog on the local machine larger than
    one million bytes.

    qsample "@size > 1000000 and #filename *.gif" /c:WEB /m:SERVERNAME
    Finds all files with the .gif extension larger than one million bytes
    in the WEB catalog on machine SERVERNAME


QSAMPLE.CXX

//+-------------------------------------------------------------------------
//
// 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.
//
// Copyright 1997 Microsoft Corporation.  All Rights Reserved.
//
// PROGRAM:  qsample.cxx
//
// PURPOSE:  Illustrates a minimal query using Microsoft Index Server.
//           Uses CICreateCommand and CITextToFullTree helper functions.
//
// PLATFORM: Windows NT
//
//--------------------------------------------------------------------------

#define UNICODE

#include <stdio.h>
#include <windows.h>

#define OLEDBVER 0x0200 // need the command tree definitions
#define DBINITCONSTANTS

#include <oledberr.h>
#include <oledb.h>

#include <ntquery.h>

// This is found in disptree.cxx

extern void DisplayCommandTree( DBCOMMANDTREE * pNode, ULONG iLevel = 0 );

//+-------------------------------------------------------------------------
//
//  Template:   XInterface
//
//  Synopsis:   Template for managing ownership of interfaces
//
//--------------------------------------------------------------------------

template<class T> class XInterface
{
public:
    XInterface( T * p = 0 ) : _p( p ) {}
    ~XInterface() { if ( 0 != _p ) _p->Release(); }
    T * operator->() { return _p; }
    T * GetPointer() const { return _p; }
    IUnknown ** GetIUPointer() { return (IUnknown **) &_p; }
    T ** GetPPointer() { return &_p; }
    void ** GetQIPointer() { return (void **) &_p; }
    T * Acquire() { T * p = _p; _p = 0; return p; }

private:
    T * _p;
};

//+-------------------------------------------------------------------------
//
//  Function:   SetCommandProperties
//
//  Synopsis:   Sets the DBPROP_USEEXTENDEDDBTYPES property to TRUE, so
//              data is returned in PROPVARIANTs, as opposed to the
//              default, which is OLE automation VARIANTs.  PROPVARIANTS
//              allow a superset of VARIANT data types.  Use of these
//              types avoids costly coercions.
//
//              Also sets the DBPROP_USECONTENTINDEX property to TRUE, so
//              the index will always be used to resolve the query (as
//              opposed to enumerating all the files on the disk), even
//              if the index is out of date.
//
//              Both of these properties are unique to Index Server's OLE DB
//              implementation.
//
//  Arguments:  [pICommand] - The ICommand used to set the property
//
//  Returns:    HRESULT result of setting the properties
//
//--------------------------------------------------------------------------

HRESULT SetCommandProperties( ICommand * pICommand )
{
    static const DBID dbcolNull = { { 0,0,0, { 0,0,0,0,0,0,0,0 } },
                                    DBKIND_GUID_PROPID, 0 };
    static const GUID guidQueryExt = DBPROPSET_QUERYEXT;

    DBPROP aProp[2];

    aProp[0].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
    aProp[0].dwOptions = DBPROPOPTIONS_SETIFCHEAP;
    aProp[0].dwStatus = 0;
    aProp[0].colid = dbcolNull;
    aProp[0].vValue.vt = VT_BOOL;
    aProp[0].vValue.boolVal = VARIANT_TRUE;

    aProp[1] = aProp[0];
    aProp[1].dwPropertyID = DBPROP_USECONTENTINDEX;

    DBPROPSET aPropSet[1];

    aPropSet[0].rgProperties = &aProp[0];
    aPropSet[0].cProperties = 2;
    aPropSet[0].guidPropertySet = guidQueryExt;

    XInterface<ICommandProperties> xICommandProperties;
    HRESULT hr = pICommand->QueryInterface( IID_ICommandProperties,
                                            xICommandProperties.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    return xICommandProperties->SetProperties( 1,          // 1 property set
                                               aPropSet ); // the properties
} //SetCommandProperties

//+-------------------------------------------------------------------------
//
//  Function:   DoQuery
//
//  Synopsis:   Creates and executes a query, then displays the results.
//
//  Arguments:  [pwcQueryCatalog]    - Catalog name over which query is run
//              [pwcQueryMachine]    - Machine name on which query is run
//              [pwcQueryRestrition] - The actual query string
//              [fDisplayTree]       - TRUE to display the command tree
//
//  Returns:    HRESULT result of the query
//
//--------------------------------------------------------------------------

HRESULT DoQuery(
    WCHAR const * pwcQueryCatalog,
    WCHAR const * pwcQueryMachine,
    WCHAR const * pwcQueryRestriction,
    BOOL          fDisplayTree )
{
    // Create an ICommand object.  The default scope for the query is the
    // entire catalog.  CICreateCommand is a shortcut for making an
    // ICommand.  The ADVQUERY sample shows the OLE DB equivalent.

    XInterface<ICommand> xICommand;
    HRESULT hr = CICreateCommand( xICommand.GetIUPointer(), // result
                                  0,                  // controlling unknown
                                  IID_ICommand,       // IID requested
                                  pwcQueryCatalog,    // catalog name
                                  pwcQueryMachine );  // machine name

    if ( FAILED( hr ) )
        return hr;

    // Set required properties on the ICommand

    hr = SetCommandProperties( xICommand.GetPointer() );
    if ( FAILED( hr ) )
        return hr;

    // Create an OLE DB query tree from a text restriction, column
    // set, and sort order.

    DBCOMMANDTREE * pTree;
    hr = CITextToFullTree( pwcQueryRestriction,      // the query itself
                           L"Size,Path",             // columns to return
                           0, // no sort. eg L"Rank[d]" for rank descending
                           0,                        // reserved
                           &pTree,                   // resulting tree
                           0,                        // no custom properties
                           0,                        // no custom properties
                           GetSystemDefaultLCID() ); // default locale
    if ( FAILED( hr ) )
        return hr;

    // If directed, display the command tree

    if ( fDisplayTree )
        DisplayCommandTree( pTree );

    // Set the tree in the ICommandTree

    XInterface<ICommandTree> xICommandTree;
    hr = xICommand->QueryInterface( IID_ICommandTree,
                                    xICommandTree.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    hr = xICommandTree->SetCommandTree( &pTree,
                                        DBCOMMANDREUSE_NONE,
                                        FALSE );
    if ( FAILED( hr ) )
        return hr;

    // Execute the query.  The query is complete when Execute() returns

    XInterface<IRowset> xIRowset;
    hr = xICommand->Execute( 0,            // no aggregating IUnknown
                             IID_IRowset,  // IID for interface to return
                             0,            // no DBPARAMs
                             0,            // no rows affected
                             xIRowset.GetIUPointer() ); // result
    if ( FAILED( hr ) )
        return hr;

    // Create an accessor, so data can be retrieved from the rowset

    XInterface<IAccessor> xIAccessor;
    hr = xIRowset->QueryInterface( IID_IAccessor,
                                   xIAccessor.GetQIPointer() );
    if ( FAILED( hr ) )
        return hr;

    // Column iOrdinals are parallel with those passed to CiTextToFullTree,
    // so MapColumnIDs isn't necessary.  These binding values for dwPart,
    // dwMemOwner, and wType are the most optimal bindings for Index Server.

    const ULONG cColumns = 2; // 2 for Size and Path
    DBBINDING aColumns[ cColumns ];
    memset( aColumns, 0, sizeof aColumns );

    aColumns[0].iOrdinal   = 1; // first column specified above (size)
    aColumns[0].obValue    = 0; // offset where value is written in GetData
    aColumns[0].dwPart     = DBPART_VALUE;  // retrieve value, not status
    aColumns[0].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // Index Server owned
    aColumns[0].wType      = DBTYPE_VARIANT | DBTYPE_BYREF; // VARIANT *

    aColumns[1] = aColumns[0];
    aColumns[1].iOrdinal   = 2; // second column specified above (path)
    aColumns[1].obValue    = sizeof (PROPVARIANT *); // offset for value

    HACCESSOR hAccessor;
    hr = xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, // rowdata accessor
                                     cColumns,           // # of columns
                                     aColumns,           // columns
                                     0,                  // ignored
                                     &hAccessor,         // result
                                     0 );                // no status
    if ( FAILED( hr ) )
        return hr;

    // Display the results of the query.  Print file size and file path.

    printf( "      Size  Path\n" );

    ULONG cRowsSoFar = 0;

    do
    {
        ULONG cRowsReturned = 0;
        const ULONG cRowsAtATime = 10;
        HROW aHRow[cRowsAtATime];
        HROW * pgrHRows = aHRow;
        hr = xIRowset->GetNextRows( 0,              // no chapter
                                    0,              // no rows to skip
                                    cRowsAtATime,   // # rows to get
                                    &cRowsReturned, // # rows returned
                                    &pgrHRows);     // resulting hrows

        if ( FAILED( hr ) )
            break;

        for ( ULONG iRow = 0; iRow < cRowsReturned; iRow++ )
        {
            PROPVARIANT * aData[cColumns];
            hr = xIRowset->GetData( aHRow[iRow],  // hrow being accessed
                                    hAccessor,    // accessor to use
                                    &aData );     // resulting data
            if ( FAILED( hr ) )
                break;

            if ( VT_I8 ==     aData[0]->vt &&
                 VT_LPWSTR == aData[1]->vt )
                printf( "%10I64d  %ws\n",
                        aData[0]->hVal,
                        aData[1]->pwszVal );
            else
                printf( "could not retrieve a file's values\n" );
        }

        if ( 0 != cRowsReturned )
            xIRowset->ReleaseRows( cRowsReturned, // # of rows to release
                                   aHRow,         // rows to release
                                   0,             // no options
                                   0,             // no refcounts
                                   0 );           // no status

        if ( DB_S_ENDOFROWSET == hr )
        {
            hr = S_OK; // succeeded, return S_OK from DoQuery
            break;
        }

        if ( FAILED( hr ) )
            break;

        cRowsSoFar += cRowsReturned;
    } while ( TRUE );

    printf( "%d files matched the query '%ws'\n",
            cRowsSoFar,
            pwcQueryRestriction );

    xIAccessor->ReleaseAccessor( hAccessor, 0 );

    return hr;
} //DoQuery

//+-------------------------------------------------------------------------
//
//  Function:   Usage
//
//  Synopsis:   Displays information about how to use the app and exits
//
//--------------------------------------------------------------------------

void Usage()
{
    printf( "usage: QSAMPLE query [/c:catalog] [/m:machine] [/d]\n\n" );
    printf( "    query        a Microsoft Index Server query\n" );
    printf( "    /c:catalog   name of the catalog, default is SYSTEM\n" );
    printf( "    /m:machine   name of the machine, default is local machine\n" );
    printf( "    /d           display the DBCOMMANDTREE, default is off\n" );
    exit( -1 );
} //Usage

//+-------------------------------------------------------------------------
//
//  Function:   wmain
//
//  Synopsis:   Entry point for the app.  Parses command line arguments
//              and issues a query.
//
//  Arguments:  [argc]     - Argument count
//              [argv]     - Arguments
//
//--------------------------------------------------------------------------

extern "C" int __cdecl wmain( int argc, WCHAR * argv[] )
{
    WCHAR const * pwcCatalog     = L"system"; // default: system catalog
    WCHAR const * pwcMachine     = L".";      // default: local machine
    WCHAR const * pwcRestriction = 0;         // no default restriction
    BOOL fDisplayTree            = FALSE;     // don't display the tree

    // Parse command line parameters

    for ( int i = 1; i < argc; i++ )
    {
        if ( L'-' == argv[i][0] || L'/' == argv[i][0] )
        {
            WCHAR wc = toupper( argv[i][1] );

            if ( ':' != argv[i][2] && 'D' != wc )
                Usage();

            if ( 'C' == wc )
                pwcCatalog = argv[i] + 3;
            else if ( 'M' == wc )
                pwcMachine = argv[i] + 3;
            else if ( 'D' == wc )
                fDisplayTree = TRUE;
            else
                Usage();
        }
        else if ( 0 != pwcRestriction )
            Usage();
        else
            pwcRestriction = argv[i];
    }

    // A query restriction is necessary.  Fail if none is given.

    if ( 0 == pwcRestriction )
        Usage();

    // Run the query

    HRESULT hr = DoQuery( pwcCatalog,
                          pwcMachine,
                          pwcRestriction,
                          fDisplayTree );

    if ( FAILED( hr ) )
    {
        printf( "the query '%ws' failed with error %#x\n",
                pwcRestriction, hr );
        return -1;
    }

    return 0;
} //wmain


JCALENDAR.FRM

VERSION 5.00
Object = "{6F146B03-E7CF-11D0-B8E7-0000F81ECCE7}#1.0#0"; "JCalendar.tlb"
Begin VB.Form Form1
   Caption         =   "Form1"
   ClientHeight    =   4460
   ClientLeft      =   4930
   ClientTop       =   3660
   ClientWidth     =   6560
   LinkTopic       =   "Form1"
   ScaleHeight     =   4460
   ScaleWidth      =   6560
   Begin VB.CommandButton nextMonth
      Caption         =   "next month"
      Height          =   490
      Left            =   600
      TabIndex        =   6
      Top             =   3960
      Width           =   970
   End
   Begin VB.TextBox yearText
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.29
         Charset         =   0
         Weight          =   400
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   320
      Left            =   3720
      TabIndex        =   4
      Text            =   "Text1"
      Top             =   480
      Width           =   1210
   End
   Begin VB.ComboBox monthList
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.29
         Charset         =   0
         Weight          =   400
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   280
      ItemData        =   "JCalendar.frx":0000
      Left            =   1560
      List            =   "JCalendar.frx":002A
      Style           =   2  'Dropdown List
      TabIndex        =   1
      Top             =   480
      Width           =   1330
   End
   Begin JCalendar.JCalendar JCalendar1
      Height          =   2890
      Left            =   960
      OleObjectBlob   =   "JCalendar.frx":0090
      TabIndex        =   0
      Top             =   960
      Width           =   4570
   End
   Begin VB.Label Label3
      Caption         =   "Year"
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.29
         Charset         =   0
         Weight          =   400
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   250
      Left            =   3720
      TabIndex        =   5
      Top             =   240
      Width           =   850
   End
   Begin VB.Label Label2
      Caption         =   "Month"
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.29
         Charset         =   0
         Weight          =   400
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   250
      Left            =   1560
      TabIndex        =   3
      Top             =   240
      Width           =   490
   End
   Begin VB.Label Label1
      Caption         =   "Label1"
      Height          =   370
      Left            =   2880
      TabIndex        =   2
      Top             =   2040
      Width           =   850
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False

Private Sub JCalendar1_yearChanged(ByVal Parameter0 As Long, ByVal Parameter1 As Long)
yearText.Text = JCalendar1.Year
End Sub

Private Sub monthList_Click()
JCalendar1.Month = monthList.ListIndex
End Sub

Private Sub Form_Load()
monthList.ListIndex = JCalendar1.Month
yearText.Text = JCalendar1.Year
End Sub

Private Sub JCalendar1_monthChanged(ByVal Parameter0 As String, ByVal Parameter1 As String)
monthList.ListIndex = JCalendar1.Month
End Sub

Private Sub yearText_Change()
JCalendar1.Year = yearText.Text
End Sub

Private Sub nextMonth_Click()
JCalendar1.nextMonth
End Sub


JCHART.FRM

VERSION 5.00
Object = "{177CF4A3-E7E0-11D0-B8E7-0000F81ECCE7}#1.0#0"; "JChart.tlb"
Begin VB.Form Form1
   Caption         =   "Form1"
   ClientHeight    =   6750
   ClientLeft      =   4480
   ClientTop       =   2750
   ClientWidth     =   7640
   LinkTopic       =   "Form1"
   ScaleHeight     =   6750
   ScaleWidth      =   7640
   Begin JChart.JChart JChart1
      Height          =   3610
      Left            =   600
      OleObjectBlob   =   "JChart.frx":0000
      TabIndex        =   7
      Top             =   360
      Width           =   6610
   End
   Begin VB.ComboBox ComboFilter
      Height          =   240
      ItemData        =   "JChart.frx":0665
      Left            =   3240
      List            =   "JChart.frx":066C
      TabIndex        =   5
      Text            =   "Combo1"
      Top             =   6000
      Width           =   2050
   End
   Begin VB.FileListBox File1
      Height          =   1340
      Left            =   3240
      Pattern         =   "*.txt"
      TabIndex        =   4
      Top             =   4440
      Width           =   2050
   End
   Begin VB.DirListBox Dir1
      Height          =   1320
      Left            =   5400
      TabIndex        =   3
      Top             =   4440
      Width           =   2170
   End
   Begin VB.DriveListBox Drive1
      Height          =   240
      Left            =   5400
      TabIndex        =   2
      Top             =   6000
      Width           =   2170
   End
   Begin VB.ComboBox listChartType
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.29
         Charset         =   0
         Weight          =   400
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   280
      ItemData        =   "JChart.frx":067D
      Left            =   600
      List            =   "JChart.frx":068D
      Style           =   2  'Dropdown List
      TabIndex        =   0
      Top             =   4440
      Width           =   1450
   End
   Begin VB.Label Label2
      Caption         =   "Select an Excel text data file"
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.29
         Charset         =   0
         Weight          =   400
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   250
      Left            =   3360
      TabIndex        =   6
      Top             =   4200
      Width           =   2170
   End
   Begin VB.Label Label1
      Caption         =   "The chart type:"
      BeginProperty Font
         Name            =   "MS Sans Serif"
         Size            =   8.29
         Charset         =   0
         Weight          =   400
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   250
      Left            =   600
      TabIndex        =   1
      Top             =   4200
      Width           =   1330
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False

Private Sub ComboFilter_Change()
File1.Pattern = ComboFilter.Text
End Sub

Private Sub ComboFilter_Click()
File1.Pattern = ComboFilter.Text
End Sub

Private Sub Dir1_Change()
File1.Path = Dir1.Path
End Sub

Private Sub Drive1_Change()
Dir1.Path = Drive1.Drive
End Sub

Private Sub File1_Click()
JChart1.dataFileName = File1.Path & "\" & File1.filename
End Sub

Private Sub Form_Load()
listChartType.ListIndex = 0
ComboFilter.ListIndex = 0
End Sub

Private Sub listChartType_Click()
JChart1.chartType = listChartType.ListIndex + 1
End Sub


README.TXT

This sample shows how to create a SafeArray in Java,
pass them to a COM server (written in Visual Basic),
and how to inspect a SafeArray returned from a COM server.

   To run the sample, ensure that Visual Basic 5.0 is installed
and that the runtime dll (MSVBVM50.DLL) is in the system path.
Visual Basic 5.0 must be installed in order for the sample to
run. Change to the directory containing this readme file
and type "run" at the command line.

You should see the output:

   ComputeSum() returned 10

   CreateSquares() returned { 0 1 4 9 16 25 36 49 64 81 100 }
   Converting results to Java array: { 0 1 4 9 16 25 36 49 64 81 100 }


README.TXT

JNotepad Documentation

This directory contains additional documentation on JNotepad. These documents deal with
the technical details of the code and structure of JNotepad. View them if you are interested
in how JNotepad works and how to customize it.

Files in this directory:

JNotepadINI.htmlDescription of the format JNotepad uses in JNOTEPAD.INI.
Useful for customizing JNotepad

javadoc\Directory which contains source code in the JavaDoc
format. Useful for browsing the class hierarchy of
JNotepad and for finding out what each class does.


README.TXT

This is what you do to build binaries of the files in the ANSISTR directory:

1) Compile the C source
cl /LD AnsiMarshaler.c /I..\..\..\include ..\..\..\lib\i386\msjava.lib user32.lib ole32.lib AnsiMarshaler.def

2) Compile the Java source
jvc AnsiMarshaller.java

3) Copy files to run location
copy *.dll ..\rundir
copy *.class ..\rundir


PCH.C

/*++

Copyright (c) 1996-1997  Microsoft Corporation.  All rights reserved.

Module Name:

    pch.c

Abstract:

    Precompiled C header file source.

--*/

/* Headers
 **********/

#pragma hdrstop


ANSIMARSHALER.C

// Copyright (c) 1997 Microsoft Corporation.  All rights reserved.

#pragma hdrstop

#define INITGUID 1
#include <windows.h>
#include "native.h" // Raw Native Interface declarations.

typedef CHAR *PSTR, **PPSTR;

#ifdef __cplusplus
#define EXTERNC     extern "C"
#else
#define EXTERNC
#endif

// Macro for declaring RNI Java methods's (and encapsulating the java class package.)
// TODO: Replace "AnsiMarshaler" with the name of your hook class.
#define JAVAMETHOD(typ, name) \
    __declspec(dllexport) \
    EXTERNC \
    typ __cdecl AnsiMarshaler_##name

#define LOGCALL(hookname) OutputDebugString("AnsiMarshaler." hookname "() called.\n")

// Export that tells what RNI version we were built under.
DWORD __cdecl RNIGetCompatibleVersion()
{
  return RNIVER;
}

//==========================================================================
// toJava
//==========================================================================
JAVAMETHOD(HString*, toJava) (OBJECT*x, PPSTR pPSTR, int flags)
{
    PSTR pstr = *pPSTR;

    LOGCALL("toJava");

    if (pstr == NULL) {
        return NULL;
    } else {
        return makeJavaString(pstr, lstrlen(pstr));
    }
}

//==========================================================================
// toExternal
//==========================================================================
JAVAMETHOD(void, toExternal) (OBJECT*x, HString* javaval, int flags, PPSTR pPSTR)
{
    PSTR pstr = NULL;

    LOGCALL("toExternal");

    if (javaval != NULL) {

        DWORD len = javaStringLength(javaval);
        pstr = (LPSTR)CoTaskMemAlloc(len+1);
        if (!pstr) {
            SignalErrorPrintf("java/lang/OutOfMemoryError", "Could not allocate from task heap!");
        } else {
            char msg[100];
            DWORD i;
            unicode *pwc = javaStringStart(javaval);

            wsprintf(msg, "Hook created new string at %lxh\n", pstr);
            OutputDebugString(msg);

            for (i = 0; i < len; i++) {
                pstr[i] = (CHAR)(pwc[i]);
            }
            pstr[i] = '\0';
        }

    }
    *pPSTR = pstr;
}

//==========================================================================
// releaseExternal
//==========================================================================
JAVAMETHOD(void, releaseExternal) (OBJECT*x, PPSTR pPSTR, int flags)
{
    PSTR pstr = *pPSTR;

    LOGCALL("releaseExternal");

    if (pstr) {
        char msg[100];
        wsprintf(msg, "Hook freeing string at %lxh\n", pstr);
        OutputDebugString(msg);

        CoTaskMemFree(pstr);
    }
}

//==========================================================================
// TODO: Remove this copy of DllMain if you are embedding this file in your
//  own DLL.
// DLL Loader entry point.
//==========================================================================
BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason,
                                PVOID pvReserved)
{
   return TRUE;
}


CUSTSAMPLE.CPP

/*++

    Copyright (c) 1996-1997  Microsoft Corporation.  All rights reserved.

--*/

#pragma hdrstop

#define INITGUID 1

#include <windows.h>
#include <stdio.h>
#include <olectl.h>          // to get the SELFREF_E_CLASS definition

#define MYCHAR CHAR
#include "debug\\CustSample.h"  // ICustSample defn (generated from CustSample.odl).

//=======================================================================
// CCustSample
//
//   This class implements our COM object which exposes only one interface
//   (ICustSample).
//
//   Strictly speaking, it should also expose IMarshal by aggregating the
//   Ole free-threaded marshaler, since this dll marks itself "Both."
//   For such a simple sample, however, this will not matter.
//=======================================================================
class CCustSample : public ICustSample {
    private:
        ULONG       m_ref;          // Reference count.

    public:
        CCustSample() {
            m_ref = 0;
        }

        //-------------------------------------------------------
        // IUnknown methods.
        //-------------------------------------------------------
        STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppv) {
            __try {
                *ppv = NULL;
            } __except (EXCEPTION_EXECUTE_HANDLER) {
                return E_POINTER;
            }

            if (riid == IID_IUnknown || riid == IID_ICustSample) {
                AddRef();
                *ppv = (ICustSample*)this;
                return S_OK;
            }
            return E_NOINTERFACE;
        }

        STDMETHOD_(ULONG, AddRef) () {
            InterlockedIncrement( (LONG*)&m_ref );
            return 1;
        }

        STDMETHOD_(ULONG, Release) () {
            LONG ulc;

            ulc = InterlockedDecrement( (LONG*)&m_ref );
            if (ulc < 0) {
                OutputDebugString("Too many releases on CCustSample object!\n");
                DebugBreak();
            }
            if (0 == ulc) {
                OutputDebugString("CCustSample: Destroy\n");
                delete this;
                return 0;
            }
            return 1;
        }

        //-------------------------------------------------------
        // ICustSample methods.
        //-------------------------------------------------------
        STDMETHOD(PassFixedPtIn)(FIXED fp)
        {
            printf("C PassFixedPtIn called: (%ld, %lu)\n",
                   (LONG)(fp.value),
                   (ULONG)(fp.fract));
            return S_OK;
        }

        STDMETHOD(PassPFixedPtOutRetVal)(FIXED *pfp)
        {
            printf("C PassPFixedPtOutRetVal called. Returning 7.25\n");
            pfp->value = 7;
            pfp->fract = 16384;
            return S_OK;
        }

        STDMETHOD(PassPFixedPtIn)(FIXED *pfp)
        {
            printf("C PassPFixedPtIn called: (%ld, %lu)\n",
                   (LONG)(pfp->value),
                   (ULONG)(pfp->fract));
            return S_OK;
        }

        STDMETHOD(PassPFixedPtOut)(FIXED *pfp)
        {
            printf("C PassPFixedPtOut called. Returning 3.25\n");
            pfp->value = 3;
            pfp->fract = 16384;
            return S_OK;
        }

        STDMETHOD(PassPFixedPtInOut)(FIXED *pfp)
        {
            printf("C PassPFixedPtInOut called. Incrementing value by 2.\n");
            pfp->value += 2;
            return S_OK;
        }

        STDMETHOD(PassFixedPtsFromComToJava)(ICustSample *obj)
        {

            // This method demonstrates invoking Java methods from COM.
            // It accepts an ICustSample interface pointer (whose implementation
            // will be in Java, though this method is naturally oblivious
            // to that), and invokes every single method on it.

            HRESULT hr;

            {
                FIXED f = {32768, 4};
                hr = obj->PassFixedPtIn(f);
                if (S_OK != hr) {
                    return hr;
                }
            }

            {
                FIXED f = {0, 0};
                hr = obj->PassPFixedPtOutRetVal(&f);
                if (S_OK != hr) {
                    return hr;
                }
                printf("(%ld, %lu)\n", (LONG)(f.value), (ULONG)(f.fract));
            }
            {
                FIXED f = {32768, 8};
                hr = obj->PassPFixedPtIn(&f);
                if (S_OK != hr) {
                    return hr;
                }
            }
            {
                FIXED f = {0, 0};
                hr = obj->PassPFixedPtOut(&f);
                if (S_OK != hr) {
                    return hr;
                }
                printf("(%ld, %lu)\n", (LONG)(f.value), (ULONG)(f.fract));
            }

            {
                FIXED f = {16384, 8};
                hr = obj->PassPFixedPtInOut(&f);
                if (S_OK != hr) {
                    return hr;
                }
                printf("(%ld, %lu)\n", (LONG)(f.value), (ULONG)(f.fract));
            }

            return S_OK;

        }

        STDMETHOD(PassMyVariantIn)(MYVARIANT v)
        {
            printf("C PassMyVariantIn called: ");
            PrintBSTR(v.bstrVal);
            printf("\n");
            return S_OK;
        }

        STDMETHOD(PassPMyVariantOutRetVal)(MYVARIANT* pv)
        {
            printf("C PassPMyVariantOutRetVal called.\n");
            pv->vt = VT_BSTR;
            pv->bstrVal = NewBSTR(L"If you can read this, PassPMyVariantOutRetVal works.");
            return S_OK;
        }

        STDMETHOD(PassPMyVariantIn)(MYVARIANT *pv)
        {
            printf("C PassPMyVariantIn called: ");
            PrintBSTR(pv->bstrVal);
            printf("\n");
            return S_OK;
        }

        STDMETHOD(PassPMyVariantOut)(MYVARIANT* pv)
        {
            printf("C PassPMyVariantOut called.\n");
            pv->vt = VT_BSTR;
            pv->bstrVal = NewBSTR(L"If you can read this, PassPMyVariantOut works.");
            return S_OK;
        }

        STDMETHOD(PassPMyVariantInOut)(MYVARIANT* pv)
        {
            printf("C PassPMyVariantInOut called: ");
            PrintBSTR(pv->bstrVal);
            printf("\n");

            FreeBSTR(pv->bstrVal);

            pv->vt = VT_BSTR;
            pv->bstrVal = NewBSTR(L"If you can read this, the 'out' part of PassPMyVariantInOut works.");
            return S_OK;
        }

        STDMETHOD(PassMyVariantFromComToJava)(ICustSample *obj)
        {

            // This method demonstrates invoking Java methods from COM.
            // It accepts an ICustSample interface pointer (whose implementation
            // will be in Java, though this method is naturally oblivious
            // to that), and invokes every single method on it.

            HRESULT hr;
            {
                MYVARIANT mv;
                mv.vt = VT_BSTR;
                mv.bstrVal = NewBSTR(L"If you can read this, PassMyVariantIn worked.");
                hr = obj->PassMyVariantIn(mv);
                if (S_OK != hr) {
                    return hr;
                }
                FreeBSTR(mv.bstrVal);
            }

            {
                MYVARIANT mv;
                FillMemory(&mv, sizeof(mv), 0xcc);

                hr = obj->PassPMyVariantOutRetVal(&mv);
                if (S_OK != hr) {
                    return hr;
                }
                PrintBSTR(mv.bstrVal);
                printf("\n");
                FreeBSTR(mv.bstrVal);
            }

            {
                MYVARIANT mv;
                mv.vt = VT_BSTR;
                mv.bstrVal = NewBSTR(L"If you can read this, PPassMyVariantIn worked.");
                hr = obj->PassPMyVariantIn(&mv);
                if (S_OK != hr) {
                    return hr;
                }
                FreeBSTR(mv.bstrVal);
            }

            {
                MYVARIANT mv;
                FillMemory(&mv, sizeof(mv), 0xcc);

                hr = obj->PassPMyVariantOut(&mv);
                if (S_OK != hr) {
                    return hr;
                }
                PrintBSTR(mv.bstrVal);
                printf("\n");
                FreeBSTR(mv.bstrVal);
            }
            {
                MYVARIANT mv;
                mv.vt = VT_BSTR;
                mv.bstrVal = NewBSTR(L"If you can read this, the 'in' part of PPassMyVariantInOut worked.");

                hr = obj->PassPMyVariantInOut(&mv);
                if (S_OK != hr) {
                    return hr;
                }
                PrintBSTR(mv.bstrVal);
                printf("\n");
                FreeBSTR(mv.bstrVal);
            }

            return S_OK;
        }

        STDMETHOD(PassPPointOut)(POINT* ppt)
        {
            printf("C PassPPointOut called. Returning [5,6]\n");
            ppt->x = 5;
            ppt->y = 6;
            return S_OK;
        }

        STDMETHOD(PassPPointInOut)(POINT* ppt)
        {
            printf("C PassPPointInOut called. Received [%ld,%ld]. Translating x & y by 3.\n", ppt->x, ppt->y);
            ppt->x += 3;
            ppt->y += 3;
            return S_OK;
        }

        STDMETHOD(PassPointFromComToJava)(ICustSample *obj)
        {

            // This method demonstrates invoking Java methods from COM.
            // It accepts an ICustSample interface pointer (whose implementation
            // will be in Java, though this method is naturally oblivious
            // to that), and invokes every single method on it.

            HRESULT hr;
            {
                POINT p = {-1,-1};
                hr = obj->PassPPointOut(&p);
                if (hr != S_OK) {
                    return hr;
                }
                printf("[%ld,%ld]\n", p.x, p.y);
            }

            {
                POINT p = {10,11};
                hr = obj->PassPPointInOut(&p);
                if (hr != S_OK) {
                    return hr;
                }
                printf("[%ld,%ld]\n", p.x, p.y);
            }
            return S_OK;
        }

        STDMETHOD(PassRectIn)(RECT r)
        {
            printf("C PassRectIn called: ");
            PrintRect(r);
            printf("\n");
            return S_OK;
        }

        STDMETHOD(PassPRectOutRetVal)(RECT *pr)
        {
            RECT r = {4,5,10,11};
            printf("C PassPRectOutRetVal called. Returning ");
            PrintRect(r);
            printf("\n");
            *pr = r;
            return S_OK;
        }

        STDMETHOD(PassPRectIn)(RECT *pr)
        {
            printf("C PassPRectIn called: ");
            PrintRect(*pr);
            printf("\n");
            return S_OK;
        }

        STDMETHOD(PassPRectOut)(RECT *pr)
        {
            RECT r = {7,8,20,31};
            printf("C PassPRectOutRetVal called. Returning ");
            PrintRect(r);
            printf("\n");
            *pr = r;
            return S_OK;
        }

        STDMETHOD(PassPRectInOut)(RECT *pr)
        {
            printf("C PassPRectInOut called. Moving rectangle by (2,2)\n");
            pr->left   += 2;
            pr->top    += 2;
            pr->right  += 2;
            pr->bottom += 2;

            return S_OK;
        }

        STDMETHOD(PassPPRectOutRetVal)(RECT **ppr)
        {

            *ppr = NewRect(6,7,8,9);
            printf("C PassPPRectOutRetVal called. Returning ");
            PrintRect(**ppr);
            printf("\n");
            return S_OK;
        }

        STDMETHOD(PassPPRectIn)(RECT **ppr)
        {
            printf("C PassPPRectIn called: ");
            PrintRect(**ppr);
            printf("\n");
            return S_OK;
        }

        STDMETHOD(PassPPRectOut)(RECT **ppr)
        {

            *ppr = NewRect(13,14,15,16);
            printf("C PassPPRectOutcalled. Returning ");
            PrintRect(**ppr);
            printf("\n");
            return S_OK;
        }

        STDMETHOD(PassPPRectInOut)(RECT **ppr)
        {

            printf("C PassPPRectInOut called: ");
            PrintRect(**ppr);
            printf("\n");

            FreeRect(*ppr);

            *ppr = NewRect(1,2,3,4);
            printf("C PassPPRectInOut called. Returning ");
            PrintRect(**ppr);
            printf("\n");
            return S_OK;
        }

        STDMETHOD(PassRectFromComToJava)(ICustSample *obj)
        {

            // This method demonstrates invoking Java methods from COM.
            // It accepts an ICustSample interface pointer (whose implementation
            // will be in Java, though this method is naturally oblivious
            // to that), and invokes every single method on it.

            HRESULT hr;
            {
                RECT r = {4,5,6,7};
                PrintRect(r);printf("\n");
                hr = obj->PassRectIn(r);
                if (S_OK != hr) {
                    return hr;
                }
            }
            {
                RECT r = {-1,-1,-1,-1};
                hr = obj->PassPRectOutRetVal(&r);
                if (S_OK != hr) {
                    return hr;
                }
                PrintRect(r);printf("\n");
            }
            {
                RECT r = {14,15,16,17};
                PrintRect(r);printf("\n");
                hr = obj->PassPRectIn(&r);
                if (S_OK != hr) {
                    return hr;
                }
            }
            {
                RECT r = {-1,-1,-1,-1};
                hr = obj->PassPRectOut(&r);
                if (S_OK != hr) {
                    return hr;
                }
                PrintRect(r);printf("\n");
            }
            {
                RECT r = {24,25,26,27};
                hr = obj->PassPRectInOut(&r);
                if (S_OK != hr) {
                    return hr;
                }
                PrintRect(r);printf("\n");
            }
            {
                RECT *pr = NULL;
                hr = obj->PassPPRectOutRetVal(&pr);
                if (S_OK != hr) {
                    return hr;
                }
                PrintRect(*pr);printf("\n");
                FreeRect(pr);
            }
            {
                RECT r = {44,45,46,47};
                PRECT pr = &r;
                hr = obj->PassPPRectIn(&pr);
                if (S_OK != hr) {
                    return hr;
                }
            }

            {
                RECT *pr = NULL;
                hr = obj->PassPPRectOut(&pr);
                if (S_OK != hr) {
                    return hr;
                }
                PrintRect(*pr);printf("\n");
                FreeRect(pr);
            }
            {
                RECT *pr = NewRect(55,56,57,58);
                hr = obj->PassPPRectInOut(&pr);
                if (S_OK != hr) {
                    return hr;
                }
                PrintRect(*pr);printf("\n");
                FreeRect(pr);
            }
            return S_OK;
        }

        STDMETHOD(PassPStrIn)(LPSTR pstr)
        {
            printf("C PassPStrIn called: '%s'\n", pstr);
            return S_OK;
        }

        STDMETHOD(PassPPStrOutRetVal)(LPSTR *ppstr)
        {
            *ppstr = NewStr("If you can read this, C PassPPStrOutRetVal works.");
            return S_OK;
        }

        STDMETHOD(PassPPStrIn)(LPSTR *ppstr)
        {
            printf("C PassPPStrIn called: '%s'\n", *ppstr);
            return S_OK;
        }

        STDMETHOD(PassPPStrOut)(LPSTR *ppstr)
        {
            *ppstr = NewStr("If you can read this, C PassPPStrOut works.");
            return S_OK;
        }

        STDMETHOD(PassPPStrInOut)(LPSTR *ppstr)
        {
            printf("C PassPPStrInOut called: '%s'\n", *ppstr);
            FreeStr(*ppstr);
            *ppstr = NewStr("If you can read this, 'out' part of C PassPPStrInOut works.");
            return S_OK;
        }

        STDMETHOD(PassAnsiFromComToJava)(ICustSample *obj)
        {

            // This method demonstrates invoking Java methods from COM.
            // It accepts an ICustSample interface pointer (whose implementation
            // will be in Java, though this method is naturally oblivious
            // to that), and invokes every single method on it.

            HRESULT hr;
            {
                hr = obj->PassPStrIn("If you can read this, Java PassPStrIn works.");
                if (S_OK != hr) {
                    return hr;
                }
            }
            {
                LPSTR pstr = (LPSTR)0xcccccccc;
                hr = obj->PassPPStrOutRetVal(&pstr);
                if (S_OK != hr) {
                    return hr;
                }
                printf("%s\n", pstr);
                FreeStr(pstr);
            }
            {
                LPSTR pstr = "If you can read this, Java PassPPStrIn works.";
                hr = obj->PassPPStrIn(&pstr);
                if (S_OK != hr) {
                    return hr;
                }
            }
            {
                LPSTR pstr = (LPSTR)0xcccccccc;
                hr = obj->PassPPStrOut(&pstr);
                if (S_OK != hr) {
                    return hr;
                }
                printf("%s\n", pstr);
                FreeStr(pstr);
            }
            {
                LPSTR pstr = NewStr("If you can read this, 'in' part of Java PassPPStrInOut works.");
                hr = obj->PassPPStrInOut(&pstr);
                if (S_OK != hr) {
                    return hr;
                }
                printf("%s\n", pstr);
                FreeStr(pstr);
            }

            return S_OK;
        }

    private:

        //------------------------------------------------------------
        // Some simple utilities.
        //------------------------------------------------------------
        BSTR NewBSTR(LPCWSTR sz)
        {
            BSTR bstr = SysAllocString(sz);
            if (!bstr) {
                printf("Out of BSTR heap space.\n");
                exit(1);
            }

            CHAR msg[100];
            wsprintf(msg, "Server allocated BSTR at %lxh\n", bstr);
            OutputDebugString(msg);
            return bstr;
        }

        VOID FreeBSTR(BSTR bstr)
        {
            CHAR msg[100];
            wsprintf(msg, "Server freeing BSTR at %lxh\n", bstr);
            OutputDebugString(msg);

            SysFreeString(bstr);
        }

        VOID PrintBSTR(BSTR bstr)
        {
            DWORD len = SysStringLen(bstr);
            DWORD i;
            putchar('"');
            for (i = 0; i < len; i++) {
                putchar((CHAR)(bstr[i]));
            }
            putchar('"');
        }

        VOID PrintRect(RECT r)
        {
            printf("[x=%ld,y=%ld,width=%ld,height=%ld]",
                   r.left,
                   r.top,
                   r.right - r.left,
                   r.bottom - r.top);
        }

        RECT *NewRect(long x, long y, long width, long height)
        {
            RECT* pr = (RECT*)CoTaskMemAlloc(sizeof(RECT));
            if (!pr) {
                printf("Out of task heap space.\n");
                exit(1);
            }

            pr->left    = x;
            pr->top     = y;
            pr->right   = x + width;
            pr->bottom  = y + height;

            CHAR msg[100];
            wsprintf(msg, "Server allocated RECT at %lxh\n", pr);
            OutputDebugString(msg);

            return pr;
        }

        VOID FreeRect(RECT *pr)
        {
            CHAR msg[100];
            wsprintf(msg, "Server freeing RECT at %lxh\n", pr);
            OutputDebugString(msg);

            CoTaskMemFree(pr);
        }

        LPSTR NewStr(LPCSTR sz)
        {
            LPSTR pstr = (LPSTR)CoTaskMemAlloc(lstrlen(sz));
            if (!pstr) {
                printf("Out of heap space.\n");
                exit(1);
            }

            lstrcpy(pstr, sz);

            CHAR msg[100];
            wsprintf(msg, "Server allocated Str at %lxh\n", pstr);
            OutputDebugString(msg);
            return pstr;
        }

        VOID FreeStr(LPSTR pstr)
        {
            CHAR msg[100];
            wsprintf(msg, "Server freeing Str at %lxh\n", pstr);
            OutputDebugString(msg);

            CoTaskMemFree(pstr);
        }

};

//=======================================================================
// J2CClassFactory
//
//   This class implements the classfactory for our COM server. Since our
//   classfactory has no intrinsic state, we use a static class factory
//   to simplify our implementation.
//
//=======================================================================
class J2CClassFactory : public IClassFactory
{
    public:

        //-----------------------------------------------------------
        // IUnknown methods.
        //-----------------------------------------------------------
        STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppv)
        {
            __try {
                *ppv = NULL;
            } __except (EXCEPTION_EXECUTE_HANDLER) {
                return E_POINTER;
            }

            if (riid == IID_IUnknown || riid == IID_IClassFactory) {
                AddRef();
                *ppv = (IClassFactory*)this;
                return S_OK;
            }
            return E_NOINTERFACE;
        }

        STDMETHOD_(ULONG, AddRef) () {
            return 1;
        }

        STDMETHOD_(ULONG, Release) () {
            return 1;
        }

        //-----------------------------------------------------------
        // IClassFactory methods.
        //-----------------------------------------------------------
        STDMETHOD(CreateInstance)(IUnknown *punkOuter, REFIID riid, LPVOID *ppv)
        {
            *ppv = NULL;

            if (punkOuter != NULL) {
                return CLASS_E_NOAGGREGATION;
            }

            CCustSample *pJ2CThing;
            HRESULT hr;

            pJ2CThing = new CCustSample();
            if (!pJ2CThing) {
                return E_OUTOFMEMORY;
            }

            pJ2CThing->AddRef();
            hr = pJ2CThing->QueryInterface(riid, ppv);
            pJ2CThing->Release();
            return hr;
        }

        STDMETHOD(LockServer)(BOOL fLock) {
            return S_OK;
            }

};

//===================================================================
// Create our first (and only) classfactory.
//===================================================================
J2CClassFactory g_CF;

// Rememeber our dll's module handle.
HINSTANCE ghInstance;

//===================================================================
// Standard DLL entry point (called by Win32 loader.)
//===================================================================
BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason,
                                PVOID pvReserved)
{
   ghInstance = hmod;

   if (dwReason == DLL_PROCESS_ATTACH) {
        OutputDebugString("CustSample.dll has successfully loaded.\n");
   }

   return TRUE;
}

//===================================================================
// Standard Ole export ("creates" a classfactory.)
//===================================================================
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{

    HRESULT hr;

    __try {
        *ppv = NULL;
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        return E_POINTER;
    }

    if (rclsid != CLSID_CCustSample) {
        return CLASS_E_CLASSNOTAVAILABLE;
    }

    return g_CF.QueryInterface(riid, ppv);

}

//===================================================================
// Simple GUID unparsing utility (for self-registering code).
//===================================================================

#define GUIDSTR_MAX (1+ 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1)

static const CHAR szDigits[] = "0123456789ABCDEF";
static const BYTE GuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
                                8, 9, '-', 10, 11, 12, 13, 14, 15 };

//--------------------------------------------------------------------------
//
//  Function:   GUID2StringA
//
//  Synopsis:   Convert GUID to string form
//
//  Arguments:  [rguid] - the guid to convert
//              [lpszy] - buffer to hold the results
//
//  Returns:    nothing
//
//  This code is massively plagiarized from the Ole sources.
//--------------------------------------------------------------------------

VOID
GUID2StringA(REFGUID rguid, LPSTR lpsz)
{
    int i;
    LPSTR p = lpsz;

    const BYTE * pBytes = (const BYTE *) &rguid;

    *p++ = '{';

    for (i = 0; i < sizeof(GuidMap); i++)
    {
        if (GuidMap[i] == '-')
        {
            *p++ = '-';
        }
        else
        {
            *p++ = szDigits[ (pBytes[GuidMap[i]] & 0xF0) >> 4 ];
            *p++ = szDigits[ (pBytes[GuidMap[i]] & 0x0F) ];
        }
    }
    *p++ = '}';
    *p   = '\0';
}

const char achTM[] = "Both";
const char achDESC[] = "Sample Custom Marshaling Server.";
const char achPROGID[] = "CCustSample";

//===================================================================
// Standard Ole export (for self-registration a la regsvr32.)
//===================================================================
__declspec(dllexport)
STDAPI
DllRegisterServer(VOID)
{
   HKEY    hKey  = NULL;
   HKEY    hKey2 = NULL;
   HKEY    hKey3 = NULL;
   DWORD   result;
   HRESULT hr = SELFREG_E_CLASS;
   CHAR    achCLSID[GUIDSTR_MAX];
   TCHAR   achModulePathName[MAX_PATH];

   // If we fail in the middle, the state of the registry entries
   // is indeterminate (as per Ole specs.)

   // Create HKEY_CLASSES_ROOT\progid\CLSID
   result = RegCreateKey(HKEY_CLASSES_ROOT, achPROGID, &hKey);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   result = RegSetValue(hKey, NULL, REG_SZ, achDESC, lstrlen(achDESC));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   result = RegCreateKey(hKey, TEXT("CLSID"), &hKey2);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   GUID2StringA(CLSID_CCustSample, achCLSID);
   result = RegSetValue(hKey2, NULL, REG_SZ, achCLSID, GUIDSTR_MAX-1);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   RegCloseKey(hKey);
   RegCloseKey(hKey2);
   hKey = NULL;
   hKey2 = NULL;

   // Create HKEY_CLASSES_ROOT\CLSID\...
   result = RegCreateKey(HKEY_CLASSES_ROOT, TEXT("CLSID"), &hKey);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegCreateKey(hKey, achCLSID, &hKey2);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegSetValue(hKey2, NULL, REG_SZ, achDESC, lstrlen(achDESC));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegCreateKey(hKey2, "InprocServer32", &hKey3);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = GetModuleFileName(ghInstance, achModulePathName, sizeof(achModulePathName)/sizeof(TCHAR));
   if (result == 0) {  //No way to detect truncation from GetModuleFileName.
      goto lExit;
   }

   result = RegSetValue(hKey3, NULL, REG_SZ, achModulePathName, lstrlen(achModulePathName));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegSetValueEx(hKey3, "ThreadingModel", 0, REG_SZ, (BYTE*)achTM, sizeof(achTM));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   RegCloseKey(hKey3);
   hKey3 = NULL;

   result = RegCreateKey(hKey2, "ProgID", &hKey3);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   result = RegSetValue(hKey3, NULL, REG_SZ, achPROGID, lstrlen(achPROGID));

if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   RegCloseKey(hKey3);
   hKey3 = NULL;

   hr = S_OK;

 lExit:
   if (hKey) {
      RegCloseKey(hKey);
   }
   if (hKey2) {
      RegCloseKey(hKey2);
   }
   if (hKey3) {
      RegCloseKey(hKey3);
   }
   return hr;

}

//===================================================================
// Standard Ole export (for self-unregistration a la regsvr32.)
//===================================================================
__declspec(dllexport)
STDAPI
DllUnregisterServer(VOID)
{
    return S_OK;
}


PCHCPP.CPP

#pragma hdrstop


README.TXT

This is what you do to build binaries of the files in the FIXEDPT directory:

1) Compile the C source
cl /LD FixedPtMarshaler.c /I..\..\..\include ..\..\..\lib\i386\msjava.lib user32.lib ole32.lib FixedPtMarshaler.def

2) Compile the Java source
jvc FixedPtMarshaler.java

3) Copy files to run location
copy *.dll ..\rundir
copy *.class ..\rundir


FIXEDPTMARSHALER.C

// Copyright (c) 1997  Microsoft Corporation.  All rights reserved.
//=====================================================================
// Sample Java/COM Marshaling Hook Class.
//
// Java Type:  double
// Com  Type:  struct {
//                 WORD     fract;
//                 short    value;
//             } FIXED;
//=====================================================================

#pragma hdrstop

#include <windows.h>
#include "native.h" // Raw Native Interface declarations.

typedef FIXED *PFIXED, **PPFIXED;

#ifdef __cplusplus
#define EXTERNC     extern "C"
#else
#define EXTERNC
#endif

// Macro for declaring RNI Java methods's (and encapsulating the java class package.)
#define JAVAMETHOD(typ, name) \
    __declspec(dllexport) \
    EXTERNC \
    typ __cdecl FixedPtMarshaler_##name

#define LOGCALL(hookname) OutputDebugString("FixedPtMarshaler." hookname "() called.\n")

// Export that tells what RNI version we were built under.
DWORD __cdecl RNIGetCompatibleVersion()
{
  return RNIVER;
}

//==========================================================================
// toJava
//==========================================================================
JAVAMETHOD(double, toJava) (OBJECT*x, PPFIXED ppFIXED, int flags)
{
    const FIXED *pFIXED = *ppFIXED;

    LOGCALL("toJava");

    // Convert FIXED to double.
    return ((double)(pFIXED->value)) + ((double)(pFIXED->fract)) / 65536.0;
}

//==========================================================================
// copyToExternal
//==========================================================================
JAVAMETHOD(void, copyToExternal) (OBJECT*x, double javaval, PPFIXED ppFIXED, int flags)
{
    FIXED *pFIXED = *ppFIXED;

    LOGCALL("copyToExternal");

    // Convert double to FIXED.
    if (javaval > 32767.0 || javaval < -32768.0) {
        SignalErrorPrintf("java/lang/IllegalArgumentException", "Overflow on double->FIXED conversion.");
    } else {
        pFIXED->value = (short)javaval;
        pFIXED->fract = (short) ((javaval - (short)javaval) * 65536.0);
    }
}

//==========================================================================
// TODO: Remove this copy of DllMain if you are embedding this file in your
//  own DLL.
// DLL Loader entry point.
//==========================================================================
BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason,
                                PVOID pvReserved)
{
   return TRUE;
}


PLOTTER.CPP

/*++

Copyright (c) 1996-1997  Microsoft Corporation.  All rights reserved.

--*/

#pragma hdrstop

#define INITGUID 1

#include <windows.h>
#include <stdio.h>
#include <olectl.h>          // to get the SELFREF_E_CLASS definition

#include "debug\\Plotter.h"  // IPlotter defn (generated from Plotter.odl).

//=======================================================================
// CPlotter
//
//   This class implements our COM object which exposes only one interface
//   (IPlotter).
//
//   Strictly speaking, it should also expose IMarshal by aggregating the
//   Ole free-threaded marshaler, since this dll marks itself "Both."
//   For such a simple sample, however, this will not matter.
//=======================================================================

class CPlotter : public IPlotter {
    private:
        ULONG       m_ref;          // Reference count.

    public:
        CPlotter() {
            m_ref = 0;
        }

        //-------------------------------------------------------
        // IUnknown methods.
        //-------------------------------------------------------
        STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppv) {
            __try {
                *ppv = NULL;
            } __except (EXCEPTION_EXECUTE_HANDLER) {
                return E_POINTER;
            }

            if (riid == IID_IUnknown || riid == IID_IPlotter) {
                AddRef();
                *ppv = (IPlotter*)this;
                return S_OK;
            }
            return E_NOINTERFACE;
        }

        STDMETHOD_(ULONG, AddRef) () {
            InterlockedIncrement( (LONG*)&m_ref );
            return 1;
        }

        STDMETHOD_(ULONG, Release) () {
            LONG ulc;

            ulc = InterlockedDecrement( (LONG*)&m_ref );
            if (ulc < 0) {
                OutputDebugString("Too many releases on CPlotter object!\n");
                DebugBreak();
            }
            if (0 == ulc) {
                OutputDebugString("CPlotter: Destroy\n");
                delete this;
                return 0;
            }
            return 1;
        }

        //-------------------------------------------------------
        // IPlotter methods.
        //-------------------------------------------------------
        STDMETHOD(DrawLine)(POINT *start, POINT *end)
        {
            printf("DrawLine called: start = (%ld,%ld), end = (%ld,%ld)\n",
                   start->x,
                   start->y,
                   end->x,
                   end->y);
            return S_OK;
        }

};

//=======================================================================
// J2CClassFactory
//
//   This class implements the classfactory for our COM server. Since our
//   classfactory has no intrinsic state, we use a static class factory
//   to simplify our implementation.
//
//=======================================================================
class J2CClassFactory : public IClassFactory
{
    public:

        //-----------------------------------------------------------
        // IUnknown methods.
        //-----------------------------------------------------------
        STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppv)
        {
            __try {
                *ppv = NULL;
            } __except (EXCEPTION_EXECUTE_HANDLER) {
                return E_POINTER;
            }

            if (riid == IID_IUnknown || riid == IID_IClassFactory) {
                AddRef();
                *ppv = (IClassFactory*)this;
                return S_OK;
            }
            return E_NOINTERFACE;
        }

        STDMETHOD_(ULONG, AddRef) () {
            return 1;
        }

        STDMETHOD_(ULONG, Release) () {
            return 1;
        }

        //-----------------------------------------------------------
        // IClassFactory methods.
        //-----------------------------------------------------------
        STDMETHOD(CreateInstance)(IUnknown *punkOuter, REFIID riid, LPVOID *ppv)
        {
            *ppv = NULL;

            if (punkOuter != NULL) {
                return CLASS_E_NOAGGREGATION;
            }

            CPlotter *pJ2CThing;
            HRESULT hr;

            pJ2CThing = new CPlotter();
            if (!pJ2CThing) {
                return E_OUTOFMEMORY;
            }

            pJ2CThing->AddRef();
            hr = pJ2CThing->QueryInterface(riid, ppv);
            pJ2CThing->Release();
            return hr;
        }

        STDMETHOD(LockServer)(BOOL fLock) {
            return S_OK;
            }

};

//===================================================================
// Create our first (and only) classfactory.
//===================================================================
J2CClassFactory g_CF;

// Rememeber our dll's module handle.
HINSTANCE ghInstance;

//===================================================================
// Standard DLL entry point (called by Win32 loader.)
//===================================================================
BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason,
                                PVOID pvReserved)
{
   ghInstance = hmod;

   if (dwReason == DLL_PROCESS_ATTACH) {
        OutputDebugString("Plotter.dll has successfully loaded.\n");
   }

   return TRUE;
}

//===================================================================
// Standard Ole export ("creates" a classfactory.)
//===================================================================
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{

    HRESULT hr;

    __try {
        *ppv = NULL;
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        return E_POINTER;
    }

    if (rclsid != CLSID_CPlotter) {
        return CLASS_E_CLASSNOTAVAILABLE;
    }

    return g_CF.QueryInterface(riid, ppv);

}

//===================================================================
// Simple GUID unparsing utility (for self-registering code).
//===================================================================

#define GUIDSTR_MAX (1+ 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1)

static const CHAR szDigits[] = "0123456789ABCDEF";
static const BYTE GuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
                                8, 9, '-', 10, 11, 12, 13, 14, 15 };

//--------------------------------------------------------------------------
//
//  Function:   GUID2StringA
//
//  Synopsis:   Convert GUID to string form
//
//  Arguments:  [rguid] - the guid to convert
//              [lpszy] - buffer to hold the results
//
//  Returns:    nothing
//
//  This code is massively plagiarized from the Ole sources.
//--------------------------------------------------------------------------

VOID
GUID2StringA(REFGUID rguid, LPSTR lpsz)
{
    int i;
    LPSTR p = lpsz;

    const BYTE * pBytes = (const BYTE *) &rguid;

    *p++ = '{';

    for (i = 0; i < sizeof(GuidMap); i++)
    {
        if (GuidMap[i] == '-')
        {
            *p++ = '-';
        }
        else
        {
            *p++ = szDigits[ (pBytes[GuidMap[i]] & 0xF0) >> 4 ];
            *p++ = szDigits[ (pBytes[GuidMap[i]] & 0x0F) ];
        }
    }
    *p++ = '}';
    *p   = '\0';
}

const char achTM[] = "Both";
const char achDESC[] = "Sample Custom Marshaling Server.";
const char achPROGID[] = "CPlotter";

//===================================================================
// Standard Ole export (for self-registration a la regsvr32.)
//===================================================================
__declspec(dllexport)
STDAPI
DllRegisterServer(VOID)
{
   HKEY    hKey  = NULL;
   HKEY    hKey2 = NULL;
   HKEY    hKey3 = NULL;
   DWORD   result;
   HRESULT hr = SELFREG_E_CLASS;
   CHAR    achCLSID[GUIDSTR_MAX];
   TCHAR   achModulePathName[MAX_PATH];

   // If we fail in the middle, the state of the registry entries
   // is indeterminate (as per Ole specs.)

   // Create HKEY_CLASSES_ROOT\progid\CLSID
   result = RegCreateKey(HKEY_CLASSES_ROOT, achPROGID, &hKey);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   result = RegSetValue(hKey, NULL, REG_SZ, achDESC, lstrlen(achDESC));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   result = RegCreateKey(hKey, TEXT("CLSID"), &hKey2);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   GUID2StringA(CLSID_CPlotter, achCLSID);
   result = RegSetValue(hKey2, NULL, REG_SZ, achCLSID, GUIDSTR_MAX-1);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   RegCloseKey(hKey);
   RegCloseKey(hKey2);
   hKey = NULL;
   hKey2 = NULL;

   // Create HKEY_CLASSES_ROOT\CLSID\...
   result = RegCreateKey(HKEY_CLASSES_ROOT, TEXT("CLSID"), &hKey);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegCreateKey(hKey, achCLSID, &hKey2);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegSetValue(hKey2, NULL, REG_SZ, achDESC, lstrlen(achDESC));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegCreateKey(hKey2, "InprocServer32", &hKey3);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = GetModuleFileName(ghInstance, achModulePathName, sizeof(achModulePathName)/sizeof(TCHAR));
   if (result == 0) {  //No way to detect truncation from GetModuleFileName.
      goto lExit;
   }

   result = RegSetValue(hKey3, NULL, REG_SZ, achModulePathName, lstrlen(achModulePathName));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegSetValueEx(hKey3, "ThreadingModel", 0, REG_SZ, (BYTE*)achTM, sizeof(achTM));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   RegCloseKey(hKey3);
   hKey3 = NULL;

   result = RegCreateKey(hKey2, "ProgID", &hKey3);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   result = RegSetValue(hKey3, NULL, REG_SZ, achPROGID, lstrlen(achPROGID));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   RegCloseKey(hKey3);
   hKey3 = NULL;

   hr = S_OK;

 lExit:
   if (hKey) {
      RegCloseKey(hKey);
   }
   if (hKey2) {
      RegCloseKey(hKey2);
   }
   if (hKey3) {
      RegCloseKey(hKey3);
   }
   return hr;

}

//===================================================================
// Standard Ole export (for self-unregistration a la regsvr32.)
//===================================================================
__declspec(dllexport)
STDAPI
DllUnregisterServer(VOID)
{
    return S_OK;
}


README.TXT

This is what you do to build binaries of the files in the POINT directory:

1) Compile the C source
cl /LD PointMarshaler.c /I..\..\..\include ..\..\..\lib\i386\msjava.lib user32.lib ole32.lib PointMarshaler.def

2) Compile the Java source
jvc PointMarshaler.java

3) Copy files to run location
copy *.dll ..\rundir
copy *.class ..\rundir


POINTMARSHALER.C

// Copyright (c) 1996-1997  Microsoft Corporation.  All rights reserved.

#pragma hdrstop

#define INITGUID 1
#include <windows.h>
#include "native.h" // Raw Native Interface declarations.

typedef POINT *PPOINT, **PPPOINT;

typedef OBJECT* JPoint;

#ifdef __cplusplus
#define EXTERNC     extern "C"
#else
#define EXTERNC
#endif

// Macro for declaring RNI Java methods's (and encapsulating the java class package.)
#define JAVAMETHOD(typ, name) \
    __declspec(dllexport) \
    EXTERNC \
    typ __cdecl PointMarshaler_##name

#define LOGCALL(hookname) OutputDebugString("PointMarshaler." hookname "() called.\n")

// Export that tells what RNI version we were built under.
DWORD __cdecl RNIGetCompatibleVersion()
{
  return RNIVER;
}

// Cache the various class and fields we will need.
ClassClass*         gPointClass;
struct fieldblock*  gPointXField;
struct fieldblock*  gPointYField;

//==========================================================================
// toJava
//==========================================================================
JAVAMETHOD(JPoint, toJava) (OBJECT*x, PPPOINT ppPOINT, int flags)
{
    PPOINT pPOINT = *ppPOINT;
    JPoint jpoint;

    LOGCALL("toJava");

    jpoint = execute_java_constructor(NULL, NULL, gPointClass, "(II)", pPOINT->x, pPOINT->y);
    return jpoint;
}

//==========================================================================
// copyToExternal
//==========================================================================
JAVAMETHOD(void, copyToExternal) (OBJECT*x, JPoint javaval, PPPOINT ppPOINT, int flags)
{
    PPOINT pPOINT = *ppPOINT;

    LOGCALL("copyToExternal");

    pPOINT->x = Field_GetInt(javaval, gPointXField);
    pPOINT->y = Field_GetInt(javaval, gPointYField);

}

//==========================================================================
// copyToJava
//==========================================================================
JAVAMETHOD(void, copyToJava) (OBJECT*x, PPPOINT ppPOINT, JPoint javaval, int flags)
{
    PPOINT pPOINT = *ppPOINT;    \

    LOGCALL("copyToJava");

    Field_SetInt(javaval, gPointXField, pPOINT->x);
    Field_SetInt(javaval, gPointYField, pPOINT->y);

}

//==========================================================================
// toUninitJava
//==========================================================================
JAVAMETHOD(JPoint, toUninitJava) (OBJECT*x, PPPOINT ppPOINT, int flags)
{
    JPoint jpoint;

    LOGCALL("toUninitJava");

    jpoint = execute_java_constructor(NULL, NULL, gPointClass, "(II)", -1, -1);
    return jpoint;
}

//==========================================================================
// TODO: Remove this copy of DllMain if you are embedding this file in your
//  own DLL.
// DLL Loader entry point.
//==========================================================================
BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason,
                                PVOID pvReserved)
{
   return TRUE;
}

//==========================================================================
// classInit (one time initialization routine)
//==========================================================================
JAVAMETHOD(void, classInit) (OBJECT*x)
{
    gPointClass = FindClass(NULL, "java/awt/Point", TRUE);
    if (!gPointClass) {
        SignalErrorPrintf("java/lang/ClassNotFoundException", "Could not preload java.awt.Point class!");
    }

    gPointXField = Class_GetField(gPointClass, "x");
    if (!gPointXField) {
        SignalErrorPrintf("java/lang/ClassNotFoundException", "Bogus java.awt.Point class!");
    }

    gPointYField = Class_GetField(gPointClass, "y");
    if (!gPointYField) {
        SignalErrorPrintf("java/lang/ClassNotFoundException", "Bogus java.awt.Point class!");
    }
}


PLOTTER.H

/* This header file machine-generated by mktyplib.exe */
/* Interface to type library: JavaLib */

#ifndef _JavaLib_H_
#define _JavaLib_H_

DEFINE_GUID(LIBID_JavaLib,0xEFFA21E0L,0x6928,0x11D0,0xBF,0xFC,0x00,0xAA,0x00,0x3C,0xFD,0xFC);

DEFINE_GUID(IID_IPlotter,0xEFFA21E1L,0x6928,0x11D0,0xBF,0xFC,0x00,0xAA,0x00,0x3C,0xFD,0xFC);

/* Definition of interface: IPlotter */
#undef INTERFACE
#define INTERFACE IPlotter

DECLARE_INTERFACE_(IPlotter, IUnknown)
{
#ifndef NO_BASEINTERFACE_FUNCS

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
    STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif

    /* IPlotter methods */
    STDMETHOD(DrawLine)(THIS_ POINT FAR* start, POINT FAR* end) PURE;
};

DEFINE_GUID(CLSID_CPlotter,0xEFFA21E2L,0x6928,0x11D0,0xBF,0xFC,0x00,0xAA,0x00,0x3C,0xFD,0xFC);

#ifdef __cplusplus
class CPlotter;
#endif

#endif


README.TXT

This is what you do to build binaries of the files in the RECT directory:

1) Compile the C source
cl /LD RectMarshaler.c /I..\..\..\include ..\..\..\lib\i386\msjava.lib user32.lib ole32.lib RectMarshaler.def

2) Compile the Java source
jvc RectMarshaler.java

3) Copy files to run location
copy *.dll ..\rundir
copy *.class ..\rundir


RECTMARSHALER.C

// Copyright (c) 1996-1997  Microsoft Corporation.  All rights reserved.

#pragma hdrstop

#define INITGUID 1
#include <windows.h>
#include "native.h" // Raw Native Interface declarations.

typedef RECT *PRECT, **PPRECT;

#ifdef __cplusplus
#define EXTERNC     extern "C"
#else
#define EXTERNC
#endif

// Macro for declaring RNI Java methods's (and encapsulating the java class package.)
// TODO: Replace "RectMarshaler" with the name of your hook class.
#define JAVAMETHOD(typ, name) \
    __declspec(dllexport) \
    EXTERNC \
    typ __cdecl RectMarshaler_##name

#define LOGCALL(hookname) OutputDebugString("RectMarshaler." hookname "() called.\n")

// Export that tells what RNI version we were built under.
DWORD __cdecl RNIGetCompatibleVersion()
{
  return RNIVER;
}

// Cache the various class and fields we will need.
ClassClass*         gRectangleClass;
struct fieldblock*  gRectangleXField;
struct fieldblock*  gRectangleYField;
struct fieldblock*  gRectangleWidthField;
struct fieldblock*  gRectangleHeightField;

//==========================================================================
// toJava
//==========================================================================
JAVAMETHOD(OBJECT*, toJava) (OBJECT*x, PPRECT ppRECT, int flags)
{

    PRECT prect = *ppRECT;

    LOGCALL("toJava");

    return execute_java_constructor(NULL,
                                    NULL,
                                    gRectangleClass,
                                    "(IIII)",
                                    prect->left,
                                    prect->top,
                                    prect->right - prect->left,
                                    prect->bottom - prect->top);
}

void copyToExternalWorker(OBJECT *javaval, PRECT pRECT)
{

    int x, y, width, height;

    x      = Field_GetInt(javaval, gRectangleXField);
    y      = Field_GetInt(javaval, gRectangleYField);
    width  = Field_GetInt(javaval, gRectangleWidthField);
    height = Field_GetInt(javaval, gRectangleHeightField);

    pRECT->left   = x;
    pRECT->top    = y;
    pRECT->right  = x + width;
    pRECT->bottom = y + height;
}

//==========================================================================
// copyToExternal
//==========================================================================
JAVAMETHOD(void, copyToExternal) (OBJECT*x, OBJECT* javaval, PPRECT ppRECT, int flags)
{
    LOGCALL("copyToExternal");

    copyToExternalWorker(javaval, *ppRECT);
}

//==========================================================================
// toExternal
//==========================================================================
JAVAMETHOD(void, toExternal) (OBJECT*x, OBJECT* javaval, int flags, PPRECT ppRECT)
{
    PRECT pRECT = NULL;

    LOGCALL("toExternal");

    pRECT = (RECT*)CoTaskMemAlloc(sizeof(RECT));
    if (!pRECT) {
        SignalErrorPrintf("java/lang/OutOfMemoryError", "Could not allocate from task heap!");
    } else {
        char msg[100];
        wsprintf(msg, "Hook allocated RECT at %lxh\n", pRECT);
        OutputDebugString(msg);
        copyToExternalWorker(javaval, pRECT);
        *ppRECT = pRECT;
    }
}

//==========================================================================
// releaseExternal
//==========================================================================
JAVAMETHOD(void, releaseExternal) (OBJECT*x, PPRECT ppRECT, int flags)
{
    PRECT pRECT = *ppRECT;
    char msg[100];

    LOGCALL("releaseExternal");
    wsprintf(msg, "Hook freeing RECT at %lxh\n", pRECT);
    OutputDebugString(msg);
    if (pRECT) {
        CoTaskMemFree(pRECT);
    }
}

//==========================================================================
// classInit (one time initialization routine)
//==========================================================================
JAVAMETHOD(void, classInit) (OBJECT*x)
{
    gRectangleClass = FindClass(NULL, "java/awt/Rectangle", TRUE);
    if (!gRectangleClass) {
        SignalErrorPrintf("java/lang/ClassNotFoundException", "Could not preload java.awt.Rectangle class!");
    }

    gRectangleXField = Class_GetField(gRectangleClass, "x");
    if (!gRectangleXField) {
        SignalErrorPrintf("java/lang/ClassNotFoundException", "Bogus java.awt.Rectangle class!");
    }

    gRectangleYField = Class_GetField(gRectangleClass, "y");
    if (!gRectangleYField) {
        SignalErrorPrintf("java/lang/ClassNotFoundException", "Bogus java.awt.Rectangle class!");
    }

    gRectangleWidthField = Class_GetField(gRectangleClass, "width");
    if (!gRectangleWidthField) {
        SignalErrorPrintf("java/lang/ClassNotFoundException", "Bogus java.awt.Rectangle class!");
    }

    gRectangleHeightField = Class_GetField(gRectangleClass, "height");
    if (!gRectangleHeightField) {
        SignalErrorPrintf("java/lang/ClassNotFoundException", "Bogus java.awt.Rectangle class!");
    }
}

//==========================================================================
// TODO: Remove this copy of DllMain if you are embedding this file in your
//  own DLL.
// DLL Loader entry Rectangle.
//==========================================================================
BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason,
                                PVOID pvReserved)
{
   return TRUE;
}


CUSTSAMPLE.H

/* This header file machine-generated by mktyplib.exe */
/* Interface to type library: JavaLib */

#ifndef _JavaLib_H_
#define _JavaLib_H_

DEFINE_GUID(LIBID_JavaLib,0x2BEBEC40L,0x6499,0x11D0,0xBF,0xFC,0x00,0xAA,0x00,0x3C,0xFD,0xFC);

typedef struct MYVARIANT {
    unsigned short vt;
    unsigned short pad1;
    unsigned short pad2;
    unsigned short pad3;
    BSTR bstrVal;
    unsigned long pad4;
} MYVARIANT;

DEFINE_GUID(IID_ICustSample,0x2BEBEC41L,0x6499,0x11D0,0xBF,0xFC,0x00,0xAA,0x00,0x3C,0xFD,0xFC);

/* Definition of interface: ICustSample */
#undef INTERFACE
#define INTERFACE ICustSample

DECLARE_INTERFACE_(ICustSample, IUnknown)
{
#ifndef NO_BASEINTERFACE_FUNCS

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
    STDMETHOD_(ULONG, Release)(THIS) PURE;
#endif

    /* ICustSample methods */
    STDMETHOD(PassFixedPtIn)(THIS_ FIXED fp) PURE;
    STDMETHOD(PassPFixedPtOutRetVal)(THIS_ FIXED FAR* pfp) PURE;
    STDMETHOD(PassPFixedPtIn)(THIS_ FIXED FAR* pfp) PURE;
    STDMETHOD(PassPFixedPtOut)(THIS_ FIXED FAR* pfp) PURE;
    STDMETHOD(PassPFixedPtInOut)(THIS_ FIXED FAR* pfp) PURE;
    STDMETHOD(PassFixedPtsFromComToJava)(THIS_ ICustSample FAR* pCustSample) PURE;
    STDMETHOD(PassMyVariantIn)(THIS_ struct MYVARIANT v) PURE;
    STDMETHOD(PassPMyVariantOutRetVal)(THIS_ struct MYVARIANT FAR* pv) PURE;
    STDMETHOD(PassPMyVariantIn)(THIS_ struct MYVARIANT FAR* pv) PURE;
    STDMETHOD(PassPMyVariantOut)(THIS_ struct MYVARIANT FAR* pv) PURE;
    STDMETHOD(PassPMyVariantInOut)(THIS_ struct MYVARIANT FAR* pv) PURE;
    STDMETHOD(PassMyVariantFromComToJava)(THIS_ ICustSample FAR* pCustSample) PURE;
    STDMETHOD(PassPPointOut)(THIS_ POINT FAR* ppt) PURE;
    STDMETHOD(PassPPointInOut)(THIS_ POINT FAR* ppt) PURE;
    STDMETHOD(PassPointFromComToJava)(THIS_ ICustSample FAR* pCustSample) PURE;
    STDMETHOD(PassRectIn)(THIS_ RECT r) PURE;
    STDMETHOD(PassPRectOutRetVal)(THIS_ RECT FAR* pr) PURE;
    STDMETHOD(PassPRectIn)(THIS_ RECT FAR* pr) PURE;
    STDMETHOD(PassPRectOut)(THIS_ RECT FAR* pr) PURE;
    STDMETHOD(PassPRectInOut)(THIS_ RECT FAR* pr) PURE;
    STDMETHOD(PassPPRectOutRetVal)(THIS_ RECT FAR* FAR* ppr) PURE;
    STDMETHOD(PassPPRectIn)(THIS_ RECT FAR* FAR* ppr) PURE;
    STDMETHOD(PassPPRectOut)(THIS_ RECT FAR* FAR* ppr) PURE;
    STDMETHOD(PassPPRectInOut)(THIS_ RECT FAR* FAR* ppr) PURE;
    STDMETHOD(PassRectFromComToJava)(THIS_ ICustSample FAR* pCustSample) PURE;
    STDMETHOD(PassPStrIn)(THIS_ MYCHAR FAR* pstr) PURE;
    STDMETHOD(PassPPStrOutRetVal)(THIS_ MYCHAR FAR* FAR* ppstr) PURE;
    STDMETHOD(PassPPStrIn)(THIS_ MYCHAR FAR* FAR* ppstr) PURE;
    STDMETHOD(PassPPStrOut)(THIS_ MYCHAR FAR* FAR* ppstr) PURE;
    STDMETHOD(PassPPStrInOut)(THIS_ MYCHAR FAR* FAR* ppstr) PURE;
    STDMETHOD(PassAnsiFromComToJava)(THIS_ ICustSample FAR* pCustSample) PURE;
};

DEFINE_GUID(CLSID_CCustSample,0x2BEBEC42L,0x6499,0x11D0,0xBF,0xFC,0x00,0xAA,0x00,0x3C,0xFD,0xFC);

#ifdef __cplusplus
class CCustSample;
#endif

#endif


TEMPLATEMARSHALER.C

// Copyright (c) 1996-1997  Microsoft Corporation.  All rights reserved.

#pragma hdrstop

#include <windows.h>
#include "native.h" // Raw Native Interface declarations.

// TODO: Replace this typedef with your own definition of ETYPE, and/or replace
// all occurrences of ETYPE, PETYPE and PPETYPE with your own type.
typedef struct {
    int dummy;
} ETYPE;

typedef ETYPE *PETYPE, **PPETYPE;

// TODO: Define JTYPE with the appropriate type.
//
//   If your Java type is           replace JTYPE with
//   --------------------           ------------------
//   byte                           char            (see below)
//   char                           unsigned short  (see below)
//   short                          short           (see below)
//   int                            int
//   long                           long
//   boolean                        BOOL
//   float                          float
//   double                         double
//   an object type                 OBJECT* or output of msjavah.exe
//   an array                       HArrayOf<elemtype> (see native.h or
//                                   output of msjavah)
//
//
//  IMPORTANT NOTE:
//     If your java type is byte, char, or short, the return types
//     of toJava and toUninitJava must be declared as "int" and your
//     C function must expand the result to a full 32-bit signed integer
//     (RNI does not expand short integer return values on your behalf)
//
//     If your java tyoe is boolean, your C function must return
//     0 or 1 only (RNI does not normalize boolean return values.)
//

typedef int JTYPE;

#ifdef __cplusplus
#define EXTERNC     extern "C"
#else
#define EXTERNC
#endif

// Macro for declaring RNI Java methods's (and encapsulating the java class package.)
// TODO: Replace "TemplateMarshaler" with the name of your hook class.
#define JAVAMETHOD(typ, name) \
    __declspec(dllexport) \
    EXTERNC \
    typ __cdecl TemplateMarshaler_##name

// This export tells what RNI version we were built under.  This is a
// NEW REQUIREMENT that was not needed for previous versions of the VM.
// DO NOT REMOVE THIS FUNCTION.  Without it, the VM will be unable to
// access your hook classes.
DWORD __cdecl RNIGetCompatibleVersion()
{
  return RNIVER;
}

//==========================================================================
// toJava
//==========================================================================
JAVAMETHOD(JTYPE, toJava) (OBJECT*x, PPETYPE ppetype, int flags)
{
    PETYPE petype = *ppetype;
    // TODO: Add code to convert *petype to a value of type JTYPE.
    // If JTYPE is an object or array type, this method should return
    // a newly created instance of JTYPE.
    return 0;
}

//==========================================================================
// copyToExternal
//==========================================================================
JAVAMETHOD(void, copyToExternal) (OBJECT*x, JTYPE javaval, PPETYPE ppetype, int flags)
{
    PETYPE petype = *ppetype;
    // TODO: Add code to convert javaval into an ETYPE and store the resulting
    // ETYPE in the (already allocated) memory pointed to by petype. Do
    // not assume anything about the initial contents of *petype.
}

//==========================================================================
// releaseByValExternal
//==========================================================================
JAVAMETHOD(void, releaseByValExternal) (OBJECT*x, PPETYPE ppetype, int flags)
{
    PETYPE petype = *ppetype;
    // TODO: Add code to destroy any embedded resources owned by *petype.
    // Do not free the memory for *petype itself (if you need to do this,
    // do it in releaseExternal()).
    //
    // If no cleanup code is needed here, this method can be deleted.
    // (do not forget to remove the reference to in the .def file.)
}

//==========================================================================
// copyToJava
//==========================================================================
JAVAMETHOD(void, copyToJava) (OBJECT*x, PPETYPE ppetype, JTYPE javaval, int flags)
{
    PETYPE petype = *ppetype;
    // TODO: Add code to modify "javaval" so it is 'equivalent' to *petype.
    // Do not modify *petype or *ppetype.
    // If JTYPE is not mutable, or you do not need to support "[out]"
    // parameters, this method can be deleted.
    // (do not forget to remove the reference to in the .def file.)
}

//==========================================================================
// toUninitJava
//==========================================================================
JAVAMETHOD(JTYPE, toUninitJava) (OBJECT*x, PPETYPE ppetype, int flags)
{
    PETYPE petype = *ppetype;
    // TODO: Add code to convert *petype to an uninitialized value of
    // type JTYPE.
    // If JTYPE is an object or array type, this method should return
    // a newly created instance of JTYPE.
    // If JTYPE is not mutable, or you do not need to support "[out]"
    // parameters, this method can be deleted.
    // (do not forget to remove the reference to in the .def file.)
    return 0;
}

//==========================================================================
// toExternal
//==========================================================================
JAVAMETHOD(void, toExternal) (OBJECT*x, JTYPE javaval, int flags, PPETYPE ppetype)
{
    PETYPE petype = NULL;
    // TODO: Add code to allocate a new ETYPE, and initialize it to be
    // "equivalent" to javaval. Store a pointer to the new ETYPE in petype.
    // If you do not require ETYPE's that outlive method calls, this method
    // can be deleted.
    // (do not forget to remove the reference to in the .def file.)
    *ppetype = petype;
}

//==========================================================================
// releaseExternal
//==========================================================================
JAVAMETHOD(void, releaseExternal) (OBJECT*x, PPETYPE ppetype, int flags)
{
    PETYPE petype = *ppetype;

    // TODO: Add code to free the memory pointed to by petype. In addition,
    // if you have implemented releaseByValExternal, call it first.
    //
    // If you do not require ETYPE's that outlive method calls, this method
    // can be deleted.
    // (do not forget to remove the reference to in the .def file.)
}

//==========================================================================
// toUninitExternal
//==========================================================================
JAVAMETHOD(void, toUninitExternal) (OBJECT*x, JTYPE javaval, int flags, PPETYPE ppetype)
{
    PETYPE petype = NULL;
    // TODO: Add code to allocate a new ETYPE. It is not necessary to initialize
    // its contents. The value of javaval should be used only to determine
    // the size of the allocation.
    // If you do not require ETYPE's that outlive method calls, or you do not
    // need to support [out] parameters, this method can be deleted.
    // (do not forget to remove the reference to in the .def file.)
    *ppetype = petype;
}

//==========================================================================
// TODO: Remove this copy of DllMain if you are embedding this file in your
//  own DLL.
// DLL Loader entry point.
//==========================================================================
BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason,
                                PVOID pvReserved)
{
   return TRUE;
}


README.TXT

This is what you do to build binaries of the files in the VARSTR directory:

1) Compile the C source
cl /LD VarStrMarshaler.c /I..\..\..\include ..\..\..\lib\i386\msjava.lib user32.lib ole32.lib oleaut32.lib VarStrMarshaler.def

2) Compile the Java source
jvc VarStrMarshaler.java

3) Copy files to run location
copy *.dll ..\rundir
copy *.class ..\rundir


VARSTRMARSHALER.C

//=====================================================================
// Sample Java/COM Marshaling Hook Class.
//
// Java Type:  String
// Com  Type:  VARIANT (BSTR type).
//
// Copyright (c) 1996-1997  Microsoft Corporation.  All rights reserved.
//=====================================================================

#pragma hdrstop

#include <windows.h>
#include "native.h" // Raw Native Interface declarations.

typedef VARIANT *PVARIANT, **PPVARIANT;

#ifdef __cplusplus
#define EXTERNC     extern "C"
#else
#define EXTERNC
#endif

// Macro for declaring RNI Java methods's (and encapsulating the java class package.)
#define JAVAMETHOD(typ, name) \
    __declspec(dllexport) \
    EXTERNC \
    typ __cdecl VarStrMarshaler_##name

// Export that tells what RNI version we were built under.
DWORD __cdecl RNIGetCompatibleVersion()
{
  return RNIVER;
}

#define LOGCALL(hookname) OutputDebugString("VarStrMarshaler." hookname "() called.\n")

//==========================================================================
// toJava
//==========================================================================
JAVAMETHOD(HString*, toJava) (OBJECT*x, PPVARIANT ppVARIANT, int flags)
{
    VARIANT *pvar = *ppVARIANT;

    LOGCALL("toJava");

    if (pvar->vt != VT_BSTR) {
        // Could try to coerce the variant, but this is just an example.
        SignalErrorPrintf("java/lang/IllegalArgumentException",
                          "Variant is of wrong type (must be VT_BSTR.)");
    } else {
        return makeJavaStringW(pvar->bstrVal, SysStringLen(pvar->bstrVal));
    }
}

//==========================================================================
// copyToExternal
//==========================================================================
JAVAMETHOD(void, copyToExternal) (OBJECT*x, HString* javaval, PPVARIANT ppVARIANT, int flags)
{
    PVARIANT pVARIANT = *ppVARIANT;
    BSTR bstr;

    LOGCALL("copyToExternal");

    if (!javaval) {
        SignalErrorPrintf("java/lang/NullPointerException", "Null string not allowed.");
    } else {
        bstr = SysAllocStringLen(javaStringStart(javaval),
                                 (UINT)(javaStringLength(javaval)));

        if (!bstr) {
            SignalErrorPrintf("java/lang/OutOfMemoryError", "Not enough room in BSTR system heap.");
        } else {

            char msg[100];
            wsprintf(msg, "Hook class allocated BSTR at %lxh\n", bstr);
            OutputDebugString(msg);

            // We explicitly do NOT call VariantClear before overwriting the variant.
            // That is the job of releaseByValExternal, which will be called when needed.
            pVARIANT->vt      = VT_BSTR;
            pVARIANT->bstrVal = bstr;
        }
    }

}

//==========================================================================
// releaseByValExternal
//==========================================================================
JAVAMETHOD(void, releaseByValExternal) (OBJECT*x, PPVARIANT ppVARIANT, int flags)
{
    PVARIANT pVARIANT = *ppVARIANT;

    LOGCALL("releaseByValExternal");
    if (pVARIANT->vt == VT_BSTR) {
        char msg[100];
        wsprintf(msg, "Hook class freeing BSTR at %lxh\n", pVARIANT->bstrVal);
        OutputDebugString(msg);

        SysFreeString(pVARIANT->bstrVal);
    }
}

//==========================================================================
// TODO: Remove this copy of DllMain if you are embedding this file in your
//  own DLL.
// DLL Loader entry point.
//==========================================================================
BOOL WINAPI DllMain(HINSTANCE hmod, DWORD dwReason,
                                PVOID pvReserved)
{
   return TRUE;
}


README.TXT

Important
=========

The key "HKEY_LOCAL_MACHINE\Software\Microsoft\Java VM\Debug" must exist to
enable debugging under the Microsoft Java VM.  You can create this key
programmatically.  You will probably not want to run with this key except when
debugging since it disables the JIT compiler, and invokes a slower than normal
version of the bytecode interpreter loop.  You can delete it using RegEdit.exe.
The debugger samples create this key themselves to enable debugging.

Files of special interest are preceded by "***".

Files
=====

Debugger\
    Java debugger sample code.

    JavaDbg.idl
        An IDL description of the debugger interfaces.

*** ReadMe.txt
        This file.

    C++ Debugger\
        Source files for Win32 C++ debugger sample console application
        JDebug.exe.

        GUIDs.cpp
            GUIDs used by the C++ debugger sample.

***     JDebug.bat
            Runs the C++ debugger sample on "Hello, world" applet.

***     JDebug.cpp
            C++ debugger sample.

        JDebug.ico
            Sample icon.

        JDebug.mak
            MS Developer's studio makefile.

        JDebug.rc
            MS Developer's studio resource file.

        Project.hpp
            Common project header file.

        RefCount.hpp
            Simple COM reference counting class.

        Resource.h
            MS Developer's studio resource header file.

        Release\
            Built release C++ debugger sample binaries.

            JDebug.exe
                Release Win32 C++ debugger sample console application.

    Hello\
        Simple "Hello, world" sample Java debuggee applet.

        Hello.bat
            Runs Hello under JView.exe.

        Hello.class
            "Hello, world" Java class file.

        Hello.java
            "Hello, world" Java source file.

    Java Debugger\
        Source files for Java debugger sample application JavaDebugger.class.

***     JavaDebugger.java
            Java debugger Java source.

        DebuggeeProcess.java

        DebuggeeProcess.class
        JavaDebugger.class
            Java debugger .class files.

        JDbgHlpr.dll
            Java debugger helper classes' native method implementations.

***     JDebug.bat
            Runs the Java debugger sample on "Hello, world" applet.

        com\ms\debug\

            *.class
                Java debugger interface and helper .class files.

        Helpers\
            Source code for Java debugger helper classes.

            ConstantPoolHelper.java
            EnumRemoteBooleanValueHelper.java
            EnumRemoteDoubleValueHelper.java
            EnumRemoteFloatValueHelper.java
            LockBytesHelper.java
            RemoteBooleanObjectHelper.java
            RemoteDoubleObjectHelper.java
            RemoteFloatObjectHelper.java
                Java debugger helper Java source.  Not all parameter types used
                by the debugger interfaces can be automatically marshaled by
                the current Java VM.  These classes and their native methods
                allow a Java debugger to use all the Java debugger interfaces
                supported by the Java VM.  The native methods of these classes
                are implemented in jdbghlpr.dll.  The need for at least some of
                these classes will be removed in future releases of the Java VM.

            JDbgHlpr\
                MS Developer's Studio source project for JDbgHlpr.dll.

                *.h
                    C header files describing Java class objects.  Generated by
                    MSJavaH.

                Helpers.cpp
                    Native methods for Java debugger interface helper classes.

                JDbgHlpr.cpp
                    DLL entry point.

                JDbgHlpr.def
                    Module definition file.

                JDbgHlpr.mak
                    MS Developer's studio makefile.

                JDbgHlpr.rc
                    MS Developer's studio resource file.

                Process.cpp
                    Native methods for DebuggeeProcess.

                Project.hpp
                    Common project header file.

                Resource.h
                    MS Developer's studio resource header file.

        Prototypes\
            Method prototypes for Java debugger interfaces.

            *.java
                Java shorthand source files describing debugger interface
                semantics for Java.  These are not the actual Java files used
                to generate the com\ms\debug .class files.  Those .class files
                contain additional COM marshaling information not present in
                these Java source files.  These Java source files are provided
                to briefly describe the semantics of the debugger interfaces
                under Java to developers.

Notes
=====

Java debugging functionality has been defined for the following areas, but not
implemented:

    1) Passing a debug string from the debuggee to the debugger.
        IRemoteProcessCallback::DebugStringEvent()

    2) Thread groups.
        IEnumRemoteThread
        IEnumRemoteThreadGroup
        IJavaEnumRemoteThread
        IJavaEnumRemoteThreadGroup
        IRemoteProcess::GetThreadGroups()
        IRemoteProcessCallback::ThreadGroupCreateEvent()
        IRemoteProcessCallback::ThreadGroupDestroyEvent()
        IRemoteThread::GetThreadGroup()
        IRemoteThreadGroup

    3) Data breakpoints.
        IRemoteObject::ClearBreakpoint()
        IRemoteObject::SetBreakpoint()
            Including ClearBreakpoint() and SetBreakpoint() of all interfaces
            derived from IRemoteObject:
                IRemoteArrayObject
                IRemoteBooleanObject
                IRemoteByteObject
                IRemoteCharObject
                IRemoteClassObject
                IRemoteContainerObject
                IRemoteDoubleObject
                IRemoteFloatObject
                IRemoteIntObject
                IRemoteLongObject
                IRemoteShortObject
        IRemoteProcessCallback::DataBreakpointEvent()

Subsequent releases of the Microsoft Java VM may implement these features.

Copyright Microsoft Corporation 1996-1997.


JAVADBG.IDL

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    javadbg.idl

Abstract:

    Java debugging interface definitions.

--*/

import "oleidl.idl";

interface IEnumLINEINFO;
interface IEnumRemoteBooleanValue;
interface IEnumRemoteByteValue;
interface IEnumRemoteCharValue;
interface IEnumRemoteDoubleValue;
interface IEnumRemoteFloatValue;
interface IEnumRemoteIntValue;
interface IEnumRemoteLongValue;
interface IEnumRemoteShortValue;
interface IEnumRemoteField;
interface IEnumRemoteObject;
interface IEnumRemoteProcess;
interface IEnumRemoteThread;
interface IEnumRemoteThreadGroup;
interface IEnumRemoteValue;
interface IJavaDebugManager;
interface IRemoteArrayField;
interface IRemoteArrayObject;
interface IRemoteBooleanObject;
interface IRemoteByteObject;
interface IRemoteCharObject;
interface IRemoteClassField;
interface IRemoteClassObject;
interface IRemoteContainerField;
interface IRemoteContainerObject;
interface IRemoteDataField;
interface IRemoteDebugManager;
interface IRemoteDebugManagerCallback;
interface IRemoteDoubleObject;
interface IRemoteField;
interface IRemoteFloatObject;
interface IRemoteIntObject;
interface IRemoteLongObject;
interface IRemoteMethodField;
interface IRemoteObject;
interface IRemoteProcess;
interface IRemoteProcessCallback;
interface IRemoteShortObject;
interface IRemoteStackFrame;
interface IRemoteThread;
interface IRemoteThreadGroup;

//
// error codes
//
cpp_quote("// error codes")
cpp_quote("//")
cpp_quote("// errors returned by IRemoteContainer::GetFieldObject")
cpp_quote("static const int E_FIELDOUTOFSCOPE       = MAKE_HRESULT(1, FACILITY_ITF, 0x01);")
cpp_quote("static const int E_FIELDNOTINOBJECT      = MAKE_HRESULT(1, FACILITY_ITF, 0x02);")
cpp_quote("static const int E_NOFIELDS              = MAKE_HRESULT(1, FACILITY_ITF, 0x03);")
cpp_quote("static const int E_NULLOBJECTREF         = MAKE_HRESULT(1, FACILITY_ITF, 0x04);")
cpp_quote("// errors returned by IRemoteProcess::FindClass")
cpp_quote("static const int E_CLASSNOTFOUND         = MAKE_HRESULT(1, FACILITY_ITF, 0x10);")
cpp_quote("static const int E_BADMETHOD             = MAKE_HRESULT(1, FACILITY_ITF, 0x20);")

[
    object,
    uuid(275d9da0-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IEnumLINEINFO : IUnknown
{
    typedef IEnumLINEINFO *LPENUMLINEINFO;

    typedef struct tagLINEINFO
    {
        USHORT offPC;
        USHORT iLine;
    }
    LINEINFO, *LPLINEINFO;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] LPLINEINFO rgelt,
        [out] ULONG *pceltFetched
    );

    HRESULT Skip
    (
        [in] ULONG celt
    );

    HRESULT Reset
    (
    );

    HRESULT Clone
    (
        [out] IEnumLINEINFO **ppEnum
    );

    HRESULT GetCount
    (
        [out] ULONG *pcelt
    );
}

[
    object,
    uuid(275d9d80-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteField : IUnknown
{
    typedef IRemoteField *LPREMOTEFIELD;

    HRESULT GetName
    (
        [out] LPOLESTR *ppszName
    );

    //
    // field kinds
    //
    enum
    {
        FIELD_KIND_DATA_OBJECT      = 0x0001,
        FIELD_KIND_DATA_PRIMITIVE   = 0x0002,
        FIELD_KIND_ARRAY            = 0x0004,
        FIELD_KIND_CLASS            = 0x0008,
        FIELD_KIND_METHOD           = 0x0010,
        FIELD_KIND_LOCAL            = 0x1000,
        FIELD_KIND_PARAM            = 0x2000,
        FIELD_KIND_THIS             = 0x4000,
    };
    typedef ULONG FIELDKIND;

    HRESULT GetKind
    (
        [out] FIELDKIND *pfk
    );

    HRESULT GetType
    (
        [out] IRemoteField **ppType
    );

    HRESULT GetContainer
    (
        [out] IRemoteContainerField **ppContainer
    );

    //
    // Modifier flags
    //
    enum
    {
        FIELD_ACC_PUBLIC        = 0x0001,
        FIELD_ACC_PRIVATE       = 0x0002,
        FIELD_ACC_PROTECTED     = 0x0004,
        FIELD_ACC_STATIC        = 0x0008,
        FIELD_ACC_FINAL         = 0x0010,
        FIELD_ACC_SYNCHRONIZED  = 0x0020,
        FIELD_ACC_VOLATILE      = 0x0040,
        FIELD_ACC_TRANSIENT     = 0x0080,
        FIELD_ACC_NATIVE        = 0x0100,
        FIELD_ACC_INTERFACE     = 0x0200,
        FIELD_ACC_ABSTRACT      = 0x0400,
    };
    typedef ULONG FIELDMODIFIERS;

    HRESULT GetModifiers
    (
        [out] FIELDMODIFIERS *pulModifiers
    );
}

[
    object,
    uuid(275d9da1-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IEnumRemoteField : IUnknown
{
    typedef IEnumRemoteField *LPENUMREMOTEFIELD;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] IRemoteField *rgelt[],
        [out] ULONG *pceltFetched
    );

    HRESULT Skip
    (
        [in] ULONG celt
    );

    HRESULT Reset
    (
    );

    HRESULT Clone
    (
        [out] IEnumRemoteField **ppEnum
    );

    HRESULT GetCount
    (
        [out] ULONG *pcelt
    );
}

[
    object,
    uuid(275d9d81-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteDataField : IRemoteField
{
    typedef IRemoteDataField *LPREMOTEDATAFIELD;
}

[
    object,
    uuid(275d9d82-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteArrayField : IRemoteDataField
{
    typedef IRemoteArrayField *LPREMOTEARRAYFIELD;

    HRESULT GetSize
    (
        [out] ULONG *pcElements
    );
}

[
    object,
    uuid(275d9d83-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteContainerField : IRemoteField
{
    typedef IRemoteContainerField *LPREMOTECONTAINERFIELD;

    HRESULT GetFields
    (
        [out] IEnumRemoteField **ppEnum,
        [in] FIELDKIND ulKind,
        [in] FIELDMODIFIERS ulModifiers,
        [in, unique] LPCOLESTR lpcszName
    );
}

[
    object,
    uuid(275d9d84-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteMethodField : IRemoteContainerField
{
    typedef IRemoteMethodField *LPREMOTEMETHODFIELD;

    HRESULT SetBreakpoint
    (
        [in] ULONG offPC
    );

    HRESULT ClearBreakpoint
    (
        [in] ULONG offPC
    );

    HRESULT GetLineInfo
    (
        [out] IEnumLINEINFO **ppEnum
    );

    HRESULT GetBytes
    (
        [out] ILockBytes **ppLockBytes
    );

    HRESULT GetScope
    (
        [in, unique] IRemoteField *pField,
        [out] ULONG *poffStart,
        [out] ULONG *pcbScope
    );

    HRESULT GetIndexedField
    (
        [in] ULONG slot,
        [in] ULONG offPC,
        [out] IRemoteField **ppField
    );
}

[
    object,
    uuid(275d9d85-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteClassField : IRemoteContainerField
{
    typedef IRemoteClassField *LPREMOTECLASSFIELD;

    //
    // Constant pool tags
    //
    enum
    {
        CP_CONSTANT_UTF8                = 1,
        CP_CONSTANT_UNICODE             = 2,
        CP_CONSTANT_INTEGER             = 3,
        CP_CONSTANT_FLOAT               = 4,
        CP_CONSTANT_LONG                = 5,
        CP_CONSTANT_DOUBLE              = 6,
        CP_CONSTANT_CLASS               = 7,
        CP_CONSTANT_STRING              = 8,
        CP_CONSTANT_FIELDREF            = 9,
        CP_CONSTANT_METHODREF           = 10,
        CP_CONSTANT_INTERFACEMETHODREF  = 11,
        CP_CONSTANT_NAMEANDTYPE         = 12
    };

    HRESULT GetFileName
    (
        [out] LPOLESTR *ppszFileName
    );

    HRESULT GetSourceFileName
    (
        [out] LPOLESTR *ppszSourceFileName
    );

    HRESULT GetSuperclass
    (
        [out] IRemoteClassField **ppSuperclass
    );

    HRESULT GetInterfaces
    (
        [out] IEnumRemoteField **ppEnum
    );

    HRESULT GetConstantPoolItem
    (
        [in] ULONG indexCP,
        [out, size_is(,*plength)] BYTE **ppCPBytes,
        [out] ULONG *plength
    );
}

[
    object,
    uuid(275d9db0-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteObject : IUnknown
{
    typedef IRemoteObject *LPREMOTEOBJECT;

    HRESULT GetType
    (
        [out] IRemoteField **ppType
    );

    HRESULT SetBreakpoint
    (
    );

    HRESULT ClearBreakpoint
    (
    );

    //
    // JAVA built-int types
    //
    typedef BYTE JAVA_BOOLEAN;
    typedef signed char JAVA_BYTE;
    typedef USHORT JAVA_CHAR;
    typedef double JAVA_DOUBLE;
    typedef float JAVA_FLOAT;
    typedef LONG JAVA_INT;
    typedef LONGLONG JAVA_LONG;
    typedef SHORT JAVA_SHORT;
    typedef LPOLESTR JAVA_STRING;
}

[
    object,
    uuid(275d9da2-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IEnumRemoteObject : IUnknown
{
    typedef IEnumRemoteObject *LPENUMREMOTEOBJECT;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] IRemoteObject *rgelt[],
        [out] ULONG *pceltFetched
    );

    HRESULT Skip
    (
        [in] ULONG celt
    );

    HRESULT Reset
    (
    );

    HRESULT Clone
    (
        [out] IEnumRemoteObject **ppEnum
    );

    HRESULT GetCount
    (
        [out] ULONG *pcelt
    );
}

[
    object,
    uuid(da0a7738-de8e-11cf-b3b3-00a0244a1dd2)
]

interface IEnumRemoteValue : IUnknown
{
    typedef IEnumRemoteValue *LPENUMREMOTEVALUE;

    HRESULT Skip
    (
        [in] ULONG celt
    );

    HRESULT Reset
    (
    );

    HRESULT Clone
    (
        [out] IEnumRemoteValue **ppEnum
    );

    HRESULT GetCount
    (
        [out] ULONG *pcelt
    );
}

[
    object,
    uuid(282e2c9c-df62-11cf-b3b3-00a0244a1dd2)
]

interface IEnumRemoteBooleanValue : IEnumRemoteValue
{
    typedef IEnumRemoteBooleanValue *LPENUMREMOTEBOOLEANVALUE;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] JAVA_BOOLEAN rgelt[],
        [out] ULONG *pceltFetched
    );
}

[
    object,
    uuid(29831ce2-df62-11cf-b3b3-00a0244a1dd2)
]

interface IEnumRemoteByteValue : IEnumRemoteValue
{
    typedef IEnumRemoteByteValue *LPENUMREMOTEBYTEVALUE;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] JAVA_BYTE rgelt[],
        [out] ULONG *pceltFetched
    );
}

[
    object,
    uuid(c5d31048-de81-11cf-b3b3-00a0244a1dd2)
]

interface IEnumRemoteCharValue : IEnumRemoteValue
{
    typedef IEnumRemoteCharValue *LPENUMREMOTECHARVALUE;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] JAVA_CHAR rgelt[],
        [out] ULONG *pceltFetched
    );
}

[
    object,
    uuid(9dc9ac24-df62-11cf-b3b3-00a0244a1dd2)
]

interface IEnumRemoteDoubleValue : IEnumRemoteValue
{
    typedef IEnumRemoteDoubleValue *LPENUMREMOTEDOUBLEVALUE;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] JAVA_DOUBLE rgelt[],
        [out] ULONG *pceltFetched
    );
}

[
    object,
    uuid(a4c10b1c-df62-11cf-b3b3-00a0244a1dd2)
]

interface IEnumRemoteFloatValue : IEnumRemoteValue
{
    typedef IEnumRemoteFloatValue *LPENUMREMOTEFLOATVALUE;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] JAVA_FLOAT rgelt[],
        [out] ULONG *pceltFetched
    );
}

[
    object,
    uuid(aa5eb51a-df62-11cf-b3b3-00a0244a1dd2)
]

interface IEnumRemoteIntValue : IEnumRemoteValue
{
    typedef IEnumRemoteIntValue *LPENUMREMOTEINTVALUE;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] JAVA_INT rgelt[],
        [out] ULONG *pceltFetched
    );
}

[
    object,
    uuid(b208e470-df62-11cf-b3b3-00a0244a1dd2)
]

interface IEnumRemoteLongValue : IEnumRemoteValue
{
    typedef IEnumRemoteLongValue *LPENUMREMOTELONGVALUE;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] JAVA_LONG rgelt[],
        [out] ULONG *pceltFetched
    );
}

[
    object,
    uuid(b74e576c-df62-11cf-b3b3-00a0244a1dd2)
]

interface IEnumRemoteShortValue : IEnumRemoteValue
{
    typedef IEnumRemoteShortValue *LPENUMREMOTESHORTVALUE;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] JAVA_SHORT rgelt[],
        [out] ULONG *pceltFetched
    );
}

[
    object,
    uuid(275d9db1-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteArrayObject : IRemoteObject
{
    typedef IRemoteArrayObject *LPREMOTEARRAYOBJECT;

    HRESULT GetElementObjects
    (
        [out] IEnumRemoteObject **ppEnum
    );

    HRESULT GetSize
    (
        [out] ULONG *pcElements
    );

    HRESULT GetElementValues
    (
        [out] IEnumRemoteValue **ppEnum
    );
}

[
    object,
    uuid(275d9db2-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteBooleanObject : IRemoteObject
{
    typedef IRemoteBooleanObject *LPREMOTEBOOLEANOBJECT;

    HRESULT GetValue
    (
        [out] JAVA_BOOLEAN *pvalue
    );

    HRESULT SetValue
    (
        [in] JAVA_BOOLEAN value
    );
}

[
    object,
    uuid(275d9db3-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteByteObject : IRemoteObject
{
    typedef IRemoteByteObject *LPREMOTEBYTEOBJECT;

    HRESULT GetValue
    (
        [out] JAVA_BYTE *pvalue
    );

    HRESULT SetValue
    (
        [in] JAVA_BYTE value
    );
}

[
    object,
    uuid(275d9db4-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteCharObject : IRemoteObject
{
    typedef IRemoteCharObject *LPREMOTECHAROBJECT;

    HRESULT GetValue
    (
        [out] JAVA_CHAR *pvalue
    );

    HRESULT SetValue
    (
        [in] JAVA_CHAR value
    );
}

[
    object,
    uuid(275d9dbb-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteClassObject : IRemoteContainerObject
{
    typedef IRemoteClassObject *LPREMOTECLASSOBJECT;

    HRESULT GetDerivedMostType
    (
        [out] IRemoteClassField **ppDerivedMostField
    );
}

[
    object,
    uuid(275d9db5-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteContainerObject : IRemoteObject
{
    typedef IRemoteContainerObject *LPREMOTECONTAINEROBJECT;

    HRESULT GetFieldObject
    (
        [in, unique] IRemoteField *pField,
        [out] IRemoteObject **ppFieldObject
    );
}

[
    object,
    uuid(275d9db6-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteDoubleObject : IRemoteObject
{
    typedef IRemoteDoubleObject *LPREMOTEDOUBLEOBJECT;

    HRESULT GetValue
    (
        [out] JAVA_DOUBLE *pvalue
    );

    HRESULT SetValue
    (
        [in] JAVA_DOUBLE *pvalue
    );
}

[
    object,
    uuid(275d9db7-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteFloatObject : IRemoteObject
{
    typedef IRemoteFloatObject *LPREMOTEFLOATOBJECT;

    HRESULT GetValue
    (
        [out] JAVA_FLOAT *pvalue
    );

    HRESULT SetValue
    (
        [in] JAVA_FLOAT *pvalue
    );
}

[
    object,
    uuid(275d9db8-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteIntObject : IRemoteObject
{
    typedef IRemoteIntObject *LPREMOTEINTOBJECT;

    HRESULT GetValue
    (
        [out] JAVA_INT *pvalue
    );

    HRESULT SetValue
    (
        [in] JAVA_INT value
    );
}

[
    object,
    uuid(275d9db9-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteLongObject : IRemoteObject
{
    typedef IRemoteLongObject *LPREMOTELONGOBJECT;

    HRESULT GetValue
    (
        [out] JAVA_LONG *pvalue
    );

    HRESULT SetValue
    (
        [in] JAVA_LONG value
    );
}

[
    object,
    uuid(275d9dba-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteShortObject : IRemoteObject
{
    typedef IRemoteShortObject *LPREMOTESHORTOBJECT;

    HRESULT GetValue
    (
        [out] JAVA_SHORT *pvalue
    );

    HRESULT SetValue
    (
        [in] JAVA_SHORT value
    );
}

[
    object,
    uuid(275d9d90-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteStackFrame : IUnknown
{
    typedef IRemoteStackFrame *LPREMOTESTACKOBJECT;

    HRESULT GetCallingFrame
    (
        [out] IRemoteStackFrame **ppCallingFrame
    );

    HRESULT GetMethodObject
    (
        [out] IRemoteContainerObject **ppMethodObject
    );

    HRESULT GetPC
    (
        [out] ULONG *offPC
    );

    HRESULT SetPC
    (
        [in] ULONG offPC
    );

    //
    // stack frame kinds
    //
    enum
    {
        FRAME_KIND_INVALID          = 0x00,
        FRAME_KIND_INTERPRETED      = 0x01,
        FRAME_KIND_NATIVE           = 0x02,
        FRAME_KIND_JIT_COMPILED     = 0x03
    };
    typedef ULONG FRAMEKIND;

    HRESULT GetKind
    (
        [out] FRAMEKIND *pfk
    );
}

[
    object,
    uuid(275d9d92-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteThreadGroup : IUnknown
{
    typedef IRemoteThreadGroup *LPREMOTETHREADGROUP;

    HRESULT GetName
    (
        [out] LPOLESTR *ppszName
    );

    HRESULT GetThreads
    (
        [out] IEnumRemoteThread **ppEnum
    );

    HRESULT GetThreadGroups
    (
        [out] IEnumRemoteThreadGroup **ppEnum
    );
}

[
    object,
    uuid(275d9da4-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IEnumRemoteThreadGroup : IUnknown
{
    typedef IEnumRemoteThreadGroup *LPENUMREMOTETHREADGROUP;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] IRemoteThreadGroup *rgelt[],
        [out] ULONG *pceltFetched
    );

    HRESULT Skip
    (
        [in] ULONG celt
    );

    HRESULT Reset
    (
    );

    HRESULT Clone
    (
        [out] IEnumRemoteThreadGroup **ppEnum
    );
}

[
    object,
    uuid(275d9d91-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteThread : IUnknown
{
    typedef IRemoteThread *LPREMOTETHREAD;

    HRESULT GetName
    (
        [out] LPOLESTR *ppszName
    );

    HRESULT GetCurrentFrame
    (
        [out] IRemoteStackFrame **ppCurrentFrame
    );

    HRESULT GetThreadGroup
    (
        [out] IRemoteThreadGroup **ppThreadGroup
    );

    HRESULT Go
    (
    );

    HRESULT Step
    (
    );

    HRESULT StepIn
    (
    );

    HRESULT StepOut
    (
    );

    HRESULT RangeStep
    (
        [in] ULONG offStart,
        [in] ULONG offEnd
    );

    HRESULT RangeStepIn
    (
        [in] ULONG offStart,
        [in] ULONG offEnd
    );

    HRESULT Destroy
    (
    );

    HRESULT Suspend
    (
    );

    HRESULT Resume
    (
    );

    HRESULT GetSuspendCount
    (
        [out] ULONG *pcSuspend
    );
}

[
    object,
    uuid(275d9da3-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IEnumRemoteThread : IUnknown
{
    typedef IEnumRemoteThread *LPENUMREMOTETHREAD;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] IRemoteThread *rgelt[],
        [out] ULONG *pceltFetched
    );

    HRESULT Skip
    (
        [in] ULONG celt
    );

    HRESULT Reset
    (
    );

    HRESULT Clone
    (
        [out] IEnumRemoteThread **ppEnum
    );
}

[
    object,
    uuid(275d9d94-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteProcessCallback : IUnknown
{
    typedef IRemoteProcessCallback *LPREMOTEPROCESSCALLBACK;

    enum
    {
        EXCEPTION_KIND_FIRST_CHANCE     = 0x0001,
        EXCEPTION_KIND_LAST_CHANCE      = 0x0002,
    };
    typedef ULONG EXCEPTIONKIND;

    HRESULT DebugStringEvent
    (
        [in, unique] IRemoteThread *pThread,
        [in] LPCOLESTR pszDebugString
    );

    HRESULT CodeBreakpointEvent
    (
        [in, unique] IRemoteThread *pThread
    );

    HRESULT DataBreakpointEvent
    (
        [in, unique] IRemoteThread *pThread,
        [in, unique] IRemoteObject *pObject
    );

    HRESULT ExceptionEvent
    (
        [in, unique] IRemoteThread *pThread,
        [in, unique] IRemoteClassField *pExceptionClass,
        [in] EXCEPTIONKIND exceptionKind
    );

    HRESULT StepEvent
    (
        [in, unique] IRemoteThread *pThread
    );

    HRESULT CanStopEvent
    (
        [in, unique] IRemoteThread *pThread
    );

    HRESULT BreakEvent
    (
        [in, unique] IRemoteThread *pThread
    );

    HRESULT ThreadCreateEvent
    (
        [in, unique] IRemoteThread *pThread
    );

    HRESULT ThreadDestroyEvent
    (
        [in, unique] IRemoteThread *pThread
    );

    HRESULT ThreadGroupCreateEvent
    (
        [in, unique] IRemoteThread *pThread,
        [in, unique] IRemoteThreadGroup *pThreadGroup
    );

    HRESULT ThreadGroupDestroyEvent
    (
        [in, unique] IRemoteThread *pThread,
        [in, unique] IRemoteThreadGroup *pThreadGroup
    );

    HRESULT ClassLoadEvent
    (
        [in, unique] IRemoteThread *pThread,
        [in, unique] IRemoteClassField *pClassType
    );

    HRESULT ClassUnloadEvent
    (
        [in, unique] IRemoteThread *pThread,
        [in, unique] IRemoteClassField *pClassType
    );

    HRESULT ProcessDestroyEvent
    (
        [in, unique] IRemoteThread *pThread
    );

    HRESULT TraceEvent
    (
        [in, unique] IRemoteThread *pThread
    );

    HRESULT LoadCompleteEvent
    (
        [in, unique] IRemoteThread *pThread
    );
}

[
    object,
    uuid(275d9d93-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteProcess : IUnknown
{
    typedef IRemoteProcess *LPREMOTEPROCESS;

    HRESULT RegisterCallback
    (
        [in, unique] IRemoteProcessCallback *pCallback
    );

    HRESULT Detach
    (
    );

    HRESULT Break
    (
    );

    HRESULT GetGlobalContainerObject
    (
        [out] IRemoteContainerObject **ppGlobalContainerObject
    );

    HRESULT FindClass
    (
        [in] LPCOLESTR pszClassName,
        [out] IRemoteClassField **ppClassType
    );

    HRESULT TraceMethods
    (
        [in] BOOL bTraceOn
    );

    HRESULT GetThreadGroups
    (
        [out] IEnumRemoteThreadGroup **ppEnum
    );
}

[
    object,
    uuid(275d9da5-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IEnumRemoteProcess : IUnknown
{
    typedef IEnumRemoteProcess *LPENUMREMOTEPROCESS;

    HRESULT Next
    (
        [in] ULONG celt,
        [out, size_is(celt), length_is(*pceltFetched)] IRemoteProcess *rgelt[],
        [out] ULONG *pceltFetched
    );

    HRESULT Skip
    (
        [in] ULONG celt
    );

    HRESULT Reset
    (
    );

    HRESULT Clone
    (
        [out] IEnumRemoteProcess **ppEnum
    );
}

[
    object,
    uuid(275d9d61-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteDebugManagerCallback : IUnknown
{
    typedef IRemoteDebugManagerCallback *LPREMOTEDEBUGMANAGERCALLBACK;

    HRESULT ProcessCreateEvent
    (
        [in, unique] IRemoteProcess *pProcessNew,
        [in, unique] IRemoteProcess *pProcessParent
    );
}

[
    object,
    uuid(275d9d60-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IRemoteDebugManager : IUnknown
{
    typedef IRemoteDebugManager *LPREMOTEDEBUGMANAGER;

    HRESULT RegisterCallback
    (
        [in, unique] IRemoteDebugManagerCallback *pCallback
    );

    HRESULT Detach
    (
    );

    HRESULT GetActiveProcesses
    (
        [out] IEnumRemoteProcess **ppEnum
    );

    HRESULT RequestCreateEvent
    (
        [in] LPCOLESTR pszProcessName,
        [in] DWORD dwParentProcessId
    );
}

[
    object,
    uuid(275d9dc0-5ff5-11cf-a5e1-00aa006bbf16)
]

interface IJavaDebugManager : IUnknown
{
    HRESULT RegisterRemoteDebugManager
    (
        [in, unique] IRemoteDebugManager *pirdm,
        [in] DWORD dwProcessID
    );

    HRESULT Detach
    (
    );
}


GUIDS.CPP

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

   guids.cpp

Abstract:

   GUID definitions.

--*/

/* Headers
 **********/

#include "project.hpp"

/* GUIDs
 ********/

#pragma data_seg(DATA_SEG_READ_ONLY)

#include <initguid.h>

#include <jdbgguid.h>

#pragma data_seg()


JDEBUG.CPP

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

   jdebug.cpp

Abstract:

   Java debugger class implementation.

   The C++ debugger sample preforms the following steps:

    1) Connects to the debug manager.
    2) Runs the Java debuggee applet.
    3) Watches for the debuggee's Java VM to be created, and puts up a message box.
    4) Connects to the debuggee's Java VM.
    5) Watches for the debuggee class to be loaded.
    6) Sets a breakpoint in a method in the debuggee class.
    7) Watches for the breakpoint to be hit, and puts up a message box.
    8) Clears the breakpoint.
    9) Watches for the debuggee's Java VM to be destroyed, and puts up a message box.
   10) Disconnects from the debuggee's Java VM.
   11) Disconnects from the debug manager.
   12) Exits.

--*/

/* Headers
 **********/

#include "project.hpp"

#include <javadbg.h>
#include <jdbgguid.h>

#include "refcount.hpp"

/* Classes
 **********/

// sample Java debugger class

class JavaDebugger : public RefCount,
                     public IRemoteDebugManagerCallback,
                     public IRemoteProcessCallback
{
private:
   /* Fields
    *********/

   // debug manager
   IRemoteDebugManager *m_pirdm;

   // remote process
   IRemoteProcess *m_pirp;

   // debuggee class name
   LPWSTR m_pwszDebugClass;

   /* Methods
    **********/

   UINT RunMessageLoop(void);
   void QuitMessageLoop(UINT uResult);

public:
   /* Methods
    **********/

   JavaDebugger(void);
   ~JavaDebugger(void);
   HRESULT Initialize(IRemoteDebugManager *pirdm);
   void Terminate(void);
   HRESULT Debug(LPCSTR pcszDebugClass);

   // IRemoteDebugManagerCallback methods

   HRESULT STDMETHODCALLTYPE ProcessCreateEvent(IRemoteProcess *pirpNew, IRemoteProcess *pirpParent);

   // IRemoteProcessCallback methods

   HRESULT STDMETHODCALLTYPE DebugStringEvent(IRemoteThread *pirth, LPCWSTR pcwszDebugMsg);
   HRESULT STDMETHODCALLTYPE CodeBreakpointEvent(IRemoteThread *pirth);
   HRESULT STDMETHODCALLTYPE DataBreakpointEvent(IRemoteThread *pirth, IRemoteObject *piro);
   HRESULT STDMETHODCALLTYPE ExceptionEvent(IRemoteThread *pirth, IRemoteClassField *pircfException, EXCEPTIONKIND exk);
   HRESULT STDMETHODCALLTYPE StepEvent(IRemoteThread *pirth);
   HRESULT STDMETHODCALLTYPE CanStopEvent(IRemoteThread *pirth);
   HRESULT STDMETHODCALLTYPE BreakEvent(IRemoteThread *pirth);
   HRESULT STDMETHODCALLTYPE ThreadCreateEvent(IRemoteThread *pirth);
   HRESULT STDMETHODCALLTYPE ThreadDestroyEvent(IRemoteThread *pirth);
   HRESULT STDMETHODCALLTYPE ThreadGroupCreateEvent(IRemoteThread *pirth, IRemoteThreadGroup *pirthg);
   HRESULT STDMETHODCALLTYPE ThreadGroupDestroyEvent(IRemoteThread *pirth, IRemoteThreadGroup *pirthg);
   HRESULT STDMETHODCALLTYPE ClassLoadEvent(IRemoteThread *pirth, IRemoteClassField *pircfClass);
   HRESULT STDMETHODCALLTYPE ClassUnloadEvent(IRemoteThread *pirth, IRemoteClassField *pircfClass);
   HRESULT STDMETHODCALLTYPE ProcessDestroyEvent(IRemoteThread *pirth);
   HRESULT STDMETHODCALLTYPE TraceEvent(IRemoteThread *pirth);
   HRESULT STDMETHODCALLTYPE LoadCompleteEvent(IRemoteThread *pirth);

   // IUnknown methods

   DEFINE_DELEGATED_REFCOUNT_ADDREF(JavaDebugger);
   DEFINE_DELEGATED_REFCOUNT_RELEASE(JavaDebugger);
   HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObject);
};

/* Module Constants
 *******************/

#pragma data_seg(DATA_SEG_READ_ONLY)

const char SPACE                 = ' ';
const char TAB                   = '\t';
const char QUOTE                 = '\'';
const char QUOTES                = '"';

const char s_cszMsgBoxTitle[]    = "Sample Java Debugger";

const char s_cszDebugKey[]       = "Software\\Microsoft\\Java VM\\Debug";

const char s_cszDebugClass[]     = "Hello";
const WCHAR s_cwszDebugMethod[]  = L"main";
const ULONG s_ulcBreakpointPC    = 0;

#pragma data_seg()

/* Macros
 *********/

//
// Compile-time type check cast macro.
//
#define SAFE_CAST(type, obj) \
   (((type)(obj) == (obj) ? 0 : 0), (type)(obj))

//
// Safely delete an object.
//
#define SAFE_DELETE(ptr) \
   { \
      if (! (ptr)) \
          ; \
      else \
      { \
          delete(ptr); \
          (ptr) = NULL;  \
      } \
   } \

/***************************** Private Functions *****************************/

//
// Create a dynamically allocated Unicode copy of an ANSI string.
//
static HRESULT ANSIToUnicode(LPCSTR pcszANSI, LPWSTR *ppwszUnicode)
{
   HRESULT hr = E_UNEXPECTED;
   int ncchANSI;
   int ncchUnicode;

   // (+ 1) for null terminator.
   ncchANSI = lstrlen(pcszANSI) + 1;

   ncchUnicode = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pcszANSI, ncchANSI, NULL, 0);

   if (ncchUnicode > 0)
   {
      *ppwszUnicode = new(WCHAR[ncchUnicode]);

      if (*ppwszUnicode)
      {
         if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pcszANSI, ncchANSI, *ppwszUnicode, ncchUnicode) == ncchUnicode)
            hr = S_OK;
         else
         {
            delete(*ppwszUnicode);
            *ppwszUnicode = NULL;
         }
      }
      else
         hr = E_OUTOFMEMORY;
   }
   else
      *ppwszUnicode = NULL;

   return(hr);
}

//
// Skip over the white space at the beginning of a string.
//
LPCSTR SkipWhiteSpace(LPCSTR pcsz)
{
   while (*pcsz == SPACE ||
          *pcsz == TAB)
      pcsz = CharNext(pcsz);

   return(pcsz);
}

//
// Skip over the meat at the beginning of a string.
//
static LPCSTR SkipNonWhiteSpace(LPCSTR pcsz)
{
   while (*pcsz &&
          *pcsz != SPACE &&
          *pcsz != TAB)
      pcsz = CharNext(pcsz);

   return(pcsz);
}

//
// Skip over a quoted substring at the beginning of a string.
//
static LPCSTR SkipQuotedArg(LPCSTR pcsz)
{
   char chQ;

   // Skip over quoted argument past matching quote.

   chQ = *pcsz;
   pcsz = CharNext(pcsz);

   while (*pcsz &&
          *pcsz != chQ)
      pcsz = CharNext(pcsz);

   if (*pcsz == chQ)
      pcsz = CharNext(pcsz);

   return(pcsz);
}

//
// Skip over a possibly quoted substring at the beginning of a string.
//
static LPCSTR SkipPossiblyQuotedArg(LPCSTR pcsz)
{
   pcsz = SkipWhiteSpace(pcsz);

   switch (*pcsz)
   {
      case QUOTE:
      case QUOTES:
         pcsz = SkipQuotedArg(pcsz);
         break;

      default:
         pcsz = SkipNonWhiteSpace(pcsz);
         break;
   }

   return(pcsz);
}

//
// Display a printf()-style message box.
//
static BOOL MyMessageBox(HWND hwndParent, UINT uStyle, LPCSTR pcszFormat, ...)
{
   char szMsg[1024];
   va_list valArgs;

   // Lamely assume that wvsprintf() won't overflow szMsg[].

   va_start(valArgs, pcszFormat);
   wvsprintf(szMsg, pcszFormat, valArgs);
   va_end(valArgs);

   return(MessageBox(hwndParent, szMsg, s_cszMsgBoxTitle, uStyle));
}

//
// Create the Java VM Debug key.
//
static BOOL CreateDebugKey(void)
{
    BOOL bResult;
    DWORD dwDisposition;
    HKEY hkeyDebug;

    bResult = (RegCreateKeyEx(HKEY_LOCAL_MACHINE, s_cszDebugKey, 0, NULL, 0, KEY_WRITE, NULL, &hkeyDebug, &dwDisposition)
              == ERROR_SUCCESS);

    if (bResult)
      RegCloseKey(hkeyDebug);

   return(bResult);
}

//
// Delete the Java VM Debug key.
//
static BOOL DeleteDebugKey(void)
{
    return(RegDeleteKey(HKEY_LOCAL_MACHINE, s_cszDebugKey) == ERROR_SUCCESS);
}

/****************************** Public Functions *****************************/

#pragma warning(disable:4100)    // "unreferenced formal parameter" warning

int __cdecl main(int argc, char *argv[])
{
   HRESULT hr;

   // Initialize OLE on this thread.

   hr = CoInitialize(NULL);

   if (SUCCEEDED(hr))
   {
       IRemoteDebugManager *pirdm;

       // Create a RemoteJavaDebugManager from JDbgMgr.exe to initiate debugging.

       hr = CoCreateInstance(CLSID_RemoteJavaDebugManager, NULL,
                             CLSCTX_LOCAL_SERVER, IID_IRemoteDebugManager,
                             (PVOID *)&pirdm);

       if (hr == S_OK)
       {
           JavaDebugger *pjd;

           // Create a JavaDebugger object to run a simple debugging session.

           pjd = new(JavaDebugger);

           if (pjd)
           {
               hr = pjd->Initialize(pirdm);

               if (hr == S_OK)
               {
                   hr = pjd->Debug(s_cszDebugClass);

                   pjd->Terminate();
               }

               pjd->Release();
               pjd = NULL;
           }
           else
               hr = E_OUTOFMEMORY;

           pirdm->Release();
           pirdm = NULL;
       }

       // Uninitialize OLE on this thread.

       CoUninitialize();
   }

   return(hr);
}

#pragma warning(default:4100)    // "unreferenced formal parameter" warning

/********************************** Methods **********************************/

JavaDebugger::JavaDebugger(void)
{
   m_pirdm = NULL;
   m_pirp = NULL;
   m_pwszDebugClass = NULL;

   return;
}

JavaDebugger::~JavaDebugger(void)
{
   Terminate();

   return;
}

HRESULT JavaDebugger::Initialize(IRemoteDebugManager *pirdm)
{
   HRESULT hr;

   // Register this JavaDebugger's callback with the debug manager so it is notified when its debuggee target class is run.

   hr = pirdm->RegisterCallback(this);

   if (hr == S_OK)
   {
      m_pirdm = pirdm;
      m_pirdm->AddRef();
   }

   return(hr);
}

void JavaDebugger::Terminate(void)
{
   if (m_pirdm)
   {
      // Detach this JavaDebugger from the debug manager.

      m_pirdm->Detach();

      m_pirdm->Release();
      m_pirdm = NULL;
   }

   SAFE_DELETE(m_pwszDebugClass);

   return;
}

//
// Run a message loop on this thread until a WM_QUIT message is encountered.
//
UINT JavaDebugger::RunMessageLoop(void)
{
   MSG msg;

   // No accelerators to load.

   // Get and dispatch messages until a WM_QUIT message is received.

   ZeroMemory(&msg, sizeof(msg));

   while (GetMessage(&msg, NULL, 0, 0))
   {
      // Translate virtual key codes.

      TranslateMessage(&msg);

      // Dispatch message to target window.

      DispatchMessage(&msg);
   }

   return(msg.wParam);
}

//
// Terminate any message loop running on this thread.
//
void JavaDebugger::QuitMessageLoop(UINT uResult)
{
   PostQuitMessage(uResult);

   return;
}

//
// Initiate a simple debugging session.
//
HRESULT JavaDebugger::Debug(LPCSTR pcszDebugClass)
{
   HRESULT hr;

   // Create the Java VM Debug key to enable Java debugging.

   if (CreateDebugKey())
   {
      // Remember the debuggee class name.

      SAFE_DELETE(m_pwszDebugClass);

      hr = ANSIToUnicode(pcszDebugClass, &m_pwszDebugClass);

      if (hr == S_OK)
      {
         LPCSTR pcszCmdLine;
         STARTUPINFO si;
         PROCESS_INFORMATION pi;

         // Treat our command line arguments as the command line to run the debuggee class.

         pcszCmdLine = SkipWhiteSpace(SkipPossiblyQuotedArg(GetCommandLine()));

         ZeroMemory(&si, sizeof(si));
         si.cb = sizeof(si);

         // Start the debuggee class process suspended so we can get its process ID before it executes.

         hr = CreateProcess(NULL, (LPSTR)pcszCmdLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi) ? S_OK : E_FAIL;

         if (hr == S_OK)
         {
            HRESULT hrResume;

            // Ask the debug manager to notify us when the debuggee class is run.

            hr = m_pirdm->RequestCreateEvent(m_pwszDebugClass, pi.dwProcessId);

            // Resume the debuggee class process to begin debugging.

            hrResume = (ResumeThread(pi.hThread) != 0xffffffff) ? S_OK : E_FAIL;

            if (hr == S_OK)
               hr = hrResume;

            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);

            // Run a message loop to dispatch OLE RPC messages.

            RunMessageLoop();
         }
      }

      // Delete the Java VM Debug key, if it hasn't already been deleted, to disable Java debugging.

      DeleteDebugKey();
   }

   return(hr);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::QueryInterface(REFIID riid,
                                                       PVOID *ppvObject)
{
    HRESULT hr = S_OK;

    if (riid == IID_IRemoteDebugManagerCallback)
       *ppvObject = SAFE_CAST(IRemoteDebugManagerCallback *, this);
    else if (riid == IID_IRemoteProcessCallback)
       *ppvObject = SAFE_CAST(IRemoteProcessCallback *, this);
    else if (riid == IID_IUnknown)
       *ppvObject = SAFE_CAST(IUnknown *, (IRemoteDebugManagerCallback *)this);
    else
    {
       *ppvObject = NULL;
       hr = E_NOINTERFACE;
    }

    if (hr == S_OK)
        AddRef();

    return(hr);
}

//
// Debugger event notification methods return an HRESULT as follows:
//
//      S_FALSE     Continue execution.
//
//      S_OK        Suspend execution of all threads in this VM until an
//                  IRemoteThread method is called on this thread to resume
//                  execution.
//
//      E_...       Error.
//

#pragma warning(disable:4100)    // "unreferenced formal parameter" warning

HRESULT STDMETHODCALLTYPE JavaDebugger::ProcessCreateEvent(
                                                    IRemoteProcess *pirpNew,
                                                    IRemoteProcess *pirpParent)
{
   HRESULT hr;

   // Register this JavaDebugger's callback with the Java VM so it is notified when interesting events occur in the debuggee.

   hr = pirpNew->RegisterCallback(this);

   if (hr == S_OK)
   {
      m_pirp = pirpNew;
      m_pirp->AddRef();
   }

   MyMessageBox(NULL, (MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND), "Process created.");

   // Delete the Java VM Debug key to disable Java debugging.

   DeleteDebugKey();

   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::DebugStringEvent(IRemoteThread *pirth, LPCWSTR pcwszDebugMsg)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::CodeBreakpointEvent(IRemoteThread *pirth)
{
   HRESULT hr;
   IRemoteStackFrame *pirsf;

   // Get the method object from the current thread's current stack frame.

   hr = pirth->GetCurrentFrame(&pirsf);

   if (hr == S_OK)
   {
      IRemoteContainerObject *pirco;

      hr = pirsf->GetMethodObject(&pirco);

      if (hr == S_OK)
      {
         IRemoteField *pirf;

         // Get the method field from the method object.

         hr = pirco->GetType(&pirf);

         if (hr == S_OK)
         {
            IRemoteMethodField *pirmf;

            hr = pirf->QueryInterface(IID_IRemoteMethodField, (PVOID *)&pirmf);

            if (hr == S_OK)
            {
               // Clear the breakpoint, and continue execution.

               hr = pirmf->ClearBreakpoint(s_ulcBreakpointPC);

               if (hr == S_OK)
                  MyMessageBox(NULL, (MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND), "Hit breakpoint %ls.%ls().%lu.",
                               m_pwszDebugClass,
                               s_cwszDebugMethod,
                               s_ulcBreakpointPC);

               pirmf->Release();
               pirmf = NULL;
            }

            pirf->Release();
            pirf = NULL;
         }

         pirco->Release();
         pirco = NULL;
      }

      pirsf->Release();
      pirsf = NULL;
   }

   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::DataBreakpointEvent(IRemoteThread *pirth, IRemoteObject *piro)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::ExceptionEvent(IRemoteThread *pirth, IRemoteClassField *pircfException, EXCEPTIONKIND exk)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::StepEvent(IRemoteThread *pirth)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::CanStopEvent(IRemoteThread *pirth)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::BreakEvent(IRemoteThread *pirth)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::ThreadCreateEvent(IRemoteThread *pirth)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::ThreadDestroyEvent(IRemoteThread *pirth)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::ThreadGroupCreateEvent(IRemoteThread *pirth, IRemoteThreadGroup *pirthg)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::ThreadGroupDestroyEvent(IRemoteThread *pirth, IRemoteThreadGroup *pirthg)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::ClassLoadEvent(IRemoteThread *pirth, IRemoteClassField *pircfClass)
{
   LPWSTR pwszClassName;

   // Get the name of the loaded class.

   if (pircfClass->GetName(&pwszClassName) == S_OK)
   {
      // Is this the class that a breakpoint is to be set in?

      if (! wcscmp(pwszClassName, m_pwszDebugClass))
      {
         IJavaEnumRemoteField *pijerf;

         // Yes.  Get the method field for the method to set a breakpoint in.

         if (pircfClass->GetFields(&pijerf, FIELD_KIND_METHOD, 0, s_cwszDebugMethod) == S_OK)
         {
            IRemoteField *pirf;
            ULONG ulcFetched;

            if (pijerf->Next(1, &pirf, &ulcFetched) == S_OK)
            {
               IRemoteMethodField *pirmf;

               if (pirf->QueryInterface(IID_IRemoteMethodField, (PVOID *)&pirmf) == S_OK)
               {
                  // Set the breakpoint, and continue execution.

                  pirmf->SetBreakpoint(s_ulcBreakpointPC);

                  pirmf->Release();
                  pirmf = NULL;
               }

               pirf->Release();
               pirf = NULL;
            }

            pijerf->Release();
            pijerf = NULL;
         }
      }

      CoTaskMemFree(pwszClassName);
      pwszClassName = NULL;
   }

   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::ClassUnloadEvent(IRemoteThread *pirth, IRemoteClassField *pircfClass)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::ProcessDestroyEvent(IRemoteThread *pirth)
{
   // Detach this JavaDebugger from the Java VM.

   m_pirp->Detach();

   m_pirp->Release();
   m_pirp = NULL;

   MyMessageBox(NULL, (MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND), "Process destroyed.");

   // Quit the message loop, and end the debugging session.

   QuitMessageLoop(0);

   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::TraceEvent(IRemoteThread *pirth)
{
   return(S_FALSE);
}

HRESULT STDMETHODCALLTYPE JavaDebugger::LoadCompleteEvent(IRemoteThread *pirth)
{
   return(S_FALSE);
}

#pragma warning(default:4100)    // "unreferenced formal parameter" warning


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by JDebug.rc
//
#define IDI_JDEBUG                      101

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


COM_MS_DEBUG_CONSTANTPOOLHELPER.H

/*  DO NOT EDIT - automatically generated by javah  */
#include <native.h>

/*  Header for class com.ms.debug.ConstantPoolHelper  */

#ifndef _Included_com_ms_debug_ConstantPoolHelper
#define _Included_com_ms_debug_ConstantPoolHelper

struct Classcom_ms_debug_IRemoteClassField;
#define Hcom_ms_debug_IRemoteClassField Classcom_ms_debug_IRemoteClassField

typedef struct Classcom_ms_debug_ConstantPoolHelper {
#pragma pack(push,1)
int32_t MSReserved;
struct Hcom_ms_debug_IRemoteClassField * m_ircf;
#pragma pack(pop)
} Classcom_ms_debug_ConstantPoolHelper;
#define Hcom_ms_debug_ConstantPoolHelper Classcom_ms_debug_ConstantPoolHelper

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) struct HArrayOfByte * __cdecl com_ms_debug_ConstantPoolHelper_GetConstantPoolItem (struct Hcom_ms_debug_ConstantPoolHelper *, long);
#ifdef __cplusplus
}
#endif
#endif


COM_MS_DEBUG_ENUMREMOTEBOOLEANVALUEHELPER.H

/*  DO NOT EDIT - automatically generated by javah  */
#include <native.h>

/*  Header for class com.ms.debug.EnumRemoteBooleanValueHelper  */

#ifndef _Included_com_ms_debug_EnumRemoteBooleanValueHelper
#define _Included_com_ms_debug_EnumRemoteBooleanValueHelper

struct Classcom_ms_debug_IJavaEnumRemoteBooleanValue;
#define Hcom_ms_debug_IJavaEnumRemoteBooleanValue Classcom_ms_debug_IJavaEnumRemoteBooleanValue

typedef struct Classcom_ms_debug_EnumRemoteBooleanValueHelper {
#pragma pack(push,1)
int32_t MSReserved;
struct Hcom_ms_debug_IJavaEnumRemoteBooleanValue * m_ierbv;
#pragma pack(pop)
} Classcom_ms_debug_EnumRemoteBooleanValueHelper;
#define Hcom_ms_debug_EnumRemoteBooleanValueHelper Classcom_ms_debug_EnumRemoteBooleanValueHelper

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) /*boolean*/ long __cdecl com_ms_debug_EnumRemoteBooleanValueHelper_GetNext (struct Hcom_ms_debug_EnumRemoteBooleanValueHelper *);
#ifdef __cplusplus
}
#endif
#endif


COM_MS_DEBUG_ENUMREMOTEDOUBLEVALUEHELPER.H

/*  DO NOT EDIT - automatically generated by javah  */
#include <native.h>

/*  Header for class com.ms.debug.EnumRemoteDoubleValueHelper  */

#ifndef _Included_com_ms_debug_EnumRemoteDoubleValueHelper
#define _Included_com_ms_debug_EnumRemoteDoubleValueHelper

struct Classcom_ms_debug_IJavaEnumRemoteDoubleValue;
#define Hcom_ms_debug_IJavaEnumRemoteDoubleValue Classcom_ms_debug_IJavaEnumRemoteDoubleValue

typedef struct Classcom_ms_debug_EnumRemoteDoubleValueHelper {
#pragma pack(push,1)
int32_t MSReserved;
struct Hcom_ms_debug_IJavaEnumRemoteDoubleValue * m_ierdv;
#pragma pack(pop)
} Classcom_ms_debug_EnumRemoteDoubleValueHelper;
#define Hcom_ms_debug_EnumRemoteDoubleValueHelper Classcom_ms_debug_EnumRemoteDoubleValueHelper

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) double __cdecl com_ms_debug_EnumRemoteDoubleValueHelper_GetNext (struct Hcom_ms_debug_EnumRemoteDoubleValueHelper *);
#ifdef __cplusplus
}
#endif
#endif


COM_MS_DEBUG_ENUMREMOTEFLOATVALUEHELPER.H

/*  DO NOT EDIT - automatically generated by javah  */
#include <native.h>

/*  Header for class com.ms.debug.EnumRemoteFloatValueHelper  */

#ifndef _Included_com_ms_debug_EnumRemoteFloatValueHelper
#define _Included_com_ms_debug_EnumRemoteFloatValueHelper

struct Classcom_ms_debug_IJavaEnumRemoteFloatValue;
#define Hcom_ms_debug_IJavaEnumRemoteFloatValue Classcom_ms_debug_IJavaEnumRemoteFloatValue

typedef struct Classcom_ms_debug_EnumRemoteFloatValueHelper {
#pragma pack(push,1)
int32_t MSReserved;
struct Hcom_ms_debug_IJavaEnumRemoteFloatValue * m_ierfv;
#pragma pack(pop)
} Classcom_ms_debug_EnumRemoteFloatValueHelper;
#define Hcom_ms_debug_EnumRemoteFloatValueHelper Classcom_ms_debug_EnumRemoteFloatValueHelper

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) float __cdecl com_ms_debug_EnumRemoteFloatValueHelper_GetNext (struct Hcom_ms_debug_EnumRemoteFloatValueHelper *);
#ifdef __cplusplus
}
#endif
#endif


COM_MS_DEBUG_LOCKBYTESHELPER.H

/*  DO NOT EDIT - automatically generated by javah  */
#include <native.h>

/*  Header for class com.ms.debug.LockBytesHelper  */

#ifndef _Included_com_ms_debug_LockBytesHelper
#define _Included_com_ms_debug_LockBytesHelper

struct Classcom_ms_debug_ILockBytes;
#define Hcom_ms_debug_ILockBytes Classcom_ms_debug_ILockBytes

typedef struct Classcom_ms_debug_LockBytesHelper {
#pragma pack(push,1)
int32_t MSReserved;
#pragma pack(pop)
} Classcom_ms_debug_LockBytesHelper;
#define Hcom_ms_debug_LockBytesHelper Classcom_ms_debug_LockBytesHelper

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) struct HArrayOfByte * __cdecl com_ms_debug_LockBytesHelper_GetBytes (struct Hcom_ms_debug_LockBytesHelper *, struct Hcom_ms_debug_ILockBytes *);
#ifdef __cplusplus
}
#endif
#endif


COM_MS_DEBUG_REMOTEBOOLEANOBJECTHELPER.H

/*  DO NOT EDIT - automatically generated by javah  */
#include <native.h>

/*  Header for class com.ms.debug.RemoteBooleanObjectHelper  */

#ifndef _Included_com_ms_debug_RemoteBooleanObjectHelper
#define _Included_com_ms_debug_RemoteBooleanObjectHelper

struct Classcom_ms_debug_IRemoteBooleanObject;
#define Hcom_ms_debug_IRemoteBooleanObject Classcom_ms_debug_IRemoteBooleanObject

typedef struct Classcom_ms_debug_RemoteBooleanObjectHelper {
#pragma pack(push,1)
int32_t MSReserved;
struct Hcom_ms_debug_IRemoteBooleanObject * m_irbo;
#pragma pack(pop)
} Classcom_ms_debug_RemoteBooleanObjectHelper;
#define Hcom_ms_debug_RemoteBooleanObjectHelper Classcom_ms_debug_RemoteBooleanObjectHelper

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) /*boolean*/ long __cdecl com_ms_debug_RemoteBooleanObjectHelper_GetValue (struct Hcom_ms_debug_RemoteBooleanObjectHelper *);
__declspec(dllexport) void __cdecl com_ms_debug_RemoteBooleanObjectHelper_SetValue (struct Hcom_ms_debug_RemoteBooleanObjectHelper *, /*boolean*/ long);
#ifdef __cplusplus
}
#endif
#endif


COM_MS_DEBUG_REMOTEDOUBLEOBJECTHELPER.H

/*  DO NOT EDIT - automatically generated by javah  */
#include <native.h>

/*  Header for class com.ms.debug.RemoteDoubleObjectHelper  */

#ifndef _Included_com_ms_debug_RemoteDoubleObjectHelper
#define _Included_com_ms_debug_RemoteDoubleObjectHelper

struct Classcom_ms_debug_IRemoteDoubleObject;
#define Hcom_ms_debug_IRemoteDoubleObject Classcom_ms_debug_IRemoteDoubleObject

typedef struct Classcom_ms_debug_RemoteDoubleObjectHelper {
#pragma pack(push,1)
int32_t MSReserved;
struct Hcom_ms_debug_IRemoteDoubleObject * m_irdo;
#pragma pack(pop)
} Classcom_ms_debug_RemoteDoubleObjectHelper;
#define Hcom_ms_debug_RemoteDoubleObjectHelper Classcom_ms_debug_RemoteDoubleObjectHelper

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) double __cdecl com_ms_debug_RemoteDoubleObjectHelper_GetValue (struct Hcom_ms_debug_RemoteDoubleObjectHelper *);
__declspec(dllexport) void __cdecl com_ms_debug_RemoteDoubleObjectHelper_SetValue (struct Hcom_ms_debug_RemoteDoubleObjectHelper *, double);
#ifdef __cplusplus
}
#endif
#endif


COM_MS_DEBUG_REMOTEFLOATOBJECTHELPER.H

/*  DO NOT EDIT - automatically generated by javah  */
#include <native.h>

/*  Header for class com.ms.debug.RemoteFloatObjectHelper  */

#ifndef _Included_com_ms_debug_RemoteFloatObjectHelper
#define _Included_com_ms_debug_RemoteFloatObjectHelper

struct Classcom_ms_debug_IRemoteFloatObject;
#define Hcom_ms_debug_IRemoteFloatObject Classcom_ms_debug_IRemoteFloatObject

typedef struct Classcom_ms_debug_RemoteFloatObjectHelper {
#pragma pack(push,1)
int32_t MSReserved;
struct Hcom_ms_debug_IRemoteFloatObject * m_irfo;
#pragma pack(pop)
} Classcom_ms_debug_RemoteFloatObjectHelper;
#define Hcom_ms_debug_RemoteFloatObjectHelper Classcom_ms_debug_RemoteFloatObjectHelper

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) float __cdecl com_ms_debug_RemoteFloatObjectHelper_GetValue (struct Hcom_ms_debug_RemoteFloatObjectHelper *);
__declspec(dllexport) void __cdecl com_ms_debug_RemoteFloatObjectHelper_SetValue (struct Hcom_ms_debug_RemoteFloatObjectHelper *, float);
#ifdef __cplusplus
}
#endif
#endif


DEBUGGEEPROCESS.H

/*  DO NOT EDIT - automatically generated by javah  */
#include <native.h>

/*  Header for class DebuggeeProcess  */

#ifndef _Included_DebuggeeProcess
#define _Included_DebuggeeProcess

typedef struct ClassDebuggeeProcess {
#pragma pack(push,1)
int32_t MSReserved;
long m_nMainThreadHandle;
long m_nProcessID;
#pragma pack(pop)
} ClassDebuggeeProcess;
#define HDebuggeeProcess ClassDebuggeeProcess

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void __cdecl DebuggeeProcess_ResumeProcess (struct HDebuggeeProcess *);
__declspec(dllexport) void __cdecl DebuggeeProcess_CreateSuspendedProcess (struct HDebuggeeProcess *, struct Hjava_lang_String *);
#ifdef __cplusplus
}
#endif
#endif


HELPERS.CPP

/*
 * helpers.cpp - Native methods.
 *
 * (C) Copyright 1996 Microsoft Corporation
 */

/* Headers
 **********/

#include "project.hpp"
#pragma hdrstop

#include "com_ms_debug_RemoteBooleanObjectHelper.h"
#include "com_ms_debug_RemoteDoubleObjectHelper.h"
#include "com_ms_debug_RemoteFloatObjectHelper.h"
#include "com_ms_debug_EnumRemoteBooleanValueHelper.h"
#include "com_ms_debug_EnumRemoteDoubleValueHelper.h"
#include "com_ms_debug_EnumRemoteFloatValueHelper.h"
#include "com_ms_debug_ConstantPoolHelper.h"
#include "com_ms_debug_LockBytesHelper.h"

/***************************** Exported Functions ****************************/

/*
 *  Native method "helpers" are needed to marshal the following debugger interface methods:
 *
 *  IJavaEnumRemoteBooleanValue::GetNext()
 *  IRemoteBooleanObject::GetValue()
 *  IRemoteBooleanObject::SetValue()
 *
 *  IJavaEnumRemoteFloatValue::GetNext()
 *  IRemoteFloatObject::GetValue()
 *  IRemoteFloatObject::SetValue()
 *
 *  IJavaEnumRemoteDoubleValue::GetNext()
 *  IRemoteDoubleObject::GetValue()
 *  IRemoteDoubleObject::SetValue()
 *
 *  IRemoteMethodField::GetBytes()
 *
 *  IRemoteClassField::GetConstantPoolItem()
 */

/* boolean */ long __cdecl com_ms_debug_EnumRemoteBooleanValueHelper_GetNext(Hcom_ms_debug_EnumRemoteBooleanValueHelper *thisptr)
{
    JAVA_BOOLEAN value;
    IJavaEnumRemoteBooleanValue *pierbv;

    pierbv = (IJavaEnumRemoteBooleanValue *)convert_Java_Object_to_IUnknown((HObject *)(thisptr->m_ierbv), &IID_IJavaEnumRemoteBooleanValue);

if (pierbv)
{
HRESULT hr;

hr = pierbv->GetNext(&value);

switch (hr)
{
case S_OK:// Retrieved next value.
break;

case S_FALSE:// No more values.
SignalError(NULL, "com/ms/com/ComSuccessException", "");
break;

default:// Error.
SignalError(NULL, "com/ms/com/ComFailException", "IJavaEnumRemoteBooleanValue::GetNext");
break;
}
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);

    return(value);
}

/* boolean */ long __cdecl com_ms_debug_RemoteBooleanObjectHelper_GetValue(Hcom_ms_debug_RemoteBooleanObjectHelper *thisptr)
{
    JAVA_BOOLEAN value;
    IRemoteBooleanObject *pirbo;

    pirbo = (IRemoteBooleanObject *)convert_Java_Object_to_IUnknown((HObject *)thisptr->m_irbo, &IID_IRemoteBooleanObject);

if (pirbo)
{
if (pirbo->GetValue(&value) != S_OK)
SignalError(NULL, "com/ms/com/ComFailException", "IRemoteBooleanObject::GetValue");
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);

    return(value);
}

void __cdecl com_ms_debug_RemoteBooleanObjectHelper_SetValue(Hcom_ms_debug_RemoteBooleanObjectHelper *thisptr, /* boolean */ long newvalue)
{
    IRemoteBooleanObject *pirbo;

    pirbo = (IRemoteBooleanObject *)convert_Java_Object_to_IUnknown((HObject *)thisptr->m_irbo, &IID_IRemoteBooleanObject);

if (pirbo)
{
if (pirbo->SetValue((JAVA_BOOLEAN)newvalue) != S_OK)
SignalError(NULL, "com/ms/com/ComFailException", "IRemoteBooleanObject::SetValue");
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);
}

JAVA_FLOAT __cdecl com_ms_debug_EnumRemoteFloatValueHelper_GetNext(Hcom_ms_debug_EnumRemoteFloatValueHelper *thisptr)
{
    JAVA_FLOAT value;
    IJavaEnumRemoteFloatValue *pierfv;

    pierfv = (IJavaEnumRemoteFloatValue *)convert_Java_Object_to_IUnknown((HObject *)thisptr->m_ierfv, &IID_IJavaEnumRemoteFloatValue);

if (pierfv)
{
HRESULT hr;

hr = pierfv->GetNext(&value);

switch (hr)
{
case S_OK:// Retrieved next value.
break;

case S_FALSE:// No more values.
SignalError(NULL, "com/ms/com/ComSuccessException", "");
break;

default:// Error.
SignalError(NULL, "com/ms/com/ComFailException", "IJavaEnumRemoteFloatValue::GetNext");
break;
}
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);

    return(value);
}

JAVA_FLOAT __cdecl com_ms_debug_RemoteFloatObjectHelper_GetValue(Hcom_ms_debug_RemoteFloatObjectHelper *thisptr)
{
    JAVA_FLOAT value;
    IRemoteFloatObject *pirfo;

    pirfo = (IRemoteFloatObject *)convert_Java_Object_to_IUnknown((HObject *)thisptr->m_irfo, &IID_IRemoteFloatObject);

if (pirfo)
{
if (pirfo->GetValue(&value) != S_OK)
SignalError(NULL, "com/ms/com/ComFailException", "IRemoteFloatObject::GetValue");
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);

    return(value);
}

void __cdecl com_ms_debug_RemoteFloatObjectHelper_SetValue(Hcom_ms_debug_RemoteFloatObjectHelper *thisptr, float newvalue)
{
    IRemoteFloatObject *pirfo;

    pirfo = (IRemoteFloatObject *)convert_Java_Object_to_IUnknown((HObject *)thisptr->m_irfo, &IID_IRemoteFloatObject);

if (pirfo)
{
if (pirfo->SetValue((JAVA_FLOAT*)&newvalue) != S_OK)
SignalError(NULL, "com/ms/com/ComFailException", "IRemoteFloatObject::SetValue");
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);
}

JAVA_DOUBLE __cdecl com_ms_debug_EnumRemoteDoubleValueHelper_GetNext(Hcom_ms_debug_EnumRemoteDoubleValueHelper *thisptr)
{
    JAVA_DOUBLE value;
    IJavaEnumRemoteDoubleValue *pierdv;

    pierdv = (IJavaEnumRemoteDoubleValue *)convert_Java_Object_to_IUnknown((HObject *)thisptr->m_ierdv, &IID_IJavaEnumRemoteDoubleValue);

if (pierdv)
{
HRESULT hr;

hr = pierdv->GetNext(&value);

switch (hr)
{
case S_OK:// Retrieved next value.
break;

case S_FALSE:// No more values.
SignalError(NULL, "com/ms/com/ComSuccessException", "");
break;

default:// Error.
SignalError(NULL, "com/ms/com/ComFailException", "IJavaEnumRemoteDoubleValue::GetNext");
break;
}
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);

    return(value);
}

JAVA_DOUBLE __cdecl com_ms_debug_RemoteDoubleObjectHelper_GetValue(Hcom_ms_debug_RemoteDoubleObjectHelper *thisptr)
{
    JAVA_DOUBLE value;
    IRemoteDoubleObject *pirdo;

    pirdo = (IRemoteDoubleObject *)convert_Java_Object_to_IUnknown((HObject *)thisptr->m_irdo, &IID_IRemoteDoubleObject);

if (pirdo)
{
if (pirdo->GetValue(&value) != S_OK)
SignalError(NULL, "com/ms/com/ComFailException", "IRemoteDoubleObject::GetValue");
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);

    return(value);
}

void __cdecl com_ms_debug_RemoteDoubleObjectHelper_SetValue(Hcom_ms_debug_RemoteDoubleObjectHelper *thisptr, double newvalue)
{
    IRemoteDoubleObject *pirdo;

    pirdo = (IRemoteDoubleObject *)convert_Java_Object_to_IUnknown((HObject *)thisptr->m_irdo, &IID_IRemoteDoubleObject);

if (pirdo)
{
if (pirdo->SetValue((JAVA_DOUBLE *)&newvalue) != S_OK)
SignalError(NULL, "com/ms/com/ComFailException", "IRemoteDoubleObject::SetValue");
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);
}

ClassArrayOfByte * __cdecl com_ms_debug_LockBytesHelper_GetBytes(Hcom_ms_debug_LockBytesHelper *, Hcom_ms_debug_ILockBytes *pjilb)
{
    ClassArrayOfByte *pjabyte = NULL;
ILockBytes *pilb;

    pilb = (ILockBytes *)convert_Java_Object_to_IUnknown((Hjava_lang_Object *)pjilb, &IID_ILockBytes);

if (pilb)
{
HRESULT hr;
STATSTG statstg;

// How many bytes can be read?

hr = pilb->Stat(&statstg, STATFLAG_NONAME);

if (hr == S_OK)
{
pjabyte = (ClassArrayOfByte *)ArrayAlloc(T_BYTE, statstg.cbSize.LowPart);

if (pjabyte)
{
ULARGE_INTEGER ulnOffset;
ULONG ulcbRead;

ulnOffset.QuadPart = 0;

hr = pilb->ReadAt(ulnOffset, pjabyte->body, statstg.cbSize.LowPart, &ulcbRead);

if (hr != S_OK)
{
SignalError(0, "com.ms.com.ComFailException", "ILockBytes::ReadAt");
pjabyte = NULL;
}
}
else
SignalError(0, JAVAPKG "OutOfMemoryError", 0);
}
else
SignalError(0, "com.ms.com.ComFailException", "ILockBytes::Stat");
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);

return(pjabyte);
}

ClassArrayOfByte * __cdecl com_ms_debug_ConstantPoolHelper_GetConstantPoolItem(Hcom_ms_debug_ConstantPoolHelper *thisptr, int niItem)
{
    ClassArrayOfByte *pjabyte = NULL;

    if (thisptr->m_ircf)
{
IRemoteClassField *pircf;

pircf = (IRemoteClassField *)convert_Java_Object_to_IUnknown((Hjava_lang_Object *)(thisptr->m_ircf), &IID_IRemoteClassField);

if (pircf)
{
HRESULT hr;
PBYTE pbyteItem;
ULONG ulcbLen;

hr = pircf->GetConstantPoolItem(niItem, &pbyteItem, &ulcbLen);

if (hr == S_OK)
{
                pjabyte = (ClassArrayOfByte *)ArrayAlloc(T_BYTE, ulcbLen);

if (pjabyte)
CopyMemory(pjabyte->body, pbyteItem, ulcbLen);
else
SignalError(0, JAVAPKG "OutOfMemoryError", 0);

CoTaskMemFree(pbyteItem);
                pbyteItem = NULL;
}
else
SignalError(0, "com.ms.com.ComFailException", "IRemoteClassField::GetConstantPoolItem");
}
else
SignalError(0, JAVAPKG "ClassCastException", 0);
}
else
SignalError(0, JAVAPKG "IllegalArgumentException", 0);

return(pjabyte);
}


JDBGHLPR.CPP

/*
 * jdbghlpr.cpp - Main module.
 *
 * (C) Copyright 1996 Microsoft Corporation
 */

/* Headers
 **********/

#include "project.hpp"
#pragma hdrstop

/****************************** Public Functions *****************************/

#pragma warning(disable:4100)// "unreferenced formal parameter" warning

BOOL APIENTRY DllMain(HINSTANCE hDLL, DWORD dwReason, PVOID pvReserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hDLL);
break;
}

return(TRUE);
}

#pragma warning(default:4100) // "unreferenced formal parameter" warning

DWORD __cdecl RNIGetCompatibleVersion()
{
return RNIVER;
}


PROCESS.CPP

/*
 * process.cpp - Native methods.
 *
 * (C) Copyright 1996 Microsoft Corporation
 */

/* Headers
 **********/

#include "project.hpp"
#pragma hdrstop

#include "DebuggeeProcess.h"

/****************************** Public Functions *****************************/

BOOL JavaStringToANSIString(Hjava_lang_String *phjsCommandLine, PSTR *ppszANSI)
{
int ncchLen;

// (+ 1) for null terminator.
    ncchLen = javaStringLength(phjsCommandLine) + 1;

    *ppszANSI = (PSTR)LocalAlloc(0, ncchLen);

if (*ppszANSI)
        javaString2CString(phjsCommandLine, *ppszANSI, ncchLen);

    return(*ppszANSI != NULL);
}

/***************************** Exported Functions ****************************/

/* A debugger requires a way to get the process ID of its debuggee process. */

void __cdecl DebuggeeProcess_CreateSuspendedProcess(HDebuggeeProcess *hdp, Hjava_lang_String *phjsCommandLine)
{
    PSTR pszCommandLine;

    hdp->m_nMainThreadHandle = 0;
    hdp->m_nProcessID = 0;

if (JavaStringToANSIString(phjsCommandLine, &pszCommandLine))
{
STARTUPINFO si;
PROCESS_INFORMATION pi;

ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);

        if (CreateProcess(NULL, pszCommandLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
{
hdp->m_nMainThreadHandle = (int)(pi.hThread);
hdp->m_nProcessID = pi.dwProcessId;

            CloseHandle(pi.hProcess);
}
else
SignalError(0, "java/io/IOException", "CreateProcess");

LocalFree(pszCommandLine);
        pszCommandLine = NULL;
}
else
SignalError(0, JAVAPKG "OutOfMemoryError", 0);

    return;
}

void __cdecl DebuggeeProcess_ResumeProcess(HDebuggeeProcess *hdp)
{
    if (hdp->m_nMainThreadHandle != NULL)
{
        if (ResumeThread((HANDLE)(hdp->m_nMainThreadHandle)) == 0xffffffff)
SignalError(0, "java/io/IOException", "ResumeThread");

CloseHandle((HANDLE)(hdp->m_nMainThreadHandle));
        hdp->m_nMainThreadHandle = 0;
}

return;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by JDbgHlpr.rc
//

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

README.TXT

SampleIME is a sample class which demostarates how to use DefaultInputManager and
InputMethodListener interface to implement an IME in Microsoft VM. SampleIME
allows users to input unicode characters by typing the hexadecimal value
of the unicode characters.

To Compile SampleIME
====================
To compile this example, just compile all of the *.java files. You
could type the following under DOS prompt if Microsoft SDK for Java
is installed:

jvc *.java

To Run SampleIME
================
To run SampleIME, please compile all the *.java file. You could
use the following under DOS prompt to start this example:

jview SampleIMETest.class

Or you could run it from Internet Explorer, open the
following file using IE:

Sample.html

To Use SampleIME
================
When this example is started, you could use TextArea and TextField
as normal. When you want to use SampleIME, click on the button which
says "Click here to activate SampleIME."

After doing so, you could click on the TextFields again,
, type '41', and press Enter. You should see an 'A' appearing on
the TextField, because 0x41( hexadecimal value ) is the unicode
represenation of 'A'. If you are using a FarEast version of Windows,
you could input CJK characters by typing their unicode value.
For example, if you type '4e00', you should see
the Chinese character "one".

As you type along, you will find the IME will move the composition
window to the lower right corner of the input caret. This is called
IME level 2 ( near-caret ) support.

Only digit 0 to digit 9, and 'a' through 'f' are accepted for this IME.
Those characters which are not accepted by the IME will appear on the TextFiled.
If you type something wrong on the IME, you could use BackSpace to
correct them.

If there are no characters appearing on the TextField, please make
sure that the input focus is on the TextField. You could do so by
clicking on the TextField.

There is a button on the left of the IME which is used to activate/
deactivate the IME. Besides, you could use mouse to click on the window
of IME except the button area to drag the IME around.

Toggle IMEs
===========
You could use Ctrl-Alt to toggle between different Java-based IMEs. In this
example, there is only one Java IME, therefore, pressing Ctrl-Alt will
activate/deactivate SampleIME. If you are running this under Far East
version of Windows, pressing Ctrl-Alt for the first time will switch to
system IME. And pressing it again will deactivate both system IME and
SampleIME. If you press Ctrl-Alt again,SampleIME will be activated.

File List
=========
 SampleIME.java
The IME itself.
 SampleIMEWindow.java
    The composition window.
 SampleIMETest.java
The sample application which uses SampleIME.
 SampleIMETestFrame.java
The Frame used by sample application.


INVOKEVM.C

//
//  invokevm.c
//
//  Copyright (c) 1997, Microsoft Corporation
//
//  Demonstrates the use of the PrepareThreadForJava and UnprepareThreadForJava
//  invocation APIs.
//

#include <windows.h>
#include <stdio.h>
#include <native.h>

void main(int argc, char *argv[])
{
    HRESULT hr;
    ThreadEntryFrame threadEntryFrame;
    ClassClass *pClass;

    hr = CoInitialize(NULL);

    if (hr == S_OK) {

        //  Call this API before calling into the Microsoft Java VM to allow the
        //  VM to allocate any per-thread structures and to do any first-time
        //  initialization.  After return from this call, Java objects may be
        //  accessed or RNI APIs may be called.
        if (PrepareThreadForJava(&threadEntryFrame)) {

            pClass = FindClass(NULL, "TestClass", TRUE);

            if (pClass != NULL) {
                execute_java_static_method(NULL, pClass, "someMethod", "()V");
            } else {
                printf("Failed to find class!\n");
            }

            //  Detaches the "entry frame" from this thread.  After return from
            //  this call, it is no longer safe to directly touch Java objects
            //  or call RNI APIs.
            UnprepareThreadForJava(&threadEntryFrame);

        } else {
            printf("Failed to initialize thread!\n");
        }

        CoUninitialize();
    }
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by jview.rc
//
#define IDS_OUTOFMEMORY                 1
#define IDS_SCODE                       2
#define IDS_ERROR                       3
#define IDS_COULDNOTINITOLE             4
#define IDS_JAVAVM                      5
#define IDS_CLASSLOADER                 6
#define IDS_NOPATHGIVEN                 7
#define IDS_INVALIDSWITCH               8
#define IDS_INVALIDPARAMETER            9
#define IDS_NOCLASSGIVEN                10
#define IDS_UNKNOWNERROR                13
#define IDS_EXECUTINGCLASS              14
#define IDS_PRESSANYKEY                 15
#define IDS_MEMBERNOTFOUND              16
#define IDS_NOMAIN                      17
#define IDS_NOJAVATHREAD                18
#define IDS_VMCANTFINDRESOURCE          19

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


STUB.CPP

/*
 * stub.cpp
 *
 * Sample code.
 *
 * (C) Copyright 1996, Microsoft Corporation and it suppliers.
 */

#pragma hdrstop

#include "stub.h"

// Macros

#define WIN32_ERROR_TO_HRESULT(err)    MAKE_SCODE(SEVERITY_ERROR, FACILITY_WIN32, (err))
#define WIN32_RESULT_TO_HRESULT(err)   ((err) == ERROR_SUCCESS ? S_OK : WIN32_ERROR_TO_HRESULT(err))
#define LAST_WIN32_ERROR_TO_HRESULT()  WIN32_RESULT_TO_HRESULT(GetLastError())

// Resource IDs for the class resource data and the main class name data.
// These are the IDs of the resources that jexegen will create when used
// with the /bindto option.

#define CLASSRESOURCEID         1000
#define MAINCLASSNAMERESOURCEID 1001

//------------------------------------------------------------------------------
// CJView::CJView:
//    Constructor
//------------------------------------------------------------------------------

CJView::CJView (int ac, char **av) : m_ac (ac), m_av (av)
{
    m_pszClassName = NULL;
    m_ppszArgs     = NULL;
    m_iArgs        = 0;
    m_pJE          = NULL;
}

//------------------------------------------------------------------------------
// CJView::~CJView:
//    Destructor
//------------------------------------------------------------------------------

CJView::~CJView ()
{
    if (m_ppszArgs)
    {
        INT n = 0;

        while (m_ppszArgs[n] != NULL)
            delete [] m_ppszArgs[n++];
        delete [] m_ppszArgs;
    }

    if (m_pJE)
    {
        m_pJE->Release();
        CoUninitialize();
    }
}

//------------------------------------------------------------------------------
// CJView::m_FatalError:
//
// Print a formatted error message to stderr
//
// Returns: Nothing
//------------------------------------------------------------------------------

void CJView::m_FatalError
(
    INT idString,
    ...
)
{
    CHAR szFmt[BUFSIZE];
    va_list va;
    va_start(va, idString);

    LoadString(NULL, IDS_ERROR, szFmt, sizeof(szFmt));
    fprintf(stderr, szFmt);

    if (idString)
        LoadString(NULL, idString, szFmt, sizeof(szFmt));
    else
        lstrcpy(szFmt, "%s");

    vfprintf(stderr, szFmt, va);
    va_end(va);
    fprintf(stderr, "\n");
}

//------------------------------------------------------------------------------
// CJView::m_FatalErrorHR:
//
//      Print a formatted error followup by an hresult tp stderr
//------------------------------------------------------------------------------

void CJView::m_FatalErrorHR
(
    HRESULT hr,
    INT     idString,
    ...
)
{
    CHAR  szFmt[BUFSIZE];
    CHAR  buf[BUFSIZE];
    DWORD res;
    va_list va;
    va_start(va, idString);

    LoadString(NULL, IDS_ERROR, szFmt, sizeof(szFmt));
    fprintf(stderr, szFmt);
    LoadString(NULL, idString, szFmt, sizeof(szFmt));
    vfprintf(stderr, szFmt, va);
    va_end(va);

    res = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
                        NULL,
                        (DWORD)hr,
                        LOCALE_SYSTEM_DEFAULT,
                        buf,
                        sizeof(buf),
                        NULL);

    CHAR szNoMain[BUFSIZE] = "";

    if (!res)
    {
        CHAR szSCODE[BUFSIZE];

        LoadString(NULL, IDS_SCODE, szSCODE, sizeof(szSCODE));
        sprintf(buf, szSCODE, (DWORD)hr);
    }
    else
    {
        // Now we check if the error is "Member not found", and if it is, we
        // will append some additional info to the error message letting
        // the user know it was main() that could not be found, since that
        // is the only time this message should be generated.

        if (hr == DISP_E_MEMBERNOTFOUND)
        {
            CHAR sz[BUFSIZE] = "";

            LoadString(NULL, IDS_NOMAIN, sz, sizeof(sz));
            sprintf(szNoMain, sz, m_pszClassName, m_pszClassName);
        }
        else
            *szNoMain = '\0';
    }

    fprintf(stderr, ": %s\n", buf);
    if (*szNoMain)
        fprintf(stderr, "%s", szNoMain);
}

//------------------------------------------------------------------------------
// CJView::m_InitComAndJava:
//
// Initializes COM and obtains the neccessary interfaces from the Java VM
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------

BOOL CJView::m_InitComAndJava ()
{
    HRESULT           hr   = E_UNEXPECTED;
    IClassFactory    *pcf  = NULL;

    hr = CoInitialize(NULL);

    if (FAILED(hr))
    {
        m_FatalErrorHR(hr, IDS_COULDNOTINITOLE);
    }
    else
    {
        hr = CoGetClassObject(CLSID_JavaExecute,
                              CLSCTX_INPROC_SERVER |
                              CLSCTX_INPROC_HANDLER |
                              CLSCTX_LOCAL_SERVER,
                              NULL,
                              IID_IClassFactory,
                              (LPVOID*)(&pcf));
        if (FAILED(hr))
        {
            m_FatalErrorHR(hr, IDS_JAVAVM);
        }
        else
        {
            hr = pcf->CreateInstance(NULL, IID_IJavaExecute2, (LPVOID *)(&m_pJE));
            if (FAILED(hr))
            {
                m_pJE = NULL;
                m_FatalErrorHR(hr, IDS_CLASSLOADER);
            }

            pcf->Release();
        }

        if (NULL == m_pJE)
            CoUninitialize();
    }

    return (m_pJE != NULL);
}

//------------------------------------------------------------------------------
// CJView::MB2WC:
//
//   Converts the multibyte string to a UNICODE string, allocating space
// for the destination string.
//
// Returns: Pointer to newly allocated and converted string, NULL if it fails
//------------------------------------------------------------------------------

LPWSTR CJView::m_MB2WC
(
    LPCSTR szAnsi,
    int    cchAnsi
)
{
   // First, determine size of converted string
   LPWSTR pwsz    = NULL;
   int    cchWide = MultiByteToWideChar(0, 0, szAnsi, cchAnsi, NULL, 0) + 1;

   if (cchWide > 0)
   {
       // Got size so allocate the space and convert the string
       if (pwsz = new WCHAR[cchWide])
          MultiByteToWideChar(0, 0, szAnsi, cchAnsi, pwsz, cchWide);
    }

   return pwsz;
}

//------------------------------------------------------------------------------
// CJView::m_WC2MB:
//
//   Converts the given UNICODE string to a multibyte string, allocating space
// for the destination string.
//
// Returns: Pointer to newly allocated and converted string, NULL if it fails
//------------------------------------------------------------------------------

LPSTR CJView::m_WC2MB
(
   LPCWSTR pwsz,
   int     cchWide
)
{
   // First, determine size of converted string
   LPSTR psz     = NULL;
   int   cchAnsi = WideCharToMultiByte(0, 0, pwsz, cchWide, NULL, 0, NULL, NULL);

   if (cchAnsi > 0)
   {
       // Got size so allocate the space and convert the string
      if (psz = new CHAR[cchAnsi])
          WideCharToMultiByte(0, 0, pwsz, cchWide, psz, cchAnsi, NULL, NULL);
    }

    return psz;
}

//------------------------------------------------------------------------------
// CJView::m_ParseParameters
//
// Parses off the command line arguments following the class simply
// copying them into a list of OLESTRS
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------

BOOL CJView::m_ParseParameters()
{
    m_iArgs = m_ac - 1;

    m_ppszArgs = new LPOLESTR[m_iArgs + 1];
    if (!m_ppszArgs)
    {
        m_FatalError(IDS_OUTOFMEMORY);
        return FALSE;
    }

    (m_ppszArgs)[0] = NULL; // Initially empty!

    // Now, run through the list of arguments and process
    int iNext = 1;
    int i;

    for (i = 0; i < m_iArgs; i++)
    {
        if (!((m_ppszArgs)[i] = m_MB2WC(m_av[iNext++])))
            break;
    }

    // If succesful, mark end of array
    if (i == m_iArgs)
    {
        (m_ppszArgs)[i] = NULL;
    }
    else
    {
        // Clean up if we fail
        int n;

        for (n = 0; n < i; n++)
            deleteSZ(m_ppszArgs[n]);
        deleteSZ(m_ppszArgs);
    }

    return (i == m_iArgs);
}

//------------------------------------------------------------------------------

BOOL CJView::LoadDataResource( DWORD dwResID, BYTE *pBuffer, DWORD *pdwSize )
{
   HMODULE hMod = GetModuleHandle(NULL);
   DWORD   dwBufferSize;
   HRSRC   hRes;
   HANDLE  hLoadedRes;
   PVOID   pData;

   if ( !pdwSize || !pBuffer )
      return FALSE;

   dwBufferSize = *pdwSize;
   *pdwSize = 0;

   hRes = FindResource( hMod, MAKEINTRESOURCE(dwResID), RT_RCDATA );

   if ( hRes == NULL )
      return FALSE;

   *pdwSize = SizeofResource( hMod, hRes );

   if ( dwBufferSize < *pdwSize )
      return FALSE;

   hLoadedRes = LoadResource( hMod, hRes );

   if ( !hLoadedRes )
      return FALSE;

   pData = LockResource( hLoadedRes );

   CopyMemory( pBuffer, pData, *pdwSize );

   FreeResource( hLoadedRes );

   return TRUE;
}

//------------------------------------------------------------------------------
// CJView::Initialize:
//
//  Performs initialization for CJView
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------

BOOL CJView::Initialize ()
{
   // Load the name of the main class file to
   // execute.

   char  szBuffer[1024];
   DWORD dwSize = sizeof(szBuffer);

   if ( !LoadDataResource( MAINCLASSNAMERESOURCEID, (BYTE *)szBuffer, &dwSize ) )
   {
      m_FatalError( IDS_NOCLASSGIVEN );
      return FALSE;
   }

   m_pszClassName = new char[ lstrlen(szBuffer) + 1];

   if ( !m_pszClassName )
   {
      m_FatalError( IDS_OUTOFMEMORY );
      return FALSE;
   }

   lstrcpy( m_pszClassName, szBuffer );

   return m_InitComAndJava();
}

//------------------------------------------------------------------------------
// RunMessageLoop:
//      Message pump for OLE
//------------------------------------------------------------------------------

UINT RunMessageLoop(void)
{
    MSG msg;

    // No accelerators to load.  Get and dispatch messages until a WM_QUIT
    // message is received.
    ZeroMemory(&msg, sizeof(msg));

    msg.wParam = S_OK;

    while (GetMessage(&msg, NULL, 0, 0))
      //
      // Dispatch message to target window.
      //
      // We don't have any windows, so there are no window procedures that
      // require TranslateMessage(&msg).
      //
      DispatchMessage(&msg);

    return(msg.wParam);
}

//------------------------------------------------------------------------------
// CJView::ExecuteClass:
//
//  Executes the given class file
//
// Returns: 0 if successful, 1 if not
//------------------------------------------------------------------------------

DWORD _stdcall RunClassThread
(
    PVOID pv
)
{
    CJView* pJV     = (CJView*)pv;
    HRESULT hr;
    int     iResult;

    if ( pJV->m_ParseParameters() )
    {
        // Tell the VM that it should look in the current module for
        // class files.  The /bindto option of the jexegen tool will
        // place the resource data in the exe with a resource ID of
        // 1001 so we tell the VM that is where the data is.

        JAVACLASSRESOURCEINFO jcri;
        jcri.hModule      = GetModuleHandle(NULL);
        jcri.dwResourceID = CLASSRESOURCEID;     // Compatible with jexegen.

        hr = pJV->m_pJE->SetClassSource( CLASS_SOURCE_TYPE_MODULERESOURCES,
                                         &jcri,
                                         sizeof(JAVACLASSRESOURCEINFO) );

        if ( !SUCCEEDED(hr) )
        {
           pJV->m_FatalError( IDS_VMCANTFINDRESOURCE );
           return 0;
        }

        // Execute.
        LPOLESTR        pszClassName = pJV->m_MB2WC(pJV->m_pszClassName);
        LPERRORINFO     pIErrorInfo = NULL;
        JAVAEXECUTEINFO jei;

        jei.cbSize       = sizeof(jei);
        jei.dwFlags      = 0;
        jei.pszClassName = pszClassName;
        jei.rgszArgs     = (LPCOLESTR *)(pJV->m_ppszArgs);
        jei.cArgs        = pJV->m_iArgs;
        jei.pszClassPath = NULL;

        hr = pJV->m_pJE->Execute(&jei, &pIErrorInfo);

        if (!SUCCEEDED(hr))
        {
            // Most likely .class file did not exist
            pJV->m_FatalErrorHR (hr, IDS_EXECUTINGCLASS, pJV->m_pszClassName);
            iResult = 1;
        }
        else if (pIErrorInfo)
        {
            // VM threw an exception while running the .class file.  We
            // get the info via the returned IErrorInfo interface
            BSTR bstrError = NULL;

            if (SUCCEEDED(pIErrorInfo->GetDescription(&bstrError)))
            {
                LPSTR pszError = pJV->m_WC2MB(bstrError);

                if (pszError)
                {
                    pJV->m_FatalError (0, pszError);
                    deleteSZ(pszError);
                }
                else
                    pJV->m_FatalError (IDS_UNKNOWNERROR);

                SysFreeString(bstrError);
            }
            else
                pJV->m_FatalError(IDS_UNKNOWNERROR);

            iResult = 1;

            pIErrorInfo->Release();
        }
        else
            // Success.
            iResult = 0;

        deleteSZ(pszClassName);
    }
    else
        iResult = 1;

    // Terminate message pump
    PostThreadMessage(pJV->m_dwMsgLoopThreadID, WM_QUIT, 0, 0);

    return (DWORD)iResult;
}

//------------------------------------------------------------------------------

int __cdecl main
(
    int    ac,
    char **av
)
{
    int     iRet = 1;
    CJView* pJV  = new CJView(ac, av);

    if ( !pJV )
    {
        CHAR szFmt[20];

        LoadString(NULL, IDS_ERROR, szFmt, sizeof(szFmt));
        fprintf(stderr, szFmt);
        LoadString(NULL, IDS_OUTOFMEMORY, szFmt, sizeof(szFmt));
        fprintf(stderr, szFmt);
        fprintf(stderr, "\n");
        return iRet;
    }

    if (pJV->Initialize())
    {
        // OK, we're ready, everything is done on the applet thread
        HANDLE hth;
        DWORD  dwThreadID;

        pJV->m_dwMsgLoopThreadID = GetCurrentThreadId();
        hth = CreateThread(NULL, 0, &RunClassThread, pJV, 0, &dwThreadID);

        if (hth)
        {
            RunMessageLoop();

            // If we returned from RunMessageLoop() as a result of
            // RunClassThread() posting the WM_QUIT message, then the thread
            // will be exiting shortly (if not already).  We wait for it to
            // terminate and grab its exit code.  1/2 second is plenty --
            // if the thread doesn't die by then, something is wrong (we
            // got a quit message from someone else, perhaps?) in which case
            // we return 1 for failure.

            if (WaitForSingleObject (hth, 500) == WAIT_OBJECT_0)
            {
                DWORD   dwRetCode = 1;

                // Thread's dead, baby... thread's dead...
                GetExitCodeThread (hth, &dwRetCode);
                iRet = dwRetCode;
            }
            CloseHandle(hth);
            hth = NULL;
        }
        else
        {
            pJV->m_FatalErrorHR(LAST_WIN32_ERROR_TO_HRESULT(),
                                IDS_NOJAVATHREAD);
        }
    }

    delete pJV;
    return iRet;
}


STUB.H

/*
 * stub.h
 *
 * (C) Copyright 1996, Microsoft Corporation and it suppliers.
 */

#ifndef _EXEGENSTUB_INCLUDED_
#define _EXEGENSTUB_INCLUDED_

#define INITGUID 1

#include <windows.h>
#include <cguid.h>
#include <stdio.h>
#include <conio.h>
#include <stdarg.h>
#include <string.h>

#include "..\..\..\include\javaexec.h"

#include "resource.h"

#define   BUFSIZE 512     // A one-size fits all buffer size...

//
// Macros
//

#define deleteSZ(p) if ((p) != NULL)  \
                    {                 \
                       delete [] (p); \
                       (p) = NULL;    \
                    }

class CJView
{
public:
    DWORD m_dwMsgLoopThreadID;

    int    m_ac;
    char **m_av;

    LPSTR     m_pszClassName;
    INT       m_iArgs;
    LPOLESTR *m_ppszArgs;

    IJavaExecute2 *m_pJE;

    void   m_FatalError       (INT idString, ...);
    void   m_FatalErrorHR     (HRESULT hr, INT idString, ...);
    BOOL   m_InitComAndJava   ();
    LPWSTR m_MB2WC            (LPCSTR szAnsi,int cchAnsi = -1);
    LPSTR  m_WC2MB            (LPCWSTR pwsz, int cchWide = -1);
    LPSTR  m_newSZ            (int cBytes);
    BOOL   m_ParseParameters  ();

    BOOL   LoadDataResource( DWORD dwResID, BYTE *pBuffer, DWORD *pdwSize );

public:
    CJView(int ac, char **av);
    ~CJView();

public:
    BOOL Initialize         ();
    BOOL ParseCommandLine   ();
    int  ExecuteClass       ();
};

#endif //_EXEGENSTUB_INCLUDED_


JAVAPROP.CPP

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    cresprop.cpp

Abstract:

    Implements the CEnumResourceJAVAPROPERTY class.

--*/

#pragma hdrstop

#include <windows.h>
#include "javaprop.hpp"

// Construct a new, empty property enumerator.

CEnumJAVAPROPERTY::CEnumJAVAPROPERTY()
{
    m_cRef = 1;

    m_pPropList   = NULL;
    m_pCurProp    = NULL;
    m_cProperties = 0;
}

CEnumJAVAPROPERTY::~CEnumJAVAPROPERTY()
{
    LPJAVAPROPLink pProp = m_pPropList;
    LPJAVAPROPLink pPropTmp;

    // Walk down the list of properties freeing each node.

    while (pProp)
    {
        if (pProp->pszKey)   delete(pProp->pszKey);
        if (pProp->pszValue) delete(pProp->pszValue);

        pPropTmp = pProp;
        pProp    = pProp->pNext;

        delete(pPropTmp);
    }
}

STDMETHODIMP
CEnumJAVAPROPERTY::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
    HRESULT hr;

    if (riid == IID_IEnumJAVAPROPERTY || riid == IID_IUnknown) {
        *ppvObj = (LPENUMJAVAPROPERTY) this;
        m_cRef++;
        hr = S_OK;
    } else {
        *ppvObj = NULL;
        hr = E_NOINTERFACE;
    }

    return hr;
}

STDMETHODIMP_(ULONG)
CEnumJAVAPROPERTY::AddRef()
{
    m_cRef++;

    return m_cRef;
}

STDMETHODIMP_(ULONG)
CEnumJAVAPROPERTY::Release()
{
    m_cRef--;

    if (m_cRef > 0)
        return m_cRef;

    delete this;
    return 0;
}

STDMETHODIMP
CEnumJAVAPROPERTY::Next(ULONG celt, LPJAVAPROPERTY rgelt, ULONG
    *pceltFetched)
{
    HRESULT hr;
    ULONG celtFetched;

    hr = S_OK;
    celtFetched = 0;

    while (hr == S_OK && celtFetched < celt) {

        if (m_pCurProp == NULL) {
            hr = S_FALSE;
            break;
        }

        // Copy the value.

        rgelt->pszValue = (LPOLESTR)
            CoTaskMemAlloc((lstrlenW(m_pCurProp->pszValue) + 1) *
            sizeof(OLECHAR));

        if (rgelt->pszValue != NULL) {
            wcscpy(rgelt->pszValue, m_pCurProp->pszValue);
        } else {
            hr = E_OUTOFMEMORY;
        }

        // Copy the key.

        if (hr == S_OK)
        {

            rgelt->pszKey = (LPOLESTR)
                CoTaskMemAlloc((lstrlenW(m_pCurProp->pszKey) + 1) *
                sizeof(OLECHAR));

            if (rgelt->pszKey != NULL) {

                wcscpy(rgelt->pszKey, m_pCurProp->pszKey);

                rgelt++;
                m_pCurProp = m_pCurProp->pNext;
                celtFetched++;

            } else {
                CoTaskMemFree(rgelt->pszValue);
                hr = E_OUTOFMEMORY;
            }
        }
    }

    if (pceltFetched != NULL)
        *pceltFetched = celtFetched;

    return hr;
}

STDMETHODIMP
CEnumJAVAPROPERTY::Skip(ULONG celt)
{
    HRESULT hr = S_OK;

    while ( celt > 0 )
    {
        if ( m_pCurProp->pNext == NULL )
        {
            hr = S_FALSE;
            break;
        }

        m_pCurProp = m_pCurProp->pNext;
        celt--;
    }

    return hr;
}

STDMETHODIMP
CEnumJAVAPROPERTY::Reset()
{
    m_pCurProp = m_pPropList;
    return S_OK;
}

STDMETHODIMP
CEnumJAVAPROPERTY::Clone(LPENUMJAVAPROPERTY *ppenum)
{
    return E_NOTIMPL;
}

// Add a property to the list of properties.  The string parameter is
// expected to be of the form <key>=<value>.

BOOL CEnumJAVAPROPERTY::Add(char *pszKeyAndValue)
{
    char *pszKey = pszKeyAndValue;
    char *pszVal = pszKey;
    int   cchUnicode;

    if ( *pszKey == '\0' )
        return FALSE;

    // Walk down the string looking for the '=' that seperates the key
    // and value.
    while ( *pszVal && *pszVal != '=' )
        pszVal++;

    if ( *pszVal == '=' ) {
       *pszVal = '\0';
       pszVal++;
    }

    // Create a new node for our linked list.
    LPJAVAPROPLink pProp = new(JAVAPROPLink);

    if ( !pProp )
        return FALSE;

    // We want to keep track of the key and value strings as unicode.
    // Convert them and stash them off into the property node.

    // Convert the key.

    cchUnicode = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszKey, -1,
                                     NULL, 0);

    pProp->pszKey = new(OLECHAR[cchUnicode]);

    if ( !pProp->pszKey )
    {
        delete(pProp);
        return FALSE;
    }

    MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,pszKey,-1,pProp->pszKey,
                        cchUnicode);

    // Convert the value.

    cchUnicode = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszVal,
                                     -1, NULL, 0);

    pProp->pszValue = new(OLECHAR[cchUnicode]);

    if ( !pProp->pszValue )
    {
        delete(pProp->pszKey);
        delete(pProp);
        return FALSE;
    }

    MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,pszVal,-1,pProp->pszValue,
                        cchUnicode);

    // Link the new node into the list.

    pProp->pNext = m_pPropList;
    m_pPropList  = pProp;
    m_pCurProp   = m_pPropList;

    return TRUE;
}


JVIEW.CPP

/*
 * jview.cpp - JView front end, including command-line parsing logic.
 *
 * (C) Copyright 1996, Microsoft Corporation and it suppliers.
 */

#pragma hdrstop

#include "jview.h"
#include "javaprop.hpp"

// Macros

#define WIN32_ERROR_TO_HRESULT(err)    MAKE_SCODE(SEVERITY_ERROR, FACILITY_WIN32, (err))

#define WIN32_RESULT_TO_HRESULT(err)   ((err) == ERROR_SUCCESS ? S_OK : WIN32_ERROR_TO_HRESULT(err))

#define LAST_WIN32_ERROR_TO_HRESULT()  WIN32_RESULT_TO_HRESULT(GetLastError())

#define APPLETVIEWER "sun.applet.AppletViewer"

//------------------------------------------------------------------------------
// LoadOemString
//
// Wrapper for the Win32 LoadString API that translates the string to the OEM
// character set before returning it to the caller.
//------------------------------------------------------------------------------

BOOL LoadOemString(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int
    nBufferMax)
{
    BOOL fResult;

    fResult = LoadString(hInstance, uID, lpBuffer, nBufferMax);

    if (fResult)
        CharToOem(lpBuffer, lpBuffer);

    return fResult;
}

//------------------------------------------------------------------------------
// CJView::CJView:
//    Constructor
//------------------------------------------------------------------------------
CJView::CJView (int ac, char **av) : m_ac (ac), m_av (av)
{
    m_fApplet      = FALSE;
    m_fPause       = FALSE;
    m_fVerify      = FALSE;
    m_pszClassPath = NULL;
    m_pszAppend    = NULL;
    m_pszPrepend   = NULL;
    m_pszClassName = NULL;
    m_ppszArgs     = NULL;
    m_iArgs        = 0;
    m_pJE          = NULL;
    m_pProperties  = NULL;
}

//------------------------------------------------------------------------------
// CJView::~CJView:
//    Destructor
//------------------------------------------------------------------------------
CJView::~CJView ()
{
    deleteSZ(m_pszClassPath);
    deleteSZ(m_pszClassName);
    deleteSZ(m_pszAppend);
    deleteSZ(m_pszPrepend);

    if (m_ppszArgs)
    {
        INT n = 0;

        while (m_ppszArgs[n] != NULL)
            delete [] m_ppszArgs[n++];
        delete [] m_ppszArgs;
    }

    if (m_pProperties)
    {
        m_pProperties->Release();
        delete(m_pProperties);
    }

    if (m_pJE)
    {
        m_pJE->Release();
        CoUninitialize();
    }
}

//------------------------------------------------------------------------------
// CJView::m_Pause:
//
// -p was given on the command line, and we have an error, thus we display
// amessage to the user to press a key to terminate JView, thus allowing enough
// time to read the message before JView termnates and the console it was
// running in goes away if being executed from the IDE
//
// Returns: Nothing
//------------------------------------------------------------------------------
void CJView::m_Pause()
{
    CHAR szText[BUFSIZE];

    LoadOemString(NULL, IDS_PRESSANYKEY, szText, sizeof(szText));
    fprintf(stderr, "%s", szText);
    _getch();
}

//------------------------------------------------------------------------------
// CJView::m_FatalError:
//
// Print a formatted error message to stderr
//
// Returns: Nothing
//------------------------------------------------------------------------------
void CJView::m_FatalError
(
    INT idString,
    ...
)
{
    CHAR szFmt[BUFSIZE];
    va_list va;
    va_start(va, idString);

    LoadOemString(NULL, IDS_ERROR, szFmt, sizeof(szFmt));
    fprintf(stderr, szFmt);

    if (idString)
        LoadOemString(NULL, idString, szFmt, sizeof(szFmt));
    else
        lstrcpy(szFmt, "%s");

    vfprintf(stderr, szFmt, va);
    va_end(va);
    fprintf(stderr, "\n");

    if (m_fPause)
        m_Pause();
}

//------------------------------------------------------------------------------
// CJView::m_FatalErrorHR:
//
//      Print a formatted error followup by an hresult tp stderr
//------------------------------------------------------------------------------
void CJView::m_FatalErrorHR
(
    HRESULT hr,
    INT     idString,
    ...
)
{
    CHAR  szFmt[BUFSIZE];
    CHAR  buf[BUFSIZE];
    DWORD res;
    va_list va;
    va_start(va, idString);

    LoadOemString(NULL, IDS_ERROR, szFmt, sizeof(szFmt));
    fprintf(stderr, szFmt);
    LoadOemString(NULL, idString, szFmt, sizeof(szFmt));
    vfprintf(stderr, szFmt, va);
    va_end(va);

    res = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
                        NULL,
                        (DWORD)hr,
                        LOCALE_SYSTEM_DEFAULT,
                        buf,
                        sizeof(buf),
                        NULL);

    CHAR szNoMain[BUFSIZE] = "";

    if (!res)
    {
        CHAR szSCODE[BUFSIZE];

        LoadOemString(NULL, IDS_SCODE, szSCODE, sizeof(szSCODE));
        sprintf(buf, szSCODE, (DWORD)hr);
    }
    else
    {
        // Convert ANSI string returned by FormatMessage to OEM.
        CharToOem(buf, buf);

        // Now we check if the error is "Member not found", and if it is, we
        // will append some additional info to the error message letting
        // the user know it was main() that could not be found, since that
        // is the only time this message should be generated.
        if (hr == DISP_E_MEMBERNOTFOUND)
        {
            CHAR sz[BUFSIZE] = "";

            LoadOemString(NULL, IDS_NOMAIN, sz, sizeof(sz));
            sprintf(szNoMain, sz, m_pszClassName, m_pszClassName);
        }
        else
            *szNoMain = '\0';
    }

    fprintf(stderr, ": %s\n", buf);
    if (*szNoMain)
        fprintf(stderr, "%s", szNoMain);

    if (m_fPause)
        m_Pause();
}

//------------------------------------------------------------------------------
// CJView::m_InitComAndJava:
//
// Initializes COM and obtains the neccessary interfaces from the Java VM
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::m_InitComAndJava ()
{
    HRESULT           hr   = E_UNEXPECTED;
    IClassFactory    *pcf  = NULL;

    hr = CoInitialize(NULL);

    if (FAILED(hr))
    {
        m_FatalErrorHR(hr, IDS_COULDNOTINITOLE);
    }
    else
    {
        hr = CoGetClassObject(CLSID_JavaExecute,
                              CLSCTX_INPROC_SERVER |
                              CLSCTX_INPROC_HANDLER |
                              CLSCTX_LOCAL_SERVER,
                              NULL,
                              IID_IClassFactory,
                              (LPVOID*)(&pcf));
        if (FAILED(hr))
        {
            m_FatalErrorHR(hr, IDS_JAVAVM);
        }
        else
        {
            hr = pcf->CreateInstance(NULL, IID_IJavaExecute, (LPVOID *)(&m_pJE));
            if (FAILED(hr))
            {
                m_pJE = NULL;
                m_FatalErrorHR(hr, IDS_CLASSLOADER);
            }

            pcf->Release();
        }

        if (NULL == m_pJE)
            CoUninitialize();
    }

    return (m_pJE != NULL);
}

//------------------------------------------------------------------------------
// CJView::MB2WC:
//
//   Converts the multibyte string to a UNICODE string, allocating space
// for the destination string.
//
// Returns: Pointer to newly allocated and converted string, NULL if it fails
//------------------------------------------------------------------------------
LPWSTR CJView::m_MB2WC
(
    LPCSTR szAnsi,
    int    cchAnsi
)
{
   // First, determine size of converted string
   //--------------------------------------------------------------------------
   LPWSTR pwsz    = NULL;
   int    cchWide = MultiByteToWideChar(0, 0, szAnsi, cchAnsi, NULL, 0) + 1;

   if (cchWide > 0)
   {
       // Got size so allocate the space and convert the string
       //----------------------------------------------------------------------
       if (pwsz = new WCHAR[cchWide])
          MultiByteToWideChar(0, 0, szAnsi, cchAnsi, pwsz, cchWide);
    }

   return pwsz;
}

//------------------------------------------------------------------------------
// CJView::m_WC2MB:
//
//   Converts the given UNICODE string to a multibyte string, allocating space
// for the destination string.
//
// Returns: Pointer to newly allocated and converted string, NULL if it fails
//------------------------------------------------------------------------------
LPSTR CJView::m_WC2MB
(
   LPCWSTR pwsz,
   int     cchWide
)
{
   // First, determine size of converted string
   //--------------------------------------------------------------------------
   LPSTR psz     = NULL;
   int   cchAnsi = WideCharToMultiByte(0, 0, pwsz, cchWide, NULL, 0, NULL, NULL);

   if (cchAnsi > 0)
   {
       // Got size so allocate the space and convert the string
       //----------------------------------------------------------------------
      if (psz = new CHAR[cchAnsi])
          WideCharToMultiByte(0, 0, pwsz, cchWide, psz, cchAnsi, NULL, NULL);
    }

    return psz;
}

//------------------------------------------------------------------------------
// CJView::m_newSZ:
//
// Allocates the given string, generating OUT OF MEMORY if it fails
//
// Returns: LPSTR to allocated buffer if successful, NULL if not
//------------------------------------------------------------------------------
LPSTR CJView::m_newSZ
(
    int cBytes
)
{
    LPSTR psz = new CHAR[cBytes + 1]; // +1 for \0

    if (!psz)
        m_FatalError(IDS_OUTOFMEMORY);

    return psz;
}

//------------------------------------------------------------------------------
// CJView::m_AppendPathString
//
// Appends the given path the the other given path, allocating and freeing
// as neccessary
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::m_AppendPathString
(
    LPSTR *ppszPath,
    LPSTR  pszAppend
)
{
    LPSTR psz    = NULL;
    BOOL  fSemi2 = *CharPrev(pszAppend, pszAppend + lstrlen(pszAppend)) != ';';

    if (*ppszPath)
    {
        // Been here before, so we append the given string to the given path
        //----------------------------------------------------------------------
        if (!(psz = m_newSZ(lstrlen(*ppszPath))))
            return FALSE;

        lstrcpy(psz, *ppszPath);
        deleteSZ(*ppszPath);

        BOOL fSemi1 = *CharPrev(psz, psz + lstrlen(psz)) != ';';

        if (!(*ppszPath = m_newSZ(lstrlen(psz) + lstrlen(pszAppend) + fSemi1 + fSemi2)))
        {
            deleteSZ(psz);
            return FALSE;
        }

        lstrcpy(*ppszPath, psz);

        // Add semi-colon between paths if original path did not have one
        //----------------------------------------------------------------------
        if (fSemi1)
            lstrcat(*ppszPath, ";");

        lstrcat(*ppszPath, pszAppend);
        deleteSZ(psz);
    }
    else
    {
        // First time here, so copy Append string into Path
        //----------------------------------------------------------------------
        if (!(*ppszPath = m_newSZ(lstrlen(pszAppend) + fSemi2)))
            return FALSE;

        lstrcpy(*ppszPath , pszAppend);
    }

    // Append final semi-colon if string being append does not have one
    //--------------------------------------------------------------------------
    if (fSemi2)
        lstrcat(*ppszPath, ";");

    return TRUE;
}

//------------------------------------------------------------------------------
// CJView::m_DisplayUsage:
//
//  Displays the usage text for JView.exe
//
// Returns: Nothing
//------------------------------------------------------------------------------
VOID CJView::m_DisplayUsage ()
{
    // NOTE: All usage lines must be sequential String
    //       IDS starting with IDS_USAGE1
    CHAR sz[BUFSIZE];
    INT  i = 0;

    m_DisplayBanner();

    while (TRUE)
    {
        LoadOemString(NULL, IDS_USAGE1 + (i++), sz, sizeof(sz));
        if (*sz == '~')
            break;
        printf(sz);
    }
}

//------------------------------------------------------------------------------
// CJView::m_ParseSwitches:
//
//  Parses off the switches portion of the command line
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::m_ParseSwitches
(
    int *piArg
)
{
    BOOL   fSuccess = TRUE;
    BOOL   fReplace = m_pszClassPath != NULL;
    LPSTR *ppsz;
    LPSTR  psz;

    *piArg = 1;

    // We support both '-' and '/' switch designators
    //--------------------------------------------------------------------------
    while ((*piArg < m_ac) && ((m_av[*piArg][0] == '-') || (m_av[*piArg][0] == '/')))
    {
        psz = CharNext(m_av[*piArg]);

// Check for the -D switch prior to CharLowering the switch string.

if ( (*psz == 'D' || *psz == 'd') && (*(psz+1)==':') )
        {
            char *pszPropStr = psz+2;

            if ( *pszPropStr == '\0' )
            {
                m_FatalError(IDS_INVALIDSWITCH, psz);
                fSuccess = FALSE;
                break;
            }

            // If we don't already have a properties object, create one.
            if ( !m_pProperties )
            {
                m_pProperties = new(CEnumJAVAPROPERTY);

                if (!m_pProperties)
                {
                    m_FatalError(IDS_OUTOFMEMORY);
                    fSuccess = FALSE;
                    break;
                }
            }

            // Add the property to the property enumerator.
            m_pProperties->Add(psz+2);
        }
else
{
// All other switches are case insensitive, so just knock 'em down to
// lowercase here.

            CharLower(psz);

            // Check for usage switch first.  This is only valid as first switch
            //----------------------------------------------------------------------
            if (*piArg == 1 && lstrcmp(psz, "?") == 0)
            {
             m_DisplayUsage();
                fSuccess = FALSE;
                break;
            }

            // Check for "run in sun.applet.AppletViewer switch" , -a
            if (lstrcmp(psz, "a") == 0)
                m_fApplet = TRUE;
            // Check for "pause after error switch" , -p
            else if (lstrcmp(psz, "p") == 0)
                m_fPause = TRUE;
            // Check for "verify all" switch, -v
            else if (lstrcmp(psz, "v") == 0)
                m_fVerify = TRUE;
            // Silently ignore the old "quiet" switch for compatibility with
            // existing scripts.
            else if (lstrcmp(psz, "q") == 0)
                ;
            else
            {
                // Check for classpath switches -cp, -cp:p, or -cp:a
                //------------------------------------------------------------------
                BOOL fAppend  = FALSE;
                BOOL fPrepend = FALSE;

                if (           !lstrcmp(psz, "cp")    ||
                   (fPrepend = !lstrcmp(psz, "cp:p")) ||
                   (fAppend  = !lstrcmp(psz, "cp:a")))
                {
                    // We have classpath switch, so check for path.  If not given,
                    // i.e, we're out of arguments, it's a bogus command line
                    //--------------------------------------------------------------
                    if (++(*piArg) == m_ac ||
                       (m_av[*piArg][0] == '-' || m_av[*piArg][0] == '/'))
                    {
                        // We're about to exit, so we don't have to worry about
                        // losing information in the ANSI to OEM conversion.
                        CharToOem(m_av[*piArg - 1], m_av[*piArg - 1]);

                        m_FatalError(IDS_NOPATHGIVEN, m_av[*piArg - 1]);
                        fSuccess = FALSE;
                        break;
                    }

                    // If we are given a class path on the command line and we also
                    // have a classpath from the environment variable CLASSPATH,
                    // the one given on the command line instructs us to ignore the
                    // environment variable, and we instead append/prepend other
                    // paths to this one instead
                    //--------------------------------------------------------------
                    if (fReplace && !(fPrepend || fAppend))
                    {
                        deleteSZ(m_pszClassPath);
                        fReplace = FALSE;
                    }

                    ppsz = fPrepend ? &m_pszPrepend : (fAppend ? &m_pszAppend :
                                                                 &m_pszClassPath);
                    m_AppendPathString(ppsz, m_av[*piArg]);
                }
                else
                {
                    // We're about to exit, so we don't have to worry about
                    // losing information in the ANSI to OEM conversion.
                    CharToOem(m_av[*piArg], m_av[*piArg]);

                    // Bogus switch!
                    m_FatalError(IDS_INVALIDSWITCH, m_av[*piArg]);
                    fSuccess = FALSE;
                    break;
                }
            }
        }

        (*piArg)++;
    }

    return fSuccess;
}

//------------------------------------------------------------------------------
// CJView::m_ParseParameters
//
// Parses off the remaining command line arguments following the class simply
// copying them into a list of OLESTRS
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::m_ParseParameters
(
    int iNext
)
{
    // If applet or stand-alone, we need a values array
    //--------------------------------------------------------------------------
    m_iArgs = m_ac - iNext;

    m_ppszArgs = new LPOLESTR[m_iArgs + 1];
    if (!m_ppszArgs)
    {
        m_FatalError(IDS_OUTOFMEMORY);
        return FALSE;
    }

    (m_ppszArgs)[0] = NULL; // Initially empty!

    // Now, run through the list of arguments and process
    //--------------------------------------------------------------------------
    int i;

    for (i = 0; i < m_iArgs; i++)
    {
        if (!((m_ppszArgs)[i] = m_MB2WC(m_av[iNext++])))
            break;
    }

    // If succesful, mark end of array
    //--------------------------------------------------------------------------
    if (i == m_iArgs)
    {
        (m_ppszArgs)[i] = NULL;
    }
    else
    {
        // Clean up if we fail
        //----------------------------------------------------------------------
        int n;

        for (n = 0; n < i; n++)
            deleteSZ(m_ppszArgs[n]);
        deleteSZ(m_ppszArgs);
    }

    return (i == m_iArgs);
}

//------------------------------------------------------------------------------
// CJView::ParseCommandLine:
//
//  Parses the command line, which must be in the format:
//
//  JView [switches] ClassName [Parameters]
//
//  switches:   None          :  Invoke main()
//              -a            :  Run as Applet
//              -cp   <path>  :  Set CLASSPATH to <path.
//              -cp:p <path>  :  Prepend <path> to CLASSPATH
//              -cp:a <path>  :  Append  <path> to CLASSPATH
//
//  Classname:  Class file to run.  Valid previously compiled .JAVA file(s)
//
//  Parameters: Name=Value
//
//              if value contains spaces, value must be contained in double
//              quotes
//
//              Name="Value contains spaces"
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::ParseCommandLine ()
{
    // Must have at least one command line arguement of class name
    // Argument 0 is JVIEW itself
    //--------------------------------------------------------------------------
    if (m_ac == 1)
    {
        m_DisplayUsage();
        return FALSE;
    }

    // Get current CLASSPATH from VM.  We start with this, and append, prepend,
    // or replace it with what we find on the command line
    //--------------------------------------------------------------------------
    LPOLESTR psz = NULL;

    if (m_pszClassPath = new CHAR[1])
        *m_pszClassPath = 0;

    // First thing we check for are switches, processing all arguments that
    // begin with "-".
    // NOTE: All switches must come before class name and parameters
    //--------------------------------------------------------------------------
    INT i;

    if (!m_ParseSwitches(&i))
        return FALSE;

    // Next on the command line should be the class file to execute.  If no more
    // arguments, it's a bogus command line
    //--------------------------------------------------------------------------
    BOOL fSuccess = TRUE;

    if (i == m_ac)
    {
        m_FatalError(IDS_NOCLASSGIVEN);
        fSuccess = FALSE;
    }
    else
    {
        // OK, we have another argument, so whatever it is we simply treat it as
        // the class file name and let the VM deal with verifying it.
        //----------------------------------------------------------------------
        if (m_pszClassName = m_newSZ(lstrlen(m_fApplet ? APPLETVIEWER : m_av[i])))
        {
            lstrcpy(m_pszClassName, m_fApplet ? APPLETVIEWER : m_av[i++]);

            // Finally, if we have any more arguments, they are all treated as
            // parameters to be used by the class
            //------------------------------------------------------------------
            if (i < m_ac)
                fSuccess = m_ParseParameters(i);
        }
    }

    if (fSuccess)
    {
        // Build final path string
        //----------------------------------------------------------------------
        if (m_pszPrepend)
        {
            if (m_pszClassPath)
                m_AppendPathString(&m_pszPrepend, m_pszClassPath);
            deleteSZ(m_pszClassPath);
            m_pszClassPath = m_pszPrepend;
            m_pszPrepend   = NULL;
        }

        if (m_pszAppend)
        {
            if (m_pszClassPath)
            {
                m_AppendPathString(&m_pszClassPath, m_pszAppend);
                deleteSZ(m_pszAppend);
            }
            else
            {
                m_pszClassPath = m_pszAppend;
                m_pszAppend    = NULL;
            }
        }
    }

    return fSuccess;
}

//------------------------------------------------------------------------------
// CJView::m_DisplayBanner:
//
//  Displays the banner for JView.exe
//
// Returns: Nothing
//------------------------------------------------------------------------------
VOID CJView::m_DisplayBanner ()
{
    // Get FileVersion info
    //--------------------------------------------------------------------------
    DWORD dwVer = 0;

    CHAR szFile[MAX_PATH];

    GetModuleFileName(NULL, szFile, sizeof(szFile));

    VS_FIXEDFILEINFO *lpInfo;
    DWORD  dw;
    DWORD  dwSize = GetFileVersionInfoSize(szFile, &dw);
    LPVOID lpData = (LPVOID)new CHAR[dwSize];

    GetFileVersionInfo(szFile, 0, dwSize, lpData);

    // Get File Version and File Description
    //--------------------------------------------------------------------------
    UINT ui;
    VerQueryValue (lpData, "\\", (LPVOID*)&lpInfo, &ui);

    CHAR sz[BUFSIZE];

    LoadOemString(NULL, IDS_BANNER1, sz, sizeof(sz));
    printf(sz, HIWORD(lpInfo->dwFileVersionMS),
               LOWORD(lpInfo->dwFileVersionMS),
               LOWORD(lpInfo->dwFileVersionLS));
    LoadOemString(NULL, IDS_BANNER2, sz, sizeof(sz));
    printf(sz);

    delete lpData;
}

//------------------------------------------------------------------------------
// CJView::Initialize:
//
//  Performs initialization for CJView
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::Initialize ()
{
   return m_InitComAndJava();
}

//------------------------------------------------------------------------------
// RunMessageLoop:
//      Message pump for OLE
//
//------------------------------------------------------------------------------
UINT RunMessageLoop(void)
{
    MSG msg;

    // No accelerators to load.  Get and dispatch messages until a WM_QUIT
    // message is received.
    ZeroMemory(&msg, sizeof(msg));

    msg.wParam = S_OK;

    while (GetMessage(&msg, NULL, 0, 0))
      //
      // Dispatch message to target window.
      //
      // We don't have any windows, so there are no window procedures that
      // require TranslateMessage(&msg).
      //
      DispatchMessage(&msg);

    return(msg.wParam);
}

//------------------------------------------------------------------------------
// CJView::SetSystemProperties:
//
// Will inform the VM of any additional system properties that we want to
// set.

HRESULT CJView::SetSystemProperties()
{
    HRESULT        hr;
    IJavaExecute2 *pije2;

    // If no properties defined, bail.
    if ( !m_pProperties )
        return S_OK;

    // Query for the IJavaExecute2 interface off of the existing
    // IJavaExecute interface.
    if (m_pJE->QueryInterface(IID_IJavaExecute2, (LPVOID*)&pije2) == S_OK)
    {
        // We found IJavaExecute2, set the system properties.
        hr = pije2->SetSystemProperties( (IEnumJAVAPROPERTY *)m_pProperties);
        pije2->Release();
    }
    else
    {
        // If the QI failed, this current VM does not support IJavaExecute2
        hr = E_FAIL;
        m_FatalError(IDS_PROPSNOTSUPPORTED);
    }

    return hr;
}

//------------------------------------------------------------------------------
// CJView::ExecuteClass:
//
//  Executes the given class file
//
// Returns: result of IJavaExecute::Execute
//------------------------------------------------------------------------------

HRESULT CJView::ExecuteClass (LPERRORINFO *ppIErrorInfo)
{
    LPOLESTR        pszClassName;
    LPOLESTR        pszClassPath;
    JAVAEXECUTEINFO jei;

    pszClassName = m_MB2WC(m_pszClassName);
    pszClassPath = m_MB2WC(m_pszClassPath);

    jei.cbSize       = sizeof(jei);
    jei.dwFlags      = m_fVerify ? JEIF_VERIFYCLASSES : 0;
    jei.pszClassName = pszClassName;
    jei.rgszArgs     = (LPCOLESTR *)(m_ppszArgs);
    jei.cArgs        = m_iArgs;
    jei.pszClassPath = pszClassPath;

    HRESULT hr = m_pJE->Execute(&jei, ppIErrorInfo);

    deleteSZ(pszClassPath);
    deleteSZ(pszClassName);
    return hr;
}

//------------------------------------------------------------------------------
// RunClassThread
//
//  Executes the given class file.  If not found, executes without .class
//  extension, if it has it.
//
// Returns: 0 if successful, 1 if not
//------------------------------------------------------------------------------

DWORD _stdcall RunClassThread
(
    PVOID pv
)
{
    CJView* pJV     = (CJView*)pv;
    int     iResult = 1;            // Assume failure.

    if (pJV->ParseCommandLine())
    {
        HRESULT hr;
        LPERRORINFO     pIErrorInfo = NULL;

        // Check for prepended path

        int len = strlen(pJV->m_pszClassName);

        if (len > 2 && (pJV->m_pszClassName[1] == ':' || strchr(pJV->m_pszClassName,'\\') != NULL))
        {
            char *fullname = pJV->m_newSZ(MAX_PATH);
            char *endofpath;

            if (GetFullPathName(pJV->m_pszClassName, MAX_PATH+1, fullname, &endofpath))
            {
                // Convert to long filename, if needed

                WIN32_FIND_DATA findinfo;
                HANDLE hfind;

                hfind = FindFirstFile(fullname, &findinfo);

                if (hfind == INVALID_HANDLE_VALUE)
                {
                    // Try appending a .class extension

                    strcat(endofpath, ".class");

                    hfind = FindFirstFile(fullname, &findinfo);
                }

                if (hfind != INVALID_HANDLE_VALUE)
                {
                    // Split path from name

                    *(endofpath-1) = '\0';

                    // Create the real class name and update length

                    deleteSZ(pJV->m_pszClassName);
                    len = lstrlen(findinfo.cFileName);
                    pJV->m_pszClassName = pJV->m_newSZ(len);
                    lstrcpy(pJV->m_pszClassName, findinfo.cFileName);

                    // Prepend to specified path

                    if (*pJV->m_pszClassPath == '\0')
                    {
                        deleteSZ(pJV->m_pszClassPath);
                        pJV->m_pszClassPath = fullname;
                    }
                    else
                    {
                        pJV->m_AppendPathString(&fullname, pJV->m_pszClassPath);
                        deleteSZ(pJV->m_pszClassPath);
                        pJV->m_pszClassPath = fullname;
                    }

                    FindClose(hfind);
                }
            }

            // else let the vm deal with whatever was specified.
        }

        // Check for .class extension.

        const char classext1[] = ".cla";
        const int classext1len = sizeof(classext1)-1;
        const char classext2[] = ".class";
        const int classext2len = sizeof(classext2)-1;

        char *classnameext = NULL;

        if (len > classext1len)
        {
            char *ext = pJV->m_pszClassName+len-classext1len;

            if (_stricmp(ext, classext1) == 0)
            {
                // Truncate at extension.

                *ext = '\0';
                classnameext = ext;
            }
            else
            {
                // Try the full extension.

                ext -= classext2len-classext1len;

                if (_stricmp(ext, classext2) == 0)
                {
                    *ext = '\0';
                    classnameext = ext;
                }
            }
        }

        // Set any user defined system properties.
        hr = pJV->SetSystemProperties();

        if ( hr == S_OK )
        {
            hr = pJV->ExecuteClass(&pIErrorInfo);

            if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && classnameext != NULL)
            {
                // Try the 1 in a billion possibility that someone is trying
                // to execute some class named 'class' in a package.  Of course,
                // if someone has a class with the same name as the package, we
                // hit it first above.

                *classnameext = '.';

                hr = pJV->ExecuteClass(&pIErrorInfo);
            }

            if (!SUCCEEDED(hr))
            {
                // We're about to exit, so we don't have to worry about
                // losing information in the ANSI to OEM conversion.
                CharToOem(pJV->m_pszClassName, pJV->m_pszClassName);

                // Most likely .class file did not exist
                pJV->m_FatalErrorHR (hr, IDS_EXECUTINGCLASS, pJV->m_pszClassName);
                iResult = 1;
            }
            else if (pIErrorInfo)
            {
                // VM threw an exception while running the .class file.  We
                // get the info via the returned IErrorInfo interface
                BSTR bstrError = NULL;

                if (SUCCEEDED(pIErrorInfo->GetDescription(&bstrError)))
                {
                    LPSTR pszError = pJV->m_WC2MB(bstrError);

                    if (pszError)
                    {
                        CharToOem(pszError, pszError);
                        pJV->m_FatalError (0, pszError);
                        deleteSZ(pszError);
                    }
                    else
                        pJV->m_FatalError (IDS_UNKNOWNERROR);

                    SysFreeString(bstrError);
                }
                else
                    pJV->m_FatalError(IDS_UNKNOWNERROR);

                iResult = 1;

                pIErrorInfo->Release();
            }
            else
              // Success.
              iResult = 0;
        }
    }

    // Terminate message pump
    PostThreadMessage(pJV->m_dwMsgLoopThreadID, WM_QUIT, 0, 0);

    return (DWORD)iResult;
}

//------------------------------------------------------------------------------
// main() - Entry point for JView
//
// Returns: 0 if successful, 1 if not
//------------------------------------------------------------------------------

#ifdef NO_CONSOLE
#define ENTRYPOINT int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
#else
#define ENTRYPOINT int __cdecl main( int, char ** )
#endif

ENTRYPOINT
{
    int     iRet = 1;
    CJView* pJV  = new CJView(__argc,__argv);

    if (!pJV)
    {
        CHAR szFmt[64];

        LoadOemString(NULL, IDS_ERROR, szFmt, sizeof(szFmt));
        fprintf(stderr, szFmt);
        LoadOemString(NULL, IDS_OUTOFMEMORY, szFmt, sizeof(szFmt));
        fprintf(stderr, szFmt);
        fprintf(stderr, "\n");
        return iRet;
    }

    if (pJV->Initialize())
    {
        // OK, we're ready, everything is done on the applet thread
        HANDLE hth;
        DWORD  dwThreadID;

        pJV->m_dwMsgLoopThreadID = GetCurrentThreadId();
        hth = CreateThread(NULL, 0, &RunClassThread, pJV, 0, &dwThreadID);

        if (hth)
        {
            RunMessageLoop();

               // If we returned from RunMessageLoop() as a result of
               // RunClassThread() posting the WM_QUIT message, then the thread
               // will be exiting shortly (if not already).  We wait for it to
               // terminate and grab its exit code.  1/2 second is plenty --
               // if the thread doesn't die by then, something is wrong (we
               // got a quit message from someone else, perhaps?) in which case
               // we return 1 for failure.

               if (WaitForSingleObject (hth, 500) == WAIT_OBJECT_0)
               {
                     DWORD   dwRetCode = 1;

                     // Thread's dead, baby... thread's dead...
                     GetExitCodeThread (hth, &dwRetCode);
                     iRet = dwRetCode;
               }
               CloseHandle(hth);
               hth = NULL;
        }
        else
        {
            pJV->m_FatalErrorHR(LAST_WIN32_ERROR_TO_HRESULT(),
                                IDS_NOJAVATHREAD);
        }
    }

    delete pJV;
    return iRet;
}


JVIEW.H

/*
 * jview.h - JView project header file.
 *
 * (C) Copyright 1996, Microsoft Corporation and it suppliers.
 */

#ifndef _JVIEW_INCLUDED_
#define _JVIEW_INCLUDED_

#define INITGUID 1

#include <windows.h>
#include <cguid.h>
#include <stdio.h>
#include <conio.h>
#include <stdarg.h>
#include <string.h>

#include <javaexec.h>

#include "resource.h"
#include "jview.h"
#include "javaprop.hpp"

#define BUFSIZE 512     // A one-size fits all buffer size...

// Macros
//------------------------------------------------------------------------------
#define deleteSZ(p) if ((p) != NULL)  \
                    {                 \
                       delete [] (p); \
                       (p) = NULL;    \
                    }

class CJView
{
public:
    DWORD m_dwMsgLoopThreadID;

    int    m_ac;
    char **m_av;

    BOOL      m_fApplet;
    BOOL      m_fPause;
    BOOL      m_fVerify;
    LPSTR     m_pszClassPath;
    LPSTR     m_pszAppend;
    LPSTR     m_pszPrepend;
    LPSTR     m_pszClassName;
    INT       m_iArgs;
    LPOLESTR *m_ppszArgs;

    IJavaExecute *m_pJE;

    CEnumJAVAPROPERTY *m_pProperties;

    void   m_Pause            ();
    void   m_FatalError       (INT idString, ...);
    void   m_FatalErrorHR     (HRESULT hr, INT idString, ...);
    BOOL   m_InitComAndJava   ();
    LPWSTR m_MB2WC            (LPCSTR szAnsi,int cchAnsi = -1);
    LPSTR  m_WC2MB            (LPCWSTR pwsz, int cchWide = -1);
    LPSTR  m_newSZ            (int cBytes);
    BOOL   m_AppendPathString (LPSTR *ppszPath, LPSTR pszAppend);
    void   m_DisplayUsage     ();
    void   m_PreParseSwitches ();
    BOOL   m_ParseSwitches    (int *piArg);
    BOOL   m_ParseParameters  (int iNext);
    void   m_DisplayBanner    ();

public:
    CJView(int ac, char **av);
    ~CJView();

public:
    BOOL Initialize         ();
    BOOL ParseCommandLine   ();
    HRESULT ExecuteClass    (LPERRORINFO*);
    HRESULT SetSystemProperties();
};

#endif //_JVIEW_INCLUDED_


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by jview.rc
//
#define IDS_OUTOFMEMORY                 1
#define IDS_SCODE                       2
#define IDS_ERROR                       3
#define IDS_COULDNOTINITOLE             4
#define IDS_JAVAVM                      5
#define IDS_CLASSLOADER                 6
#define IDS_NOPATHGIVEN                 7
#define IDS_INVALIDSWITCH               8
#define IDS_INVALIDPARAMETER            9
#define IDS_NOCLASSGIVEN                10
#define IDS_UNKNOWNERROR                13
#define IDS_EXECUTINGCLASS              14
#define IDS_PRESSANYKEY                 15
#define IDS_MEMBERNOTFOUND              16
#define IDS_NOMAIN                      17
#define IDS_NOJAVATHREAD                18
#define IDS_PROPSNOTSUPPORTED           19

#define IDS_USAGE1                      1001
#define IDS_USAGE2                      1002
#define IDS_USAGE3                      1003
#define IDS_USAGE4                      1004
#define IDS_USAGE5                      1005
#define IDS_USAGE6                      1006
#define IDS_USAGE7                      1007
#define IDS_USAGE8                      1008
#define IDS_USAGE9                      1009
#define IDS_USAGE10                     1010
#define IDS_USAGE11                     1011
#define IDS_USAGE12                     1012
#define IDS_USAGE13                     1013
#define IDS_USAGELAST                   1014
#define IDS_BANNER1                     1020
#define IDS_BANNER2                     1021

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SIEVE.H

/*  DO NOT EDIT - automatically generated by msjavah  */
#include <native.h>

/*  Header for class Sieve  */

#ifndef _Included_Sieve
#define _Included_Sieve

#define HSieve ClassSieve
typedef struct ClassSieve {
#pragma pack(push,4)
long MSReserved;
#define Sieve_F 3.2
#pragma pack(pop)
} ClassSieve;

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) long __cdecl Sieve_CountPrimes (struct HSieve *, struct HArrayOfByte *);
#ifdef __cplusplus
}
#endif

#endif  /* _Included_Sieve */


SIEVEDEMO.C

//
//  SieveDemo.c
//
//  Copyright (c) 1997, Microsoft Corporation.  All rights reserved.
//

#include <stdarg.h>
#include <native.h>
#include "Sieve.h"

DWORD __cdecl RNIGetCompatibleVersion()
{
    return RNIVER;
}

long __cdecl Sieve_CountPrimes(struct HSieve *phThis, HArrayOfByte *phFlags)
{
    unsigned long count = 0;
    unsigned long i;

    for (i = 0; i < obj_length(phFlags); i++)
        (phFlags->body)[i] = 1;

    for (i = 2; i < obj_length(phFlags); i++)
    {
        if ((phFlags->body)[i] != 0)
        {
            unsigned long k;
            for (k = i + i; k < obj_length(phFlags); k += i)
                (phFlags->body)[k] = 0;

            count++;
        }
    }

    return count;
}


README.TXT

============================================================================
This sample demonstrates Java->COM and COM->Java communications.

The sample is invoked from Java.  The constructor of the class we invoke
instantiates a COM-implemented object.  Three methods are then exercised.

The first two methods take and return numeric arguments.  The third method
instantiates a Java-implemented object, and passes the interface pointer back
to Java, caching a local copy for its own use later on.  The new java object
is then used from both Java and COM transparently.  That is, the COM object
treats it no differently than any other COM object, and Java treats it as a
Java object.

Copyright (c) 1996-1997 Microsoft Corporation.  All rights reserved.

----------------------------------------------------------------------------
CONTENTS:

odl\                            ** interface descriptions
NatCom.odl
jNatCom.odl

com\                            ** COM server
natcom.cpp
natcom.def
natcom.h
makefile

java\
_1\                     ** run this to exec sample
JMain.java
makefile

_2\                     ** called from COM and java
JMain.java
reg.bat
makefile

----------------------------------------------------------------------------
BUILD INSTRUCTIONS:
The executable files that are built from this sample are included, so you do
not need to build the sample in order to run it.  However, the following
information is included if you want to build the sample yourself.

Dependencies:
  jvc.exe, cpdbase.dll, msjvc.dll, jactivex.exe, javareg.exe.
  (All are included in the SDK BIN directory.
   JVC.EXE must be version 1.02.3920 or later.)

To build from this subdirectory:
  nmake /a

----------------------------------------------------------------------------
RUNNING THE SAMPLE:

  Use the GO.BAT in this subdirectory.  This batch file does the following:

  1. Registers the COM server (regsvr32 com\natcom.dll)

  2. Registers the Java server (java\_2\reg.bat)

  3. Executes JVIEW java._1.JMain
     (Note: Your CLASSPATH must include '.' subdirectory)

  4. Un-registers the Java server (java\_2\unreg.bat)

  5. Un-registers the COM server (regsvr32 /u com\natcom.dll)

----------------------------------------------------------------------------
EXPECTED CONSOLE OUTPUT:

2 * 5 = 10
inc.Square(10) = 100
invoked from COM: Hello World
invoked from Java: Hello World

============================================================================


NATCOM.CPP

/*
Copyright (c) 1996-1997  Microsoft Corporation.  All rights reserved.
*/

#include <windows.h>
#include "natcom.h"
#include <iostream.h>
#include <olectl.h>
#include <stdio.h>

// globals

ULONGg_cObject = 0 ;
HANDLEg_hDllMain ;

////////////////////////////////////////////////////////////////////////////////
//
//ObjectCreated()
//
//increments ref count of objects which reference this .DLL
//
////
VOID ObjectCreated(VOID)
{
    InterlockedIncrement( (LONG*)&g_cObject ) ;
}

////////////////////////////////////////////////////////////////////////////////
//
//ObjectDestroyed()
//
//decrements ref count of objects which reference this .dll
//
////
VOID ObjectDestroyed(VOID)
{
    InterlockedDecrement( (LONG*)&g_cObject ) ;
}

////////////////////////////////////////////////////////////////////////////////
//
//CNatCom
//
//contains our COM object
//
////
class CNatCom : public IUnknown
{
////////////////////////////////////////////////////////////////////////
//
//CImpINatCom
//
//implements the INatCom interface.  IUnknown delegates to
//controlling unknown (CNatCom if not aggregated).
//
////
class CImpINatCom : public INatCom
{
friend class CNatCom ;

// PUBLIC ------------------------------------------------------

public :

////////////////////////////////////////////////////////////////
//
//
//CImpINatCom()
//
//constructor;  Initialize member variables.
//
////
CImpINatCom()
{
m_cRef = 0 ;
m_pNatCom = NULL ;
m_punkCCW = NULL ;
}

////////////////////////////////////////////////////////////////
//
//~CImpINatCom()
//
//destructor; release CCW if we have one.
//
////
~CImpINatCom()
{
if (m_punkCCW)
{
m_punkCCW->Release() ;
}
}

////////////////////////////////////////////////////////////////
//
//QueryInterface()
//
//delegating QI.
//
////
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
{
return m_pUnkOuter->QueryInterface(riid,ppv) ;
}

////////////////////////////////////////////////////////////////
//
//AddRef()
//
////
ULONG __stdcall AddRef(void)
{
return m_pUnkOuter->AddRef() ;
}

////////////////////////////////////////////////////////////////
//
//Release()
//
//delegating Release
//
////
ULONG __stdcall Release(void)
{
return m_pUnkOuter->Release() ;
}

////////////////////////////////////////////////////////////////
//
//MultParam()
//
//multiplies the two passed arguments and returns the
//value back in lVal.
//
//parameters:
//lMultmultiplier
//lValreturns value we are multiplied by
//
////
HRESULT __stdcall MultParam(long lMult, long *lVal)
{
*lVal = *lVal * lMult ;

return S_OK ;
}

////////////////////////////////////////////////////////////////
//
//Square()
//
//squares lVal, returning the result back in lResult.
//
//parameters:
//lValvalue to be squared
//lResult return value = lVal squared
//
////
HRESULT __stdcall Square(long lVal, long *lResult)
{
*lResult = lVal * lVal ;

return S_OK ;
}

////////////////////////////////////////////////////////////////
//
//GetClass()
//
//CoCreates a java-implemented object (CCW); returns the
//pointer into the v-table to the callee; exercises one
//method in the ccw.
//
//parameters:
//pjNatCompointer to our CCW.
//
////
HRESULT __stdcall GetClass(jINatCom **pjNatCom)
{
HRESULT hr ;

if (FAILED(hr = CoCreateInstance(CLSID_jCNatCom, NULL, CLSCTX_SERVER, IID_IUnknown, (void **)&m_punkCCW)))
{
return hr ;
}

if (FAILED(hr = m_punkCCW->QueryInterface(IID_jINatCom, (void **)&m_pNatCom)))
{
m_punkCCW->Release() ;// IID_IUnknown
m_punkCCW = NULL ;

return hr ;
}

// per the rules of COM we leave the refcount at 2, since
//  we're passing the pointer back to the caller.  Caller
//  must Release() the object.

// set member variable
*pjNatCom = m_pNatCom ;

// invoke java-implemented method
m_pNatCom->ccwHelloWorld() ;

return S_OK ;
}

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

private :

ULONGm_cRef ;// ref count
IUnknown*m_pUnkOuter ;// controlling unknown
jINatCom*m_pNatCom ;// ccw interface pointer
IUnknown*m_punkCCW ;// IUnknown of our CCW
} ;

CImpINatCom m_CImpINatCom ;

// PUBLIC --------------------------------------------------------------

public :

////////////////////////////////////////////////////////////////////////
//
//CNatCom()
//
//constructor;  initializes variables, including those of
//CImpINatCom to point to controlling unknown.
//
////
CNatCom(IUnknown *punkOuter)
{
ObjectCreated() ;

m_cRef = 0 ;

// if this is non-null, we're being aggregated
if (punkOuter)
{
m_pUnkOuter = punkOuter ;
m_CImpINatCom.m_pUnkOuter = punkOuter ;
}
else
{
m_pUnkOuter = this ;
m_CImpINatCom.m_pUnkOuter = this ;
}
}

////////////////////////////////////////////////////////////////////////
//
//~CNatCom()
//
//destructor.
//
////
~CNatCom()
{
ObjectDestroyed() ;
}

////////////////////////////////////////////////////////////////////////
//
//QueryInterface()
//
//non-delegating QI
//
////
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
{
*ppv = NULL ;

if (riid == IID_IUnknown)
{
*ppv = this ;
}

if (riid == IID_INatCom)
{
*ppv = &m_CImpINatCom ;
}

if (*ppv == NULL)
{
return E_NOINTERFACE ;
}

((IUnknown *) *ppv)->AddRef() ;

return NOERROR ;
}

////////////////////////////////////////////////////////////////////////
//
//AddRef()
//
//non-delegating AddRef()
//
////
ULONG __stdcall AddRef(void)
{
InterlockedIncrement( (LONG*)&m_cRef ) ;

return m_cRef ;
}

////////////////////////////////////////////////////////////////////////
//
//Release()
//
//non-delegating Release
//
////
ULONG __stdcall Release(void)
{
if (!InterlockedDecrement( (LONG*)&m_cRef ))
{
delete this ;
return 0 ;
}

return m_cRef ;
}

// PRIVATE -------------------------------------------------------------

private :

ULONGm_cRef ;// ref count
IUnknown*m_pUnkOuter ;// controlling unknown
} ;

// CLASS FACTORY --------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
//
//CNatComCF
//
//class factory for CNatCom
//
////
class CNatComCF : public IClassFactory
{
public :

////////////////////////////////////////////////////////////////////////
//
//CNatComCF()
//
//constructor
//
////
CNatComCF()
{
m_cRef = 0 ;
}

///////////////////////////////////////////////////////////////////////
//
//CreateInstance()
//
//IClassFactory CreateInstance() method implementation
//
////
HRESULT __stdcall CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
CNatCom *cmg ;
HRESULT hr ;

*ppv = NULL ;

if ((punkOuter) && (riid != IID_IUnknown))
{
return CLASS_E_NOAGGREGATION ;
}

cmg = new CNatCom(punkOuter) ;

hr = cmg->QueryInterface(riid, ppv) ;

if (FAILED(hr))
{
delete cmg ;
return hr ;
}

return S_OK ;
}

////////////////////////////////////////////////////////////////////////
//
//QueryInterface()
//
//IUnknown QueryInterface() method implementation
//
////
HRESULT __stdcall QueryInterface(REFIID iid, void **ppv)
{
*ppv = NULL ;

if (iid == IID_IUnknown || iid == IID_IClassFactory)
*ppv = this ;
else
return E_NOINTERFACE ;

AddRef() ;

return S_OK ;
}

////////////////////////////////////////////////////////////////////////
//
//AddRef()
//
//IUnknown AddRef() method implementation
//
////
ULONG __stdcall AddRef(void)
{
return ++m_cRef ;
}

////////////////////////////////////////////////////////////////////////
//
//Release()
//
//IUnknown Release() method implementation
//
////
ULONG __stdcall Release(void)
{
if (--m_cRef == 0)
{
delete this ;
return 0 ;
}

return m_cRef ;
}

////////////////////////////////////////////////////////////////////////
//
//LockServer()
//
//IClassFactory LockServer() method implementation
//
////
HRESULT __stdcall LockServer(BOOL bLock)
{
bLock ? ObjectCreated() : ObjectDestroyed() ;

return S_OK ;
}

// PRIVATE -------------------------------------------------------------

private :

ULONGm_cRef ;// refcount
} ;

////////////////////////////////////////////////////////////////////////////////
//
//DllMain()
//
//entry point
//
////
BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
  case DLL_PROCESS_ATTACH :
g_hDllMain = hDLLInst ;
}

return TRUE ;
}

// -----------------------------------------------------------------------------

TCHAR achSampleDesc[]= "CNatCom";
TCHAR achInprocServer32[]= "InprocServer32";
TCHAR achSampleProgID[] = "CNatCom";
TCHAR achProgID[]= "CNatCom";
TCHAR achThreadingModel[]= "ThreadingModel";
TCHAR achFree[] = "Both";

#define GUIDSTR_MAX(1+ 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1)
static const CHAR szDigits[]= "0123456789ABCDEF";
static const BYTE GuidMap[]= { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
    8, 9, '-', 10, 11, 12, 13, 14, 15 };

////////////////////////////////////////////////////////////////////////////////
//
//GUID2StringA()
//
//converts GUID to string form
//
////
VOID GUID2StringA(REFGUID rguid, LPSTR lpsz)
{
inti;
LPSTRp = lpsz;

const BYTE * pBytes = (const BYTE *) &rguid;

*p++ = '{';

for (i = 0; i < sizeof(GuidMap); i++)
{
if (GuidMap[i] == '-')
{
*p++ = '-';
}
else
{
*p++ = szDigits[ (pBytes[GuidMap[i]] & 0xF0) >> 4 ];
*p++ = szDigits[ (pBytes[GuidMap[i]] & 0x0F) ];
}
}

*p++ = '}';
*p   = '\0';
}

////////////////////////////////////////////////////////////////////////////////
//
//NTCompatibleRegDeleteKey()
//
//checks that registry key does not have any subkeys prior to deletion
//
////
LONG NTCompatibleRegDeleteKey(HKEY hKey, LPCTSTR szSubKey)
{
TCHAR achName[MAX_PATH+1];
HKEY  hSubkey;

if (ERROR_SUCCESS != RegOpenKey(hKey, szSubKey, &hSubkey))
return REGDB_E_INVALIDVALUE;

if (ERROR_SUCCESS == RegEnumKey(hSubkey, 0, achName, sizeof(achName)/sizeof(TCHAR)))
{
RegCloseKey(hSubkey);
// There's still one subkey: fail the call.
return REGDB_E_INVALIDVALUE;
}

RegCloseKey(hSubkey);
return RegDeleteKey(hKey, szSubKey);
}

////////////////////////////////////////////////////////////////////////////////
//
//DllRegisterServer()
//
//registers this COM server via the registry.
//
////
STDAPI DllRegisterServer(VOID)
{
HKEYhKey  = NULL;
HKEYhKey2  = NULL;
HKEYhKey3  = NULL;
DWORDresult;
HRESULT hr = SELFREG_E_CLASS;
CHARachCLSID[GUIDSTR_MAX];
CHARachLIBID[GUIDSTR_MAX] ;
TCHARachModulePathName[MAX_PATH];
TCHARachCurrentDirectory[MAX_PATH] ;

// CLSID

GUID2StringA(CLSID_CNatCom, achCLSID) ;

// If we fail in the middle, the state of the registry entries
// is indeterminate (as per Ole specs.)

// Create HKEY_CLASSES_ROOT\progid\CLSID
result = RegCreateKey(HKEY_CLASSES_ROOT, achSampleProgID, &hKey);
if (result != ERROR_SUCCESS) goto lExit;

result = RegSetValue(hKey, NULL, REG_SZ, achSampleDesc, lstrlen(achSampleDesc));
if (result != ERROR_SUCCESS) goto lExit;

result = RegCreateKey(hKey, TEXT("CLSID"), &hKey2);
if (result != ERROR_SUCCESS) goto lExit;

result = RegSetValue(hKey2, NULL, REG_SZ, achCLSID, GUIDSTR_MAX-1);
if (result != ERROR_SUCCESS) goto lExit;

RegCloseKey(hKey);
RegCloseKey(hKey2);
hKey = NULL;
hKey2 = NULL;

// Create HKEY_CLASSES_ROOT\CLSID\...

// create CLSID key
result = RegCreateKey(HKEY_CLASSES_ROOT, TEXT("CLSID"), &hKey);
if (result != ERROR_SUCCESS) goto lExit ;

// create CLSID/GUID key
result = RegCreateKey(hKey, achCLSID, &hKey2);
if (result != ERROR_SUCCESS) goto lExit ;

// put in sample description value into CLSID\GUID key
result = RegSetValue(hKey2, NULL, REG_SZ, achSampleDesc, lstrlen(achSampleDesc));
if (result != ERROR_SUCCESS) goto lExit ;

// get our path ..
result = GetModuleFileName(g_hDllMain, achModulePathName, sizeof(achModulePathName)/sizeof(TCHAR));
if (result == 0) goto lExit ;

// create subkey under CLSID\GUID
result = RegCreateKey(hKey2, achInprocServer32, &hKey3);
if (result != ERROR_SUCCESS) goto lExit ;

// set key value to the path obtained above
result = RegSetValue(hKey3, NULL, REG_SZ, achModulePathName, lstrlen(achModulePathName));
if (result != ERROR_SUCCESS) goto lExit ;

// both
result = RegSetValueEx(hKey3, achThreadingModel, 0, REG_SZ, (BYTE*)achFree, sizeof(achFree));
if (result != ERROR_SUCCESS) goto lExit;

RegCloseKey(hKey3);
hKey3 = NULL;

// PROGID

result = RegCreateKey(hKey2, achProgID, &hKey3);
if (result != ERROR_SUCCESS) goto lExit;

result = RegSetValue(hKey3, NULL, REG_SZ, achSampleProgID, lstrlen(achSampleProgID));
if (result != ERROR_SUCCESS) goto lExit;

RegCloseKey(hKey3);
hKey3 = NULL;

hr = S_OK ;

 lExit :

// close up
if (hKey) RegCloseKey(hKey);
if (hKey2) RegCloseKey(hKey2);
if (hKey3) RegCloseKey(hKey3);

return hr ;
}

////////////////////////////////////////////////////////////////////////////////
//
//DllUnregisterServer()
//
//removes our registry entries (entered via DllRegisterServer())
//
////
STDAPI DllUnregisterServer(VOID)
{
HKEYhKey  = NULL;
HKEYhKey2 = NULL;
DWORDresult;
HRESULT hr = SELFREG_E_CLASS;
CHARachCLSID[GUIDSTR_MAX];
CHARachLIBID[GUIDSTR_MAX];

// If we fail in the middle, the state of the registry entries
// is indeterminate (as per Ole specs.)
GUID2StringA(CLSID_CNatCom, achCLSID);

result = RegOpenKey(HKEY_CLASSES_ROOT, achSampleProgID, &hKey);
if (result == ERROR_SUCCESS)
{
NTCompatibleRegDeleteKey(hKey, TEXT("CLSID"));
RegCloseKey(hKey);
hKey = NULL;
NTCompatibleRegDeleteKey(HKEY_CLASSES_ROOT, achSampleProgID);
}

result = RegOpenKey(HKEY_CLASSES_ROOT, TEXT("CLSID"), &hKey);
result = RegOpenKey(hKey, achCLSID, &hKey2);

if (result == ERROR_SUCCESS)
{
NTCompatibleRegDeleteKey(hKey2, achInprocServer32);
NTCompatibleRegDeleteKey(hKey2, achProgID);
RegCloseKey(hKey2);
hKey2 = NULL;
NTCompatibleRegDeleteKey(hKey, achCLSID);
}

// If this fails, it means somebody else added a subkey to this tree.
// We're not allowed to touch it so ignore the failure.

RegCloseKey(hKey);
hKey = NULL;

hr = S_OK;

return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
//DllGetClassObject()
//
//COM entry point
//
////
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, VOID **ppv)
{
HRESULT hr ;
class CNatComCF *pcf;

*ppv = NULL ;

if (rclsid != CLSID_CNatCom)
{
return CLASS_E_CLASSNOTAVAILABLE;
}

pcf = new CNatComCF() ;

if (!pcf)
{
return CLASS_E_CLASSNOTAVAILABLE;
}

hr = pcf->QueryInterface(riid, ppv) ;

if (FAILED(hr))
{
delete pcf ;
return hr ;
}

return S_OK ;
}

////////////////////////////////////////////////////////////////////////////////
//
//DllCanUnloadNow()
//
//returns refcount of objects which are using this .dll
//
////
STDAPI DllCanUnloadNow()
{
return g_cObject ;
}


NATCOM.H

/*

Copyright (c) 1996-1997  Microsoft Corporation.  All rights reserved.

*/

#include <objbase.h>
#include <initguid.h>
#include "jnatcom.h"
#include "common.h"

extern "C" STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv) ;
extern "C" STDAPI DllRegisterServer(VOID) ;
extern "C" STDAPI DllUnregisterServer(VOID) ;
extern "C" STDAPI DllCanUnloadNow(void) ;


NATLIB.C

/*

Copyright (c) 1996-1997  Microsoft Corporation

*/

#include <windows.h>
#include <native.h>
#include "JMain.h"
#include "JFoo.h"
#include "JThread1.h"
#include "JThread2.h"

////////////////////////////////////////////////////////////////////////////////
//
//RNIGetCompatibleVersion
//
//Called by the virtual machine to determine what level of RNI compatibility
//this DLL offers.
//
////
DWORD __cdecl RNIGetCompatibleVersion()
{
return RNIVER;
}

////////////////////////////////////////////////////////////////////////////////
//
//JMain_Square()
//
//returns the square of its parameter;  simple callout from java to
//native code to perform this operation.
//
////
__declspec(dllexport) long __cdecl JMain_Square(struct HJMain *pThis, long lVal)
{
return lVal * lVal ;
}

////////////////////////////////////////////////////////////////////////////////
//
//JMain_DoubleInstanceVar()
//
//executes a java-implemented method to retrieve the value of an
//instance variable;  returns twice the retrieved value.
//
////
__declspec(dllexport) void __cdecl JMain_DoubleInstanceVar(struct HJMain *pThis)
{
long lVal ;

lVal = execute_java_dynamic_method(0, (HObject*) pThis, "GetiVar1", "()I") ;

pThis->iVar1 = lVal << 1 ;
}

////////////////////////////////////////////////////////////////////////////////
//
//Jmain_showMemberArray()
//
//executes a class method which returns a private member array of
//ints (java); loops through and displays the array members.
//
////
__declspec(dllexport) void __cdecl JMain_showMemberArray(struct HJMain *pThis)
{
ClassArrayOfInt *paiVar1 ;
long*plVar2 ;
intcIndex ;

// get a pointer to an instance array of int, and display it.
paiVar1 = (ClassArrayOfInt *) execute_java_dynamic_method(0, (HObject *) pThis, "GetiCVar2", "()[I") ;
plVar2 = paiVar1->body ;

for (cIndex = 0; cIndex < paiVar1->length; cIndex++)
{
printf("%5d", *plVar2++) ;
}
printf("\n") ;
}

////////////////////////////////////////////////////////////////////////////////
//
//JMain_Various()
//
//finds JFoo class; executes a static method in that class
//
////
__declspec(dllexport) void __cdecl JMain_Various(struct HJMain *pThis)
{
_int64 i64Val = 10 ;
ClassClass *pcls = FindClass(NULL, "JFoo", TRUE) ;

if (pcls)
{
// and exec the static method
execute_java_static_method(NULL, pcls, "StaticMethod", "(J)V", i64Val) ;
}
}

////////////////////////////////////////////////////////////////////////////////
//
//w32CreateEvent()
//
//wrapper for win32 API CreateEvent()
//
////
__declspec(dllexport) long __cdecl JMain_w32CreateEvent(struct HJMain *pThis, long bInit)
{
BOOLbInitialState ;
HANDLEhEvent ;

// determine if initial state is signaled or non-signaled.
if (bInit)
{
bInitialState = TRUE ;
}
else
{
bInitialState = FALSE ;
}

// create Event object and return handle
return (long) CreateEvent(NULL, FALSE, bInitialState, NULL) ;
}

////////////////////////////////////////////////////////////////////////////////
//
//w32PulseEvent()
//
//wrapper for win32 API PulseEvent()
//
////
__declspec(dllexport) void __cdecl JThread1_w32PulseEvent(struct HJThread1 *pThis,long hEvent)
{
PulseEvent((HANDLE) hEvent) ;
}

////////////////////////////////////////////////////////////////////////////////
//
//w32CloseHandle()
//
//wrapper for win32 API CloseHandle()
//
////
__declspec(dllexport) long __cdecl JThread2_w32CloseHandle(struct HJThread2 *pThis, long hObject)
{
return (long) CloseHandle((HANDLE) hObject) ;
}

////////////////////////////////////////////////////////////////////////////////
//
//GCSafeNative()
//
//method demonstrates GC safe way of performing GC-sensitive operations
//in native code.
//
////
__declspec(dllexport) void __cdecl JThread2_GCSafeNative(struct HJThread2 *pThis, long hEvent)
{
struct
{
HJFoo *pJFoo1, *pJFoo2 ;
} gcSafe ;// struct holds out pointers to JObjects
GCFrame gcf ;

// GCFramePush() creates storage for GC-sensitive data (JObjects in this case)
GCFramePush(&gcf, &gcSafe, sizeof(gcSafe)) ;

// set our member variables to point to two JObjects
gcSafe.pJFoo1 = (HJFoo *) execute_java_constructor(0, "JFoo", 0, "(I)", 10) ;
gcSafe.pJFoo2 = (HJFoo *) execute_java_constructor(0, "JFoo", 0, "()") ;

// call GCEnable() - we don't know how long we might be blocked
GCEnable() ;

// block
WaitForSingleObject((HANDLE) hEvent, INFINITE) ;

// display instance variable values for above-created JObjects.
printf("gcSafe.pJFoo1->m_iVal = %d\tgcSafe.pJFoo2->m_iVal = %d\n", gcSafe.pJFoo1->m_iVal, gcSafe.pJFoo2->m_iVal) ;

// disable GC and pop our storage
GCDisable() ;
GCFramePop(&gcf) ;
}


COMSAMP.CPP

#define INITGUID 1

#include <windows.h>
#include <olectl.h>

#include "comsamp.h"

TCHAR achSampleProgID[]        = "comntv";
TCHAR achInprocServer32[]      = "InprocServer32";
TCHAR achProgID[]              = "ProgID";
TCHAR achThreadingModel[]      = "ThreadingModel";
TCHAR achBoth[]                = "Both";

DWORD g_ObjectCount = 0;
HINSTANCE ghInstance = 0;

VOID ObjectCreated(VOID)
{
    InterlockedIncrement( (LONG*)&g_ObjectCount );
}

VOID ObjectDestroyed(VOID)
{
    InterlockedDecrement( (LONG*)&g_ObjectCount );
}

HRESULT CheckJavaWriteAccess (BSTR pwszFilename)
{
    // If there is no vm in the process, then we could not have
    // been called directly from java code, and remote calls are
    // implicitly trusted.
    if (GetModuleHandle("MSJAVA") == NULL)
        return S_OK;

    HRESULT hr;

    IMyCOMObjectSecurity *psecurity;

    hr = CoCreateInstance(
            CLSID_CMyCOMObjectSecurity,
            NULL,
            CLSCTX_ALL,
            IID_IMyCOMObjectSecurity,
            (PVOID*)&psecurity
            );
    if (SUCCEEDED(hr))
    {
        hr = psecurity->CheckRead(pwszFilename);
        psecurity->Release();

        // The VM does not convert SecurityException to a useful HRESULT, so do it here.
        if (hr == E_FAIL)
            hr = HRESULT_FROM_WIN32(ERROR_PRIVILEGE_NOT_HELD);
    }

    return hr;
}

class CMyCOMObject : public IMyInterface
{
    ULONG refcount;

public:

    CMyCOMObject ()
    {
        refcount = 1;
    }

    // IUnknown methods

    HRESULT STDMETHODCALLTYPE QueryInterface (REFIID riid, LPVOID* ppv)
    {
        *ppv = NULL;

        if (riid == IID_IUnknown)
        {
            *ppv = (IUnknown*)this;
        }
        else if (riid == IID_IMyInterface)
        {
            *ppv = (IMyInterface*)this;
        }

        if (*ppv)
        {
            ((IUnknown*)(*ppv))->AddRef();
            return S_OK;
        }

        return E_NOINTERFACE;
    }

    ULONG STDMETHODCALLTYPE AddRef ()
    {
        InterlockedIncrement((LONG*)&refcount);
        return 0xcccccccc;
    }

    ULONG STDMETHODCALLTYPE Release ()
    {
        if (!InterlockedDecrement((LONG*)&refcount))
        {
            delete this;
            return 0;
        }

        return 0xcccccccc;
    }

    // IMyInterface methods

    HRESULT STDMETHODCALLTYPE COMCheckedLoad (BSTR pwszFilename, BYTE buffer[], int *bufsize)
    {
        HRESULT hr = CheckJavaWriteAccess(pwszFilename);
        if (SUCCEEDED(hr))
        {
            hr = JavaCheckedLoad(pwszFilename, buffer, bufsize);
        }

        return hr;
    }

    HRESULT STDMETHODCALLTYPE JavaCheckedLoad (BSTR pwszFilename, BYTE buffer[], int *bufsize)
    {
        if (pwszFilename == NULL || buffer == NULL || bufsize == NULL)
            return E_POINTER;

        if (*bufsize < 0)
            return E_INVALIDARG;

        HRESULT hr = E_FAIL;

        CHAR filename[MAX_PATH];
        int filenamelen = WideCharToMultiByte(
                CP_ACP,
                0,
                pwszFilename,
                -1,
                filename,
                sizeof(filename)-1,
                NULL,
                NULL);
        if (filenamelen != 0)
        {
            filename[filenamelen] = '\0';

            HANDLE file = CreateFile(
                    filename,
                    GENERIC_READ,
                    FILE_SHARE_READ,
                    NULL,
                    OPEN_EXISTING,
                    FILE_FLAG_SEQUENTIAL_SCAN,
                    NULL
                    );
            if (file != INVALID_HANDLE_VALUE)
            {
                DWORD size = GetFileSize(file, NULL);
                if (size != 0xffffffff)
                {
                    DWORD toread = size;
                    if (toread > *bufsize)
                        toread = *bufsize;

                    DWORD read;
                    if (ReadFile(
                            file,
                            buffer,
                            toread,
                            &read,
                            NULL
                            )
                          && read == size
                          )
                    {
                        *bufsize = read;
                        hr = S_OK;
                    }
                }

                CloseHandle(file);
            }
        }

        if (hr == E_FAIL)
            hr = HRESULT_FROM_WIN32(GetLastError());

        return hr;
    }
};

class CMyComObjectClassFactory : public IClassFactory
{
public:

    CMyComObjectClassFactory ()
    {
        ObjectCreated();
    }

    ~CMyComObjectClassFactory ()
    {
        ObjectDestroyed();
    }

    // IUnknown methods

    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppv)
    {
        *ppv = NULL;
        if (riid == IID_IUnknown)
        {
            *ppv = (IUnknown*)this;
        }
        else if (riid == IID_IClassFactory)
        {
            *ppv = (IClassFactory*)this;
        }
        if (*ppv)
        {
            ((IUnknown*)(*ppv))->AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;

    }

    ULONG STDMETHODCALLTYPE AddRef ()
    {
        return 0xcccccccc;
    }

    ULONG STDMETHODCALLTYPE Release ()
    {
        return 0xcccccccc;
    }

    // IClassFactory methods

    HRESULT STDMETHODCALLTYPE CreateInstance (IUnknown *punkOuter, REFIID riid, VOID **ppv)
    {
        HRESULT hr;
        *ppv = NULL;

        if (punkOuter)
        {
            return CLASS_E_NOAGGREGATION;
        }

        CMyCOMObject *obj = new CMyCOMObject();
        if (obj == NULL)
        {
            return E_OUTOFMEMORY;
        }

        hr = obj->QueryInterface(riid, ppv);
        obj->Release();
        return hr;
    }

    HRESULT STDMETHODCALLTYPE LockServer (BOOL fLock)
    {
        if (fLock)
            ObjectCreated();
        else
            ObjectDestroyed();
        return S_OK;
    }
};

CMyComObjectClassFactory g_CMyCOMObjectFactory;

extern "C"
BOOL WINAPI DllMain (HINSTANCE hmod, DWORD dwReason, PVOID pvReserved)
{
    ghInstance = hmod;
    return TRUE;
}

STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
    *ppv = NULL;
    if (rclsid == CLSID_CMyCOMObject)
    {
        return g_CMyCOMObjectFactory.QueryInterface(riid, ppv);
    }
    else
    {
        return CLASS_E_CLASSNOTAVAILABLE;
    }
}

STDAPI DllCanUnloadNow ()
{
    return g_ObjectCount;
}

// Plagiarized from comsamp.

#define GUIDSTR_MAX (1+ 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1)

static const CHAR szDigits[] = "0123456789ABCDEF";
static const BYTE GuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
                                8, 9, '-', 10, 11, 12, 13, 14, 15 };

//--------------------------------------------------------------------------
//
//  Function:   GUID2StringA
//
//  Synopsis:   Convert GUID to string form
//
//  Arguments:  [rguid] - the guid to convert
//              [lpszy] - buffer to hold the results
//
//  Returns:    nothing
//
//  This code is massively plagiarized from the Ole sources.
//--------------------------------------------------------------------------

VOID
GUID2StringA(REFGUID rguid, LPSTR lpsz)
{
    int i;
    LPSTR p = lpsz;

    const BYTE * pBytes = (const BYTE *) &rguid;

    *p++ = '{';

    for (i = 0; i < sizeof(GuidMap); i++)
    {
        if (GuidMap[i] == '-')
        {
            *p++ = '-';
        }
        else
        {
            *p++ = szDigits[ (pBytes[GuidMap[i]] & 0xF0) >> 4 ];
            *p++ = szDigits[ (pBytes[GuidMap[i]] & 0x0F) ];
        }
    }
    *p++ = '}';
    *p   = '\0';
}

//--------------------------------------------------------------------------
//
//  NTCompatibleRegDeleteKey
//
//--------------------------------------------------------------------------
LONG
NTCompatibleRegDeleteKey(HKEY hKey, LPCTSTR szSubKey)
{
   TCHAR achName[MAX_PATH+1];
   HKEY  hSubkey;

   if (ERROR_SUCCESS != RegOpenKey(hKey, szSubKey, &hSubkey)) {
      return REGDB_E_INVALIDVALUE;
   }

   if (ERROR_SUCCESS == RegEnumKey(hSubkey, 0, achName, sizeof(achName)/sizeof(TCHAR))) {
      RegCloseKey(hSubkey);
      // There's still one subkey: fail the call.
      return REGDB_E_INVALIDVALUE;
   }
   RegCloseKey(hSubkey);
   return RegDeleteKey(hKey, szSubKey);
}

STDAPI DllRegisterServer ()
{
   HKEY    hKey  = NULL;
   HKEY    hKey2 = NULL;
   HKEY    hKey3 = NULL;
   DWORD   result;
   HRESULT hr = SELFREG_E_CLASS;
   CHAR    achCLSID[GUIDSTR_MAX];
   TCHAR   achModulePathName[MAX_PATH];

   // If we fail in the middle, the state of the registry entries
   // is indeterminate (as per Ole specs.)

   // Create HKEY_CLASSES_ROOT\progid\CLSID
   result = RegCreateKey(HKEY_CLASSES_ROOT, achSampleProgID, &hKey);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   result = RegCreateKey(hKey, TEXT("CLSID"), &hKey2);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   GUID2StringA(CLSID_CMyCOMObject, achCLSID);
   result = RegSetValue(hKey2, NULL, REG_SZ, achCLSID, GUIDSTR_MAX-1);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   RegCloseKey(hKey);
   RegCloseKey(hKey2);
   hKey = NULL;
   hKey2 = NULL;

   // Create HKEY_CLASSES_ROOT\CLSID\...
   result = RegCreateKey(HKEY_CLASSES_ROOT, TEXT("CLSID"), &hKey);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegCreateKey(hKey, achCLSID, &hKey2);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegCreateKey(hKey2, achInprocServer32, &hKey3);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = GetModuleFileName(ghInstance, achModulePathName, sizeof(achModulePathName)/sizeof(TCHAR));
   if (result == 0) {  //No way to detect truncation from GetModuleFileName.
      goto lExit;
   }

   result = RegSetValue(hKey3, NULL, REG_SZ, achModulePathName, lstrlen(achModulePathName));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   result = RegSetValueEx(hKey3, achThreadingModel, 0, REG_SZ, (BYTE*)achBoth, sizeof(achBoth));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }

   RegCloseKey(hKey3);
   hKey3 = NULL;

   result = RegCreateKey(hKey2, achProgID, &hKey3);
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   result = RegSetValue(hKey3, NULL, REG_SZ, achSampleProgID, lstrlen(achSampleProgID));
   if (result != ERROR_SUCCESS) {
      goto lExit;
   }
   RegCloseKey(hKey3);
   hKey3 = NULL;

   hr = S_OK;

 lExit:
   if (hKey) {
      RegCloseKey(hKey);
   }
   if (hKey2) {
      RegCloseKey(hKey2);
   }
   if (hKey3) {
      RegCloseKey(hKey3);
   }
   return hr;

}

//----------------------------------------------------------------------
// DllUnregisterServer(): Called by OLE.
//----------------------------------------------------------------------
STDAPI DllUnregisterServer(VOID)
{
   HKEY    hKey  = NULL;
   HKEY    hKey2 = NULL;
   DWORD   result;
   HRESULT hr = SELFREG_E_CLASS;
   CHAR    achCLSID[GUIDSTR_MAX];

   // If we fail in the middle, the state of the registry entries
   // is indeterminate (as per Ole specs.)
   GUID2StringA(CLSID_CMyCOMObject, achCLSID);

   result = RegOpenKey(HKEY_CLASSES_ROOT, achSampleProgID, &hKey);
   if (result == ERROR_SUCCESS) {
      NTCompatibleRegDeleteKey(hKey, TEXT("CLSID"));
      RegCloseKey(hKey);
      hKey = NULL;
      NTCompatibleRegDeleteKey(HKEY_CLASSES_ROOT, achSampleProgID);
   }
   // If this fails, it means somebody else added a subkey to this tree.
   // We're not allowed to touch it so ignore the failure.

   result = RegOpenKey(HKEY_CLASSES_ROOT, TEXT("CLSID"), &hKey);
   if (result == ERROR_SUCCESS) {

      result = RegOpenKey(hKey, achCLSID, &hKey2);
      if (result == ERROR_SUCCESS) {
         NTCompatibleRegDeleteKey(hKey2, achInprocServer32);
         NTCompatibleRegDeleteKey(hKey2, achProgID);
         RegCloseKey(hKey2);
         hKey2 = NULL;
         NTCompatibleRegDeleteKey(hKey, achCLSID);
      }

      // If this fails, it means somebody else added a subkey to this tree.
      // We're not allowed to touch it so ignore the failure.

      RegCloseKey(hKey);
      hKey = NULL;
   }

   hr = S_OK;

   return hr;

}


COMSAMP.IDL

// comntv.idl
//
// Created 09/08/97
//
// (C)Copyright 1997 Microsoft Corporation, All rights reserved.
//

cpp_quote("EXTERN_C const CLSID CLSID_CMyCOMObject;")
cpp_quote("EXTERN_C const CLSID CLSID_CMyCOMObjectSecurity;")

[
    uuid(8EACC4B0-289A-11d1-BA1D-006008039BF0),
]
library JavaSecuritySampleLibrary
{
    importlib("stdole32.tlb");

    [
        object,
        uuid(8EACC4B1-289A-11d1-BA1D-006008039BF0),
        pointer_default(unique),
    ]
    interface IMyInterface : IUnknown
    {
        // Checks FILEIO from COM.
        HRESULT COMCheckedLoad ([in] BSTR filename, [out] byte buffer[], [in,out] int *bufsize);

        // Checks FILEIO on Java side before calling through COM.
        HRESULT JavaCheckedLoad ([in] BSTR filename, [out] byte buffer[], [in,out] int *bufsize);
    }

    [
        uuid(8EACC4B2-289A-11d1-BA1D-006008039BF0),
    ]
    coclass CMyCOMObject
    {
        interface IMyInterface;
    }

    [
        object,
        uuid(8EACC4B3-289A-11d1-BA1D-006008039BF0),
        pointer_default(unique),
    ]
    interface IMyCOMObjectSecurity : IUnknown
    {
        HRESULT CheckRead ([in] BSTR filename);
    }

    [
        uuid(8EACC4B4-289A-11d1-BA1D-006008039BF0),
    ]
    coclass CMyCOMObjectSecurity
    {
        interface IMyCOMObjectSecurity;
    }
}


RNISAMP.CPP

// rnisamp.cpp
//
// Created 09/08/97
//
// (C)Copyright 1997 Microsoft Corporation, All rights reserved.
//

#include <windows.h>
#include <stdarg.h>
#include <native.h>

#include "rnisamp.h"

typedef struct Hjava_lang_SecurityManager HSecurity;

__declspec(dllexport)
DWORD __cdecl RNIGetCompatibleVersion()
{
    return RNIVER;
}

__declspec(dllexport)
struct HArrayOfByte * __cdecl rnisamp_MyRNIObject_RNICheckedLoad (
    struct Hrnisamp_MyRNIObject *_jthis,
    struct Hjava_lang_String *_jfilename
    )
{
    GCFrame gcf;
    struct {
        Hrnisamp_MyRNIObject *jthis;
        HString *jfilename;
    } gc;
    GCFramePush(&gcf, &gc, sizeof(gc));
    gc.jthis = _jthis;
    gc.jfilename = _jfilename;

    HSecurity *jsecurity = java_lang_System_getSecurityManager();
    if (jsecurity != NULL)
    {
        jsecurity->checkRead(gc.jfilename);
        if (exceptionOccurred(EE()))
        {
            GCFramePop(&gcf);
            return NULL;
        }
    }

    GCFramePop(&gcf);
    return rnisamp_MyRNIObject_JavaCheckedLoad0(gc.jthis, gc.jfilename);
}

__declspec(dllexport)
struct HArrayOfByte * __cdecl rnisamp_MyRNIObject_JavaCheckedLoad0 (
    struct Hrnisamp_MyRNIObject *_jthis,
    struct Hjava_lang_String *_jfilename
    )
{
    CHAR filename[MAX_PATH];

    if (_jfilename == NULL)
    {
        SignalError(EE(), JAVAPKG "NullPointerException", NULL);
        return NULL;
    }

    javaString2CString(_jfilename, filename, sizeof(filename));
    HArrayOfByte *jdata = NULL;
    BOOL outofmemory = FALSE;

    GCEnable();
    HANDLE file = CreateFile(
            filename,
            GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_SEQUENTIAL_SCAN,
            NULL
            );
    GCDisable();
    if (file != INVALID_HANDLE_VALUE)
    {
        DWORD size = GetFileSize(file, NULL);
        if (size != 0xffffffff)
        {
            jdata = (HArrayOfByte*)ArrayAlloc(T_BYTE, size);
            if (jdata != NULL)
            {
                HObject **jdatapin = GCNewPinnedHandle((HObject*)jdata);
                if (jdatapin != NULL)
                {
                    DWORD read;
                    if (!(ReadFile(
                            file,
                            &jdata->body[0],
                            jdata->length,
                            &read,
                            NULL
                            )
                          && read == size
                          ))
                    {
                        jdata = NULL;
                    }

                    GCFreePinnedHandle(jdatapin);
                }
                else
                {
                    outofmemory = TRUE;
                    jdata = NULL;
                }
            }
            else
            {
                outofmemory = TRUE;
            }
        }

        CloseHandle(file);
    }

    if (jdata != NULL)
        return jdata;

    if (outofmemory)
        SignalError(EE(), JAVAPKG "OutOfMemoryError", NULL);
    else
        SignalError(EE(), "java/io/IOException", filename);
    return NULL;
}


README.TXT

Microsoft  Agent supports the presentation of software agents as interactive personalities within the Microsoft Windows  interface. Microsoft  Agent comes with a cast of three characters: Merlin, Genie, and Robby. You can use these characters as interactive assistants to introduce, guide, entertain, or otherwise enhance Web pages or applications. Microsoft  Agent also includes tools for defining custom characters.

Each sample includes a Readme.txt file with further instructions for building and running the sample. The instructions assume that you are using Microsoft Visual Basic , Microsoft Visual C++ , or Microsoft Visual J++, as appropriate.

VB, C++, Java Samples:

<sdkdir>\Samples\Internet\MSAgent\VB\Hello\
Illustrates using Microsoft Agent from Visual Basic.

<sdkdir>\Samples\Internet\MSAgent\C\Hello\
Illustrates using Microsoft Agent from C++.

<sdkdir>\Samples\Internet\MSAgent\C\Hello2\
Illustrates Hello World with AgentNotifySink. Demonstrates how to receive Agent events in C++.

<sdkdir>\Samples\Internet\MSAgent\C\Hello3\
Illustrates Hello World with AgentNotifySink and two characters. Demonstrates how to use Agent events to coordinate multiple characters in C++.

<sdkdir>\Samples\Internet\MSAgent\Java\Hello\
Illustrates using Microsoft Agent from a Java applet.

<sdkdir>\Samples\Internet\MSAgent\Java\Hello2\
Illustrates Hello World with AgentNotifySink. Demonstrates how to receive Agent events in Java.

<sdkdir>\Samples\Internet\MSAgent\Java\Hello3\
Illustrates Hello World with AgentNotifySink and two characters. Demonstrates how to use Agent events to coordinate multiple characters in Java.

HTML Samples:

These HTML pages demonstrate how to use Microsoft Agent characters from VBScript and JScript. Each sample will attempt to install its required components if they are not already installed. These samples download their character animations from http://activex.microsoft.com/characters/, so you must be connected to the internet.

<sdkdir>\Samples\Internet\MSAgent\HTML\Hello.htm
Illustrates the most basic use of Microsoft Agent from VBScript.
Requires: Agent Core Components, Text-To-Speech Engine.

<sdkdir>\Samples\Internet\MSAgent\HTML\Hello2.htm
Illustrates the most basic use of Microsoft Agent from JScript.
Requires: Agent Core Components, Text-To-Speech Engine.

<sdkdir>\Samples\Internet\MSAgent\HTML\Hello3.htm
A revised version of the Hello World sample that uses Request objects and the RequestComplete event to manage errors and report progress.
Requires: Agent Core Components, Text-To-Speech Engine.

<sdkdir>\Samples\Internet\MSAgent\HTML\Goodbye.htm
Demonstrates the basics of scripting a Microsoft Agent character to respond to voice commands on a web page.
Requires: Agent Core Components, Text-To-Speech Engine, Command and Control Engine.


READMEC.TXT

How to Install a C++ Sample for Microsoft Agent
-----------------------------------------------

*** Perform the following steps once for all samples ***

1. Install the Microsoft Agent core control. To run samples which produce
speech output, install the TruVoice text-to-speech engine.  To run samples
which take speech input, install the Microsoft Command and Control speech
engine.

2. Download the character animation files for the three standard
characters: Genie.acs, Merlin.acs and Robby.acs.  Create a directory
/Program Files/Microsoft Agent/Characters/ and put the three character
files in it.  (You may select a different location for these files, but
then you will need to replace the path string within each sample to
refer to the new location.)

*** Perform the following steps once for all C++ samples ***

1. Download the required include files.  Create a local directory for
these files.  (For instance, /Program Files/Micrsoft Agent/Include/)
Unzip the files and put them in the local directory you created.

2. Start up Microsoft Visual C++.  From the Tools menu, select Options,
then click on the Directories tab.  Type the complete path to the local
directory you created in step 2 into the list of include directories.
Click OK.

*** Perform the following steps for each C++ sample ***

1. Download the sample.  Create a local directory for the sample.  (E.g.
/Program Files/DevStudio/MyProjects/AgentSample/)  Unzip the samples files
and put them into the directory you created.

2. Start up Microsoft Visual C++.  From the File menu, select Open Workspace.
Navigate to the folder you created in step 1.  Select the workspace file (it
has a .dsw file extension) for this sample.  Click Open.

3. From the Build menu, select Execute.  When prompted to build the exe file,
click Yes.


HELLO.CPP

#ifndef STRICT
#define STRICT
#endif

//==========================================================================
//
//  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.
//
//  Copyright (C) 1997 Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------
//
// This sample demonstrates the simplest Microsoft Agent application
//
//==========================================================================

#include <windows.h>
#include <tchar.h>
#include "AgtSvr.h"
#include "AgtSvr_i.c"

static const LPWSTR kpwszCharacter = L"\\program files\\microsoft agent\\characters\\genie.acs";
static const LPTSTR kpszAppTitle = _T("Microsoft Agent Samples");

// This function checks to see if the version of Microsoft Agent
// installed on the user's system is compatible with the version
// we compiled with, i.e. it's version is greater than or equal to
// constants defined in AgtSvr.h.  Microsoft Agent guarantees
// backward compatibility.

BOOL IsValidAgentVersion(IAgent *pAgent) {

HRESULT hRes;
IDispatch *pdAgent = NULL;
ITypeInfo *pTypeInfo = NULL;
ITypeLib *pTypeLib = NULL;
TLIBATTR *pTypeLibAttr = NULL;
BOOL bValid = FALSE;
UINT uiIndex;

__try {

// Query for IDispatch

hRes = pAgent->QueryInterface(IID_IDispatch, (LPVOID *)&pdAgent);

if (FAILED(hRes))
__leave;

// Get the TypeInfo

hRes = pdAgent->GetTypeInfo(0, 0, &pTypeInfo);

if (FAILED(hRes))
__leave;

// Get it's containing TypeLib

hRes = pTypeInfo->GetContainingTypeLib(&pTypeLib, &uiIndex);

if (FAILED(hRes))
__leave;

// Get the attributes of the TypeLib

hRes = pTypeLib->GetLibAttr(&pTypeLibAttr);

if (FAILED(hRes))
__leave;

// Check the major and minor versions of the type library
// to those in AgentServer.h.

if ((pTypeLibAttr->wMajorVerNum > AGENT_VERSION_MAJOR) ||
((pTypeLibAttr->wMajorVerNum == AGENT_VERSION_MAJOR) &&
 (pTypeLibAttr->wMinorVerNum >= AGENT_VERSION_MINOR)))
bValid = TRUE;

}
__finally {
}

if (pTypeLib) {

if (pTypeLibAttr)
pTypeLib->ReleaseTLibAttr(pTypeLibAttr);

pTypeLib->Release();
}

if (pTypeInfo)
pTypeInfo->Release();

if (pdAgent)
pdAgent->Release();

return bValid;
}

extern "C" int PASCAL WinMain(HINSTANCE hInst,
  HINSTANCE hInstPrev,
  LPSTR lpCmdLine,
  int nCmdShow) {

HRESULThRes;
_TCHARszError[256];
VARIANTvPath;
BSTRbszSpeak;
longlCharID;
longlRequestID;
IAgent   *pAgent;
IDispatch   *pdCharacter;
IAgentCharacter    *pCharacter = NULL;

// Initialize OLE

    if (FAILED(OleInitialize(NULL))) {
MessageBox(NULL,
   _T("There was an error initializing OLE."),
   kpszAppTitle,
   MB_OK | MB_ICONERROR);
        return -1;
}

// Create an instance of the Agent server

hRes = CoCreateInstance(CLSID_AgentServer,
NULL,
CLSCTX_SERVER,
IID_IAgent,
(LPVOID *)&pAgent);
if (FAILED(hRes)) {

wsprintf(szError, _T("There was an error initializing Microsoft Agent, code = 0x%x"), hRes);

MessageBox(NULL,
   szError,
   kpszAppTitle,
   MB_OK | MB_ICONERROR | MB_TOPMOST);
return -1;
}

// Check to see if it is a compatible version

if (!IsValidAgentVersion(pAgent)) {

pAgent->Release();

MessageBox(NULL,
   _T("The version of Microsoft Agent installed on this system is out of date.\nUpgrade to a new version of Microsoft Agent."),
   kpszAppTitle,
   MB_OK | MB_ICONEXCLAMATION | MB_TOPMOST);
return -1;
}

// Create a variant to store the full path of the character to load

VariantInit(&vPath);

vPath.vt = VT_BSTR;
vPath.bstrVal = SysAllocString(kpwszCharacter);

if (vPath.bstrVal == NULL) {

MessageBox(NULL,
   _T("Out of memory!"),
   kpszAppTitle,
   MB_OK | MB_ICONERROR | MB_TOPMOST);

return -1;
}

__try {

// Load the character

hRes = pAgent->Load(vPath, &lCharID, &lRequestID);

if (FAILED(hRes))
__leave;

// Get it's dispinterface

hRes = pAgent->GetCharacter(lCharID, &pdCharacter);

if (FAILED(hRes))
__leave;

// Query for IAgentCharacter

hRes = pdCharacter->QueryInterface(IID_IAgentCharacter, (LPVOID *)&pCharacter);

// Release the IDispatch

pdCharacter->Release();

// Did we get the IAgentCharacter interface?

if (FAILED(hRes))
__leave;

// Show the character.  The first parameter tells Microsoft
// Agent to show the character by playing an animation.

hRes = pCharacter->Show(FALSE, &lRequestID);

if (FAILED(hRes))
__leave;

// Make the character speak

bszSpeak = SysAllocString(L"Hello World!");

hRes = pCharacter->Speak(bszSpeak, NULL, &lRequestID);

SysFreeString(bszSpeak);

if (FAILED(hRes))
__leave;

// This is a very simplistic sample.  Sleep for 10 seconds
// and then die.

Sleep(10000);

}
__finally {

if (FAILED(hRes)) {

wsprintf(szError, _T("An error occurred in Microsoft Agent, code = 0x%x"), hRes);

MessageBox(NULL,
   szError,
   kpszAppTitle,
   MB_OK | MB_ICONERROR | MB_TOPMOST);
}
}

// Clean up

if (pCharacter) {

// Release the character interface

pCharacter->Release();

// Unload the character.  NOTE:  releasing the character
// interface does NOT make the character go away.  You must
// call Unload.

pAgent->Unload(lCharID);
}

// Release the Agent

pAgent->Release();

VariantClear(&vPath);

OleUninitialize();

return 0;
}


HELLO2.H

#ifndef _HelloAgent2_h_
#define _HelloAgent2_h_

#ifndef STRICT
#define STRICT
#endif

#include <windows.h>
#include <tchar.h>
#include "AgtSvr.h"

#endif


NOTIFY.CPP

#include "Notify.h"

//==========================================================================
//
//  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.
//
//  Copyright (C) 1997 Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------
//
// AgentNotifySink
//
//Boilerplate IDispatch implementation with stubs for all
//IAgentNotifySink methods except for RequestComplete.
//
//==========================================================================

extern long g_lDone;

// IUnknown methods

STDMETHODIMP AgentNotifySink::QueryInterface (REFIID riid, LPVOID *ppv) {

    *ppv = NULL;

    if ((riid == IID_IUnknown) || (riid == IID_IAgentNotifySink))
        *ppv = this;

    if (*ppv == NULL)
        return E_NOINTERFACE;

    ((LPUNKNOWN)*ppv)->AddRef();

    return NOERROR;
}

STDMETHODIMP_ (ULONG) AgentNotifySink::AddRef() {

return ++m_cRefs;
}

STDMETHODIMP_(ULONG) AgentNotifySink::Release() {

if (--m_cRefs != 0)
return m_cRefs;

delete this;
return 0;
}

// IDispatch methods

STDMETHODIMP AgentNotifySink::GetTypeInfoCount(UINT *pctInfo) {

    *pctInfo = 1;
    return NOERROR;
}

STDMETHODIMP AgentNotifySink::GetTypeInfo(UINT itInfo, LCID lcid, ITypeInfo **ppTypeInfo) {

    HRESULT hRes;
    ITypeLib *pLib;

    *ppTypeInfo = NULL;

if (itInfo != 0)
        return TYPE_E_ELEMENTNOTFOUND;

    if (ppTypeInfo == NULL)
        return E_POINTER;

if ((PRIMARYLANGID(lcid) != LANG_NEUTRAL) &&
(PRIMARYLANGID(lcid) != LANG_ENGLISH))
return DISP_E_UNKNOWNLCID;

    hRes = LoadRegTypeLib(LIBID_AgentServerObjects,
  1,
  0,
  PRIMARYLANGID(lcid),
  &pLib);
    if (FAILED(hRes))
        return hRes;

hRes = pLib->GetTypeInfoOfGuid(IID_IAgentNotifySink, ppTypeInfo);

pLib->Release();

    if (FAILED(hRes))
        return hRes;

    (*ppTypeInfo)->AddRef();

    return NOERROR;
}

STDMETHODIMP AgentNotifySink::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID) {

    HRESULT hRes;
    ITypeInfo *pInfo;

// REFIID must be NULL

if (riid != IID_NULL)
        return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

// Get the TypeInfo for the specified lcid

    hRes = GetTypeInfo(0, lcid, &pInfo);

if (FAILED(hRes))
return hRes;

// Use the TypeInfo to get the DISPIDs of the specified names.
// That's the whole point here.  Let TypeInfo do the work so
// we don't have to.

hRes = pInfo->GetIDsOfNames(rgszNames, cNames, rgDispID);

pInfo->Release();

    return hRes;
}

STDMETHODIMP AgentNotifySink::Invoke(DISPID dispID, REFIID riid, LCID lcid,
 unsigned short wFlags, DISPPARAMS *pDispParams,
 VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
 UINT *puArgErr) {

HRESULT hRes;
ITypeInfo *pInfo;

    // The riid parameter is always supposed to be IID_NULL

if (riid != IID_NULL)
        return DISP_E_UNKNOWNINTERFACE;

// Get the type info for the specified lcid

    hRes = GetTypeInfo(0, lcid, &pInfo);

    if (FAILED(hRes))
        return hRes;

    // Clear exceptions

    SetErrorInfo(0L, NULL);

hRes = pInfo->Invoke(this,
 dispID,
 wFlags,
 pDispParams,
 pVarResult,
 pExcepInfo,
 puArgErr);

    pInfo->Release();

    return hRes;
}

// IAgentNotifySink methods

STDMETHODIMP AgentNotifySink::Command(long dwCommandID, IUnknown * punkUserInput) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::ActivateInputState(long dwCharID, long bActivated) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::Restart() {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::Shutdown() {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::VisibleState(long dwCharID, long bVisible, long lCause) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::Click(long dwCharID, short fwKeys, long X, long Y) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::DblClick(long dwCharID, short fwKeys, long X, long Y) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::DragStart(long dwCharID, short fwKeys, long X, long Y) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::DragComplete(long dwCharID, short fwKeys, long X, long Y) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::RequestStart(long dwRequestID) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::RequestComplete(long dwRequestID, long hrStatus) {

// When we get g_lDone exit the sample app

if (dwRequestID == g_lDone)
PostQuitMessage(0);

return NOERROR;
}

STDMETHODIMP AgentNotifySink::BookMark(long dwBookMarkID) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::Idle(long dwCharID, long bStart) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::Move(long dwCharID, long X, long Y, long lCause) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::Size(long dwCharID, long lWidth, long lHeight) {

return NOERROR;
}

STDMETHODIMP AgentNotifySink::BalloonVisibleState(long dwCharID, long bVisible) {

return NOERROR;
}


NOTIFY.H

#ifndef _AgentNotifySink_h_
#define _AgentNotifySink_h_

#include "Hello2.h"

class AgentNotifySink : public IAgentNotifySink {
public:
AgentNotifySink(){}
   ~AgentNotifySink(){}

    STDMETHODIMP QueryInterface(REFIID, LPVOID FAR *);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

STDMETHODIMP GetTypeInfoCount(UINT *);
STDMETHODIMP GetTypeInfo(UINT, LCID, ITypeInfo **);
STDMETHODIMP GetIDsOfNames(REFIID, OLECHAR **, UINT, LCID, DISPID *);
STDMETHODIMP Invoke(DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *);

    STDMETHODIMP Command(long dwCommandID, IUnknown * punkUserInput);
    STDMETHODIMP ActivateInputState(long dwCharID, long bActivated);
    STDMETHODIMP Restart();
    STDMETHODIMP Shutdown();
    STDMETHODIMP VisibleState(long dwCharID, long bVisible, long lCause);
STDMETHODIMP Click(long dwCharID, short fwKeys, long X, long Y);
STDMETHODIMP DblClick(long dwCharID, short fwKeys, long X, long Y);
STDMETHODIMP DragStart(long dwCharID, short fwKeys, long X, long Y);
STDMETHODIMP DragComplete(long dwCharID, short fwKeys, long X, long Y);
STDMETHODIMP RequestStart(long dwRequestID);
STDMETHODIMP RequestComplete(long dwRequestID, long hrStatus);
STDMETHODIMP BookMark(long dwBookMarkID);
    STDMETHODIMP Idle(long dwCharID, long bStart);
STDMETHODIMP Move(long dwCharID, long X, long Y, long lCause);
STDMETHODIMP Size(long dwCharID, long lWidth, long lHeight);
STDMETHODIMP BalloonVisibleState(long dwCharID, long bVisible);

private:
ULONGm_cRefs;
};

#endif


HELLO2.CPP

#include "Hello2.h"
#include "AgtSvr_i.c"
#include "Notify.h"

//==========================================================================
//
//  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.
//
//  Copyright (C) 1997 Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------
//
// This sample demonstrates the simplest Microsoft Agent application that
// uses a notification sink.
//
//==========================================================================

static const LPWSTR kpwszCharacter = L"\\Program Files\\Microsoft Agent\\characters\\genie.acs";
static const LPTSTR kpszAppTitle = _T("Microsoft Agent Samples");

extern long g_lDone = 0;

// This function checks to see if the version of Microsoft Agent
// installed on the user's system is compatible with the version
// we compiled with, i.e. it's version is greater than or equal to
// constants defined in AgentServer.h.  Microsoft Agent guarantees
// backward compatibility.

BOOL IsValidAgentVersion(IAgent *pAgent) {

HRESULT hRes;
IDispatch *pdAgent = NULL;
ITypeInfo *pTypeInfo = NULL;
ITypeLib *pTypeLib = NULL;
TLIBATTR *pTypeLibAttr = NULL;
BOOL bValid = FALSE;
UINT uiIndex;

__try {

// Query for IDispatch

hRes = pAgent->QueryInterface(IID_IDispatch, (LPVOID *)&pdAgent);

if (FAILED(hRes))
__leave;

// Get the TypeInfo

hRes = pdAgent->GetTypeInfo(0, 0, &pTypeInfo);

if (FAILED(hRes))
__leave;

// Get it's containing TypeLib

hRes = pTypeInfo->GetContainingTypeLib(&pTypeLib, &uiIndex);

if (FAILED(hRes))
__leave;

// Get the attributes of the TypeLib

hRes = pTypeLib->GetLibAttr(&pTypeLibAttr);

if (FAILED(hRes))
__leave;

// Check the major and minor versions of the type library
// to those in AgentServer.h.

if ((pTypeLibAttr->wMajorVerNum > AGENT_VERSION_MAJOR) ||
((pTypeLibAttr->wMajorVerNum == AGENT_VERSION_MAJOR) &&
 (pTypeLibAttr->wMinorVerNum >= AGENT_VERSION_MINOR)))
bValid = TRUE;

}
__finally {
}

if (pTypeLib) {

if (pTypeLibAttr)
pTypeLib->ReleaseTLibAttr(pTypeLibAttr);

pTypeLib->Release();
}

if (pTypeInfo)
pTypeInfo->Release();

if (pdAgent)
pdAgent->Release();

return bValid;
}

extern "C" int PASCAL WinMain(HINSTANCE hInst,
  HINSTANCE hInstPrev,
  LPSTR lpCmdLine,
  int nCmdShow) {

HRESULThRes;
_TCHARszError[256];
VARIANTvPath;
BSTRbszSpeak;
MSGmsg;
longlCharID;
longlRequestID;
longlNotifySinkID = -1;
IAgent   *pAgent;
IDispatch   *pdCharacter;
IAgentCharacter    *pCharacter = NULL;
AgentNotifySink   *pSink = NULL;

// Initialize OLE

    if (FAILED(OleInitialize(NULL))) {
MessageBox(NULL,
   _T("There was an error initializing OLE."),
   kpszAppTitle,
   MB_OK | MB_ICONERROR);
        return -1;
}

// Create an instance of the Agent server

hRes = CoCreateInstance(CLSID_AgentServer,
NULL,
CLSCTX_SERVER,
IID_IAgent,
(LPVOID *)&pAgent);
if (FAILED(hRes)) {

wsprintf(szError, _T("There was an error initializing Microsoft Agent, code = 0x%x"), hRes);

MessageBox(NULL,
   szError,
   kpszAppTitle,
   MB_OK | MB_ICONERROR);
return -1;
}

// Check to see if it is a compatible version

if (!IsValidAgentVersion(pAgent)) {

pAgent->Release();

MessageBox(NULL,
   _T("The version of Microsoft Agent installed on this system is out of date.\nUpgrade to a new version of Microsoft Agent."),
   kpszAppTitle,
   MB_OK | MB_ICONEXCLAMATION | MB_TOPMOST);
return -1;
}

// Create a notify sink

pSink = new AgentNotifySink;

if (pSink == NULL) {

MessageBox(NULL,
   _T("Out of memory!"),
   kpszAppTitle,
   MB_OK | MB_ICONERROR);

return -1;
}

pSink->AddRef();

// And register it with Microsoft Agent

hRes = pAgent->Register((IUnknown *)pSink, &lNotifySinkID);

if (FAILED(hRes))
return -1;

// Create a variant to store the full path of the character to load

VariantInit(&vPath);

vPath.vt = VT_BSTR;
vPath.bstrVal = SysAllocString(kpwszCharacter);

if (vPath.bstrVal == NULL) {

MessageBox(NULL,
   _T("Out of memory!"),
   kpszAppTitle,
   MB_OK | MB_ICONERROR);

return -1;
}

try {

// Load the character

hRes = pAgent->Load(vPath, &lCharID, &lRequestID);

if (FAILED(hRes))
throw hRes;

// Get it's dispinterface

hRes = pAgent->GetCharacter(lCharID, &pdCharacter);

if (FAILED(hRes))
throw hRes;

// Query for IAgentCharacter

hRes = pdCharacter->QueryInterface(IID_IAgentCharacter, (LPVOID *)&pCharacter);

// Release the IDispatch

pdCharacter->Release();

// Did we get the IAgentCharacter interface?

if (FAILED(hRes))
throw hRes;

// Show the character.  The first parameter tells Microsoft
// Agent to show the character by playing an animation.

hRes = pCharacter->Show(FALSE, &lRequestID);

if (FAILED(hRes))
throw hRes;

// Make the character speak

bszSpeak = SysAllocString(L"Hello World!");

hRes = pCharacter->Speak(bszSpeak, NULL, &g_lDone);

SysFreeString(bszSpeak);

if (FAILED(hRes))
throw hRes;

}
catch (HRESULT) {

wsprintf(szError, _T("An error occurred in Microsoft Agent, code = 0x%x"), hRes);

MessageBox(NULL,
   szError,
   kpszAppTitle,
   MB_OK | MB_ICONERROR);
}

// Start the message loop.  A WM_QUIT message will be posted
// when we get the RequestComplete notification from Microsoft Agent.

if (SUCCEEDED(hRes)) {
while (GetMessage(&msg, NULL, 0, 0) > 0)
DispatchMessage(&msg);
}

// Clean up

if (pCharacter) {

// Release the character interface

pCharacter->Release();

// Unload the character.  NOTE:  releasing the character
// interface does NOT make the character go away.  You must
// call Unload.

pAgent->Unload(lCharID);
}

// Release objects

if (pSink) {

if (lNotifySinkID != -1)
pAgent->Unregister(lNotifySinkID);

pSink->Release();
}

pAgent->Release();

VariantClear(&vPath);

OleUninitialize();

return 0;
}


HELLO3.H

#ifndef _HelloAgent3_h_
#define _HelloAgent3_h_

#ifndef STRICT
#define STRICT
#endif

#include <windows.h>
#include <tchar.h>
#include "AgtSvr.h"

#endif


NOTIFY.H

#ifndef _AgentNotifySink_h_
#define _AgentNotifySink_h_

#include "Hello3.h"

class AgentNotifySink : public IAgentNotifySink {
public:
AgentNotifySink(){}
   ~AgentNotifySink(){}

    STDMETHODIMP QueryInterface(REFIID, LPVOID FAR *);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

STDMETHODIMP GetTypeInfoCount(UINT *);
STDMETHODIMP GetTypeInfo(UINT, LCID, ITypeInfo **);
STDMETHODIMP GetIDsOfNames(REFIID, OLECHAR **, UINT, LCID, DISPID *);
STDMETHODIMP Invoke(DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *);

    STDMETHODIMP Command(long dwCommandID, IUnknown * punkUserInput);
    STDMETHODIMP ActivateInputState(long dwCharID, long bActivated);
    STDMETHODIMP Restart();
    STDMETHODIMP Shutdown();
    STDMETHODIMP VisibleState(long dwCharID, long bVisible, long lCause);
STDMETHODIMP Click(long dwCharID, short fwKeys, long X, long Y);
STDMETHODIMP DblClick(long dwCharID, short fwKeys, long X, long Y);
STDMETHODIMP DragStart(long dwCharID, short fwKeys, long X, long Y);
STDMETHODIMP DragComplete(long dwCharID, short fwKeys, long X, long Y);
STDMETHODIMP RequestStart(long dwRequestID);
STDMETHODIMP RequestComplete(long dwRequestID, long hrStatus);
STDMETHODIMP BookMark(long dwBookMarkID);
    STDMETHODIMP Idle(long dwCharID, long bStart);
STDMETHODIMP Move(long dwCharID, long X, long Y, long lCause);
STDMETHODIMP Size(long dwCharID, long lWidth, long lHeight);
STDMETHODIMP BalloonVisibleState(long dwCharID, long bVisible);

private:
ULONGm_cRefs;
};

#endif


HELLO3.CPP

#include "Hello3.h"
#include "AgtSvr_i.c"
#include "Notify.h"

//==========================================================================
//
//  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.
//
//  Copyright (C) 1997 Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------
//
// This sample demonstrates loading 2 characters and synchronizing
// their actions.
//
//==========================================================================

// The AgentHRESULT object makes it easier to handle errors by
// throwing a C++ exception when it is assigned a FAILED HRESULT.
// You MUST use this object only in try/catch blocks.  By using
// this object you don't have to check the return value after every
// call into Microsoft Agent.

struct AgentHRESULT {

AgentHRESULT& operator =(const HRESULT hRes) {
if (FAILED(hRes))
throw hRes;
return *this;
}
};

// Constants

static const LPWSTR kpwszGenie = L"\\Program Files\\Microsoft Agent\\characters\\genie.acs";
static const LPWSTR kpwszMerlin = L"\\Program Files\\Microsoft Agent\\characters\\merlin.acs";
static const LPTSTR kpszAppTitle = _T("Microsoft Agent Samples");

// Globals

extern long g_lDone = 0;

// Forward references

HRESULT LoadCharacter(IAgent *pAgent,
  LPWSTR pwszPath,
  long *plID,
  IAgentCharacter **ppCharacter);

BOOLInitAgent(IAgent **ppAgent,
  long *plSinkID,
  AgentNotifySink **ppSink);

extern "C" int PASCAL WinMain(HINSTANCE hInst,
  HINSTANCE hInstPrev,
  LPSTR lpCmdLine,
  int nCmdShow) {

HRESULThRes;
AgentHRESULTahRes;
_TCHARszError[256];
BSTRbszSpeak = NULL;
MSGmsg;
longlGenieID;
longlMerlinID;
longlRequestID;
longlWaitID;
longlNotifySinkID = -1;
IAgent   *pAgent;
IAgentCharacter    *pGenie = NULL;
IAgentCharacter   *pMerlin = NULL;
AgentNotifySink   *pSink = NULL;

// Initialize the app

if (!InitAgent(&pAgent, &lNotifySinkID, &pSink))
return -1;

try {

// Load the characters

ahRes = LoadCharacter(pAgent, kpwszGenie, &lGenieID, &pGenie);
ahRes = LoadCharacter(pAgent, kpwszMerlin, &lMerlinID, &pMerlin);

// Position them

ahRes = pGenie->MoveTo(0, 100, 0, &lRequestID);
ahRes = pMerlin->MoveTo(200, 100, 0, &lRequestID);

// Show them

ahRes = pGenie->Show(FALSE, &lRequestID);
ahRes = pMerlin->Show(FALSE, &lRequestID);

// Have Genie gesture towards Merlin

ahRes = pGenie->GestureAt(150, 100, &lRequestID);

// And have Genie say hello to Merlin

bszSpeak = SysAllocString(L"Hi there Merlin.");

ahRes = pGenie->Speak(bszSpeak, NULL, &lWaitID);

SysFreeString(bszSpeak);
bszSpeak = NULL;

// Make Merlin wait for Genie to finish speaking

ahRes = pMerlin->Wait(lWaitID, &lRequestID);

// Have Merlin gesture towards Genie

ahRes = pMerlin->GestureAt(0, 100, &lRequestID);

// And then speak back to Genie

bszSpeak = SysAllocString(L"Hi Genie.  See you later");

ahRes = pMerlin->Speak(bszSpeak, NULL, &g_lDone);

SysFreeString(bszSpeak);
bszSpeak = NULL;

}
catch (HRESULT hResError) {

hRes = hResError;

wsprintf(szError, _T("An error occurred in Microsoft Agent, code = 0x%x"), hRes);

MessageBox(NULL,
   szError,
   kpszAppTitle,
   MB_OK | MB_ICONERROR);

SysFreeString(bszSpeak);
}

// Start the message loop.  A WM_QUIT message will be posted
// when we get the RequestComplete notification from Microsoft Agent.

if (SUCCEEDED(hRes)) {
while (GetMessage(&msg, NULL, 0, 0) > 0)
DispatchMessage(&msg);
}

// Release the character interfaces.  NOTE:  releasing the character
// interface does NOT make the character go away.  You must
// call Unload.

if (pGenie) {
pGenie->Release();
pAgent->Unload(lGenieID);
}

if (pMerlin) {
pMerlin->Release();
pAgent->Unload(lMerlinID);
}

// Release the notify sink

if (pSink) {
if (lNotifySinkID != -1)
pAgent->Unregister(lNotifySinkID);

pSink->Release();
}

// We're done

pAgent->Release();

OleUninitialize();

return 0;
}

// This function checks to see if the version of Microsoft Agent
// installed on the user's system is compatible with the version
// we compiled with, i.e. it's version is greater than or equal to
// constants defined in AgentServer.h.  Microsoft Agent guarantees
// backward compatibility.

BOOL IsValidAgentVersion(IAgent *pAgent) {

HRESULT hRes;
IDispatch *pdAgent = NULL;
ITypeInfo *pTypeInfo = NULL;
ITypeLib *pTypeLib = NULL;
TLIBATTR *pTypeLibAttr = NULL;
BOOL bValid = FALSE;
UINT uiIndex;

__try {

// Query for IDispatch

hRes = pAgent->QueryInterface(IID_IDispatch, (LPVOID *)&pdAgent);

if (FAILED(hRes))
__leave;

// Get the TypeInfo

hRes = pdAgent->GetTypeInfo(0, 0, &pTypeInfo);

if (FAILED(hRes))
__leave;

// Get it's containing TypeLib

hRes = pTypeInfo->GetContainingTypeLib(&pTypeLib, &uiIndex);

if (FAILED(hRes))
__leave;

// Get the attributes of the TypeLib

hRes = pTypeLib->GetLibAttr(&pTypeLibAttr);

if (FAILED(hRes))
__leave;

// Check the major and minor versions of the type library
// to those in AgentServer.h.

if ((pTypeLibAttr->wMajorVerNum > AGENT_VERSION_MAJOR) ||
((pTypeLibAttr->wMajorVerNum == AGENT_VERSION_MAJOR) &&
 (pTypeLibAttr->wMinorVerNum >= AGENT_VERSION_MINOR)))
bValid = TRUE;

}
__finally {
}

if (pTypeLib) {

if (pTypeLibAttr)
pTypeLib->ReleaseTLibAttr(pTypeLibAttr);

pTypeLib->Release();
}

if (pTypeInfo)
pTypeInfo->Release();

if (pdAgent)
pdAgent->Release();

return bValid;
}

BOOL InitAgent(IAgent **ppAgent,
   long *plSinkID,
   AgentNotifySink **ppSink) {

HRESULThRes;
_TCHARszError[256];

// Initialize OLE

    if (FAILED(OleInitialize(NULL))) {
MessageBox(NULL,
   _T("There was an error initializing OLE."),
   kpszAppTitle,
   MB_OK | MB_ICONERROR);
        return FALSE;
}

// Create an instance of the Agent server

hRes = CoCreateInstance(CLSID_AgentServer,
NULL,
CLSCTX_SERVER,
IID_IAgent,
(LPVOID *)ppAgent);
if (FAILED(hRes)) {

wsprintf(szError, _T("There was an error initializing Microsoft Agent, code = 0x%x"), hRes);

MessageBox(NULL,
   szError,
   kpszAppTitle,
   MB_OK | MB_ICONERROR | MB_TOPMOST);
        return FALSE;
}

// Check to see if it is a compatible version

if (!IsValidAgentVersion(*ppAgent)) {
(*ppAgent)->Release();
*ppAgent = NULL;
MessageBox(NULL,
   _T("The version of Microsoft Agent installed on this system is out of date.\nUpgrade to a new version of Microsoft Agent."),
   kpszAppTitle,
   MB_OK | MB_ICONEXCLAMATION | MB_TOPMOST);
return FALSE;
}

// Create a notify sink

*ppSink = new AgentNotifySink;

if (*ppSink == NULL) {

MessageBox(NULL,
   _T("Out of memory!"),
   kpszAppTitle,
   MB_OK | MB_ICONERROR | MB_TOPMOST);

        return FALSE;
}

(*ppSink)->AddRef();

// And register it with Microsoft Agent

hRes = (*ppAgent)->Register((IUnknown *)(*ppSink), plSinkID);

if (FAILED(hRes))
return FALSE;
else
return TRUE;
}

HRESULT LoadCharacter(IAgent *pAgent,
  LPWSTR pwszPath,
  long *plID,
  IAgentCharacter **ppCharacter) {

VARIANTvPath;
IDispatch  *pdCharacter;
HRESULThRes;
longlRequestID;

// Create a VARIANT for the path

VariantInit(&vPath);

vPath.vt = VT_BSTR;
vPath.bstrVal = SysAllocString(pwszPath);

if (vPath.bstrVal == NULL)
return E_OUTOFMEMORY;

// Load the character

hRes = pAgent->Load(vPath, plID, &lRequestID);

if (FAILED(hRes))
return hRes;

// Get it's dispinterface

hRes = pAgent->GetCharacter(*plID, &pdCharacter);

if (FAILED(hRes))
return hRes;

// Query for IAgentCharacter

hRes = pdCharacter->QueryInterface(IID_IAgentCharacter, (LPVOID *)ppCharacter);

// Release the IDispatch

pdCharacter->Release();

return hRes;
}


GENIE.ACF

'  U
   >         H P    t @        p        >j   t   l


READMEJAVA.TXT

How to Install a Java Sample for Microsoft Agent
------------------------------------------------

*** Perform the following steps once for all samples ***

1. Install the Microsoft Agent core control. To run samples which produce
speech output, install the TruVoice text-to-speech engine.  To run samples
which take speech input, install the Microsoft Command and Control speech
engine.

2. Download the character animation files for the three standard
characters: Genie.acs, Merlin.acs and Robby.acs.  Create a directory
/Program Files/Microsoft Agent/Characters/ and put the three character
files in it.  (You may select a different location for these files, but
then you will need to replace the path string within each sample to
refer to the new location.)

*** Perform the following steps once for all Java samples ***

1. Import the Agent Server COM class to Java.  Start up Microsoft Visual J++.
Run the Java Type Library Wizard from the Tools menu.  Scroll through the
list of items and find the entry for "Microsoft Agent Server 1.5".
Place a check mark in the box next to this entry, then click OK.

*** Perform the following steps for each Java sample ***

1. Download the sample.  Create a local directory for the sample.  (E.g.
/Program Files/DevStudio/MyProjects/AgentSample/)  Unzip the samples files
and put them into the directory you created.

2. Start up Microsoft Visual J++.  From the File menu, select Open Workspace.
Navigate to the folder you created in step 1.  Select the workspace file (it
has a .dsw file extension) for this sample.  Click Open.

3. From the Build menu, select Execute.  When prompted that files need to be
built, Click Yes.  When prompted for a Class File Name, enter the name of
the sample.  For instance, if you downloaded the HelloJ sample, enter
"HelloJ".  Note that the name is case sensitive.


READMEVB.TXT

How to Install a Visual Basic Sample for Microsoft Agent
--------------------------------------------------------

*** Perform the following steps once for all samples ***

1. Install the Microsoft Agent core control. To run samples which produce
speech output, install the TruVoice text-to-speech engine.  To run samples
which take speech input, install the Microsoft Command and Control speech
engine.

2. Download the character animation files for the three standard
characters: Genie.acs, Merlin.acs and Robby.acs.  Create a directory
/Program Files/Microsoft Agent/Characters/ and put the three character
files in it.  (You may select a different location for these files, but
then you will need to replace the path string within each sample to
refer to the new location.)

*** Perform the following steps for each VB sample ***

1. Download the sample.  Create a local directory for the sample.  (E.g.
/Program Files/DevStudio/VB/AgentSample/)  Unzip the samples files
and put them into the directory you created.

2. Start up Microsoft Visual Basic.  From the File menu, select Open Project.
Navigate to the folder you created in step 1.  Select the project file (it
has a .vbp file extension) for this sample.  Click Open.

3. From the Run menu, select Start.


HELLO.FRM

VERSION 5.00
Object = "{F5BE8BC2-7DE6-11D0-91FE-00C04FD701A5}#1.5#0"; "AGENTCTL.DLL"
Begin VB.Form Form1
   Caption         =   "Hello, World"
   ClientHeight    =   1680
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   4680
   LinkTopic       =   "Form1"
   ScaleHeight     =   1680
   ScaleWidth      =   4680
   StartUpPosition =   3  'Windows Default
   Begin VB.TextBox TextBox
      Appearance      =   0  'Flat
      Height          =   615
      Left            =   240
      MultiLine       =   -1  'True
      TabIndex        =   1
      ToolTipText     =   "Type something in here for the Genie to say, then press the Say it! button"
      Top             =   120
      Width           =   4335
   End
   Begin VB.CommandButton Button
      Caption         =   "Say it!"
      Height          =   495
      Left            =   1920
      TabIndex        =   0
      ToolTipText     =   "Type some text into the text box, then press this button to hear Genie say it"
      Top             =   960
      Width           =   1215
   End
   Begin AgentObjectsCtl.Agent Agent1
      Left            =   240
      Top             =   960
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Dim Genie As IAgentCtlCharacter
Const DATAPATH = "\program files\microsoft agent\characters\genie.acs"

Private Sub Form_Load()
    Agent1.Characters.Load "Genie", DATAPATH
    Set Genie = Agent1.Characters("Genie")
    TextBox.Text = "Hello World!"
End Sub

Private Sub Button_Click()
    Genie.Show
    Genie.Speak TextBox.Text
    Genie.Hide
End Sub


README.TXT

------------------------------------------------------------------------------------------
             Microsoft Chat Control 1.0 & Microsoft Chat Protocol Control 2.0 Readme File
------------------------------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

HOW TO USE THIS DOCUMENT
========================

To view Readme.txt on screen in Windows Notepad, maximize the Notepad
window.

To print Readme.txt, open the file in Notepad or another word processor,
and then on the File menu, click Print.

CONTENTS
========

INSTALLING THE CHAT CONTROLS
OVERVIEW OF THE MICROSOFT CHAT CONTROL SAMPLES
KNOWN PROBLEMS

INSTALLING THE CHAT CONTROLS
============================

The Samples will work only with Windows 95 and Windows NT 4.0.

To install the chat control, run the chatcntl.exe in the \complib\i386 directory.
This executable program will:

1. Copy the binaries (chatsock.dll, mschat.ocx and mschatpr.ocx) to your
   System directory.

2. Register the controls as an OCX.

OVERVIEW OF THE MICROSOFT CHAT CONTROL SAMPLES
==============================================

The chatocx directory contains the following sample code, which
demonstrates the use of the control in web pages, Microsoft Visual Basic
Scripting and Visual Basic:

  Four Visual Basic applications that use the Chat Control 1.1

  Five HTML web pages that use the chat control 1.1 and use Visual Basic Scripting

  One MFC chat application that uses chat control 1.1

  5 samples that use Chat Control 2.0 beta 1

For more information on the samples, see the Sample.txt file in this directory.
In addition, each subdirectory under Chatocx contains a text file with more
detailed information on that sample.

The samples requires that you have Visual Basic 4.0 runtime, mschat.ocx and
chatsock.dll installed on your system. If you have problems running any of the
applications you can use the Application Setup Wizard in Visual Basic 4.0 to create
a setup program, which will create an installation program that installs all the
required files for the samples. The Application setup wizard will not detect that
the applications need to install chatsock.dll, you will need to add this to the
list of files to install, when you run the wizard.

KNOWN PROBLEMS
==============
If you experience any problems connecting to the chat server with any of
the demos, please try to connect again, since the chat server might be down
at this time.  If you continue to experience this problem, you may not be
able to connect to the chat server if you are using a proxy server or firewall
that doesn't pass port "6667". The system administrator for your Internet
Service Provider or your company can tell you if they allow pass through for
port "6667".


BANDLG.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       BanDlg.h
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_BANDLG_H__B8AF51DF_DD08_11D0_AD50_00C04FC29DE5__INCLUDED_)
#define AFX_BANDLG_H__B8AF51DF_DD08_11D0_AD50_00C04FC29DE5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

/////////////////////////////////////////////////////////////////////////////
// CBanDlg dialog

class CBanDlg : public CDialog
{
// Construction
public:
CBanDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CBanDlg)
enum { IDD = IDD_BAN };
longm_iDuration;
CStringm_szReason;
CStringm_szValue;
intm_iOperation;
intm_iProperty;
intm_iBan;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CBanDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CBanDlg)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_BANDLG_H__B8AF51DF_DD08_11D0_AD50_00C04FC29DE5__INCLUDED_)


CHAPPY.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       Chappy.cpp : Defines the class behaviors for the application.
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"

#include "MainFrm.h"
#include "ChppyDoc.h"
#include "ChppyVw.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CChappyApp

BEGIN_MESSAGE_MAP(CChappyApp, CWinApp)
//{{AFX_MSG_MAP(CChappyApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// NOTE - the ClassWizard will add and remove mapping macros here.
//    DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Standard file based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChappyApp construction

CChappyApp::CChappyApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CChappyApp object

CChappyApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CChappyApp initialization

BOOL CChappyApp::InitInstance()
{
AfxEnableControlContainer();

// Initialize OLE 2.0 libraries
if (!AfxOleInit())
{
AfxMessageBox("Failed to init OLE libraries" /*IDP_OLE_INIT_FAILED*/);
return FALSE;
}

// Standard initialization
// If you are not using these features and wish to reduce the size
//  of your final executable, you should remove from the following
//  the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls();// Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic();// Call this when linking to MFC statically
#endif

LoadStdProfileSettings();  // Load standard INI file options (including MRU)

// Register the application's document templates.  Document templates
//  serve as the connection between documents, frame windows and views.

CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CChappyDoc),
RUNTIME_CLASS(CMainFrame),       // main SDI frame window
RUNTIME_CLASS(CChappyView));
AddDocTemplate(pDocTemplate);

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;

return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
CAboutDlg();

// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
// No message handlers
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CChappyApp::OnAppAbout()
{
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}

/////////////////////////////////////////////////////////////////////////////
// CChappyApp commands


CHAPPY.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       Chappy.h : main header file for the CHAPPY application
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __CHAPPY_H__
#define __CHAPPY_H__

#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// CChappyApp:
// See Chappy.cpp for the implementation of this class
//

class CChappyApp : public CWinApp
{
public:
CChappyApp();

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CChappyApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL

// Implementation

//{{AFX_MSG(CChappyApp)
afx_msg void OnAppAbout();
// NOTE - the ClassWizard will add and remove member functions here.
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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

#endif //__CHAPPY_H__


CHPPYDOC.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       ChappyDoc.cpp : implementation of the CChappyDoc class
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"

#include "ChppyDoc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CChappyDoc

IMPLEMENT_DYNCREATE(CChappyDoc, CDocument)

BEGIN_MESSAGE_MAP(CChappyDoc, CDocument)
//{{AFX_MSG_MAP(CChappyDoc)
// NOTE - the ClassWizard will add and remove mapping macros here.
//    DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChappyDoc construction/destruction

CChappyDoc::CChappyDoc()
{
// TODO: add one-time construction code here

}

CChappyDoc::~CChappyDoc()
{
}

BOOL CChappyDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;

// TODO: add reinitialization code here
// (SDI documents will reuse this document)

return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CChappyDoc serialization

void CChappyDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
TRACE ("Save !! \n");
}
else
{
// TODO: add loading code here
TRACE ("Load !! \n");
}
}

/////////////////////////////////////////////////////////////////////////////
// CChappyDoc diagnostics

#ifdef _DEBUG
void CChappyDoc::AssertValid() const
{
CDocument::AssertValid();
}

void CChappyDoc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CChappyDoc commands


CHPPYDOC.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       ChappyDoc.h : interface of the CChappyDoc class
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __CHAPPYDOC_H__
#define __CHAPPYDOC_H__

class CChappyDoc : public CDocument
{
protected: // create from serialization only
CChappyDoc();
DECLARE_DYNCREATE(CChappyDoc)

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CChappyDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CChappyDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
//{{AFX_MSG(CChappyDoc)
// NOTE - the ClassWizard will add and remove member functions here.
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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

#endif //__CHAPPYDOC_H__


CHPPYVW.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       ChappyView.cpp : implementation of the CChappyView class
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"

#include "ChppyDoc.h"
#include "ChppyVw.h"
#include "Globals.h"
#include "EnterDlg.h"
#include "chtitems.h"
#include "mainfrm.h"
#include "LogInDlg.h"
#include "PWordDlg.h"
#include "KickDlg.h"
#include "BanDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CChappyView

IMPLEMENT_DYNCREATE(CChappyView, CView)

BEGIN_MESSAGE_MAP(CChappyView, CView)
//{{AFX_MSG_MAP(CChappyView)
ON_WM_CREATE()
ON_NOTIFY(TCN_SELCHANGE, IDC_MESSAGE_TAB, OnSelchangeTab1)
ON_WM_SIZE()
ON_COMMAND(ID_FILE_NEW, OnFileNew)
ON_WM_DESTROY()
ON_COMMAND(ID_BTN_SEND, OnBtnSend)
ON_COMMAND(ID_BTN_WHISPER, OnBtnWhisper)
ON_COMMAND(ID_ROOM_CONNECT, OnRoomConnect)
ON_COMMAND(ID_BTN_DO, OnBtnDo)
ON_COMMAND(ID_BTN_THINK, OnBtnThink)
ON_COMMAND(ID_ROOM_DISCONNECT, OnRoomDisconnect)
ON_UPDATE_COMMAND_UI(ID_ROOM_CONNECT, OnUpdateRoomConnect)
ON_UPDATE_COMMAND_UI(ID_ROOM_DISCONNECT, OnUpdateRoomDisconnect)
ON_COMMAND(ID_SERVER_LOGIN, OnRoomLogin)
ON_UPDATE_COMMAND_UI(ID_SERVER_LOGIN, OnUpdateRoomLogin)
ON_UPDATE_COMMAND_UI(ID_SERVER_LOGOUT, OnUpdateRoomLogout)
ON_COMMAND(ID_SERVER_LOGOUT, OnRoomLogout)
ON_COMMAND(ID_ROOM_LISTROOMS, OnRoomListrooms)
ON_COMMAND(ID_ROOMLIST_ENTERROOM, OnRoomListEnterRoom)
ON_UPDATE_COMMAND_UI(ID_ROOMLIST_ENTERROOM, OnUpdateRoomListEnterRoom)
ON_UPDATE_COMMAND_UI(ID_ROOM_LISTROOMS, OnUpdateRoomListRooms)
ON_COMMAND(ID_PARTLIST_KICK, OnPartlistKick)
ON_COMMAND(ID_FILE_SAVE, OnFileSave)
ON_UPDATE_COMMAND_UI(ID_PARTLIST_BAN, OnUpdatePartlistBan)
ON_COMMAND(ID_PARTLIST_BAN, OnPartlistBan)
ON_COMMAND(ID_SERVER_BANUSER, OnServerBanuser)
ON_UPDATE_COMMAND_UI(ID_SERVER_BANUSER, OnUpdateServerBanuser)
ON_UPDATE_COMMAND_UI(ID_PARTLIST_KICK, OnUpdatePartlistKick)
ON_COMMAND(ID_PARTLIST_MAKEHOST, OnPartlistMakehost)
ON_UPDATE_COMMAND_UI(ID_PARTLIST_MAKEHOST, OnUpdatePartlistMakehost)
ON_COMMAND(ID_PARTLIST_MAKEPARTICIPANT, OnPartlistMakeparticipant)
ON_UPDATE_COMMAND_UI(ID_PARTLIST_MAKEPARTICIPANT, OnUpdatePartlistMakeparticipant)
ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
ON_COMMAND(ID_HISTORY_CLEAR, OnHistoryClear)
ON_UPDATE_COMMAND_UI(ID_HISTORY_CLEAR, OnUpdateHistoryClear)

//}}AFX_MSG_MAP
END_MESSAGE_MAP()

//ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
//nON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)

//EnableToolTips(TRUE);

/////////////////////////////////////////////////////////////////////////////
// CChappyView construction/destruction

CChappyView::CChappyView()
{
// TODO: add construction code here
m_cx = m_cy = 0;
m_iLastTab = -1; //first chat room
m_iVisibleTab = 0;
m_iServerConnectionState = csDisconnected;
m_iServerConnectionType = -1;
for (long i=0; i < MAX_ROOMS; ++i)
{
m_ChatRoom[i] = NULL;
}
}

CChappyView::~CChappyView()
{
//disconnect from the server
//m_ocxChat.Disconnect();
for (long i=0; i < MAX_ROOMS; ++i)
{
if (m_ChatRoom[i])
{
delete(m_ChatRoom[i]);
m_ChatRoom[i] = NULL;
}
}
}

BOOL CChappyView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
//  the CREATESTRUCT cs

return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CChappyView diagnostics

#ifdef _DEBUG
void CChappyView::AssertValid() const
{
CView::AssertValid();
}

void CChappyView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}

CChappyDoc* CChappyView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CChappyDoc)));
return (CChappyDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CChappyView drawing

void CChappyView::OnDraw(CDC* pDC)
{
//TRACE0("DRAW \n");
//CChappyDoc* pDoc = GetDocument();
//ASSERT_VALID(pDoc);

int iParticListSize = 0;
if ( (LIST_TAB != m_iVisibleTab) && (MESSAGE_TAB != m_iVisibleTab) )
{
//the tab contains a room
iParticListSize = m_ChatRoom[m_iVisibleTab]->m_ctrlPartListWnd.GetCx();

//TRACE1("size = %u", iParticListSize);
m_ChatRoom[m_iVisibleTab]->m_ctrlPartListWnd.MoveWindow(m_cx - iParticListSize,
0,
iParticListSize,
m_cy,
TRUE);
}

m_ctrlTabWnd.MoveWindow(0,
0,
m_cx - iParticListSize,
m_cy - INPUT_RICHEDIT_SIZE,
TRUE);

//the history/room list window
HWND hWnd = NULL;
switch (m_iVisibleTab)
{
case LIST_TAB:
hWnd = m_ctrlRoomList;
break;
case MESSAGE_TAB:
//server and private messages
hWnd = m_ctrlPrivateMsg;
break;
default:
//all the other tabs are rooms
hWnd = m_ChatRoom[m_iVisibleTab]->m_ctrlHistoryWnd;
}

::MoveWindow(hWnd,
6,
6,
m_cx - iParticListSize - 20,
m_cy - INPUT_RICHEDIT_SIZE - 42,
TRUE);

m_ctrlSendToolBar.MoveWindow(0,
m_cy - INPUT_RICHEDIT_SIZE,
SEND_TB_SIZE ,
INPUT_RICHEDIT_SIZE,
FALSE);

m_ctrlInputWnd.MoveWindow(SEND_TB_SIZE ,
m_cy - INPUT_RICHEDIT_SIZE,
m_cx - iParticListSize - SEND_TB_SIZE,
INPUT_RICHEDIT_SIZE,
TRUE);

}

/////////////////////////////////////////////////////////////////////////////
// CChappyView message handlers

void CChappyView::OnDestroy()
{
//disconnect from the server
if (csConnected <= m_iServerConnectionState)
{
OnRoomLogout();
}

//commctrl faults if do not destroy the windows in the proper order
m_ctrlRoomList.DestroyWindow();
m_ctrlPrivateMsg.DestroyWindow();
m_ctrlTabWnd.DestroyWindow();
m_ctrlSendToolBar.DestroyWindow();
m_ctrlInputWnd.DestroyWindow();

CView::OnDestroy();

}

int CChappyView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;

//some random initial size
RECT rec;
rec.left= 0;
rec.top= 0;
rec.right= 100;
rec.bottom= 100;

BOOL ret = FALSE;

//create the tab control
ret = m_ctrlTabWnd.Create(WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | WS_THICKFRAME |
TCS_BOTTOM | TCS_TABS ,
rec, this, IDC_MESSAGE_TAB);
ASSERT (ret);

//create the input rich edit control
ret = m_ctrlInputWnd.Create(WS_CHILD | WS_BORDER |  WS_VISIBLE | WS_VSCROLL |
ES_MULTILINE | ES_AUTOVSCROLL,
rec,
this,
IDC_INPUT_RICHEDIT);
ASSERT (ret);

////set the font for the input richedit
//CHARFORMAT cf;
//m_ctrlInputWnd.GetDefaultCharFormat(cf);
//
//
//cf.cbSize = sizeof (CHARFORMAT);
//cf.dwMask = CFM_FACE ;
////cf.dwEffects;
////cf.yHeight;
////cf.yOffset;
////cf.crTextColor=RGB(250,0,0);
////cf.bCharSet;
////cf.bPitchAndFamily;
//sprintf(cf.szFaceName, "Script");
//
//m_ctrlInputWnd.SetDefaultCharFormat(cf);
//

//create the toolbar with the send/wisper buttons
ret = m_ctrlSendToolBar.Create(this,//GetParent(),
WS_CHILD |  WS_VISIBLE |
TBSTYLE_TOOLTIPS | CBRS_RIGHT | CCS_NORESIZE ,
IDR_SEND_TOOLBAR);
ASSERT (ret);

//load the toolbar
ret = m_ctrlSendToolBar.LoadToolBar(IDR_SEND_TOOLBAR);
ASSERT (ret);

//enable the tooltips
ret = m_ctrlSendToolBar.EnableToolTips(TRUE);
ASSERT (ret);
ret = EnableToolTips(TRUE);
ASSERT (ret);

//** The OCX **
RECT rect;

rect.left= 1;
rect.top= 1;
rect.right= 5000;
rect.bottom= 5000;

if (!m_ocxChat.Create(NULL, WS_CHILD, rect, this, IDC_OCX_CHAT))
{
AfxMessageBox (IDS_FAIL_TO_LOAD_OCX);
return -1;
}

//create the list of Chat rooms
CString szTabName;
szTabName.LoadString (IDS_ROOM_LIST_TAB);
long iTab = AddTab(szTabName);
m_ctrlTabWnd.ChangeImage(iTab, IMG_ROOM_LIST);
m_ctrlRoomList.Create(LVS_REPORT | LVS_SINGLESEL | LVS_SORTASCENDING,
rect,
&m_ctrlTabWnd,
IDC_ROOM_LIST);
ASSERT(m_ctrlRoomList);

//create a history windows for private and server messages
szTabName.LoadString (IDS_MESSAGES_TAB);
iTab = AddTab(szTabName);
m_ctrlTabWnd.ChangeImage(iTab, IMG_OFF);
m_ctrlPrivateMsg.Create(WS_CHILD | WS_BORDER | WS_VSCROLL |
ES_MULTILINE | ES_AUTOVSCROLL| WS_EX_NOPARENTNOTIFY,
rec,
&m_ctrlTabWnd,
IDC_HISTORY_RICHEDIT);
ASSERT(m_ctrlPrivateMsg);

//Log in a chat server
OnRoomLogin();

m_ctrlRoomList.ShowWindow(SW_SHOW);
//insert the first chat room
//OnFileNew();

return 0;
}

void CChappyView::OnFileNew()
{
// new room
CStringszTmp;
szTmp.LoadString(IDS_CHAT_TAB);
long iTab = AddTab(szTmp);
m_ctrlTabWnd.ChangeImage(iTab, IMG_OFF);

//create a new chat room
m_ChatRoom[m_iLastTab] = (CRoom *) new CRoom;
m_ChatRoom[m_iLastTab]->Create(m_ctrlTabWnd, m_ocxChat.GetChannels());

if (!m_iLastTab) m_ChatRoom[m_iLastTab]->OnFocus(TRUE); // the first tab needs to get focus

//give focus to the new tab
m_ctrlTabWnd.SetCurrentTab(m_iLastTab);

//redraw and update status
Invalidate();

}

void CChappyView::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)
{
TRACE0("CChappyView::OnSelchangeTab1\n");

int iOldTab;

iOldTab = m_iVisibleTab;
m_iVisibleTab = m_ctrlTabWnd.GetCurFocus();

//hide them all
m_ctrlRoomList.ShowWindow(SW_HIDE);
m_ctrlPrivateMsg.ShowWindow(SW_HIDE);

if ( (LIST_TAB != iOldTab) && (MESSAGE_TAB != iOldTab) )
{
if (m_ChatRoom[iOldTab])
{
m_ChatRoom[iOldTab]->OnFocus(FALSE);
}
}

switch (m_iVisibleTab)
{
case LIST_TAB:
m_ctrlRoomList.ShowWindow(SW_SHOW);
break;
case MESSAGE_TAB:
//server and private messages
m_ctrlPrivateMsg.ShowWindow(SW_SHOW);
m_ctrlTabWnd.ChangeImage(m_iVisibleTab, IMG_OFF);
break;
default:
//all the other tabs are rooms
if (m_ChatRoom[m_iVisibleTab])
{
m_ChatRoom[m_iVisibleTab]->OnFocus(TRUE);
m_ctrlTabWnd.ChangeImage(m_iVisibleTab, IMG_OFF);
}
}

Invalidate();

*pResult = 0;
}

void CChappyView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);

m_cx = cx;
m_cy = cy;
}

BOOL CChappyView::OnToolTipNotify( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
TRACE0 ("OnToolTipNotify\n");

// need to handle both ANSI and UNICODE versions of the message
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
UINT nID = pNMHDR->idFrom;
TRACE ("  HWnd: %u\n",nID);

if (((pNMHDR->code == TTN_NEEDTEXTA) || (pNMHDR->code == TTN_NEEDTEXTW)) &&
(pTTTA->uFlags & TTF_IDISHWND) )
{
        // idFrom is actually the HWND of the tool
        nID = ::GetDlgCtrlID((HWND)nID);
       TRACE ("  ID: %u\n",nID);
}

if( (nID == ID_BTN_SEND) ||
  (nID == ID_BTN_WHISPER) )
{
CString szTipText;
szTipText.LoadString(nID);

        //pTTT->lpszText =(LPTSTR) (LPCTSTR) szTipText;// MAKEINTRESOURCE(nID);
        pTTTA->hinst = AfxGetResourceHandle();
pTTTW->hinst = AfxGetResourceHandle();

#ifndef _UNICODE
if (pNMHDR->code == TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText, (LPTSTR) (LPCTSTR) szTipText, _countof(pTTTA->szText));
else
_mbstowcsz(pTTTW->szText, (LPTSTR) (LPCTSTR) szTipText, _countof(pTTTA->szText));
#else
if (pNMHDR->code == TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText, (LPTSTR) (LPCTSTR) szTipText, _countof(pTTTA->szText));
else
lstrcpyn(pTTTW->szText, (LPTSTR) (LPCTSTR) szTipText, _countof(pTTTA->szText));
#endif

// bring the tooltip window above other popup windows
::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);

return TRUE;
}

    return FALSE;
}

void CChappyView::OnInitialUpdate()
{
CView::OnInitialUpdate();

BOOL ret = EnableToolTips(TRUE);
ASSERT(ret);

}

//////////////////////////////////////////////////////////////////////////////
//  OCX Event handlers
//////////////////////////////////////////////////////////////////////////////

BEGIN_EVENTSINK_MAP(CChappyView, CView)
    //{{AFX_EVENTSINK_MAP(CChappyView)

ON_EVENT(CChappyView, IDC_OCX_CHAT, 116 /* OnAddMember */, OnOnAddMemberMschatpr, VTS_DISPATCH VTS_BSTR VTS_DISPATCH)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 114 /* OnBeginEnumeration */, OnOnBeginEnumerationMschatpr, VTS_DISPATCH VTS_I4)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 113 /* OnChannelError */, OnOnChannelErrorMschatpr, VTS_DISPATCH VTS_I4 VTS_BSTR)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 123 /* OnChannelProperty */, OnOnChannelPropertyMschatpr, VTS_DISPATCH VTS_DISPATCH)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 124 /* OnChannelPropertyChanged */, OnOnChannelPropertyChangedMschatpr, VTS_DISPATCH VTS_BSTR VTS_BSTR VTS_VARIANT VTS_VARIANT)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 112 /* OnChannelState */, OnOnChannelStateMschatpr, VTS_DISPATCH VTS_I4)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 111 /* OnConnectionError */, OnOnConnectionErrorMschatpr, VTS_I4 VTS_BSTR VTS_VARIANT)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 110 /* OnConnectionState */, OnOnConnectionStateMschatpr, VTS_I4)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 117 /* OnDelMember */, OnOnDelMemberMschatpr, VTS_DISPATCH VTS_BSTR)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 115 /* OnEndEnumeration */, OnOnEndEnumerationMschatpr, VTS_DISPATCH VTS_I4)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 126 /* OnInvitation */, OnOnInvitationMschatpr, VTS_BSTR VTS_DISPATCH)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 127 /* OnKnock */, OnOnKnockMschatpr, VTS_DISPATCH VTS_DISPATCH VTS_I4)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 122 /* OnMemberKicked */, OnOnMemberKickedMschatpr, VTS_DISPATCH VTS_BSTR VTS_BSTR VTS_BSTR)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 120 /* OnMemberProperty */, OnOnMemberPropertyMschatpr, VTS_DISPATCH VTS_DISPATCH)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 121 /* OnMemberPropertyChanged */, OnOnMemberPropertyChangedMschatpr, VTS_DISPATCH VTS_BSTR VTS_BSTR VTS_BSTR VTS_VARIANT VTS_VARIANT)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 130 /* OnMessage */, OnOnMessageMschatpr, VTS_DISPATCH VTS_BSTR VTS_I4 VTS_VARIANT VTS_VARIANT VTS_VARIANT)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 128 /* OnPrivateMessage */, OnOnPrivateMessageMschatpr, VTS_BSTR VTS_I4 VTS_VARIANT VTS_VARIANT)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 131 /* OnProtocolMessage */, OnOnProtocolMessageMschatpr, VTS_BSTR VTS_BSTR VTS_VARIANT VTS_PBOOL)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 125 /* OnServerProperty */, OnOnServerPropertyMschatpr, VTS_DISPATCH)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 129 /* OnServerTextMessage */, OnOnServerTextMessageMschatpr, VTS_I4 VTS_BSTR)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 118 /* OnUserProperty */, OnOnUserPropertyMschatpr, VTS_DISPATCH)
ON_EVENT(CChappyView, IDC_OCX_CHAT, 119 /* OnUserPropertyChanged */, OnOnUserPropertyChangedMschatpr, VTS_BSTR VTS_BSTR VTS_BSTR VTS_VARIANT VTS_VARIANT)

//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

void CChappyView::OnOnMessageMschatpr(LPDISPATCH Channel, LPCTSTR SenderNickname, long MessageType, const VARIANT FAR& Message, const VARIANT FAR& RecipientNicknames, const VARIANT FAR& DataMessageTag)
{
TRACE ("--- CChappyView::OnOnMessageMschatpr - %u %s %u %u \n", Channel, SenderNickname, MessageType, Message.vt );

if (((VT_BSTR == Message.vt) || ((VT_BYREF | VT_VARIANT) == Message.vt))&&
((msgtNormal == MessageType) || (msgtWhisper == MessageType)) || (msgtAction == MessageType)) //comic chat sends info messages
{
CString szTmp;
if (VT_BSTR == Message.vt)
{
szTmp = Message.bstrVal;
}
else if ((VT_BYREF | VT_VARIANT) == Message.vt)
{
szTmp = Message.pvarVal->bstrVal;
}

long iTab = GetTabFromIndex(Channel);
if (m_ChatRoom[iTab])
{
long iMT = IMG_OFF;
if (iTab != m_iVisibleTab)
{
if (msgtNormal == MessageType)
{
iMT = IMG_SAY;
}
else if (msgtWhisper == MessageType)
{
iMT = IMG_WHISPER;
}
else if (msgtAction == MessageType)
{
iMT = IMG_DO;
}
m_ctrlTabWnd.ChangeImage(iTab, iMT);
}

m_ChatRoom[iTab]->m_ctrlHistoryWnd.UpdateMessageUI(SenderNickname, MessageType, szTmp);
}
}

}

void CChappyView::OnOnPrivateMessageMschatpr(LPCTSTR SenderNickname, long PrivateMessageType, const VARIANT FAR& Message, const VARIANT FAR& DataMessageTag)
{
TRACE0 ("--- CChappyView::OnOnPrivateMessageMschatpr\n");

if ((VT_BSTR == Message.vt) && (0x20 > PrivateMessageType)) //comic chat sends info messages
{
CString szTmp(Message.bstrVal);

m_ctrlPrivateMsg.AddText("- Private message from ", TRUE);
m_ctrlPrivateMsg.UpdateMessageUI(SenderNickname, PrivateMessageType, szTmp);
m_ctrlTabWnd.ChangeImage(1, IMG_IMPORTANT);
//m_ChatRoom[m_iVisibleTab].AddText("--- End Private message ---", TRUE);
}
}

void CChappyView::OnOnServerTextMessageMschatpr(long ServerMessageType, LPCTSTR Text)
{
TRACE0 ("--- CChappyView::OnOnServerTextMessageMschatpr\n");

static long iCurrentType_s = -1;
m_ctrlPrivateMsg.SetTextColor(RGB(0, 64, 128)); //nice blue
//Post a message in the history control
//CString szInMsg;
if (ServerMessageType != iCurrentType_s)
{
//print the message type only once
iCurrentType_s = ServerMessageType;
CString szTmp;
szTmp.LoadString(IDS_SMT_NORMAL + ServerMessageType);
m_ctrlPrivateMsg.AddText(szTmp, TRUE);
}
m_ctrlPrivateMsg.AddText("\t");
m_ctrlPrivateMsg.AddText(Text);
m_ctrlPrivateMsg.AddText("\n");
m_ctrlPrivateMsg.SetTextColor(GetSysColor (COLOR_WINDOWTEXT)); //default text color
}

void CChappyView::OnOnAddMemberMschatpr(LPDISPATCH Channel, LPCTSTR Nickname, LPDISPATCH MemberItems)
{
TRACE2 ("--- CChappyView::OnOnAddMemberMschatpr: Channel: %u Nickname: %s \n",Channel,Nickname);

//add the user to the listview control
HTREEITEM iTreeItem;
long iTab = GetTabFromIndex(Channel);

ASSERT (m_ChatRoom[iTab]);
iTreeItem = m_ChatRoom[iTab]->m_ctrlPartListWnd.InsertItem(Nickname);

ASSERT (Channel);
IChannelObjectIChannel(Channel);
IChannel.m_bAutoRelease = FALSE;  //do not release the object when done

//let's go get some of thouse hard to find properties
IChatItemsITmpChatItems = IChannel.GetMemberProperty("IPAddress", COleVariant(Nickname));
ASSERT (ITmpChatItems);

//let's see what the server returned
COleSafeArray vValidItemsArray(ITmpChatItems.GetValidItems());

TRACE1 ("Array Dimensions: %i \n",vValidItemsArray.GetDim());

long iSize = (long) vValidItemsArray.GetOneDimSize();
TRACE1 (" Number of elements: %i \n",iSize);

HLOCAL element = LocalAlloc (GMEM_ZEROINIT | LMEM_FIXED ,vValidItemsArray.GetElemSize()+2);//+2 just to be safe
for (long i = 0; i <iSize ; ++i)
{
COleVariant data((LPCSTR)element);
vValidItemsArray.GetElement(&i, (void *) data);
CString szItemName (data.bstrVal);
TRACE2 ("%u. Item Name: %s\n",i, szItemName);
COleVariant vItem(ITmpChatItems.GetItem(szItemName));

CString szTextOut;
if (VT_BSTR == vItem.vt)
{
CString szItem(vItem.bstrVal);
szTextOut.Format("%s: %s", szItemName, szItem);
}
else if ((VT_I2 == vItem.vt) || (VT_I4 == vItem.vt))
{
long iItem = (VT_I2 == vItem.vt) ? vItem.iVal : vItem.lVal;
szTextOut.Format("%s: %u", szItemName, iItem);
}

if (!szTextOut.IsEmpty())
{
TRACE0 (szTextOut);
TRACE0 ("\n");

//insert the item

m_ChatRoom[iTab]->m_ctrlPartListWnd.InsertItem(szTextOut, iTreeItem);

}
}
LocalFree(element);

//Post a message in the history control
CString szInMsg;
CString szTmp;
szTmp.LoadString(IDS_ADD_MEMBER);
szInMsg.Format("%s %s\n\n",Nickname, szTmp); //blabla entered the room
m_ChatRoom[iTab]->m_ctrlHistoryWnd.AddText(szInMsg, TRUE);

//m_ctrlPartListWnd.InsertItem("Name", item);
//m_ctrlPartListWnd.InsertItem("e-mail", item);

}

void CChappyView::OnOnDelMemberMschatpr(LPDISPATCH Channel, LPCTSTR Nickname)
{
TRACE0 ("--- CChappyView::OnOnDelMemberMschatpr\n");
long iTab = GetTabFromIndex(Channel);

ASSERT(m_ChatRoom[iTab]);
m_ChatRoom[iTab]->m_ctrlPartListWnd.DeleteItemFromText (Nickname);

//Post a message in the history control
CString szInMsg;
CString szTmp;
szTmp.LoadString(IDS_DEL_MEMBER);
szInMsg.Format("%s %s\n\n",Nickname, szTmp); //blabla exit the room
m_ChatRoom[iTab]->m_ctrlHistoryWnd.AddText(szInMsg, TRUE);

}

void CChappyView::OnOnChannelStateMschatpr(LPDISPATCH Channel, long NewChannelState)
{
TRACE1 ("--- CChappyView::OnOnChannelStateMschatpr %u\n", NewChannelState);
long iTab = GetTabFromIndex(Channel);

ASSERT(m_ChatRoom[iTab]);

if (chsClosed == NewChannelState)
{
m_ChatRoom[iTab]->DestroyRoom();
//delete (m_ChatRoom[iTab]);
//m_ctrlTabWnd.DeleteItem(iTab);

//rename the tab
CString szEmptyRoomTab;
szEmptyRoomTab.LoadString(IDS_CHAT_TAB);
m_ctrlTabWnd.RenameTab(iTab, szEmptyRoomTab);

}

}

void CChappyView::OnOnConnectionStateMschatpr(long NewConnectionState)
{
TRACE1 ("--- CChappyView::OnOnConnectionStateMschatpr, State: %u\n", NewConnectionState);

m_iServerConnectionState = NewConnectionState;
m_iServerConnectionType = (csConnected <= m_iServerConnectionState) ? m_ocxChat.GetConnectionType() : -1;

//as soon as we are logged in we display the list of rooms (if requested)
if (m_bShowRoomList && (csLogged == m_iServerConnectionState))
{
OnRoomListrooms();
}
else if (csLogged == m_iServerConnectionState) //just once
{
//no room list
CString szNoRoomList;
szNoRoomList.LoadString(IDS_NO_ROOM_LIST_MSG);
m_ctrlRoomList.AddColumn(szNoRoomList);
}
else if (csDisconnected == m_iServerConnectionState) //just once
{
//clean up !!
//CleanUp();
}
}

void CChappyView::OnOnChannelPropertyMschatpr(LPDISPATCH Channel, LPDISPATCH ChannelItems)
{
TRACE1 ("--- CChappyView::OnOnChannelPropertyMschatpr: Channel: %u\n",Channel);

static int iItemIndex = -1;

if (NULL == Channel)
{
//populate the room list
CChatItemsITmpChatItems (ChannelItems);
ITmpChatItems.m_bAutoRelease = FALSE;
ASSERT (ITmpChatItems);
CString szType = ITmpChatItems.GetAssociatedType();

//let's see what the server returned
COleSafeArray vValidItemsArray(ITmpChatItems.GetValidItems());

TRACE1 ("Array Dimensions: %i \n",vValidItemsArray.GetDim());

long iSize = (long) vValidItemsArray.GetOneDimSize();
TRACE1 (" Number of elements: %i \n",iSize);

HLOCAL element = LocalAlloc (GMEM_ZEROINIT | LMEM_FIXED ,vValidItemsArray.GetElemSize()+2);//+2 just to be safe
for (long i = 0; i <iSize ; ++i)
{
COleVariant data((LPCSTR)element);
vValidItemsArray.GetElement(&i, (void *) data);
CString szItemName (data.bstrVal);
TRACE2 ("%u. Item Name: %s\n",i, szItemName);

//insert the a new column if we do not have one already
if ( i >= m_ctrlRoomList.GetNumOfColumns())
{
m_ctrlRoomList.AddColumn(szItemName);
}

//get the item
COleVariant vItem(ITmpChatItems.GetItem(szItemName));

CString szTextOut;
if (VT_BSTR == vItem.vt)
{
CString szItem(vItem.bstrVal);
szTextOut.Format("%s", szItem);
}
else if ((VT_I2 == vItem.vt) || (VT_I4 == vItem.vt))
{
long iItem = (VT_I2 == vItem.vt) ? vItem.iVal : vItem.lVal;
szTextOut.Format("%u", iItem);
}

if (!szTextOut.IsEmpty())
{
TRACE0 (szTextOut);
TRACE0 ("\n");

if (0 == i) // means that this is the room name
{
//insert the item
m_ctrlRoomList.InsertNextItem(szTextOut);
}
else
{
//add the sub items to the current item
m_ctrlRoomList.AddSubItem(i, szTextOut);
}
}
}
LocalFree(element);
}
}

void CChappyView::OnOnMemberKickedMschatpr(LPDISPATCH Channel, LPCTSTR KickedNickname, LPCTSTR KickerNickname, LPCTSTR Reason)
{
TRACE0 ("--- CChappyView::OnOnMemberKickedMschatpr\n");

ASSERT (Channel);
IChannelObjectIChannel(Channel);
IChannel.m_bAutoRelease = FALSE;  //do not release the object when done

CString szFormat;
szFormat.LoadString(IDS_KICKED_MSG);
CString szMessage;
szMessage.Format ((LPCTSTR) szFormat, KickedNickname, IChannel.GetChannelName(), Reason);

TRACE0 (szMessage);
TRACE0 ("\n");

AfxMessageBox(szMessage);
}

void CChappyView::OnOnChannelErrorMschatpr(LPDISPATCH Channel, long ErrorCode, LPCTSTR Description)
{
TRACE0 ("--- CChappyView::OnOnChannelErrorMschatpr\n");
ASSERT (Channel);
IChannelObjectIChannel(Channel);
IChannel.m_bAutoRelease = FALSE;  //do not release the object when done

CString szFormat;
szFormat.LoadString(IDS_CHANNEL_ERROR_MSG);
CString szMessage;
szMessage.Format ((LPCTSTR) szFormat, IChannel.GetChannelName(), ErrorCode, Description);

TRACE0 (szMessage);
TRACE0 ("\n");

AfxMessageBox(szMessage);
}

void CChappyView::OnOnConnectionErrorMschatpr(long ErrorCode, LPCTSTR Description, const VARIANT FAR& ChannelName)
{
TRACE0 ("--- CChappyView::OnOnConnectionErrorMschatpr\n");

CString szFormat;
szFormat.LoadString(IDS_CONNECTION_ERROR_MSG);
CString szMessage;
szMessage.Format ((LPCTSTR) szFormat, ErrorCode, Description);

TRACE0 (szMessage);
TRACE0 ("\n");

AfxMessageBox(szMessage);
}

void CChappyView::OnOnChannelPropertyChangedMschatpr(LPDISPATCH Channel, LPCTSTR ModifierNickname, LPCTSTR ChannelPropertyName, const VARIANT FAR& OldChannelProperty, const VARIANT FAR& NewChannelProperty)
{
TRACE0 ("--- CChappyView::OnOnChannelPropertyChangedMschatpr\n");
}

void CChappyView::OnOnServerPropertyMschatpr(LPDISPATCH ServerItems)
{
TRACE0 ("--- CChappyView::OnOnServerPropertyMschatpr\n");
}

void CChappyView::OnOnProtocolMessageMschatpr(LPCTSTR MessagePrefix, LPCTSTR MessageCommand, const VARIANT FAR& MessageParameters, BOOL FAR* EnableDefault)
{
TRACE0 ("--- CChappyView::OnOnProtocolMessageMschatpr\n");
}

void CChappyView::OnOnUserPropertyMschatpr(LPDISPATCH UserItems)
{
TRACE0 ("--- CChappyView::OnOnUserPropertyMschatpr\n");
}

void CChappyView::OnOnUserPropertyChangedMschatpr(LPCTSTR ModifiedNickname, LPCTSTR ModifierNickname, LPCTSTR UserPropertyName, const VARIANT FAR& OldUserProperty, const VARIANT FAR& NewUserProperty)
{
TRACE0 ("--- CChappyView::OnOnUserPropertyChangedMschatpr\n");
}

void CChappyView::OnOnMemberPropertyMschatpr(LPDISPATCH Channel, LPDISPATCH MemberItems)
{
TRACE0 ("--- CChappyView::OnOnMemberPropertyMschatpr\n");
}

void CChappyView::OnOnMemberPropertyChangedMschatpr(LPDISPATCH Channel, LPCTSTR ModifiedNickname, LPCTSTR ModifierNickname, LPCTSTR MemberPropertyName, const VARIANT FAR& OldMemberProperty, const VARIANT FAR& NewMemberProperty)
{
TRACE0 ("--- CChappyView::OnOnMemberPropertyChangedMschatpr\n");
}

void CChappyView::OnOnInvitationMschatpr(LPCTSTR ChannelName, LPDISPATCH InviterItems)
{
TRACE0 ("--- CChappyView::OnOnInvitationMschatpr\n");
}
void CChappyView::OnOnKnockMschatpr(LPDISPATCH Channel, LPDISPATCH KnockerItems, long ErrorCode)
{
TRACE0 ("--- CChappyView::OnOnKnockMschatpr\n");
}

void CChappyView::OnOnBeginEnumerationMschatpr(LPDISPATCH Channel, long EnumType)
{
TRACE0 ("--- CChappyView::OnOnBeginEnumerationMschatpr\n");
}

void CChappyView::OnOnEndEnumerationMschatpr(LPDISPATCH Channel, long EnumType)
{
TRACE2 ("--- CChappyView::OnOnEndEnumerationMschatpr - %u %u\n", Channel, EnumType);

}

//////////////////////////////////////////////////////////////////////////////
//  Command message handler
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//  Sending chat messages
//////////////////////////////////////////////////////////////////////////////

void CChappyView::OnBtnSend()
{
PostChatMessage(msgtNormal);
}

void CChappyView::OnBtnWhisper()
{
PostChatMessage(msgtWhisper);
}

void CChappyView::OnBtnDo()
{
PostChatMessage(msgtAction);

}

void CChappyView::OnBtnThink()
{
PostChatMessage(msgtThought);
}

void CChappyView::PostChatMessage(long iMsgType_p)
{

if (!IsRoomTab(m_iVisibleTab))
{
//not in a room tab
return;
}

ASSERT(m_ChatRoom[m_iVisibleTab]);

//select all
m_ctrlInputWnd.SetSel(0,-1);
if (SEL_EMPTY == m_ctrlInputWnd.GetSelectionType())
{
//notting to send
return ;
}

//get the selection text
CString szSendText = m_ctrlInputWnd.GetSelText();
//The control clean itself on VK_RETURN but we need to clean it
//if we hit the toobar button
m_ctrlInputWnd.Clear();

//Send the text if we got any
if ("" == szSendText)
{
//notting to send
return ;
}

m_ChatRoom[m_iVisibleTab]->SendMessage(m_szMyAlias, iMsgType_p, szSendText);

//clean up the control
m_ctrlInputWnd.SetSel(0,-1);
m_ctrlInputWnd.Clear();
}

void CChappyView::OnHistoryClear()
{
TRACE("CChappyView::OnFileSave\n");

ASSERT (m_iVisibleTab > LIST_TAB);

CHistory*ctrlVisibleHistory;

if (m_iVisibleTab == MESSAGE_TAB)
{
ctrlVisibleHistory = &m_ctrlPrivateMsg;
}
else
{
ctrlVisibleHistory = m_ChatRoom[m_iVisibleTab]->GetRoomHistoryCtrlPtr();
}

ctrlVisibleHistory->ClearAll();

}

//////////////////////////////////////////////////////////////////////////////
//  Log in and out of servers
//////////////////////////////////////////////////////////////////////////////

void CChappyView::OnRoomLogin()
{
ASSERT (m_ocxChat);

long iState = GetServerConnectionState();

//we enable the menu only if we are disconnected or logged
ASSERT ( (csLogged == iState) || (csDisconnected == iState));

CLogInDlg LogInDlg;

LogInDlg.m_szNickname.LoadString(IDS_DEFAULT_NICKNAME);
LogInDlg.m_szServerName.LoadString(IDS_DEFAULT_SERVER);

int iRet = LogInDlg.DoModal();
if (IDOK == iRet)
{
//this will tell us if we display the room list at startup
m_bShowRoomList = LogInDlg.m_bShowRoomList;

COleVariant vRealName;//(_T("myrealname"));
COleVariant vUserName; //the ocx will get windows user name
COleVariant vSecurity;(_T("NTLM;ALL"));
COleVariant vPassword(LogInDlg.m_szPassword);
//COleVariant vUserModes(1L,VT_I4);
m_szMyAlias= LogInDlg.m_szNickname;
m_ocxChat.Login(LogInDlg.m_szServerName, LogInDlg.m_szNickname, vUserName , vRealName, vPassword, vSecurity);
//clean the list view
m_ctrlRoomList.ClearList();
}
}

void CChappyView::OnRoomLogout()
{
long iState = GetServerConnectionState();

ASSERT (csDisconnected != iState);
ASSERT (csDisconnecting != iState);

m_ctrlPrivateMsg.AddText("\n");

if ( (csConnecting == iState) ||
 (csLogging == iState) )
{
m_ocxChat.CancelConnectLogin();
}
else
{
m_ocxChat.Disconnect();
}

//clean the list view
m_ctrlRoomList.ClearList();
}

//////////////////////////////////////////////////////////////////////////////
//  Enter and exit rooms
//////////////////////////////////////////////////////////////////////////////

void CChappyView::OnRoomListEnterRoom()
{

CString szRoomName = m_ctrlRoomList.GetItemText(m_ctrlRoomList.GetSelectedItem(), 0);

CPWordDlgdlgPW;
if (IDOK == dlgPW.DoModal())
{
//add the tab
OnFileNew();
//enter room
EnterRoom(m_iLastTab, szRoomName, dlgPW.m_szPassword);
}

}

void CChappyView::EnterRoom(long iTab, CString & szRoom, CString & szPassword)
{
//we never use the fist 2 items in the array of rooms
ASSERT(m_ChatRoom[iTab]);

m_ChatRoom[iTab]->EnterRoom(iTab, szRoom, szPassword);

//if I succed change the name of the tab
m_ctrlTabWnd.RenameTab(iTab, szRoom);
m_ctrlTabWnd.SetCurrentTab(m_iLastTab);

//The tab grabs focus and covers the history, a repaint fixes it
Invalidate();
}

void CChappyView::OnRoomConnect()
{
ASSERT(m_ocxChat);

CEnterDlgdlgEnter;

int iDlgRet = dlgEnter.DoModal();

if (iDlgRet == IDOK)
{

TRACE1("Room URL: %s\n",(LPCTSTR) dlgEnter.m_szRoom);

//enter room
EnterRoom(m_iVisibleTab, dlgEnter.m_szRoom, dlgEnter.m_szPassword);

}
else
{
if (iDlgRet != IDCANCEL)
{
AfxMessageBox ("Error Displaying the dialog: %i", iDlgRet);
}
}

}

void CChappyView::OnRoomDisconnect()
{
if (csLogged == m_iServerConnectionState)
{
//exit the room
ASSERT (m_ChatRoom[m_iVisibleTab]);
m_ChatRoom[m_iVisibleTab]->LeaveChannel();
}
}

//////////////////////////////////////////////////////////////////////////////
//  UI Update handlers
//////////////////////////////////////////////////////////////////////////////

void CChappyView::OnUpdateRoomConnect(CCmdUI* pCmdUI)
{
pCmdUI->Enable ((csLogged == m_iServerConnectionState)&&
(IsRoomTab(m_iVisibleTab))&&
(chsClosed == GetCurrentChannelState())) ;

}

void CChappyView::OnUpdateRoomDisconnect(CCmdUI* pCmdUI)
{
pCmdUI->Enable ((csLogged == m_iServerConnectionState)&&
(IsRoomTab(m_iVisibleTab))&&
(chsClosed != GetCurrentChannelState()));
}

void CChappyView::OnUpdateRoomLogin(CCmdUI* pCmdUI)
{
pCmdUI->Enable (csDisconnected == m_iServerConnectionState);

}

void CChappyView::OnUpdateRoomLogout(CCmdUI* pCmdUI)
{
pCmdUI->Enable ((csDisconnected != m_iServerConnectionState) &&
(csDisconnecting != m_iServerConnectionState));

CString szNewMenuText;
if ((csConnecting == m_iServerConnectionState) ||
(csLogging == m_iServerConnectionState))
{
szNewMenuText.LoadString(IDS_MNU_CANCEL_LOGGING);
}
else
{
szNewMenuText.LoadString(IDS_MNU_LOGOUT);
}

pCmdUI->SetText(szNewMenuText);

}

void CChappyView::OnUpdateRoomListEnterRoom(CCmdUI* pCmdUI)
{
pCmdUI->Enable( (-1 != m_ctrlRoomList.GetSelectedItem()) );
}

void CChappyView::OnUpdateRoomListRooms(CCmdUI* pCmdUI)
{
pCmdUI->Enable ((csDisconnected != m_iServerConnectionState) &&
(csDisconnecting != m_iServerConnectionState));

}

void CChappyView::OnUpdatePartlistKick(CCmdUI* pCmdUI)
{
pCmdUI->Enable((IsRoomTab(m_iVisibleTab))&&
(!m_ChatRoom[m_iVisibleTab]->GetSelectedMember().IsEmpty()));
}

void CChappyView::OnUpdatePartlistBan(CCmdUI* pCmdUI)
{
pCmdUI->Enable((IsRoomTab(m_iVisibleTab))&&
(!m_ChatRoom[m_iVisibleTab]->GetSelectedMember().IsEmpty()));

}

void CChappyView::OnUpdateServerBanuser(CCmdUI* pCmdUI)
{
pCmdUI->Enable ((csDisconnected != m_iServerConnectionState) &&
(csDisconnecting != m_iServerConnectionState));
}

void CChappyView::OnUpdatePartlistMakehost(CCmdUI* pCmdUI)
{
pCmdUI->Enable((IsRoomTab(m_iVisibleTab))&&
(!m_ChatRoom[m_iVisibleTab]->GetSelectedMember().IsEmpty()));

}

void CChappyView::OnUpdatePartlistMakeparticipant(CCmdUI* pCmdUI)
{
pCmdUI->Enable((IsRoomTab(m_iVisibleTab))&&
(!m_ChatRoom[m_iVisibleTab]->GetSelectedMember().IsEmpty()));
}

void CChappyView::OnUpdateFileSave(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_iVisibleTab > LIST_TAB);

}

void CChappyView::OnUpdateHistoryClear(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_iVisibleTab > LIST_TAB);
}

//////////////////////////////////////////////////////////////////////////////
//  Room list
//////////////////////////////////////////////////////////////////////////////

void CChappyView::OnRoomListrooms()
{
TRACE0 ("--- CChappyView::OnRoomListrooms\n");

//clean the list view
m_ctrlRoomList.ClearList();

CChatItemsITmpChatItems = m_ocxChat.GetNewChatItems();
ASSERT (ITmpChatItems);
ITmpChatItems.SetAssociatedType("Query");
m_ocxChat.ListChannels(ITmpChatItems);
//the list is populated asyncronusly in OnOnChannelPropertyMschatpr
}

//////////////////////////////////////////////////////////////////////////////
//  Kick and Ban
//////////////////////////////////////////////////////////////////////////////

void CChappyView::OnPartlistKick()
{
CKickDlgdlgKick;

if (IDOK == dlgKick.DoModal())
{
m_ChatRoom[m_iVisibleTab]->KickMember(dlgKick.m_szReason);
}
}

void CChappyView::OnPartlistBan()
{
m_ChatRoom[m_iVisibleTab]->BanMember(m_ocxChat.GetNewChatItems());
}

void CChappyView::OnServerBanuser()
{
//pop the dialog
CBanDlg dlgBan;

dlgBan.m_iBan= 1;
dlgBan.m_iOperation = 0;
dlgBan.m_iProperty= 0;
dlgBan.m_iDuration= 1;

if ( IDOK == dlgBan.DoModal())
{

CChatItemsITmpChatItems = m_ocxChat.GetNewChatItems();
ASSERT (ITmpChatItems);
ITmpChatItems.SetAssociatedType("Query");

CString szProperty;
szProperty.LoadString(IDS_IP_ADDRESS_ITEM+dlgBan.m_iProperty);
ITmpChatItems.SetItem (szProperty, COleVariant(dlgBan.m_szValue));

if (0 < dlgBan.m_iOperation)
{
CString szOperationName;
szOperationName.LoadString(IDS_IP_ADDRESS_OP_NAME+dlgBan.m_iProperty);
CString szOperation;
szOperation.LoadString(IDS_CONTAINS_OP+dlgBan.m_iOperation-1);
ITmpChatItems.SetItem (szOperationName, COleVariant(szOperation));
}

m_ocxChat.BanUsers(ITmpChatItems, dlgBan.m_iBan, COleVariant(dlgBan.m_szReason), COleVariant(dlgBan.m_iDuration));

}
}

//////////////////////////////////////////////////////////////////////////////
//  change members property
//////////////////////////////////////////////////////////////////////////////

void CChappyView::OnPartlistMakehost()
{
m_ChatRoom[m_iVisibleTab]->MakeHost(TRUE);
}

void CChappyView::OnPartlistMakeparticipant()
{
m_ChatRoom[m_iVisibleTab]->MakeHost(FALSE);
}

//////////////////////////////////////////////////////////////////////////////
//Utilities
//////////////////////////////////////////////////////////////////////////////

long CChappyView::GetIndexFromChannel(LPDISPATCH Channel_p)
{
for (int i = 0; i <= m_iLastTab; ++i)
{

if(m_ChatRoom[i] &&
(Channel_p == m_ChatRoom[i]->GetChannel()))
{
return m_ChatRoom[i]->GetIndex();
}
}

return -1;

//
//ASSERT (Channel_p);
//IChannelObjectIChannel(Channel_p);
//IChannel.m_bAutoRelease = FALSE;  //do not release the object when done
////The tabs are in sync with the indexes
//return (IChannel.GetIndex());

}

long CChappyView::AddTab(CString & szTitle_p)
{
m_iLastTab++;

//insert a new tab
TC_ITEM tcItem;
tcItem.mask = TCIF_TEXT;

char szTabTitle[256];
sprintf(szTabTitle, " %s ",(LPCTSTR) szTitle_p); //e.g." Rooms "
tcItem.pszText = szTabTitle;

int ret =  m_ctrlTabWnd.InsertItem(m_iLastTab ,&tcItem);
ASSERT (m_iLastTab==ret);
return m_iLastTab;
}

//|void CChappyView::CleanUp()
//|{
//|for (long i=0; i < MAX_ROOMS; ++i)
//|{
//|if (m_ChatRoom[i])
//|{
//|m_ChatRoom[i]->DestroyRoom();
//|
//|CString szEmptyRoomTab;
//|szEmptyRoomTab.LoadString(IDS_CHAT_TAB);
//|m_ctrlTabWnd.RenameTab(i, szEmptyRoomTab);
//|}
//|}
//|}

//////////////////////////////////////////////////////////////////////////////
//  SAVE
//////////////////////////////////////////////////////////////////////////////

void CChappyView::OnFileSave()
{
TRACE("CChappyView::OnFileSave\n");

ASSERT (m_iVisibleTab > LIST_TAB);

CHistory*ctrlVisibleHistory;

if (m_iVisibleTab == MESSAGE_TAB)
{
ctrlVisibleHistory = &m_ctrlPrivateMsg;
}
else
{
ctrlVisibleHistory = m_ChatRoom[m_iVisibleTab]->GetRoomHistoryCtrlPtr();
}

ctrlVisibleHistory->OnFileSave();

}


CHPPYVW.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       ChappyView.h : interface of the CChappyView class
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __CHAPPYVIEW_H__
#define __CHAPPYVIEW_H__

#include "globals.h"

#define LIST_TAB0
#define MESSAGE_TAB1
#define MAX_ROOMS50

#define _countof(array) (sizeof(array)/sizeof(array[0]))

#include "InputRE.h"
#include "RTab.h"
#include "TB.h"
#include "Room.h"
#include "ChppyDoc.h"
//#include "mschatx.h"
#include "mschatpr.h"
#include "RListCtr.h"
#include "History.h"

#define INPUT_RICHEDIT_SIZE 85
#define SEND_TB_SIZE29

class CChappyView : public CView
{
protected: // create from serialization only
CChappyView();
DECLARE_DYNCREATE(CChappyView)

// Attributes
public:
CChappyDoc* GetDocument();

// Operations
public:
//inline CWnd &GetHistoryCtrl(){ASSERT (m_iVisibleTab); return m_ctrlHistoryWnd[m_iVisibleTab];};
inline CWnd &GetTabCtrl(){return m_ctrlTabWnd;};
inline CWnd &GetToolBarCtrl(){return m_ctrlSendToolBar;};
inline CWnd &GetInputCtrl(){return m_ctrlInputWnd;};

inline long GetServerConnectionState(){return m_iServerConnectionState;};
inline long GetServerConnectionType(){return m_iServerConnectionType;};
inline long GetCurrentChannelState(){return ((csLogged <= m_iServerConnectionState)&&
 (IsRoomTab(m_iVisibleTab))&&
 (m_ChatRoom[m_iVisibleTab]->GetChannelObj()))
? m_ChatRoom[m_iVisibleTab]->GetChannelObj().GetChannelState()
: chsClosed;};

inline BOOL IsRoomTab(long iTabNumber){return (m_iVisibleTab > 1);};

inline long GetTabFromIndex(LPDISPATCH Channel_p) { return (GetIndexFromChannel(Channel_p));};

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CChappyView)
public:
virtual void OnDraw(CDC* pDC);  // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
virtual void OnInitialUpdate();
//}}AFX_VIRTUAL

// Implementation
public:
void EnterRoom(long iTab, CString &szRoom, CString &szPassword);
long AddTab(CString &szTitle_p);

longm_iServerConnectionState;
longm_iServerConnectionType;

void UpdateStatusUI();
long GetIndexFromChannel(LPDISPATCH Channel_p);
void PostChatMessage(long iMsgType_p);
virtual ~CChappyView();

BOOL OnToolTipNotify( UINT id, NMHDR * pNMHDR, LRESULT * pResult );

#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

private:
BOOLm_bShowRoomList;
intm_cy;//window size
intm_cx;

intm_iLastTab;//last tab added
intm_iVisibleTab;//current tab selected

CRoom*m_ChatRoom[MAX_ROOMS];//the list of rooms
//if you connect to more then 256 rooms you are a freak !

//the chat OCX
//IMsChatPrm_IChatPr;
CMsChatPrm_ocxChat;

CStringm_szMyAlias;

CRoomsTabm_ctrlTabWnd;
CTBm_ctrlSendToolBar;
CInputREditm_ctrlInputWnd;
CRoomListCtrlm_ctrlRoomList;
CHistorym_ctrlPrivateMsg;

private:
void CleanUp();

protected:

// Generated message map functions
protected:
//{{AFX_MSG(CChappyView)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnFileNew();
afx_msg void OnDestroy();
afx_msg void OnBtnSend();
afx_msg void OnBtnWhisper();
afx_msg void OnRoomConnect();
afx_msg void OnBtnDo();
afx_msg void OnBtnThink();
afx_msg void OnRoomDisconnect();
afx_msg void OnUpdateRoomConnect(CCmdUI* pCmdUI);
afx_msg void OnUpdateRoomDisconnect(CCmdUI* pCmdUI);
afx_msg void OnRoomLogin();
afx_msg void OnUpdateRoomLogin(CCmdUI* pCmdUI);
afx_msg void OnUpdateRoomLogout(CCmdUI* pCmdUI);
afx_msg void OnRoomLogout();
afx_msg void OnRoomListrooms();
afx_msg void OnRoomListEnterRoom();
afx_msg void OnUpdateRoomListEnterRoom(CCmdUI* pCmdUI);
afx_msg void OnOnBeginEnumerationMschatpr(LPDISPATCH Channel, long EnumType);
afx_msg void OnOnEndEnumerationMschatpr(LPDISPATCH Channel, long EnumType);
afx_msg void OnOnMessageMschatpr(LPDISPATCH Channel, LPCTSTR SenderNickname, long MessageType, const VARIANT FAR& Message, const VARIANT FAR& RecipientNicknames, const VARIANT FAR& DataMessageTag);
afx_msg void OnOnPrivateMessageMschatpr(LPCTSTR SenderNickname, long PrivateMessageType, const VARIANT FAR& Message, const VARIANT FAR& DataMessageTag);
afx_msg void OnOnAddMemberMschatpr(LPDISPATCH Channel, LPCTSTR Nickname, LPDISPATCH MemberItems);
afx_msg void OnOnDelMemberMschatpr(LPDISPATCH Channel, LPCTSTR Nickname);
afx_msg void OnOnChannelStateMschatpr(LPDISPATCH Channel, long NewChannelState);
afx_msg void OnOnConnectionStateMschatpr(long NewConnectionState);
afx_msg void OnOnProtocolMessageMschatpr(LPCTSTR MessagePrefix, LPCTSTR MessageCommand, const VARIANT FAR& MessageParameters, BOOL FAR* EnableDefault);
afx_msg void OnOnServerTextMessageMschatpr(long ServerMessageType, LPCTSTR Text);
afx_msg void OnOnUserPropertyMschatpr(LPDISPATCH UserItems);
afx_msg void OnOnUserPropertyChangedMschatpr(LPCTSTR ModifiedNickname, LPCTSTR ModifierNickname, LPCTSTR UserPropertyName, const VARIANT FAR& OldUserProperty, const VARIANT FAR& NewUserProperty);
afx_msg void OnOnMemberPropertyMschatpr(LPDISPATCH Channel, LPDISPATCH MemberItems);
afx_msg void OnOnMemberPropertyChangedMschatpr(LPDISPATCH Channel, LPCTSTR ModifiedNickname, LPCTSTR ModifierNickname, LPCTSTR MemberPropertyName, const VARIANT FAR& OldMemberProperty, const VARIANT FAR& NewMemberProperty);
afx_msg void OnOnChannelPropertyMschatpr(LPDISPATCH Channel, LPDISPATCH ChannelItems);
afx_msg void OnOnChannelPropertyChangedMschatpr(LPDISPATCH Channel, LPCTSTR ModifierNickname, LPCTSTR ChannelPropertyName, const VARIANT FAR& OldChannelProperty, const VARIANT FAR& NewChannelProperty);
afx_msg void OnOnServerPropertyMschatpr(LPDISPATCH ServerItems);
afx_msg void OnOnChannelErrorMschatpr(LPDISPATCH Channel, long ErrorCode, LPCTSTR Description);
afx_msg void OnOnConnectionErrorMschatpr(long ErrorCode, LPCTSTR Description, const VARIANT FAR& ChannelName);
afx_msg void OnOnInvitationMschatpr(LPCTSTR ChannelName, LPDISPATCH InviterItems);
afx_msg void OnOnKnockMschatpr(LPDISPATCH Channel, LPDISPATCH KnockerItems, long ErrorCode);
afx_msg void OnOnMemberKickedMschatpr(LPDISPATCH Channel, LPCTSTR KickedNickname, LPCTSTR KickerNickname, LPCTSTR Reason);
afx_msg void OnUpdateRoomListRooms(CCmdUI* pCmdUI);
afx_msg void OnPartlistKick();
afx_msg void OnFileSave();
afx_msg void OnUpdatePartlistBan(CCmdUI* pCmdUI);
afx_msg void OnPartlistBan();
afx_msg void OnServerBanuser();
afx_msg void OnUpdateServerBanuser(CCmdUI* pCmdUI);
afx_msg void OnUpdatePartlistKick(CCmdUI* pCmdUI);
afx_msg void OnPartlistMakehost();
afx_msg void OnUpdatePartlistMakehost(CCmdUI* pCmdUI);
afx_msg void OnPartlistMakeparticipant();
afx_msg void OnUpdatePartlistMakeparticipant(CCmdUI* pCmdUI);
afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI);
afx_msg void OnHistoryClear();
afx_msg void OnUpdateHistoryClear(CCmdUI* pCmdUI);
DECLARE_EVENTSINK_MAP()
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

#ifndef _DEBUG  // debug version in ChappyView.cpp
inline CChappyDoc* CChappyView::GetDocument()
   { return (CChappyDoc*)m_pDocument; }
#endif

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

#endif //__CHAPPYVIEW_H__


CHTITEMS.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       Chtitems.cpp
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.

#include "stdafx.h"
#include "chtitems.h"

/////////////////////////////////////////////////////////////////////////////
// CChatItems properties

/////////////////////////////////////////////////////////////////////////////
// CChatItems operations

CString CChatItems::GetAssociatedType()
{
CString result;
InvokeHelper(0x1, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CChatItems::SetAssociatedType(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x1, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

VARIANT CChatItems::GetValidItems()
{
VARIANT result;
InvokeHelper(0x2, DISPATCH_PROPERTYGET, VT_VARIANT, (void*)&result, NULL);
return result;
}

long CChatItems::GetItemValid(LPCTSTR ItemName)
{
long result;
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x3, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, parms,
ItemName);
return result;
}

VARIANT CChatItems::GetItem(LPCTSTR ItemName)
{
VARIANT result;
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x4, DISPATCH_PROPERTYGET, VT_VARIANT, (void*)&result, parms,
ItemName);
return result;
}

void CChatItems::SetItem(LPCTSTR ItemName, const VARIANT& newValue)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x4, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 ItemName, &newValue);
}

void CChatItems::InvalidateItem(LPCTSTR ItemName)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x37, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ItemName);
}


CHTITEMS.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       Chtitems.h
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_CHTITEMS_H__C473B984_FFA9_11D0_AD6F_00C04FC29DE5__INCLUDED_)
#define AFX_CHTITEMS_H__C473B984_FFA9_11D0_AD6F_00C04FC29DE5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.

/////////////////////////////////////////////////////////////////////////////
// CChatItems wrapper class

class CChatItems : public COleDispatchDriver
{
public:
CChatItems() {}// Calls COleDispatchDriver default constructor
CChatItems(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
CChatItems(const CChatItems& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

// Attributes
public:

// Operations
public:
CString GetAssociatedType();
void SetAssociatedType(LPCTSTR lpszNewValue);
VARIANT GetValidItems();
long GetItemValid(LPCTSTR ItemName);
VARIANT GetItem(LPCTSTR ItemName);
void SetItem(LPCTSTR ItemName, const VARIANT& newValue);
void InvalidateItem(LPCTSTR ItemName);
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_CHTITEMS_H__C473B984_FFA9_11D0_AD6F_00C04FC29DE5__INCLUDED_)


ENTERDLG.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       EnterDlg.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "EnterDlg.h"

#include "globals.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CEnterDlg dialog

CEnterDlg::CEnterDlg(CWnd* pParent /*=NULL*/)
: CDialog(CEnterDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CEnterDlg)
//m_szNickName = _T("");
m_szPassword = _T("");
m_szRoom = _T("");
//}}AFX_DATA_INIT
}

void CEnterDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CEnterDlg)
//DDX_Text(pDX, IDC_EDIT_NICKNAME, m_szNickName);
DDX_Text(pDX, IDC_EDIT_PASSWORD, m_szPassword);
DDX_Text(pDX, IDC_EDIT_ROOM, m_szRoom);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CEnterDlg, CDialog)
//{{AFX_MSG_MAP(CEnterDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEnterDlg message handlers

BOOL CEnterDlg::OnInitDialog()
{
CDialog::OnInitDialog();

//default values for the controls
m_szRoom.LoadString(IDS_DEFAULT_CHAT_ROOM);

UpdateData(FALSE); //put the data into the controls

return TRUE;  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}


ENTERDLG.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       EnterDlg.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

/////////////////////////////////////////////////////////////////////////////
// CEnterDlg dialog
#include "chappy.h"

class CEnterDlg : public CDialog
{
// Construction
public:
CEnterDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CEnterDlg)
enum { IDD = IDD_CHAT_CONNECT };
//CStringm_szNickName;
CStringm_szPassword;
CStringm_szRoom;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CEnterDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CEnterDlg)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};


FLATTB.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       FlatTB.cpp
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#define _AFX_NO_OLE_SUPPORT
#include <afxpriv.h>
#include "flatTB.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

// globals for fast drawing (shared globals)
#ifndef _MAC
static HDC hDCGlyphs = NULL;
static HDC hDCMono = NULL;
#else
#define hDCGlyphs   m_hDCGlyphs
#define hDCMono     m_hDCMono
#endif
static HBRUSH hbrDither = NULL;

/////////////////////////////////////////////////////////////////////////////
// Init / Term

#ifndef _MAC
static HBITMAP AFXAPI CreateDitherBitmap();
#else
static HBITMAP AFXAPI CreateDitherBitmap(BOOL bMonochrome);
#endif

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

// a special struct that will cleanup automatically
struct _AFX_TOOLBAR_TERM
{
~_AFX_TOOLBAR_TERM()
{
#ifndef _MAC
AfxDeleteObject((HGDIOBJ*)&hDCMono);
AfxDeleteObject((HGDIOBJ*)&hDCGlyphs);
#endif
AfxDeleteObject((HGDIOBJ*)&hbrDither);
}
};

static const _AFX_TOOLBAR_TERM toolbarTerm;

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

#ifdef AFX_CORE3_SEG
#pragma code_seg(AFX_CORE3_SEG)
#endif

// TONYCL: START: OFFICE97 LOOK AND FEEL

struct CToolBarData
{
WORD wVersion;
WORD wWidth;
WORD wHeight;
WORD wItemCount;
//WORD aItems[wItemCount]

WORD* items()
{ return (WORD*)(this+1); }
};

// Support loading a toolbar from a resource
BOOL CFlatToolbar::LoadToolBar(LPCTSTR lpszResourceName)
{
ASSERT_VALID(this);
ASSERT(lpszResourceName != NULL);

// determine location of the bitmap in resource fork
HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_TOOLBAR);
HRSRC hRsrc = ::FindResource(hInst, lpszResourceName, RT_TOOLBAR);
if (hRsrc == NULL)
return FALSE;

HGLOBAL hGlobal = LoadResource(hInst, hRsrc);
if (hGlobal == NULL)
return FALSE;

CToolBarData* pData = (CToolBarData*)LockResource(hGlobal);
if (pData == NULL)
return FALSE;
ASSERT(pData->wVersion == 1);

UINT* pItems = new UINT[pData->wItemCount];
for (int i = 0; i < pData->wItemCount; i++)
pItems[i] = pData->items()[i];
BOOL bResult = SetButtons(pItems, pData->wItemCount);
delete[] pItems;

if (bResult)
{
// set new sizes of the buttons
CSize sizeImage(pData->wWidth, pData->wHeight);
CSize sizeButton(pData->wWidth + 7, pData->wHeight + 7);
SetSizes(sizeButton, sizeImage);

// load bitmap now that sizes are known by the toolbar control
bResult = LoadBitmap(lpszResourceName);
}

UnlockResource(hGlobal);
FreeResource(hGlobal);

return bResult;
}
// TONYCL: END: OFFICE97 LOOK AND FEEL

#ifndef _MAC
static HBITMAP AFXAPI CreateDitherBitmap()
#else
static HBITMAP AFXAPI CreateDitherBitmap(BOOL bMonochrome)
#endif
{
struct  // BITMAPINFO with 16 colors
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD      bmiColors[16];
} bmi;
memset(&bmi, 0, sizeof(bmi));

bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = 8;
bmi.bmiHeader.biHeight = 8;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 1;
bmi.bmiHeader.biCompression = BI_RGB;

COLORREF clr = ::GetSysColor(COLOR_BTNFACE);
#ifdef _MAC
// if monochrome or the face color is already white, use black instead to make
// sure that we draw a dither - otherwise we'll have a white on white bitmap
if (bMonochrome || clr == RGB(255, 255, 255))
clr = 0;
#endif
bmi.bmiColors[0].rgbBlue = GetBValue(clr);
bmi.bmiColors[0].rgbGreen = GetGValue(clr);
bmi.bmiColors[0].rgbRed = GetRValue(clr);

clr = ::GetSysColor(COLOR_BTNHIGHLIGHT);
bmi.bmiColors[1].rgbBlue = GetBValue(clr);
bmi.bmiColors[1].rgbGreen = GetGValue(clr);
bmi.bmiColors[1].rgbRed = GetRValue(clr);

// initialize the brushes
long patGray[8];
for (int i = 0; i < 8; i++)
   patGray[i] = (i & 1) ? 0xAAAA5555L : 0x5555AAAAL;

HDC hDC = GetDC(NULL);
HBITMAP hbm = CreateDIBitmap(hDC, &bmi.bmiHeader, CBM_INIT,
(LPBYTE)patGray, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS);
ReleaseDC(NULL, hDC);

return hbm;
}

// create a mono bitmap mask:
void CFlatToolbar::CreateMask(int iImage, CPoint ptOffset,
 BOOL bHilite, BOOL bHiliteShadow)
{
// initalize whole area with 0's
PatBlt(hDCMono, 0, 0, m_sizeButton.cx-2, m_sizeButton.cy-2, WHITENESS);

// create mask based on color bitmap
// convert this to 1's
SetBkColor(hDCGlyphs, globalData.clrBtnFace);
BitBlt(hDCMono, ptOffset.x, ptOffset.y, m_sizeImage.cx, m_sizeImage.cy,
hDCGlyphs, iImage * m_sizeImage.cx, 0, SRCCOPY);

if (bHilite)
{
// convert this to 1's
SetBkColor(hDCGlyphs, globalData.clrBtnHilite);

// OR in the new 1's
BitBlt(hDCMono, ptOffset.x, ptOffset.y, m_sizeImage.cx, m_sizeImage.cy,
hDCGlyphs, iImage * m_sizeImage.cx, 0, SRCPAINT);

if (bHiliteShadow)
BitBlt(hDCMono, 1, 1, m_sizeButton.cx-3, m_sizeButton.cy-3,
hDCMono, 0, 0, SRCAND);
}
}

// Raster Ops
#define ROP_DSPDxax  0x00E20746L
#define ROP_PSDPxax  0x00B8074AL

BOOL CFlatToolbar::DrawButton(CDC* pDC, int x, int y, int iImage, UINT nStyle)
{
ASSERT_VALID(pDC);

int dx = m_sizeButton.cx;
int dy = m_sizeButton.cy;
if (!globalData.bWin4)
{
// make the coordinates the interior of the button
x += 1;
y += 1;
dx -= 2;
dy -= 2;

// border around button
pDC->FillSolidRect(x,    y-1,    dx, 1,  globalData.clrWindowFrame);
pDC->FillSolidRect(x,    y+dy,   dx, 1,  globalData.clrWindowFrame);
pDC->FillSolidRect(x-1,  y,  1,  dy, globalData.clrWindowFrame);
pDC->FillSolidRect(x+dx, y,  1,  dy, globalData.clrWindowFrame);
}

#ifdef _MAC
if (m_bMonochrome)
return DrawMonoButton(pDC, x, y, dx, dy, iImage, nStyle);
#endif

// interior grey
pDC->FillSolidRect(x, y, dx, dy, globalData.clrBtnFace);

// determine offset of bitmap (centered within button)
CPoint ptOffset;
ptOffset.x = (dx - m_sizeImage.cx - 1) / 2;
ptOffset.y = (dy - m_sizeImage.cy) / 2;

if (nStyle & (TBBS_PRESSED | TBBS_CHECKED))
{
// pressed in or checked
pDC->Draw3dRect(x, y, dx, dy,
globalData.bWin4 ? globalData.clrWindowFrame : globalData.clrBtnShadow,
globalData.bWin4 ? globalData.clrBtnHilite : globalData.clrBtnFace);

/* TONYCL: OFFICE97 LOOK AND FEEL
if (globalData.bWin4)
{
pDC->Draw3dRect(x + 1, y + 1, dx - 2, dy - 2,
globalData.clrBtnShadow, globalData.clrBtnFace);
}
*/

// for any depressed button, add one to the offsets.
ptOffset.x += 1;
ptOffset.y += 1;
}
// TONYCL: START: OFFICE97 LOOK AND FEEL
if (nStyle & TBBS_UPSTATE)
{
pDC->Draw3dRect(x, y, dx, dy, globalData.clrBtnHilite,
globalData.bWin4 ? globalData.clrWindowFrame : globalData.clrBtnShadow);
}
// TONYCL: END: OFFICE97 LOOK AND FEEL

if ((nStyle & TBBS_PRESSED) || !(nStyle & TBBS_DISABLED))
{
// normal image version
BitBlt(pDC->m_hDC, x + ptOffset.x, y + ptOffset.y,
m_sizeImage.cx, m_sizeImage.cy,
hDCGlyphs, iImage * m_sizeImage.cx, 0, SRCCOPY);

if (nStyle & TBBS_PRESSED)
return TRUE;        // nothing more to do (rest of style is ignored)
}

if (nStyle & (TBBS_DISABLED | TBBS_INDETERMINATE))
{
// disabled or indeterminate version
CreateMask(iImage, ptOffset, TRUE, FALSE);

pDC->SetTextColor(0L);                  // 0's in mono -> 0 (for ROP)
pDC->SetBkColor((COLORREF)0x00FFFFFFL); // 1's in mono -> 1

if (nStyle & TBBS_DISABLED)
{
// disabled - draw the hilighted shadow
HGDIOBJ hbrOld = pDC->SelectObject(globalData.hbrBtnHilite);
if (hbrOld != NULL)
{
// draw hilight color where we have 0's in the mask
BitBlt(pDC->m_hDC, x + 1, y + 1,
m_sizeButton.cx - 2, m_sizeButton.cy - 2,
hDCMono, 0, 0, ROP_PSDPxax);
pDC->SelectObject(hbrOld);
}
}

//BLOCK: always draw the shadow
{
HGDIOBJ hbrOld = pDC->SelectObject(globalData.hbrBtnShadow);
if (hbrOld != NULL)
{
// draw the shadow color where we have 0's in the mask
BitBlt(pDC->m_hDC, x, y,
m_sizeButton.cx - 2, m_sizeButton.cy - 2,
hDCMono, 0, 0, ROP_PSDPxax);
pDC->SelectObject(hbrOld);
}
}
}

// if it is checked do the dither brush avoiding the glyph
if (nStyle & (TBBS_CHECKED | TBBS_INDETERMINATE))
{
HGDIOBJ hbrOld = pDC->SelectObject(hbrDither);
if (hbrOld != NULL)
{
ptOffset.x -= globalData.cxBorder2;
ptOffset.y -= globalData.cyBorder2;
CreateMask(iImage, ptOffset, ~(nStyle & TBBS_INDETERMINATE),
nStyle & TBBS_DISABLED);

pDC->SetTextColor(0L);              // 0 -> 0
pDC->SetBkColor((COLORREF)0x00FFFFFFL); // 1 -> 1

ASSERT(globalData.cxBorder2 == globalData.cyBorder2);
int delta = (nStyle & TBBS_INDETERMINATE) ?
globalData.bWin4 ? globalData.cxBorder2*2 : 3 : globalData.cxBorder2*2;

// only draw the dither brush where the mask is 1's
BitBlt(pDC->m_hDC,
x + globalData.cxBorder2, y + globalData.cyBorder2, dx-delta, dy-delta,
hDCMono, 0, 0, ROP_DSPDxax);
pDC->SelectObject(hbrOld);
}
}

return TRUE;
}

#ifdef _MAC
BOOL CFlatToolbar::DrawMonoButton(CDC* pDC, int x, int y, int dx, int dy,
int iImage, UINT nStyle)
{
// interior is black if pressed, white if not
if (nStyle & (TBBS_PRESSED | TBBS_CHECKED))
{
pDC->FillSolidRect(x, y, dx, dy, RGB(0, 0, 0));
pDC->SetBkColor(RGB(255, 255, 255));    // bkcolor was set by PatB
}
else
{
pDC->FillSolidRect(x, y, dx, dy, RGB(0xFF, 0xFF, 0xFF));
}

CPoint ptOffset;
ptOffset.x = (dx - m_sizeImage.cx - 1) / 2;
ptOffset.y = (dy - m_sizeImage.cy) / 2;

if ((nStyle & TBBS_PRESSED) || !(nStyle & TBBS_DISABLED))
{
// normal image version
BitBlt(pDC->m_hDC, x + ptOffset.x, y + ptOffset.y, m_sizeImage.cx,
m_sizeImage.cy, hDCGlyphs, iImage * m_sizeImage.cx, 0,
(nStyle & (TBBS_PRESSED | TBBS_CHECKED)) ? NOTSRCCOPY : SRCCOPY);

if (nStyle & (TBBS_PRESSED | TBBS_CHECKED))
return TRUE;        // nothing more to do (rest of style is ignored)
}

if (nStyle & TBBS_DISABLED)
{
BitBlt(pDC->m_hDC, x + ptOffset.x, y + ptOffset.y, m_sizeImage.cx,
m_sizeImage.cy, hDCGlyphs, iImage * m_sizeImage.cx, 0, SRCCOPY);

int ropOld = pDC->SetROP2(R2_MASKNOTPEN);
RECT rect;
SetRect(&rect, 0, 0, m_sizeImage.cx, m_sizeImage.cy);
OffsetRect(&rect, x + ptOffset.x, y + ptOffset.y);
AfxFillRect(pDC->m_hDC, &rect, hbrDither);
pDC->SetROP2(ropOld);

return TRUE;
}

// if it is checked do the dither brush avoiding the glyph
if (nStyle & (TBBS_CHECKED | TBBS_INDETERMINATE))
{
HGDIOBJ hbrOld = pDC->SelectObject(hbrDither);
if (hbrOld != NULL)
{
CreateMask(iImage, ptOffset, ~(nStyle & TBBS_INDETERMINATE),
nStyle & TBBS_DISABLED);

pDC->SetTextColor(0L);              // 0 -> 0
pDC->SetBkColor((COLORREF)0x00FFFFFFL); // 1 -> 1

int delta = (nStyle & TBBS_INDETERMINATE) ? 3 : 1;

// only draw the dither brush where the mask is 1's
CRect rect(0, 0, dx, dy);
::InvertRect(hDCMono, &rect);

BitBlt(pDC->m_hDC, x, y, dx, dy, hDCMono, 0, 0, ROP_PSDPxax);
pDC->SelectObject(hbrOld);
}
}

return TRUE;
}
#endif

BOOL CFlatToolbar::PrepareDrawButton(DrawState& ds)
{
ASSERT(m_hbmImageWell != NULL);
ASSERT(m_sizeButton.cx > 2 && m_sizeButton.cy > 2);

// We need to kick-start the bitmap selection process.
ds.hbmOldGlyphs = (HBITMAP)SelectObject(hDCGlyphs, m_hbmImageWell);
ds.hbmMono = CreateBitmap(m_sizeButton.cx-2, m_sizeButton.cy-2,
1, 1, NULL);
ds.hbmMonoOld = (HBITMAP)SelectObject(hDCMono, ds.hbmMono);
if (ds.hbmOldGlyphs == NULL || ds.hbmMono == NULL || ds.hbmMonoOld == NULL)
{
TRACE0("Error: can't draw toolbar.\n");
AfxDeleteObject((HGDIOBJ*)&ds.hbmMono);
return FALSE;
}
return TRUE;
}

void CFlatToolbar::EndDrawButton(DrawState& ds)
{
SelectObject(hDCMono, ds.hbmMonoOld);
AfxDeleteObject((HGDIOBJ*)&ds.hbmMono);
SelectObject(hDCGlyphs, ds.hbmOldGlyphs);
}

/////////////////////////////////////////////////////////////////////////////
// CFlatToolbar creation etc

struct AFX_TBBUTTON
{
UINT nID;        // Command ID that this button sends
UINT nStyle;    // TBBS_ styles
int iImage;     // index into mondo bitmap of this button's picture
// or size of this spacer
};

inline AFX_TBBUTTON* CFlatToolbar::_GetButtonPtr(int nIndex) const
{
ASSERT(nIndex >= 0 && nIndex < m_nCount);
ASSERT(m_pData != NULL);
return ((AFX_TBBUTTON*)m_pData) + nIndex;
}

/*
DIBs use RGBQUAD format:
0xbb 0xgg 0xrr 0x00

Reasonably efficient code to convert a COLORREF into an
RGBQUAD is byte-order-dependent, so we need different
code depending on the byte order we're targeting.
*/
#ifndef _MAC
#define RGB_TO_RGBQUAD(r,g,b)   (RGB(b,g,r))
#define CLR_TO_RGBQUAD(clr)     (RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr)))
#else
#define RGB_TO_RGBQUAD(r,g,b)   (RGB(r,g,b) << 8)
#define CLR_TO_RGBQUAD(clr)     (clr << 8)
#endif

#ifndef _MAC
HBITMAP AFXAPI LoadSysColorBitmap(HINSTANCE hInst, HRSRC hRsrc)
#else
HBITMAP AFXAPI LoadSysColorBitmap(HINSTANCE hInst, HRSRC hRsrc,
HDC hDCGlyphs, BOOL bMonochrome)
#endif
{
struct COLORMAP
{
// use DWORD instead of RGBQUAD so we can compare two RGBQUADs easily
DWORD rgbqFrom;
int iSysColorTo;
};
static const COLORMAP sysColorMap[] =
{
// mapping from color in DIB to system color
{ RGB_TO_RGBQUAD(0x00, 0x00, 0x00),  COLOR_BTNTEXT },       // black
{ RGB_TO_RGBQUAD(0x80, 0x80, 0x80),  COLOR_BTNSHADOW },     // dark grey
{ RGB_TO_RGBQUAD(0xC0, 0xC0, 0xC0),  COLOR_BTNFACE },       // bright grey
{ RGB_TO_RGBQUAD(0xFF, 0xFF, 0xFF),  COLOR_BTNHIGHLIGHT }   // white
};
const int nMaps = 4;

HGLOBAL hglb;
if ((hglb = ::LoadResource(hInst, hRsrc)) == NULL)
return NULL;

LPBITMAPINFOHEADER lpBitmap = (LPBITMAPINFOHEADER)LockResource(hglb);
if (lpBitmap == NULL)
return NULL;

// make copy of BITMAPINFOHEADER so we can modify the color table
const int nColorTableSize = 16;
UINT nSize = lpBitmap->biSize + nColorTableSize * sizeof(RGBQUAD);
LPBITMAPINFOHEADER lpBitmapInfo = (LPBITMAPINFOHEADER)::malloc(nSize);
if (lpBitmapInfo == NULL)
return NULL;
memcpy(lpBitmapInfo, lpBitmap, nSize);

// color table is in RGBQUAD DIB format
DWORD* pColorTable =
(DWORD*)(((LPBYTE)lpBitmapInfo) + (UINT)lpBitmapInfo->biSize);

for (int iColor = 0; iColor < nColorTableSize; iColor++)
{
// look for matching RGBQUAD color in original
for (int i = 0; i < nMaps; i++)
{
if (pColorTable[iColor] == sysColorMap[i].rgbqFrom)
{
#ifdef _MAC
if (bMonochrome)
{
// all colors except text become white
if (sysColorMap[i].iSysColorTo != COLOR_BTNTEXT)
pColorTable[iColor] = RGB_TO_RGBQUAD(255, 255, 255);
}
else
#endif
pColorTable[iColor] =
CLR_TO_RGBQUAD(::GetSysColor(sysColorMap[i].iSysColorTo));
break;
}
}
}

int nWidth = (int)lpBitmapInfo->biWidth;
int nHeight = (int)lpBitmapInfo->biHeight;
HDC hDCScreen = ::GetDC(NULL);
HBITMAP hbm = ::CreateCompatibleBitmap(hDCScreen, nWidth, nHeight);
::ReleaseDC(NULL, hDCScreen);

if (hbm != NULL)
{
HBITMAP hbmOld = (HBITMAP)::SelectObject(hDCGlyphs, hbm);

LPBYTE lpBits;
lpBits = (LPBYTE)(lpBitmap + 1);
lpBits += (1 << (lpBitmapInfo->biBitCount)) * sizeof(RGBQUAD);

StretchDIBits(hDCGlyphs, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight,
lpBits, (LPBITMAPINFO)lpBitmapInfo, DIB_RGB_COLORS, SRCCOPY);
SelectObject(hDCGlyphs, hbmOld);

#ifdef _MAC
// We don't change this bitmap any more, so get rid of the big,
// wasteful Macintosh port
::SetBitmapReadOnly(hbm, BRO_READONLY);
#endif
}

// free copy of bitmap info struct and resource itself
::free(lpBitmapInfo);
::FreeResource(hglb);

return hbm;
}

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

CFlatToolbar::CFlatToolbar()
{
m_hbmImageWell = NULL;
m_hInstImageWell = NULL;
m_hRsrcImageWell = NULL;
m_iButtonCapture = -1;      // nothing captured

// TONYCL: START: OFFICE97 LOOK AND FEEL
m_nUpButtonIndex = -1;// No previous upstate button
// TONYCL: END: OFFICE97 LOOK AND FEEL

// UISG standard sizes
m_sizeButton.cx = 24;
m_sizeButton.cy = 22;
m_sizeImage.cx = 16;
m_sizeImage.cy = 15;
m_cyTopBorder = m_cyBottomBorder = 3;   // 3 pixel for top/bottom gaps

// adjust sizes when running on Win4
if (globalData.bWin4)
{
m_sizeButton.cx = 23;
m_cySharedBorder = m_cxSharedBorder = 0;
m_cxDefaultGap = 8;
}
else
{
m_cxDefaultGap = 6;
m_cySharedBorder = m_cxSharedBorder = 1;
}

#ifdef _MAC
m_hDCGlyphs = CreateCompatibleDC(NULL);
m_hDCMono = ::CreateCompatibleDC(NULL);
if (m_hDCGlyphs == NULL || m_hDCMono == NULL)
AfxThrowResourceException();
#endif

// initialize the toolbar drawing engine
static BOOL bInitialized;
if (!bInitialized)
{
#ifndef _MAC
hDCGlyphs = CreateCompatibleDC(NULL);

// Mono DC and Bitmap for disabled image
hDCMono = ::CreateCompatibleDC(NULL);
#endif

#ifndef _MAC
HBITMAP hbmGray = ::CreateDitherBitmap();
#else
HBITMAP hbmGray = ::CreateDitherBitmap(m_bMonochrome);
#endif
if (hbmGray != NULL)
{
ASSERT(hbrDither == NULL);
hbrDither = ::CreatePatternBrush(hbmGray);
AfxDeleteObject((HGDIOBJ*)&hbmGray);
}

#ifndef _MAC
if (hDCGlyphs == NULL || hDCMono == NULL || hbrDither == NULL)
AfxThrowResourceException();
#else
if (hbrDither == NULL)
AfxThrowResourceException();
#endif
bInitialized = TRUE;
}
}

CFlatToolbar::~CFlatToolbar()
{
#ifdef _MAC
ASSERT(m_hDCGlyphs != NULL);
VERIFY(::DeleteDC(m_hDCGlyphs));

ASSERT(m_hDCMono != NULL);
VERIFY(::DeleteDC(m_hDCMono));
#endif

AfxDeleteObject((HGDIOBJ*)&m_hbmImageWell);
}

BOOL CFlatToolbar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
{
if (pParentWnd != NULL)
ASSERT_VALID(pParentWnd);   // must have a parent

// save the style
m_dwStyle = dwStyle;
if (nID == AFX_IDW_TOOLBAR)
m_dwStyle |= CBRS_HIDE_INPLACE;

// create the HWND
CRect rect;
rect.SetRectEmpty();
LPCTSTR lpszClass = AfxRegisterWndClass(0, ::LoadCursor(NULL, IDC_ARROW),
(HBRUSH)(COLOR_BTNFACE+1), NULL);
if (!CWnd::Create(lpszClass, NULL, dwStyle, rect, pParentWnd, nID))
return FALSE;

// Note: Parent must resize itself for control bar to be resized

return TRUE;
}

void CFlatToolbar::SetSizes(SIZE sizeButton, SIZE sizeImage)
{
ASSERT_VALID(this);
ASSERT(sizeButton.cx > 0 && sizeButton.cy > 0);
ASSERT(sizeImage.cx > 0 && sizeImage.cy > 0);

// button must be big enough to hold image + 3 pixels on each side
ASSERT(sizeButton.cx >= sizeImage.cx + 6);
ASSERT(sizeButton.cy >= sizeImage.cy + 6);

m_sizeButton = sizeButton;
m_sizeImage = sizeImage;

// set height
Invalidate();   // just to be nice if called when toolbar is visible
}

void CFlatToolbar::SetHeight(int cyHeight)
{
ASSERT_VALID(this);

int nHeight = cyHeight;
if (m_dwStyle & CBRS_BORDER_TOP)
cyHeight -= globalData.cyBorder2;
if (m_dwStyle & CBRS_BORDER_BOTTOM)
cyHeight -= globalData.cyBorder2;
m_cyBottomBorder = (cyHeight - m_sizeButton.cy) / 2;
// if there is an extra pixel, m_cyTopBorder will get it
m_cyTopBorder = cyHeight - m_sizeButton.cy - m_cyBottomBorder;
if (m_cyTopBorder < 0)
{
TRACE1("Warning: CFlatToolbar::SetHeight(%d) is smaller than button.\n",
nHeight);
m_cyBottomBorder += m_cyTopBorder;
m_cyTopBorder = 0;  // will clip at bottom
}
// bottom border will be ignored (truncate as needed)
Invalidate();   // just to be nice if called when toolbar is visible
}

BOOL CFlatToolbar::LoadBitmap(UINT nIDBitmap)
{
return LoadBitmap(MAKEINTRESOURCE(nIDBitmap));
}

BOOL CFlatToolbar::LoadBitmap(LPCTSTR lpszResourceName)
{
ASSERT_VALID(this);
ASSERT(lpszResourceName != NULL);

AfxDeleteObject((HGDIOBJ*)&m_hbmImageWell);     // get rid of old one

m_hInstImageWell = AfxFindResourceHandle(lpszResourceName, RT_BITMAP);
if ((m_hRsrcImageWell = ::FindResource(m_hInstImageWell,
lpszResourceName, RT_BITMAP)) == NULL)
return FALSE;

#ifndef _MAC
m_hbmImageWell = LoadSysColorBitmap(m_hInstImageWell, m_hRsrcImageWell);
#else
m_hbmImageWell = LoadSysColorBitmap(m_hInstImageWell, m_hRsrcImageWell,
m_hDCGlyphs, m_bMonochrome);
#endif
return (m_hbmImageWell != NULL);
}

BOOL CFlatToolbar::SetButtons(const UINT* lpIDArray, int nIDCount)
{
ASSERT_VALID(this);
ASSERT(nIDCount >= 1);  // must be at least one of them
ASSERT(lpIDArray == NULL ||
AfxIsValidAddress(lpIDArray, sizeof(UINT) * nIDCount, FALSE));

// first allocate array for panes and copy initial data
if (!AllocElements(nIDCount, sizeof(AFX_TBBUTTON)))
return FALSE;
ASSERT(nIDCount == m_nCount);

if (lpIDArray != NULL)
{
int iImage = 0;
// go through them adding buttons
AFX_TBBUTTON* pTBB = (AFX_TBBUTTON*)m_pData;
for (int i = 0; i < nIDCount; i++, pTBB++)
{
ASSERT(pTBB != NULL);
if ((pTBB->nID = *lpIDArray++) == 0)
{
// separator
pTBB->nStyle = TBBS_SEPARATOR;
// width of separator includes 2 pixel overlap
pTBB->iImage = m_cxDefaultGap + m_cxSharedBorder * 2;
}
else
{
// a command button with image
pTBB->nStyle = TBBS_BUTTON;
pTBB->iImage = iImage++;
}
}
}
return TRUE;
}

#ifdef AFX_CORE3_SEG
#pragma code_seg(AFX_CORE3_SEG)
#endif

/////////////////////////////////////////////////////////////////////////////
// CFlatToolbar attribute access

int CFlatToolbar::CommandToIndex(UINT nIDFind) const
{
ASSERT_VALID(this);

AFX_TBBUTTON* pTBB = _GetButtonPtr(0);
for (int i = 0; i < m_nCount; i++, pTBB++)
if (pTBB->nID == nIDFind)
return i;
return -1;
}

UINT CFlatToolbar::GetItemID(int nIndex) const
{
ASSERT_VALID(this);

return _GetButtonPtr(nIndex)->nID;
}

void CFlatToolbar::GetItemRect(int nIndex, LPRECT lpRect) const
{
ASSERT_VALID(this);
ASSERT(nIndex >= 0 && nIndex < m_nCount);
ASSERT(AfxIsValidAddress(lpRect, sizeof(RECT)));

BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) ? TRUE : FALSE;
CRect rect;
rect.SetRectEmpty();        // only need top and left
CalcInsideRect(rect, bHorz);
AFX_TBBUTTON* pTBB = (AFX_TBBUTTON*)m_pData;
for (int iButton = 0; iButton < nIndex; iButton++, pTBB++)
{
ASSERT(pTBB != NULL);
// skip this button or separator
if (bHorz)
{
rect.left += (pTBB->nStyle & TBBS_SEPARATOR) ?
pTBB->iImage : m_sizeButton.cx;
rect.left -= m_cxSharedBorder;    // go back for overlap
}
else
{
rect.top += (pTBB->nStyle & TBBS_SEPARATOR) ?
pTBB->iImage : m_sizeButton.cy;
rect.top -= m_cySharedBorder;    // go back for overlap
}
}
ASSERT(iButton == nIndex);
ASSERT(pTBB == _GetButtonPtr(nIndex));

// button or image width
if (bHorz)
{
// TONYCL: START: OFFICE97 LOOK AND FEEL
// If we are not floating then shift the buttons down to allow for a gripper
if (!IsFloating()) {
rect.left += 3;
}
// TONYCL: END: OFFICE97 LOOK AND FEEL

int cx = (pTBB->nStyle & TBBS_SEPARATOR) ? pTBB->iImage : m_sizeButton.cx;
lpRect->right = (lpRect->left = rect.left) + cx;
lpRect->bottom = (lpRect->top = rect.top) + m_sizeButton.cy;
}
else
{
// TONYCL: START: OFFICE97 LOOK AND FEEL
// If we are not floating then shift the buttons down to allow for a gripper
if (!IsFloating()) {
rect.top += 3;
}
// TONYCL: END: OFFICE97 LOOK AND FEEL

int cy = (pTBB->nStyle & TBBS_SEPARATOR) ? pTBB->iImage : m_sizeButton.cy;
lpRect->bottom = (lpRect->top = rect.top) + cy;
lpRect->right = (lpRect->left = rect.left) + m_sizeButton.cx;
}
}

UINT CFlatToolbar::GetButtonStyle(int nIndex) const
{
return _GetButtonPtr(nIndex)->nStyle;
}

void CFlatToolbar::SetButtonStyle(int nIndex, UINT nStyle)
{
AFX_TBBUTTON* pTBB = _GetButtonPtr(nIndex);
UINT nOldStyle = pTBB->nStyle;
if (nOldStyle != nStyle)
{
// update the style and invalidate
pTBB->nStyle = nStyle;

// invalidate the button only if both styles not "pressed"
if (!(nOldStyle & nStyle & TBBS_PRESSED))
InvalidateButton(nIndex);
}
}

CSize CFlatToolbar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
{
ASSERT_VALID(this);

CSize size = CControlBar::CalcFixedLayout(bStretch, bHorz);

CRect rect;
rect.SetRectEmpty();        // only need top and left
CalcInsideRect(rect, bHorz);
AFX_TBBUTTON* pTBB = (AFX_TBBUTTON*)m_pData;
int nButtonDist = 0;

if (!bStretch)
{
for (int iButton = 0; iButton < m_nCount; iButton++, pTBB++)
{
ASSERT(pTBB != NULL);
// skip this button or separator
nButtonDist += (pTBB->nStyle & TBBS_SEPARATOR) ?
pTBB->iImage : (bHorz ? m_sizeButton.cx : m_sizeButton.cy);
// go back one for overlap
nButtonDist -= bHorz ? m_cxSharedBorder : m_cySharedBorder;
}
if (bHorz)
size.cx = nButtonDist - rect.Width() + m_cxSharedBorder;
else
size.cy = nButtonDist - rect.Height() + m_cySharedBorder;
}

if (bHorz)
size.cy = m_sizeButton.cy - rect.Height(); // rect.Height() < 0
else
size.cx = m_sizeButton.cx - rect.Width(); // rect.Width() < 0

return size;
}

void CFlatToolbar::GetButtonInfo(int nIndex, UINT& nID, UINT& nStyle, int& iImage) const
{
ASSERT_VALID(this);

AFX_TBBUTTON* pTBB = _GetButtonPtr(nIndex);
nID = pTBB->nID;
nStyle = pTBB->nStyle;
iImage = pTBB->iImage;
}

void CFlatToolbar::SetButtonInfo(int nIndex, UINT nID, UINT nStyle, int iImage)
{
ASSERT_VALID(this);

AFX_TBBUTTON* pTBB = _GetButtonPtr(nIndex);
pTBB->nID = nID;
pTBB->iImage = iImage;
pTBB->nStyle = nStyle;
InvalidateButton(nIndex);
}

void CFlatToolbar::DoPaint(CDC* pDC)
{
ASSERT_VALID(this);
ASSERT_VALID(pDC);

#ifdef _MAC
#ifdef _DEBUG
// turn off validation to speed up button drawing
int wdSav = WlmDebug(WD_NOVALIDATE | WD_ASSERT);
#endif
#endif

CControlBar::DoPaint(pDC);// draw border

// if no toolbar loaded, don't draw any buttons
if (m_hbmImageWell == NULL)
return;

BOOL bHorz = m_dwStyle & CBRS_ORIENT_HORZ ? TRUE : FALSE;
CRect rect;
GetClientRect(rect);
CalcInsideRect(rect, bHorz);

// force the full size of the button
if (bHorz)
rect.bottom = rect.top + m_sizeButton.cy;
else
rect.right = rect.left + m_sizeButton.cx;

DrawState ds;
if (!PrepareDrawButton(ds))
return;     // something went wrong

// TONYCL: START: OFFICE97 LOOK AND FEEL
// Only draw the gripper stripes when we are docked
if (!IsFloating()) {
{
CRect rect;
GetClientRect(rect);

// Draw the two gripper stripes
if (bHorz) {
// Adjust the sizes to fit into the client area properly
rect.left += 3;
rect.top += 3;
rect.bottom -= 3;
pDC->Draw3dRect(rect.left, rect.top, 3, rect.Height(), globalData.clrBtnHilite, globalData.clrBtnShadow);

rect.left += 4;
pDC->Draw3dRect(rect.left, rect.top, 3, rect.Height(), globalData.clrBtnHilite, globalData.clrBtnShadow);
}
else {
// Adjust the sizes to fit into the client area properly
rect.top += 3;
rect.left += 3;
rect.right -= 3;
pDC->Draw3dRect(rect.left, rect.top, rect.Width(), 3, globalData.clrBtnHilite, globalData.clrBtnShadow);

rect.top += 4;
pDC->Draw3dRect(rect.left, rect.top, rect.Width(), 3, globalData.clrBtnHilite, globalData.clrBtnShadow);
}
}

// Shift the buttons down by 2 pixels to allow for the grippers
if (bHorz)
rect.left += 3;
else
rect.top += 3;
}

// TONYCL: END: OFFICE97 LOOK AND FEEL

AFX_TBBUTTON* pTBB = (AFX_TBBUTTON*)m_pData;
for (int iButton = 0; iButton < m_nCount; iButton++, pTBB++)
{
ASSERT(pTBB != NULL);
if (pTBB->nStyle & TBBS_SEPARATOR)
{
// separator
if (bHorz)
rect.right = rect.left + pTBB->iImage;
else
rect.bottom = rect.top + pTBB->iImage;

// TONYCL: START: OFFICE97 LOOK AND FEEL
{
// Draw a 3D seperator
if (bHorz) {

int nOffset;
nOffset = rect.left + 1 + ((rect.Width() - 2) / 2);
pDC->Draw3dRect(nOffset, rect.top, 2, rect.Height(), globalData.clrBtnShadow, globalData.clrBtnHilite);
}
else {
int nOffset;
nOffset = rect.top + 1 + ((rect.Height() - 2) / 2);
pDC->Draw3dRect(rect.left, nOffset, rect.Width(), 2, globalData.clrBtnShadow, globalData.clrBtnHilite);
}
}
// TONYCL: END: OFFICE97 LOOK AND FEEL
}
else
{
if (bHorz)
rect.right = rect.left + m_sizeButton.cx;
else
rect.bottom = rect.top + m_sizeButton.cy;
if (!globalData.bWin32s || pDC->RectVisible(&rect))
{
DrawButton(pDC, rect.left, rect.top,
pTBB->iImage, pTBB->nStyle);
}
}
// adjust for overlap
if (bHorz)
rect.left = rect.right - m_cxSharedBorder;
else
rect.top = rect.bottom - m_cySharedBorder;
}
EndDrawButton(ds);

#ifdef _MAC
#ifdef _DEBUG
WlmDebug(wdSav);
#endif
#endif
}

void CFlatToolbar::InvalidateButton(int nIndex)
{
ASSERT_VALID(this);

CRect rect;
GetItemRect(nIndex, &rect);
InvalidateRect(rect, FALSE);    // don't erase background
}

int CFlatToolbar::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
ASSERT_VALID(this);

// check child windows first by calling CControlBar
int nHit = CControlBar::OnToolHitTest(point, pTI);
if (nHit != -1)
return nHit;

// now hit test against CFlatToolbar buttons
nHit = ((CFlatToolbar*)this)->HitTest(point);
if (nHit != -1)
{
AFX_TBBUTTON* pTBB = _GetButtonPtr(nHit);
if (pTI != NULL)
{
GetItemRect(nHit, &pTI->rect);
pTI->uId = pTBB->nID;
pTI->hwnd = m_hWnd;
pTI->lpszText = LPSTR_TEXTCALLBACK;
}
nHit = pTBB->nID;
}
return nHit;
}

int CFlatToolbar::HitTest(CPoint point) // in window relative coords
{
if (m_pData == NULL)
return -1;// no buttons

BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) ? TRUE : FALSE;
CRect rect;
rect.SetRectEmpty();        // only need top and left
CalcInsideRect(rect, bHorz);
AFX_TBBUTTON* pTBB = (AFX_TBBUTTON*)m_pData;
ASSERT(pTBB != NULL);
if (bHorz)
{
if (point.y < rect.top || point.y >= rect.top + m_sizeButton.cy)
return -1;      // no Y hit
for (int iButton = 0; iButton < m_nCount; iButton++, pTBB++)
{
if (point.x < rect.left)
break;      // missed it
rect.left += (pTBB->nStyle & TBBS_SEPARATOR) ?
pTBB->iImage : m_sizeButton.cx;
if (point.x < rect.left && !(pTBB->nStyle & TBBS_SEPARATOR))
return iButton;     // hit !
rect.left -= m_cxSharedBorder;    // go back for overlap
}
}
else
{
if (point.x < rect.left || point.x >= rect.left + m_sizeButton.cx)
return -1;      // no X hit
for (int iButton = 0; iButton < m_nCount; iButton++, pTBB++)
{
if (point.y < rect.top)
break;      // missed it
rect.top += (pTBB->nStyle & TBBS_SEPARATOR) ?
pTBB->iImage : m_sizeButton.cy;
if (point.y < rect.top && !(pTBB->nStyle & TBBS_SEPARATOR))
return iButton;     // hit !
rect.top -= m_cySharedBorder;    // go back for overlap
}
}

return -1;      // nothing hit
}

/////////////////////////////////////////////////////////////////////////////
// CFlatToolbar message handlers

BEGIN_MESSAGE_MAP(CFlatToolbar, CControlBar)
//{{AFX_MSG_MAP(CFlatToolbar)
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
ON_WM_CANCELMODE()
ON_WM_SYSCOLORCHANGE()
ON_WM_WINDOWPOSCHANGED()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CFlatToolbar::OnLButtonDown(UINT nFlags, CPoint point)
{
if ((m_iButtonCapture = HitTest(point)) < 0) // nothing hit
{
CControlBar::OnLButtonDown(nFlags, point);
return;
}

AFX_TBBUTTON* pTBB = _GetButtonPtr(m_iButtonCapture);
ASSERT(!(pTBB->nStyle & TBBS_SEPARATOR));

// update the button before checking for disabled status
UpdateButton(m_iButtonCapture);
if (pTBB->nStyle & TBBS_DISABLED)
{
m_iButtonCapture = -1;
return;     // don't press it
}

// TONYCL: START:
// Kill the UPSTATE style so that the button is drawn in the downstate
pTBB->nStyle &= ~TBBS_UPSTATE;
// TONYCL: END:

pTBB->nStyle |= TBBS_PRESSED;
InvalidateButton(m_iButtonCapture);
UpdateWindow(); // immediate feedback

SetCapture();
GetOwner()->SendMessage(WM_SETMESSAGESTRING, (WPARAM)pTBB->nID);
}

void CFlatToolbar::OnMouseMove(UINT /*nFlags*/, CPoint point)
{
if (m_iButtonCapture >= 0)
{
AFX_TBBUTTON* pTBB = _GetButtonPtr(m_iButtonCapture);
ASSERT(!(pTBB->nStyle & TBBS_SEPARATOR));

UINT nNewStyle = (pTBB->nStyle & ~TBBS_PRESSED);
int iButtonCapture = m_iButtonCapture;
if (GetCapture() != this)
{
m_iButtonCapture = -1; // lost capture
GetOwner()->SendMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);
}
else
{
// should be pressed if still hitting the captured button
if (HitTest(point) == m_iButtonCapture)
nNewStyle |= TBBS_PRESSED;
}
SetButtonStyle(iButtonCapture, nNewStyle);
UpdateWindow(); // immediate feedback
}
// TONYCL: START: OFFICE97 LOOK AND FEEL
else {
// We have to be the active application
if (GetForegroundWindow()->GetSafeHwnd() == AfxGetMainWnd()->GetSafeHwnd()) {
// Hit test the button, and then draw the button in the up state
int nButtonID = HitTest(point);// Which button are we over?

if (nButtonID == -1) {
ReleaseCapture();
}
else {
SetCapture();
}

// Are we over the same button as we were last time?
if (m_nUpButtonIndex == nButtonID) {
return;
}
 // Is the new button -1, and were we over a button?
else if ((nButtonID == -1) && (m_nUpButtonIndex != -1)) {
// Reset the button state so it's flat
UINT nStyle = GetButtonStyle(m_nUpButtonIndex);
nStyle &= ~TBBS_UPSTATE;
SetButtonStyle(m_nUpButtonIndex, nStyle);
InvalidateButton(m_nUpButtonIndex);
m_nUpButtonIndex = -1;
UpdateWindow(); // immediate feedback
}
// We are over a button
else if (nButtonID != -1 && (!(GetButtonStyle(nButtonID) & TBBS_DISABLED))) {
// Put the button into the upstate
UINT nStyle = GetButtonStyle(nButtonID);
nStyle |= TBBS_UPSTATE;
SetButtonStyle(nButtonID, nStyle);
InvalidateButton(nButtonID);

// If we were over a different button to the one before, flatten it
if (m_nUpButtonIndex != -1) {
UINT nStyle = GetButtonStyle(m_nUpButtonIndex);
nStyle &= ~TBBS_UPSTATE;
SetButtonStyle(m_nUpButtonIndex, nStyle);
InvalidateButton(m_nUpButtonIndex);
}

m_nUpButtonIndex = nButtonID;
UpdateWindow(); // immediate feedback
}
}
}
// TONYCL: END: OFFICE97 LOOK AND FEEL
}

void CFlatToolbar::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_iButtonCapture < 0)
{
CControlBar::OnLButtonUp(nFlags, point);
return;     // not captured
}

AFX_TBBUTTON* pTBB = _GetButtonPtr(m_iButtonCapture);
ASSERT(!(pTBB->nStyle & TBBS_SEPARATOR));
UINT nIDCmd = 0;

UINT nNewStyle = (pTBB->nStyle & ~TBBS_PRESSED);
if (GetCapture() == this)
{
// we did not lose the capture
ReleaseCapture();
if (HitTest(point) == m_iButtonCapture)
{
// give button a chance to update
UpdateButton(m_iButtonCapture);

// then check for disabled state
if (!(pTBB->nStyle & TBBS_DISABLED))
{
// pressed, will send command notification
nIDCmd = pTBB->nID;

if (pTBB->nStyle & TBBS_CHECKBOX)
{
// auto check: three state => down
if (nNewStyle & TBBS_INDETERMINATE)
nNewStyle &= ~TBBS_INDETERMINATE;

nNewStyle ^= TBBS_CHECKED;
}
}
}
}

GetOwner()->SendMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);

int iButtonCapture = m_iButtonCapture;
m_iButtonCapture = -1;
if (nIDCmd != 0)
GetOwner()->SendMessage(WM_COMMAND, nIDCmd);    // send command

SetButtonStyle(iButtonCapture, nNewStyle);
UpdateButton(iButtonCapture);

UpdateWindow(); // immediate feedback
}

// TONYCL: START: OFFICE97 LOOK AND FEEL
void CFlatToolbar::OnWindowPosChanged( WINDOWPOS* lpwndpos )
{
// Force a repaint of the window to fix a repaint bug
InvalidateRect(NULL);
UpdateWindow();
}
// TONYCL: END: OFFICE97 LOOK AND FEEL

void CFlatToolbar::OnCancelMode()
{
CControlBar::OnCancelMode();

if (m_iButtonCapture >= 0)
{
AFX_TBBUTTON* pTBB = _GetButtonPtr(m_iButtonCapture);
ASSERT(!(pTBB->nStyle & TBBS_SEPARATOR));
UINT nNewStyle = (pTBB->nStyle & ~TBBS_PRESSED);
if (GetCapture() == this)
ReleaseCapture();
SetButtonStyle(m_iButtonCapture, nNewStyle);
m_iButtonCapture = -1;
UpdateWindow();
}
}

void CFlatToolbar::OnSysColorChange()
{
#ifdef _MAC
CControlBar::OnSysColorChange();

ASSERT(hDCGlyphs != NULL);
VERIFY(::DeleteDC(hDCGlyphs));
hDCGlyphs = ::CreateCompatibleDC(NULL);

ASSERT(hDCMono != NULL);
VERIFY(::DeleteDC(hDCMono));
hDCMono = ::CreateCompatibleDC(NULL);
#endif

// re-initialize global dither brush
#ifndef _MAC
HBITMAP hbmGray = ::CreateDitherBitmap();
#else
HBITMAP hbmGray = ::CreateDitherBitmap(m_bMonochrome);
#endif
if (hbmGray != NULL)
{
HBRUSH hbrNew = ::CreatePatternBrush(hbmGray);
if (hbrNew != NULL)
{
AfxDeleteObject((HGDIOBJ*)&hbrDither);      // free old one
hbrDither = hbrNew;
}
::DeleteObject(hbmGray);
}

// re-color bitmap for toolbar
if (m_hbmImageWell != NULL)
{
HBITMAP hbmNew;
#ifndef _MAC
hbmNew = LoadSysColorBitmap(m_hInstImageWell, m_hRsrcImageWell);
#else
hbmNew = LoadSysColorBitmap(m_hInstImageWell, m_hRsrcImageWell,
m_hDCGlyphs, m_bMonochrome);
#endif
if (hbmNew != NULL)
{
::DeleteObject(m_hbmImageWell);     // free old one
m_hbmImageWell = hbmNew;
}
}
}

/////////////////////////////////////////////////////////////////////////////
// CFlatToolbar idle update through CToolCmdUI class

#define CToolCmdUI COldToolCmdUI

class CToolCmdUI : public CCmdUI        // class private to this file !
{
public: // re-implementations only
virtual void Enable(BOOL bOn);
virtual void SetCheck(int nCheck);
virtual void SetText(LPCTSTR lpszText);
};

void CToolCmdUI::Enable(BOOL bOn)
{
m_bEnableChanged = TRUE;
CFlatToolbar* pToolBar = (CFlatToolbar*)m_pOther;
ASSERT(pToolBar != NULL);
ASSERT_KINDOF(CFlatToolbar, pToolBar);
ASSERT(m_nIndex < m_nIndexMax);

UINT nNewStyle = pToolBar->GetButtonStyle(m_nIndex) & ~TBBS_DISABLED;
if (!bOn)
nNewStyle |= TBBS_DISABLED;
ASSERT(!(nNewStyle & TBBS_SEPARATOR));
pToolBar->SetButtonStyle(m_nIndex, nNewStyle);
}

void CToolCmdUI::SetCheck(int nCheck)
{
ASSERT(nCheck >= 0 && nCheck <= 2); // 0=>off, 1=>on, 2=>indeterminate
CFlatToolbar* pToolBar = (CFlatToolbar*)m_pOther;
ASSERT(pToolBar != NULL);
ASSERT_KINDOF(CFlatToolbar, pToolBar);
ASSERT(m_nIndex < m_nIndexMax);

UINT nNewStyle = pToolBar->GetButtonStyle(m_nIndex) &
~(TBBS_CHECKED | TBBS_INDETERMINATE);
if (nCheck == 1)
nNewStyle |= TBBS_CHECKED;
else if (nCheck == 2)
nNewStyle |= TBBS_INDETERMINATE;
ASSERT(!(nNewStyle & TBBS_SEPARATOR));
pToolBar->SetButtonStyle(m_nIndex, nNewStyle | TBBS_CHECKBOX);
}

void CToolCmdUI::SetText(LPCTSTR)
{
// ignore it
}

void CFlatToolbar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
{
CToolCmdUI state;
state.m_pOther = this;

state.m_nIndexMax = (UINT)m_nCount;
for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
  state.m_nIndex++)
{
AFX_TBBUTTON* pTBB = _GetButtonPtr(state.m_nIndex);
state.m_nID = pTBB->nID;

// ignore separators
if (!(pTBB->nStyle & TBBS_SEPARATOR))
state.DoUpdate(pTarget, bDisableIfNoHndler);
}

// update the dialog controls added to the toolbar
UpdateDialogControls(pTarget, bDisableIfNoHndler);
}

void CFlatToolbar::UpdateButton(int nIndex)
{
// determine target of command update
CFrameWnd* pTarget = (CFrameWnd*)GetOwner();
if (pTarget == NULL || !pTarget->IsFrameWnd())
pTarget = GetParentFrame();

// send the update notification
if (pTarget != NULL)
{
CToolCmdUI state;
state.m_pOther = this;
state.m_nIndex = nIndex;
state.m_nIndexMax = (UINT)m_nCount;
AFX_TBBUTTON* pTBB = _GetButtonPtr(nIndex);
state.m_nID = pTBB->nID;
state.DoUpdate(pTarget, pTarget->m_bAutoMenuEnable);
}
}

/////////////////////////////////////////////////////////////////////////////
// CFlatToolbar diagnostics

#ifdef _DEBUG
void CFlatToolbar::AssertValid() const
{
CControlBar::AssertValid();
ASSERT(m_hbmImageWell == NULL ||
(globalData.bWin32s || ::GetObjectType(m_hbmImageWell) == OBJ_BITMAP));

if (m_hbmImageWell != NULL)
{
ASSERT(m_hRsrcImageWell != NULL);
ASSERT(m_hInstImageWell != NULL);
}
}

void CFlatToolbar::Dump(CDumpContext& dc) const
{
CControlBar::Dump(dc);

dc << "m_hbmImageWell = " << (UINT)m_hbmImageWell;
dc << "\nm_hInstImageWell = " << (UINT)m_hInstImageWell;
dc << "\nm_hRsrcImageWell = " << (UINT)m_hRsrcImageWell;
dc << "\nm_iButtonCapture = " << m_iButtonCapture;
dc << "\nm_sizeButton = " << m_sizeButton;
dc << "\nm_sizeImage = " << m_sizeImage;

if (dc.GetDepth() > 0)
{
for (int i = 0; i < m_nCount; i++)
{
AFX_TBBUTTON* pTBB = _GetButtonPtr(i);
dc << "\ntoolbar button[" << i << "] = {";
dc << "\n\tnID = " << pTBB->nID;
dc << "\n\tnStyle = " << pTBB->nStyle;
if (pTBB->nStyle & TBBS_SEPARATOR)
dc << "\n\tiImage (separator width) = " << pTBB->iImage;
else
dc <<"\n\tiImage (bitmap image index) = " << pTBB->iImage;
dc << "\n}";
}
}

dc << "\n";
}
#endif

#undef new
#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

IMPLEMENT_DYNAMIC(CFlatToolbar, CControlBar)

/////////////////////////////////////////////////////////////////////////////
// Cached system metrics, etc

GLOBAL_DATA globalData;

// Initialization code
GLOBAL_DATA::GLOBAL_DATA()
{
// Cache various target platform version information
DWORD dwVersion = ::GetVersion();
nWinVer = (LOBYTE(dwVersion) << 8) + HIBYTE(dwVersion);
bWin32s = (dwVersion & 0x80000000) != 0;
bWin4 = (BYTE)dwVersion >= 4;
bNotWin4 = 1 - bWin4;   // for convenience
#ifndef _MAC
bSmCaption = bWin4;
#else
bSmCaption = TRUE;
#endif
bWin31 = bWin32s && !bWin4; // Windows 95 reports Win32s

// Cached system metrics (updated in CWnd::OnWinIniChange)
UpdateSysMetrics();

// Border attributes
hbrLtGray = ::CreateSolidBrush(RGB(192, 192, 192));
hbrDkGray = ::CreateSolidBrush(RGB(128, 128, 128));
ASSERT(hbrLtGray != NULL);
ASSERT(hbrDkGray != NULL);

// Cached system values (updated in CWnd::OnSysColorChange)
hbrBtnFace = NULL;
hbrBtnShadow = NULL;
hbrBtnHilite = NULL;
hbrWindowFrame = NULL;
hpenBtnShadow = NULL;
hpenBtnHilite = NULL;
hpenBtnText = NULL;
UpdateSysColors();

// cxBorder2 and cyBorder are 2x borders for Win4
cxBorder2 = bWin4 ? CX_BORDER*2 : CX_BORDER;
cyBorder2 = bWin4 ? CY_BORDER*2 : CY_BORDER;

// allocated on demand
hStatusFont = NULL;
hToolTipsFont = NULL;
}

// Termination code
GLOBAL_DATA::~GLOBAL_DATA()
{
// cleanup standard brushes
AfxDeleteObject((HGDIOBJ*)&hbrLtGray);
AfxDeleteObject((HGDIOBJ*)&hbrDkGray);
AfxDeleteObject((HGDIOBJ*)&hbrBtnFace);
AfxDeleteObject((HGDIOBJ*)&hbrBtnShadow);
AfxDeleteObject((HGDIOBJ*)&hbrBtnHilite);
AfxDeleteObject((HGDIOBJ*)&hbrWindowFrame);

// cleanup standard pens
AfxDeleteObject((HGDIOBJ*)&hpenBtnShadow);
AfxDeleteObject((HGDIOBJ*)&hpenBtnHilite);
AfxDeleteObject((HGDIOBJ*)&hpenBtnText);

// clean up objects we don't actually create
AfxDeleteObject((HGDIOBJ*)&hStatusFont);
AfxDeleteObject((HGDIOBJ*)&hToolTipsFont);
}

void GLOBAL_DATA::UpdateSysColors()
{
clrBtnFace = ::GetSysColor(COLOR_BTNFACE);
clrBtnShadow = ::GetSysColor(COLOR_BTNSHADOW);
clrBtnHilite = ::GetSysColor(COLOR_BTNHIGHLIGHT);
clrBtnText = ::GetSysColor(COLOR_BTNTEXT);
clrWindowFrame = ::GetSysColor(COLOR_WINDOWFRAME);

AfxDeleteObject((HGDIOBJ*)&hbrBtnFace);
AfxDeleteObject((HGDIOBJ*)&hbrBtnShadow);
AfxDeleteObject((HGDIOBJ*)&hbrBtnHilite);
AfxDeleteObject((HGDIOBJ*)&hbrWindowFrame);

hbrBtnFace = ::CreateSolidBrush(clrBtnFace);
ASSERT(hbrBtnFace != NULL);
hbrBtnShadow = ::CreateSolidBrush(clrBtnShadow);
ASSERT(hbrBtnShadow != NULL);
hbrBtnHilite = ::CreateSolidBrush(clrBtnHilite);
ASSERT(hbrBtnHilite != NULL);
hbrWindowFrame = ::CreateSolidBrush(clrWindowFrame);
ASSERT(hbrWindowFrame != NULL);

AfxDeleteObject((HGDIOBJ*)&hpenBtnShadow);
AfxDeleteObject((HGDIOBJ*)&hpenBtnHilite);
AfxDeleteObject((HGDIOBJ*)&hpenBtnText);

hpenBtnShadow = ::CreatePen(PS_SOLID, 0, clrBtnShadow);
ASSERT(hpenBtnShadow != NULL);
hpenBtnHilite = ::CreatePen(PS_SOLID, 0, clrBtnHilite);
ASSERT(hpenBtnHilite != NULL);
hpenBtnText = ::CreatePen(PS_SOLID, 0, clrBtnText);
ASSERT(hpenBtnText != NULL);
}

void GLOBAL_DATA::UpdateSysMetrics()
{
// Device metrics for screen
HDC hDCScreen = GetDC(NULL);
ASSERT(hDCScreen != NULL);
cxPixelsPerInch = GetDeviceCaps(hDCScreen, LOGPIXELSX);
cyPixelsPerInch = GetDeviceCaps(hDCScreen, LOGPIXELSY);
ReleaseDC(NULL, hDCScreen);
}

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


FLATTB.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       FlatTB.h
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef _FLATTOOLBAR_H_
#define _FLATTOOLBAR_H_

struct AFX_TBBUTTON;        // private to implementation

#ifndef _MAC
HBITMAP AFXAPI AfxLoadSysColorBitmap(HINSTANCE hInst, HRSRC hRsrc);
#else
HBITMAP AFXAPI AfxLoadSysColorBitmap(HINSTANCE hInst, HRSRC hRsrc,
HDC hDCGlyphs, BOOL bMonochrome);
#endif

// TONYCL: START: OFFICE97 LOOK AND FEEL
#define TBBS_UPSTATEMAKELONG(0,0x80)
// TONYCL: END: OFFICE97 LOOK AND FEEL

class CFlatToolbar : public CControlBar
{
DECLARE_DYNAMIC(CFlatToolbar)

// Construction
public:
CFlatToolbar();
BOOL Create(CWnd* pParentWnd,
DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP,
UINT nID = AFX_IDW_TOOLBAR);

void SetSizes(SIZE sizeButton, SIZE sizeImage);
// button size should be bigger than image
void SetHeight(int cyHeight);
// call after SetSizes, height overrides bitmap size
BOOL LoadBitmap(LPCTSTR lpszResourceName);
BOOL LoadBitmap(UINT nIDResource);
BOOL SetButtons(const UINT* lpIDArray, int nIDCount);
// lpIDArray can be NULL to allocate empty buttons

// Support loading a toolbar from a resource
BOOL LoadToolBar(LPCTSTR lpszResourceName);
inline BOOL LoadToolBar(UINT nIDResource) { return LoadToolBar(MAKEINTRESOURCE(nIDResource)); }

// Attributes
public: // standard control bar things
int CommandToIndex(UINT nIDFind) const;
UINT GetItemID(int nIndex) const;
virtual void GetItemRect(int nIndex, LPRECT lpRect) const;
UINT GetButtonStyle(int nIndex) const;
void SetButtonStyle(int nIndex, UINT nStyle);

public:
// for changing button info
void GetButtonInfo(int nIndex, UINT& nID, UINT& nStyle, int& iImage) const;
void SetButtonInfo(int nIndex, UINT nID, UINT nStyle, int iImage);

// Implementation
public:
int m_nUpButtonIndex;
virtual ~CFlatToolbar();
virtual CSize CalcFixedLayout(BOOL bStretch, BOOL bHorz);

#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:
inline AFX_TBBUTTON* _GetButtonPtr(int nIndex) const;
void InvalidateButton(int nIndex);
void UpdateButton(int nIndex);
void CreateMask(int iImage, CPoint offset,
BOOL bHilite, BOOL bHiliteShadow);

// for custom drawing
struct DrawState
{
HBITMAP hbmMono;
HBITMAP hbmMonoOld;
HBITMAP hbmOldGlyphs;
};
BOOL PrepareDrawButton(DrawState& ds);
BOOL DrawButton(CDC* pDC, int x, int y, int iImage, UINT nStyle);
#ifdef _MAC
BOOL DrawMonoButton(CDC* pDC, int x, int y, int dx, int dy,
int iImage, UINT nStyle);
#endif
void EndDrawButton(DrawState& ds);

protected:
CSize m_sizeButton;         // size of button
CSize m_sizeImage;          // size of glyph
int m_cxSharedBorder;       // shared x border between buttons
int m_cySharedBorder;       // shared y border between buttons
HBITMAP m_hbmImageWell;     // glyphs only
int m_iButtonCapture;       // index of button with capture (-1 => none)
HRSRC m_hRsrcImageWell;     // handle to loaded resource for image well
HINSTANCE m_hInstImageWell; // instance handle to load image well from

#ifdef _MAC
// Macintosh toolbars need per-toolbar DCs in order to
// work correctly in multiple-monitor environments

HDC m_hDCGlyphs;            // per-toolbar DC for glyph images
HDC m_hDCMono;              // per-toolbar DC for mono glyph masks
#endif
virtual void DoPaint(CDC* pDC);
virtual void OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler);
virtual int HitTest(CPoint point);
virtual int OnToolHitTest(CPoint point, TOOLINFO* pTI) const;

//{{AFX_MSG(CFlatToolbar)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnCancelMode();
afx_msg void OnSysColorChange();
afx_msg void OnWindowPosChanged( WINDOWPOS* lpwndpos );
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////
// Auxiliary System/Screen metrics

struct GLOBAL_DATA
{
// system metrics
int cxBorder2, cyBorder2;

// device metrics for screen
int cxPixelsPerInch, cyPixelsPerInch;

// solid brushes with convenient gray colors and system colors
HBRUSH hbrLtGray, hbrDkGray;
HBRUSH hbrBtnHilite, hbrBtnFace, hbrBtnShadow;
HBRUSH hbrWindowFrame;
HPEN hpenBtnHilite, hpenBtnShadow, hpenBtnText;

// color values of system colors used for CToolBar
COLORREF clrBtnFace, clrBtnShadow, clrBtnHilite;
COLORREF clrBtnText, clrWindowFrame;

// special GDI objects allocated on demand
HFONT   hStatusFont;
HFONT   hToolTipsFont;

// other system information
UINT    nWinVer;        // Major.Minor version numbers
BOOLbWin32s;// TRUE if Win32s (or Windows 95)
BOOL    bWin4;          // TRUE if Windows 4.0
BOOL    bNotWin4;       // TRUE if not Windows 4.0
BOOL    bSmCaption;     // TRUE if WS_EX_SMCAPTION is supported
BOOLbWin31; // TRUE if actually Win32s on Windows 3.1

// Implementation
GLOBAL_DATA();
~GLOBAL_DATA();
void UpdateSysColors();
void UpdateSysMetrics();
};

extern GLOBAL_DATA globalData;

// Note: afxData.cxBorder and afxData.cyBorder aren't used anymore
#define CX_BORDER   1
#define CY_BORDER   1

// determine number of elements in an array (not bytes)
#define _countof(array) (sizeof(array)/sizeof(array[0]))

BOOL AFXAPI AfxCustomLogFont(UINT nIDS, LOGFONT* pLogFont);
void AFXAPI AfxDeleteObject(HGDIOBJ* pObject);

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

#endif //!_FLATTOOLBAR_H_


GLOBALS.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       Globals.H
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __GLOBALS_H__
#define __GLOBALS_H__

#define IDC_MESSAGE_TAB5001
#define IDC_INPUT_RICHEDIT5002
#define IDC_PART_LIST_TREE5003
#define IDC_SEND_TOOLBAR5004
#define IDC_HISTORY_RICHEDIT5005
#define IDC_ROOM_LIST5006

//image list indexs
#define IMG_OFF0
#define IMG_IMPORTANT1
#define IMG_SAY2
#define IMG_WHISPER3
#define IMG_DO4
#define IMG_ROOM_LIST5

//|typedef /* [public][public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0001
//|    {stServerMessage= 1,
//|stChannelAccount= 2,
//|stChannelClientData= 3,
//|stChannelHostKey= 4,
//|stChannelKeyword= 5,
//|stChannelLanguage= 6,
//|stChannelName= 7,
//|stChannelOnJoin= 8,
//|stChannelOnPart= 9,
//|stChannelOwnerKey= 10,
//|stChannelRating= 11,
//|stChannelTopic= 12,
//|stChannelURL= 13
//|    }enumStringType;
//|
//|typedef /* [public][public][public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0002
//|    {etMessageOfTheDay= 0,
//|etUsers= 0x1,
//|etChannels= 0x2,
//|etChannelsTruncated= 0x3,
//|etMembers= 0x4,
//|etInitialMembers= 0x5
//|    }enumEnumType;
//|
//|typedef /* [public][public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0003
//|    {ctIRC= 0,
//|ctIRCX= 0x1
//|    }enumConnectionType;
//|
//|typedef /* [public][public][public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0004
//|    {csDisconnected= 0,
//|csConnecting= 0x1,
//|csConnected= 0x2,
//|csLogging= 0x3,
//|csLogged= 0x4,
//|csDisconnecting= 0x5
//|    }enumConnectionState;
//|
//|typedef /* [public][public][public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0005
//|    {chsClosed= 0,
//|chsOpening= 0x1,
//|chsOpen= 0x2
//|    }enumChannelState;
//|
//|typedef /* [public][public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0006
//|    {smtNormal= 0,
//|smtNotice= 0x1,
//|smtMessageOfTheDay= 0x2,
//|smtServerInfo= 0x3,
//|smtError= 0x4
//|    }enumServerMessageType;
//|
//|typedef /* [public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0007
//|    {pmtNormal= 0,
//|pmtNotice= 0x1,
//|pmtData= 0x2,
//|pmtAway= 0x4,
//|pmtVersion= 0x8,
//|pmtLagTime= 0x10,
//|pmtLocalTime= 0x20,
//|pmtAction= 0x40,
//|pmtSound= 0x80,
//|pmtCTCP= 0x100,
//|pmtInfo= 0x200
//|    }enumPrivateMessageType;
//|
//|typedef /* [public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0008
//|    {msgtNormal= 0,
//|msgtNotice= 0x1,
//|msgtData= 0x2,
//|msgtWhisper= 0x4,
//|msgtThought= 0x8,
//|msgtAction= 0x10,
//|msgtSound= 0x20,
//|msgtCTCP= 0x40,
//|msgtInfo= 0x80
//|    }enumMessageType;
//|
//|typedef /* [public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0009
//|    {umNone= 0,
//|umClientIgnored= 0x1,
//|umNotClientIgnored= 0x2,
//|umWhisper= 0x4,
//|umNoWhisper= 0x8,
//|umAuthUser= 0x10,
//|umNotAuthUser= 0x20,
//|umAway= 0x40,
//|umNotAway= 0x80,
//|umSysop= 0x100,
//|umNotSysop= 0x200,
//|umAdmin= 0x400,
//|umNotAdmin= 0x800,
//|umInvisible= 0x1000,
//|umNotInvisible= 0x2000,
//|umNotices= 0x4000,
//|umNoNotices= 0x8000,
//|umWallops= 0x10000,
//|umNoWallops= 0x20000
//|    }enumUserMode;
//|
//|typedef /* [public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0010
//|    {mmNone= 0,
//|mmClientIgnored= 0x1,
//|mmNotClientIgnored= 0x2,
//|mmWhisper= 0x4,
//|mmNoWhisper= 0x8,
//|mmAuthMember= 0x10,
//|mmNotAuthMember= 0x20,
//|mmAway= 0x40,
//|mmNotAway= 0x80,
//|mmSysop= 0x100,
//|mmNotSysop= 0x200,
//|mmOwner= 0x400,
//|mmNotOwner= 0x800,
//|mmHost= 0x1000,
//|mmNotHost= 0x2000,
//|mmVoice= 0x4000,
//|mmNoVoice= 0x8000
//|    }enumMemberMode;
//|
//|typedef /* [public] */
//|enum __MIDL___MIDL_itf_mschatx_0000_0011
//|    {cmNone= 0,
//|cmPublic= 0,
//|cmPrivate= 0x1,
//|cmHidden= 0x2,
//|cmSecret= 0x4,
//|cmModerated= 0x8,
//|cmNoExtern= 0x10,
//|cmTopicop= 0x20,
//|cmInvite= 0x40,
//|cmKnock= 0x80,
//|cmNoWhisper= 0x100,
//|cmRegistered= 0x200,
//|cmService= 0x400,
//|cmAuthOnly= 0x800,
//|cmCloneable= 0x1000,
//|cmClone= 0x2000,
//|cmAuditorium= 0x4000,
//|cmCreateOnly= 0x10000000
//|    }enumChannelMode;
//|
//|

/****************************************
 * Generated header for library: MsChatPr
 * at Thu Aug 14 10:35:49 1997
 * using MIDL 3.01.75
 ****************************************/
/* [helpstring][version][uuid] */

typedef /* [public][public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0001
    {stServerMessage= 1,
stChannelAccount= 2,
stChannelClientData= 3,
stChannelHostKey= 4,
stChannelKeyword= 5,
stChannelLanguage= 6,
stChannelName= 7,
stChannelOnJoin= 8,
stChannelOnPart= 9,
stChannelOwnerKey= 10,
stChannelRating= 11,
stChannelSubject= 12,
stChannelTopic= 13
    }enumStringType;

typedef /* [public][public][public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0002
    {etMessageOfTheDay= 0,
etUsers= 0x1,
etChannels= 0x2,
etChannelsTruncated= 0x3,
etMembers= 0x4,
etInitialMembers= 0x5
    }enumEnumType;

typedef /* [public][public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0003
    {ctIRC= 0,
ctIRCX= 0x1
    }enumConnectionType;

typedef /* [public][public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0004
    {cvtToUTF8= 0,
cvtToUnicode= 0x1
    }enumConversionType;

typedef /* [public][public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0005
    {cvsNormal= 0,
cvsNickname= 0x1,
cvsChannelName= 0x2
    }enumConversionSource;

typedef /* [public][public][public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0006
    {csDisconnected= 0,
csConnecting= 0x1,
csConnected= 0x2,
csLogging= 0x3,
csLogged= 0x4,
csDisconnecting= 0x5
    }enumConnectionState;

typedef /* [public][public][public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0007
    {chsClosed= 0,
chsOpening= 0x1,
chsOpen= 0x2
    }enumChannelState;

typedef /* [public][public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0008
    {smtNormal= 0,
smtNotice= 0x1,
smtMessageOfTheDay= 0x2,
smtServerInfo= 0x3,
smtError= 0x4
    }enumServerMessageType;

typedef /* [public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0009
    {pmtNormal= 0,
pmtNotice= 0x1,
pmtData= 0x2,
pmtDataRequest= 0x4,
pmtDataReply= 0x8,
pmtDataRaw= 0x10,
pmtAway= 0x20,
pmtVersion= 0x40,
pmtLagTime= 0x80,
pmtLocalTime= 0x100,
pmtAction= 0x200,
pmtSound= 0x400,
pmtCTCP= 0x800,
pmtInfo= 0x1000
    }enumPrivateMessageType;

typedef /* [public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0010
    {msgtNormal= 0,
msgtNotice= 0x1,
msgtData= 0x2,
msgtDataRequest= 0x4,
msgtDataReply= 0x8,
msgtDataRaw= 0x10,
msgtWhisper= 0x20,
msgtThought= 0x40,
msgtAction= 0x80,
msgtSound= 0x100,
msgtCTCP= 0x200,
msgtInfo= 0x400
    }enumMessageType;

typedef /* [public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0011
    {umNone= 0,
umClientIgnored= 0x1,
umNotClientIgnored= 0x2,
umAuthUser= 0x4,
umNotAuthUser= 0x8,
umAway= 0x10,
umNotAway= 0x20,
umSysop= 0x40,
umNotSysop= 0x80,
umAdmin= 0x100,
umNotAdmin= 0x200,
umInvisible= 0x400,
umNotInvisible= 0x800,
umNotices= 0x1000,
umNoNotices= 0x2000,
umWallops= 0x4000,
umNoWallops= 0x8000
    }enumUserMode;

typedef /* [public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0012
    {mmNone= 0,
mmClientIgnored= 0x1,
mmNotClientIgnored= 0x2,
mmAuthMember= 0x4,
mmNotAuthMember= 0x8,
mmAway= 0x10,
mmNotAway= 0x20,
mmSysop= 0x40,
mmNotSysop= 0x80,
mmOwner= 0x100,
mmNotOwner= 0x200,
mmHost= 0x400,
mmNotHost= 0x800,
mmVoice= 0x1000,
mmNoVoice= 0x2000
    }enumMemberMode;

typedef /* [public] */
enum __MIDL___MIDL_itf_mschatpr_0000_0013
    {cmNone= 0,
cmPublic= 0,
cmPrivate= 0x1,
cmHidden= 0x2,
cmSecret= 0x4,
cmModerated= 0x8,
cmNoExtern= 0x10,
cmTopicop= 0x20,
cmInvite= 0x40,
cmKnock= 0x80,
cmNoWhisper= 0x100,
cmRegistered= 0x200,
cmService= 0x400,
cmAuthOnly= 0x800,
cmCloneable= 0x1000,
cmClone= 0x2000,
cmAuditorium= 0x4000,
cmNoFormat= 0x8000,
cmCreateOnly= 0x10000000
    }enumChannelMode;

#endif //__GLOBALS_H__


HISTORY.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       History.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"
#include "History.h"
#include "Globals.h"
#include "Utility.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CHistory

CHistory::CHistory()
{
}

CHistory::~CHistory()
{
DestroyWindow();
}

BEGIN_MESSAGE_MAP(CHistory, CRichEditCtrl)
//{{AFX_MSG_MAP(CHistory)
ON_WM_RBUTTONDOWN()
ON_COMMAND(IDM_SELECT_FONT, OnSelectFont)
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CHistory message handlers

void CHistory::OnRButtonDown(UINT nFlags, CPoint point)
{
CPoint pt(point);
ClientToScreen(&pt);
PopMenu (HISTORY_MENU, pt);
CRichEditCtrl::OnRButtonDown(nFlags, point);
}

void CHistory::OnSelectFont()
{
CFontDialog FontDlg;

//GetDefaultCharFormat(FontDlg.m_cf);

int ret  = FontDlg.DoModal();

if (IDOK == ret)
{
//SetDefaultCharFormat(FontDlg.m_cf);
}

}

int CHistory::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CREdit::OnCreate(lpCreateStruct) == -1)
return -1;

SetReadOnly();

return 0;
}

void CHistory::UpdateMessageUI(LPCTSTR SenderNickname_p, long iMsgType_p,  CString szMessage_p)
{
CString szMessage;
CString szMsgType;
szMsgType.LoadString(IDS_TEXT_MSG_TYPE + iMsgType_p);

TogleItalicText(msgtWhisper == iMsgType_p);

szMessage.Format("%s %s\n\t", SenderNickname_p, szMsgType);
AddText (szMessage, TRUE);

AddText (szMessage_p, FALSE);
AddText ("\n\n", FALSE);
LineScroll(3);
}

void CHistory::OnFileSave()
{
CFileDialog dlgSave (FALSE,
"rtf",
"*.rtf",
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
"*.rtf");

if ( IDOK == dlgSave.DoModal())
{
//save
CFile fSaveFile;
CFileException e;
if (!fSaveFile.Open(dlgSave.GetPathName(), CFile::modeCreate | CFile::modeWrite , &e))
{
#ifdef _DEBUG
afxDump << "File could not be opened " << e.m_cause << "\n";
#endif
return;
}

SetSel(0,-1);
CString szText = GetSelText();
int iTextLength = GetWindowTextLength();
SetSel(iTextLength,iTextLength);

int iBufferSize = szText.GetLength();
LPCSTR lpBuffer = szText.LockBuffer();

TRY
{
fSaveFile.Write(lpBuffer, iBufferSize);
}
CATCH( CFileException, e )
{
#ifdef _DEBUG
afxDump << "File could not save " << e->m_cause << "\n";
#endif
}
END_CATCH

TRY
{
fSaveFile.Close();
}
CATCH( CFileException, e )
{
#ifdef _DEBUG
afxDump << "File could be closed " << e->m_cause << "\n";
#endif
}
END_CATCH

szText.UnlockBuffer();

}
}

void CHistory::ClearAll()
{
SetReadOnly(FALSE);
SetSel(0,-1);
CRichEditCtrl::Clear();
SetReadOnly(TRUE);
}


HISTORY.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       History.H
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __HISTORY_H__
#define __HISTORY_H__

#include "REdit.h"

/////////////////////////////////////////////////////////////////////////////
// CChatRoom window

#define HISTORY_MENU0

class CHistory : public CREdit
{
// Construction
public:
CHistory();

// Attributes
public:
void UpdateMessageUI(LPCTSTR SenderNickname_p, long iMsgType_p,  CString szMessage_p);
void OnFileSave();

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CHistory)
//}}AFX_VIRTUAL

// Implementation
public:
void ClearAll();
virtual ~CHistory();

// Generated message map functions
protected:
//{{AFX_MSG(CHistory)
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnSelectFont();
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

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

#endif //__HISTORY_H__


INPUTRE.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       InputRE.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"
#include "InputRE.h"
#include "ChppyVw.h"
#include "Globals.h"
#include "Utility.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CInputREdit

CInputREdit::CInputREdit()
{
m_bIsCtrlDown = FALSE;
}

CInputREdit::~CInputREdit()
{
}

BEGIN_MESSAGE_MAP(CInputREdit, CRichEditCtrl)
//{{AFX_MSG_MAP(CInputREdit)
ON_WM_KEYUP()
ON_WM_KEYDOWN()
ON_WM_RBUTTONDOWN()
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CInputREdit message handlers

void CInputREdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
TRACE3 ("KEY_DOWN: %u %u %u\n",nChar, nRepCnt, nFlags);

switch (nChar)
{
case CTRL_KEY:
m_bIsCtrlDown = TRUE;
break;

case VK_RETURN:
//-- RETURN == SEND
//-- CTRL+RETURN == new line
//catch the KEY_DOWN because KEY_UP it would not work with StickKeys turned on
// series of events with StickKeys
//CTRL DOWN
//ENTER DOWN
//CTRL UP
//ENTER UP  <-- seams that we are not pressing CTRL
if (m_bIsCtrlDown) break;

TRACE0 ("-- ENTER -- \n");

//Post the message in the chat room
((CChappyView *)GetParent())->PostChatMessage(msgtNormal);

//exit function without calling the parent OnKeyDown()
break;

default:
break;
}
CRichEditCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CInputREdit::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
TRACE3 ("KEY_UP: %u %u %u\n",nChar, nRepCnt, nFlags);

switch (nChar)
{
case VK_RETURN:
if (m_bIsCtrlDown) break;
//clear the control
SetSel(0,-1);
Clear();
break;

case CTRL_KEY:
m_bIsCtrlDown = FALSE;

default:
break;
}

CRichEditCtrl::OnKeyUp(nChar, nRepCnt, nFlags);
}

void CInputREdit::OnRButtonDown(UINT nFlags, CPoint point)
{
CPoint pt(point);
ClientToScreen(&pt);
PopMenu (INPUT_MENU, pt);

CRichEditCtrl::OnRButtonDown(nFlags, point);
}

int CInputREdit::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CREdit::OnCreate(lpCreateStruct) == -1)
return -1;

return 0;
}


INPUTRE.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       InputRE.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __INPUTRE_H__
#define __INPUTRE_H__

#include "REdit.h"

#define CTRL_KEY17
#define INPUT_MENU2

/////////////////////////////////////////////////////////////////////////////
// CInputREdit window

class CInputREdit : public CREdit
{
// Construction
public:
CInputREdit();

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CInputREdit)
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CInputREdit();

// Generated message map functions
protected:
//{{AFX_MSG(CInputREdit)
afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
private:
BOOL m_bIsCtrlDown;
};

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

#endif //__INPUTRE_H__


KICKDLG.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       KickDlg.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chappy.h"
#include "KickDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CKickDlg dialog

CKickDlg::CKickDlg(CWnd* pParent /*=NULL*/)
: CDialog(CKickDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CKickDlg)
m_szReason = _T("");
//}}AFX_DATA_INIT
}

void CKickDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CKickDlg)
DDX_Text(pDX, IDC_REASON, m_szReason);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CKickDlg, CDialog)
//{{AFX_MSG_MAP(CKickDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CKickDlg message handlers

int CKickDlg::DoModal()
{
//SetFocus();
return CDialog::DoModal();
}

BOOL CKickDlg::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
{
BOOL bRet = CDialog::Create(IDD, pParentWnd);
ASSERT (bRet);
SetFocus();
return bRet;
}


KICKDLG.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       KickDlg.H : Header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_KICKDLG_H__8EC66C4D_D94B_11D0_AD4F_00C04FC29DE5__INCLUDED_)
#define AFX_KICKDLG_H__8EC66C4D_D94B_11D0_AD4F_00C04FC29DE5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

/////////////////////////////////////////////////////////////////////////////
// CKickDlg dialog

class CKickDlg : public CDialog
{
// Construction
public:
CKickDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CKickDlg)
enum { IDD = IDD_KICK };
CStringm_szReason;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CKickDlg)
public:
virtual int DoModal();
virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL);
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CKickDlg)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_KICKDLG_H__8EC66C4D_D94B_11D0_AD4F_00C04FC29DE5__INCLUDED_)


LOGINDLG.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       CLogInDlg.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chappy.h"
#include "LogInDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CLogInDlg dialog

CLogInDlg::CLogInDlg(CWnd* pParent /*=NULL*/)
: CDialog(CLogInDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CLogInDlg)
m_szPassword = _T("");
m_szServerName = _T("");
m_szNickname = _T("");
m_bShowRoomList = FALSE;
//}}AFX_DATA_INIT
}

void CLogInDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CLogInDlg)
DDX_Text(pDX, IDC_PASSWORD, m_szPassword);
DDX_CBString(pDX, IDC_SERVER, m_szServerName);
DDX_Text(pDX, IDC_NICKNAME, m_szNickname);
DDX_Check(pDX, IDC_ROOM_LIST_CHECK, m_bShowRoomList);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CLogInDlg, CDialog)
//{{AFX_MSG_MAP(CLogInDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLogInDlg message handlers


LOGINDLG.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       CLogInDlg.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_CLogInDlg_H__1D7CE41F_B542_11D0_AD41_00C04FC29DE5__INCLUDED_)
#define AFX_CLogInDlg_H__1D7CE41F_B542_11D0_AD41_00C04FC29DE5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

/////////////////////////////////////////////////////////////////////////////
// CLogInDlg dialog

class CLogInDlg : public CDialog
{
// Construction
public:
CLogInDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CLogInDlg)
enum { IDD = IDD_LOG_IN_DLG };
CStringm_szPassword;
CStringm_szServerName;
CStringm_szNickname;
BOOLm_bShowRoomList;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CLogInDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CLogInDlg)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_CLogInDlg_H__1D7CE41F_B542_11D0_AD41_00C04FC29DE5__INCLUDED_)


MAINFRM.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       MainFrm.cpp : implementation of the CMainFrame class
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"
#include "ChppyVW.h"

#include "MainFrm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMainFrame

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
// NOTE - the ClassWizard will add and remove mapping macros here.
//    DO NOT EDIT what you see in these blocks of generated code !
ON_WM_CREATE()
ON_UPDATE_COMMAND_UI(IDS_CS_DISCONNECTED, OnUpdateStatus)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

static UINT indicators[] =
{
ID_SEPARATOR,           // status line indicator
IDS_CS_DISCONNECTED,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL
};

/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
// TODO: add member initialization code here

}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;

if (!m_wndToolBar.Create(this) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1;      // fail to create
}

if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
  sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1;      // fail to create
}

// TODO: Remove this if you don't want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

// TODO: Delete these three lines if you don't want the toolbar to
//  be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);

return 0;
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
//  the CREATESTRUCT cs

return CFrameWnd::PreCreateWindow(cs);
}

void CMainFrame::OnUpdateStatus(CCmdUI *pCmdUI)
{
CChappyView *TheView = (CChappyView *)GetActiveView();
if (!TheView) return; //the view is gone

    pCmdUI->Enable();

CString szLogStatus;
szLogStatus.LoadString (IDS_CS_DISCONNECTED + TheView->GetServerConnectionState());

CString szCT;
szCT.LoadString (IDS_CT_IRC + TheView->GetServerConnectionType());

szCT += szLogStatus;
pCmdUI->SetText(szCT);

//While I'm at it I'm going to update the text of the first pane
//TheView->GetCurrentChannelState();

}

/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
CFrameWnd::Dump(dc);
}

#endif //_DEBUG


MAINFRM.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       MainFrm.h : interface of the CMainFrame class
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __MAINFRM_H__
#define __MAINFRM_H__

#include "FlatTB.h"

class CMainFrame : public CFrameWnd
{
protected: // create from serialization only
CMainFrame();
DECLARE_DYNCREATE(CMainFrame)

// Attributes
public:

// Operations
public:
CStatusBar  m_wndStatusBar;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMainFrame)
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CMainFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:  // control bar embedded members
CFlatToolbar    m_wndToolBar;

// Generated message map functions
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnUpdateStatus(CCmdUI *pCmdUI);

// NOTE - the ClassWizard will add and remove member functions here.
//    DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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

#endif //__MAINFRM_H__


MSCHATPR.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       MsChatPr.cpp
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.

#include "stdafx.h"
#include "mschatpr.h"

// Dispatch interfaces referenced by this interface
#include "chtitems.h"

/////////////////////////////////////////////////////////////////////////////
// CMsChatPr

IMPLEMENT_DYNCREATE(CMsChatPr, CWnd)

/////////////////////////////////////////////////////////////////////////////
// CMsChatPr properties

/////////////////////////////////////////////////////////////////////////////
// CMsChatPr operations

long CMsChatPr::GetConnectionState()
{
long result;
InvokeHelper(0x28, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

long CMsChatPr::GetConnectionType()
{
long result;
InvokeHelper(0x29, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

long CMsChatPr::GetOpeningChannels()
{
long result;
InvokeHelper(0x2a, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

long CMsChatPr::GetPropertyAccessTimeOut()
{
long result;
InvokeHelper(0x2b, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CMsChatPr::SetPropertyAccessTimeOut(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0x2b, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

long CMsChatPr::GetProcessProtocolMessages()
{
long result;
InvokeHelper(0x2c, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CMsChatPr::SetProcessProtocolMessages(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0x2c, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

CString CMsChatPr::GetConvertedString(long ConversionType, long ConversionSource, LPCTSTR Source, long PostProcess)
{
CString result;
static BYTE parms[] =
VTS_I4 VTS_I4 VTS_BSTR VTS_I4;
InvokeHelper(0x2d, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, parms,
ConversionType, ConversionSource, Source, PostProcess);
return result;
}

LPDISPATCH CMsChatPr::GetChannels()
{
LPDISPATCH result;
InvokeHelper(0x2e, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, NULL);
return result;
}

CChatItems CMsChatPr::GetNewChatItems()
{
LPDISPATCH pDispatch;
InvokeHelper(0x2f, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&pDispatch, NULL);
return CChatItems(pDispatch);
}

long CMsChatPr::GetMaxStringLength(long StringType)
{
long result;
static BYTE parms[] =
VTS_I4;
InvokeHelper(0x30, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, parms,
StringType);
return result;
}

CString CMsChatPr::GetServerName()
{
CString result;
InvokeHelper(0x31, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

CString CMsChatPr::GetNickname()
{
CString result;
InvokeHelper(0x32, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

CChatItems CMsChatPr::GetUserProperty(LPCTSTR UserPropertyName, const VARIANT& Nickname)
{
LPDISPATCH pDispatch;
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x33, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&pDispatch, parms,
UserPropertyName, &Nickname);
return CChatItems(pDispatch);
}

CChatItems CMsChatPr::GetChannelProperty(LPCTSTR ChannelPropertyName, const VARIANT& ChannelName)
{
LPDISPATCH pDispatch;
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x34, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&pDispatch, parms,
ChannelPropertyName, &ChannelName);
return CChatItems(pDispatch);
}

CChatItems CMsChatPr::GetServerProperty(LPCTSTR ServerPropertyName)
{
LPDISPATCH pDispatch;
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x35, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&pDispatch, parms,
ServerPropertyName);
return CChatItems(pDispatch);
}

void CMsChatPr::AboutBox()
{
InvokeHelper(0x5a, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CMsChatPr::Connect(LPCTSTR ServerName)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x5b, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ServerName);
}

void CMsChatPr::Login(LPCTSTR ServerName, LPCTSTR Nickname, const VARIANT& UserName, const VARIANT& RealName, const VARIANT& Password, const VARIANT& SecurityPackages)
{
static BYTE parms[] =
VTS_BSTR VTS_BSTR VTS_VARIANT VTS_VARIANT VTS_VARIANT VTS_VARIANT;
InvokeHelper(0x5c, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ServerName, Nickname, &UserName, &RealName, &Password, &SecurityPackages);
}

void CMsChatPr::Disconnect()
{
InvokeHelper(0x5d, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CMsChatPr::CancelConnectLogin()
{
InvokeHelper(0x5e, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CMsChatPr::ListChannels(LPDISPATCH ChannelQueryItems)
{
static BYTE parms[] =
VTS_DISPATCH;
InvokeHelper(0x5f, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ChannelQueryItems);
}

void CMsChatPr::QueryChannelProperty(LPCTSTR ChannelPropertyName, const VARIANT& ChannelName)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x60, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ChannelPropertyName, &ChannelName);
}

void CMsChatPr::KillChannel(LPCTSTR ChannelName, const VARIANT& Reason)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x61, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ChannelName, &Reason);
}

void CMsChatPr::ListUsers(LPDISPATCH UserQueryItems)
{
static BYTE parms[] =
VTS_DISPATCH;
InvokeHelper(0x62, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 UserQueryItems);
}

void CMsChatPr::QueryUserProperty(LPCTSTR UserPropertyName, const VARIANT& Nickname)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x63, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 UserPropertyName, &Nickname);
}

void CMsChatPr::ChangeUserProperty(LPCTSTR UserPropertyName, const VARIANT& NewUserProperty, const VARIANT& Nickname)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT VTS_VARIANT;
InvokeHelper(0x64, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 UserPropertyName, &NewUserProperty, &Nickname);
}

void CMsChatPr::IgnoreUsers(LPDISPATCH UserItems, long Set)
{
static BYTE parms[] =
VTS_DISPATCH VTS_I4;
InvokeHelper(0x65, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 UserItems, Set);
}

void CMsChatPr::BanUsers(LPDISPATCH UserItems, long Set, const VARIANT& Reason, const VARIANT& Duration)
{
static BYTE parms[] =
VTS_DISPATCH VTS_I4 VTS_VARIANT VTS_VARIANT;
InvokeHelper(0x66, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 UserItems, Set, &Reason, &Duration);
}

void CMsChatPr::KickUser(LPCTSTR Nickname, const VARIANT& Reason)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x67, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 Nickname, &Reason);
}

void CMsChatPr::ChangeNickname(LPCTSTR NewNickname)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x68, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 NewNickname);
}

void CMsChatPr::SetAway(long Away, const VARIANT& TextMessage)
{
static BYTE parms[] =
VTS_I4 VTS_VARIANT;
InvokeHelper(0x69, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 Away, &TextMessage);
}

void CMsChatPr::QueryServerProperty(LPCTSTR ServerPropertyName)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x6a, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ServerPropertyName);
}

void CMsChatPr::SendInvitation(LPCTSTR Nickname, const VARIANT& ChannelName)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x6b, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 Nickname, &ChannelName);
}

void CMsChatPr::SendPrivateMessage(long PrivateMessageType, const VARIANT& Message, const VARIANT& RecipientNicknames, const VARIANT& DataMessageTag)
{
static BYTE parms[] =
VTS_I4 VTS_VARIANT VTS_VARIANT VTS_VARIANT;
InvokeHelper(0x6c, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 PrivateMessageType, &Message, &RecipientNicknames, &DataMessageTag);
}

void CMsChatPr::SendProtocolMessage(LPCTSTR TextMessage)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x6d, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 TextMessage);
}


MSCHATPR.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       MsChatPr.H
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.

// Dispatch interfaces referenced by this interface
class CChatItems;

/////////////////////////////////////////////////////////////////////////////
// CMsChatPr wrapper class

class CMsChatPr : public CWnd
{
protected:
DECLARE_DYNCREATE(CMsChatPr)
public:
CLSID const& GetClsid()
{
static CLSID const clsid
= { 0x39397c0, 0x46df, 0x11d0, { 0x99, 0xcb, 0x0, 0xc0, 0x4f, 0xd6, 0x44, 0x97 } };
return clsid;
}
virtual BOOL Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd, UINT nID,
CCreateContext* pContext = NULL)
{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); }

    BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID,
CFile* pPersist = NULL, BOOL bStorage = FALSE,
BSTR bstrLicKey = NULL)
{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID,
pPersist, bStorage, bstrLicKey); }

// Attributes
public:

// Operations
public:
long GetConnectionState();
long GetConnectionType();
long GetOpeningChannels();
long GetPropertyAccessTimeOut();
void SetPropertyAccessTimeOut(long nNewValue);
long GetProcessProtocolMessages();
void SetProcessProtocolMessages(long nNewValue);
CString GetConvertedString(long ConversionType, long ConversionSource, LPCTSTR Source, long PostProcess);
LPDISPATCH GetChannels();
CChatItems GetNewChatItems();
long GetMaxStringLength(long StringType);
CString GetServerName();
CString GetNickname();
CChatItems GetUserProperty(LPCTSTR UserPropertyName, const VARIANT& Nickname);
CChatItems GetChannelProperty(LPCTSTR ChannelPropertyName, const VARIANT& ChannelName);
CChatItems GetServerProperty(LPCTSTR ServerPropertyName);
void AboutBox();
void Connect(LPCTSTR ServerName);
void Login(LPCTSTR ServerName, LPCTSTR Nickname, const VARIANT& UserName, const VARIANT& RealName, const VARIANT& Password, const VARIANT& SecurityPackages);
void Disconnect();
void CancelConnectLogin();
void ListChannels(LPDISPATCH ChannelQueryItems);
void QueryChannelProperty(LPCTSTR ChannelPropertyName, const VARIANT& ChannelName);
void KillChannel(LPCTSTR ChannelName, const VARIANT& Reason);
void ListUsers(LPDISPATCH UserQueryItems);
void QueryUserProperty(LPCTSTR UserPropertyName, const VARIANT& Nickname);
void ChangeUserProperty(LPCTSTR UserPropertyName, const VARIANT& NewUserProperty, const VARIANT& Nickname);
void IgnoreUsers(LPDISPATCH UserItems, long Set);
void BanUsers(LPDISPATCH UserItems, long Set, const VARIANT& Reason, const VARIANT& Duration);
void KickUser(LPCTSTR Nickname, const VARIANT& Reason);
void ChangeNickname(LPCTSTR NewNickname);
void SetAway(long Away, const VARIANT& TextMessage);
void QueryServerProperty(LPCTSTR ServerPropertyName);
void SendInvitation(LPCTSTR Nickname, const VARIANT& ChannelName);
void SendPrivateMessage(long PrivateMessageType, const VARIANT& Message, const VARIANT& RecipientNicknames, const VARIANT& DataMessageTag);
void SendProtocolMessage(LPCTSTR TextMessage);
};


MSCHATPR.IDL

#include <olectl.h> // MsChatPr.idl : IDL source for MsChatPr.ocx
//

// This file will be processed by the MIDL tool to
// produce the type library (MsChatPr.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
uuid(9FE8A900-96AC-11CF-9086-00AA006C3FBC),
version(2.0),
helpstring("Microsoft Chat Protocol Control 2.0")
]
library MsChatPr
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

// constants and enumerations
typedef enum
{
stServerMessage=  1,
stChannelAccount=  2,
stChannelClientData=  3,
stChannelHostKey=  4,
stChannelKeyword=  5,
stChannelLanguage=  6,
stChannelName=  7,
stChannelOnJoin=  8,
stChannelOnPart=  9,
stChannelOwnerKey= 10,
stChannelRating= 11,
stChannelSubject= 12,
stChannelTopic= 13
} enumStringType;

 typedef enum
{
etMessageOfTheDay= 0x00,
etUsers= 0x01,
etChannels= 0x02,
etChannelsTruncated= 0x03,
etMembers= 0x04,
etInitialMembers= 0x05
} enumEnumType;

typedef enum
{
ctIRC= 0x00,
ctIRCX= 0x01
} enumConnectionType;

typedef enum
{
cvtToUTF8= 0x00,
cvtToUnicode= 0x01
} enumConversionType;

typedef enum
{
cvsNormal= 0x00,
cvsNickname= 0x01,
cvsChannelName= 0x02
} enumConversionSource;

 typedef enum
{
csDisconnected= 0x00,
csConnecting= 0x01,
csConnected= 0x02,
csLogging= 0x03,
csLogged= 0x04,
csDisconnecting= 0x05
} enumConnectionState;

 typedef enum
{
chsClosed= 0x00,
chsOpening= 0x01,
chsOpen= 0x02
} enumChannelState;

 typedef enum
{
smtNormal= 0x00,
smtNotice= 0x01,
smtMessageOfTheDay= 0x02,
smtServerInfo= 0x03,
smtError= 0x04
} enumServerMessageType;

 typedef enum
{
pmtNormal= 0x0000,
pmtNotice= 0x0001,
pmtData= 0x0002,
pmtDataRequest= 0x0004,
pmtDataReply= 0x0008,
pmtDataRaw= 0x0010,
pmtAway= 0x0020,
pmtVersion= 0x0040,
pmtLagTime= 0x0080,
pmtLocalTime= 0x0100,
pmtAction= 0x0200,
pmtSound= 0x0400,
pmtCTCP= 0x0800,
pmtInfo= 0x1000
} enumPrivateMessageType;

 typedef enum
{
msgtNormal= 0x0000,
msgtNotice= 0x0001,
msgtData= 0x0002,
msgtDataRequest= 0x0004,
msgtDataReply= 0x0008,
msgtDataRaw= 0x0010,
msgtWhisper= 0x0020,
msgtThought= 0x0040,
msgtAction= 0x0080,
msgtSound= 0x0100,
msgtCTCP= 0x0200,
msgtInfo= 0x0400
} enumMessageType;

typedef enum
{
umNone= 0x00000000,
umClientIgnored= 0x00000001,
umNotClientIgnored= 0x00000002,
umAuthUser= 0x00000004,
umNotAuthUser= 0x00000008,
umAway= 0x00000010,
umNotAway= 0x00000020,
umSysop= 0x00000040,
umNotSysop= 0x00000080,
umAdmin= 0x00000100,
umNotAdmin= 0x00000200,
umInvisible= 0x00000400,
umNotInvisible= 0x00000800,
umNotices= 0x00001000,
umNoNotices= 0x00002000,
umWallops= 0x00004000,
umNoWallops= 0x00008000
} enumUserMode;

typedef enum
{
mmNone= 0x00000000,
mmClientIgnored= 0x00000001,
mmNotClientIgnored= 0x00000002,
mmAuthMember= 0x00000004,
mmNotAuthMember= 0x00000008,
mmAway= 0x00000010,
mmNotAway= 0x00000020,
mmSysop= 0x00000040,
mmNotSysop= 0x00000080,
mmOwner= 0x00000100,
mmNotOwner= 0x00000200,
mmHost= 0x00000400,
mmNotHost= 0x00000800,
mmVoice= 0x00001000,
mmNoVoice= 0x00002000
} enumMemberMode;

typedef enum
{
cmNone= 0x00000000,
cmPublic= 0x00000000,
cmPrivate= 0x00000001,
cmHidden= 0x00000002,
cmSecret= 0x00000004,
cmModerated= 0x00000008,
cmNoExtern= 0x00000010,
cmTopicop= 0x00000020,
cmInvite= 0x00000040,
cmKnock= 0x00000080,
cmNoWhisper= 0x00000100,
cmRegistered= 0x00000200,
cmService= 0x00000400,
cmAuthOnly= 0x00000800,
cmCloneable= 0x00001000,
cmClone= 0x00002000,
cmAuditorium= 0x00004000,
cmNoFormat= 0x00008000,
cmCreateOnly= 0x10000000
} enumChannelMode;

[
object,
        uuid(011eee45-46df-11d0-99cb-00c04fd64497),
dual,
helpstring("IChatItems Interface"),
pointer_default(unique)
]
interface IChatItems : IDispatch
{
[propget, id(1)] HRESULT AssociatedType([out, retval] BSTR *pVal);
[propput, id(1)] HRESULT AssociatedType([in] BSTR newVal);
[propget, id(2)] HRESULT ValidItems([out, retval] VARIANT *pVal);
[propget, id(3)] HRESULT ItemValid([in] BSTR ItemName, [out, retval] BOOL *pVal);
[propget, id(4)] HRESULT Item([in] BSTR ItemName, [out, retval] VARIANT *pVal);
[propput, id(4)] HRESULT Item([in] BSTR ItemName, [in] VARIANT newVal);
[id(55)] HRESULT InvalidateItem([in] BSTR ItemName);
};

[
        uuid(011EEE46-46DF-11D0-99CB-00C04FD64497),
helpstring("Microsoft ChatItems Object")
]
coclass ChatItems
{
[default] interface IChatItems;
};

[
object,
        uuid(011eee43-46df-11d0-99cb-00c04fd64497),
dual,
helpstring("IChannelObject Interface"),
pointer_default(unique)
]
interface IChannelObject : IDispatch
{
[propget, id(10)] HRESULT Index([out, retval] long *pVal);
[propget, id(11)] HRESULT ChannelState([out, retval] enumChannelState *pVal);
[propget, id(12)] HRESULT ChannelName([out, retval] BSTR *pVal);
[propput, id(12)] HRESULT ChannelName([in] BSTR newVal);
[propget, id(13)] HRESULT ChannelTopic([out, retval] BSTR *pVal);
[propget, id(14)] HRESULT MemberCount([out, retval] long *pVal);
[propget, id(15)] HRESULT MemberIdentity([in,optional] VARIANT vMemberNickname, [out, retval] BSTR *pVal);
[propget, id(16)] HRESULT MemberModes([in,optional] VARIANT vMemberNickname, [out, retval] long *pVal);
[propget, id(17)] HRESULT ChannelProperty([in] BSTR ChannelPropertyName, [out, retval] IChatItems* *pVal);
[propget, id(18)] HRESULT MemberProperty([in] BSTR MemberPropertyName, [in,optional] VARIANT vMemberNickname, [out, retval] IChatItems* *pVal);
[id(60)] HRESULT CreateChannel([in,optional] VARIANT ChannelName, [in,optional] VARIANT ChannelKeyword, [in,optional] VARIANT ChannelModes, [in,optional] VARIANT MaxMemberCount);
[id(61)] HRESULT JoinChannel([in,optional] VARIANT ChannelName, [in,optional] VARIANT ChannelKeyword);
[id(62)] HRESULT LeaveChannel();
[id(63)] HRESULT SendMessage([in] long MessageType, [in] VARIANT Message, [in,optional] VARIANT RecipientNicknames, [in,optional] VARIANT DataMessageTag);
[id(64)] HRESULT SendInvitation([in] BSTR Nickname);
[id(65)] HRESULT ListMembers([in] ChatItems* MemberQueryItems);
[id(66)] HRESULT QueryMemberProperty([in] BSTR MemberPropertyName, [in,optional] VARIANT MemberNickname);
[id(67)] HRESULT ChangeMemberProperty([in] BSTR MemberPropertyName, [in] VARIANT NewMemberProperty, [in,optional] VARIANT MemberNickname);
[id(68)] HRESULT BanMembers([in] ChatItems* MemberItems, [in] BOOL Set);
[id(69)] HRESULT KickMember([in] BSTR MemberNickname, [in,optional] VARIANT Reason);
[id(70)] HRESULT QueryChannelProperty([in] BSTR ChannelPropertyName);
[id(71)] HRESULT ChangeChannelProperty([in] BSTR ChannelPropertyName, [in] VARIANT NewChannelProperty);
};

[
        uuid(011EEE44-46DF-11D0-99CB-00C04FD64497),
helpstring("Microsoft Channel Object")
]
coclass Channel
{
[default] interface IChannelObject;
};

[
object,
        uuid(011eee41-46df-11d0-99cb-00c04fd64497),
dual,
helpstring("IChannelsCollection Interface"),
pointer_default(unique)
]
interface IChannelsCollection : IDispatch
{
[propget, id(DISPID_VALUE)] HRESULT Item([in,optional] VARIANT vIndex, [out, retval] IChannelObject* *pVal);
[propget, id(DISPID_NEWENUM)] HRESULT _NewEnum([out, retval] IUnknown* *pVal);
[propget, id(30)] HRESULT Count([out, retval] long *pVal);
[id(80)] HRESULT Add([in,optional] VARIANT vIndex, [in,optional] VARIANT vKey, [out, retval] IChannelObject** ppChannelObject);
[id(81)] HRESULT Remove([in] VARIANT vIndex);
};

[
        uuid(011EEE42-46DF-11D0-99CB-00C04FD64497),
helpstring("Microsoft Channels Collection Object")
]
coclass Channels
{
[default] interface IChannelsCollection;
};

[
object,
        uuid(011eee40-46df-11d0-99cb-00c04fd64497),
dual,
helpstring("IMsChatPr Interface"),
pointer_default(unique)
]
interface IMsChatPr : IDispatch
{
[propget, id(40)] HRESULT ConnectionState([out, retval] enumConnectionState *pVal);
[propget, id(41)] HRESULT ConnectionType([out, retval] enumConnectionType *pVal);
[propget, id(42)] HRESULT OpeningChannels([out, retval] long *pVal);
[propget, id(43)] HRESULT PropertyAccessTimeOut([out, retval] long *pVal);
[propput, id(43)] HRESULT PropertyAccessTimeOut([in] long newVal);
[propget, id(44)] HRESULT ProcessProtocolMessages([out, retval] BOOL *pVal);
[propput, id(44)] HRESULT ProcessProtocolMessages([in] BOOL newVal);
[propget, id(45)] HRESULT ConvertedString([in] enumConversionType ConversionType, [in] enumConversionSource ConversionSource, [in] BSTR Source, [in] BOOL PostProcess, [out, retval] BSTR *pVal);
[propget, id(46)] HRESULT Channels([out, retval] IDispatch* *pVal);
[propget, id(47)] HRESULT NewChatItems([out, retval] IChatItems* *pVal);
[propget, id(48)] HRESULT MaxStringLength([in] enumStringType StringType, [out, retval] long *pVal);
[propget, id(49)] HRESULT ServerName([out, retval] BSTR *pVal);
[propget, id(50)] HRESULT Nickname([out, retval] BSTR *pVal);
[propget, id(51)] HRESULT UserProperty([in] BSTR UserPropertyName, [in,optional] VARIANT Nickname, [out, retval] IChatItems* *pVal);
[propget, id(52)] HRESULT ChannelProperty([in] BSTR ChannelPropertyName, [in,optional] VARIANT ChannelName, [out, retval] IChatItems* *pVal);
[propget, id(53)] HRESULT ServerProperty([in] BSTR ServerPropertyName, [out, retval] IChatItems* *pVal);
[id(90)] HRESULT AboutBox();
[id(91)] HRESULT Connect([in] BSTR ServerName);
[id(92)] HRESULT Login([in] BSTR ServerName, [in] BSTR Nickname, [in,optional] VARIANT UserName, [in,optional] VARIANT RealName, [in,optional] VARIANT Password, [in,optional] VARIANT SecurityPackages);
[id(93)] HRESULT Disconnect();
[id(94)] HRESULT CancelConnectLogin();
[id(95)] HRESULT ListChannels([in] ChatItems* ChannelQueryItems);
[id(96)] HRESULT QueryChannelProperty([in] BSTR ChannelPropertyName, [in,optional] VARIANT ChannelName);
[id(97)] HRESULT KillChannel([in] BSTR ChannelName, [in,optional] VARIANT Reason);
[id(98)] HRESULT ListUsers([in] ChatItems* UserQueryItems);
[id(99)] HRESULT QueryUserProperty([in] BSTR UserPropertyName, [in,optional] VARIANT Nickname);
[id(100)] HRESULT ChangeUserProperty([in] BSTR UserPropertyName, [in] VARIANT NewUserProperty, [in,optional] VARIANT Nickname);
[id(101)] HRESULT IgnoreUsers([in] ChatItems* UserItems, [in] BOOL Set);
[id(102)] HRESULT BanUsers([in] ChatItems* UserItems, [in] BOOL Set, [in,optional] VARIANT Reason, [in,optional] VARIANT Duration);
[id(103)] HRESULT KickUser([in] BSTR Nickname, [in,optional] VARIANT Reason);
[id(104)] HRESULT ChangeNickname([in] BSTR NewNickname);
[id(105)] HRESULT SetAway([in] BOOL Away, [in,optional] VARIANT TextMessage);
[id(106)] HRESULT QueryServerProperty([in] BSTR ServerPropertyName);
[id(107)] HRESULT SendInvitation([in] BSTR Nickname, [in,optional] VARIANT ChannelName);
[id(108)] HRESULT SendPrivateMessage([in] long PrivateMessageType, [in] VARIANT Message, [in] VARIANT RecipientNicknames, [in,optional] VARIANT DataMessageTag);
[id(109)] HRESULT SendProtocolMessage([in] BSTR TextMessage);
};

[
        uuid(0254af20-46df-11d0-99cb-00c04fd64497),
helpstring("IMsChatPrEvent Interface")
]
dispinterface _IMsChatPrEvent
{
properties:
//  Event interface has no properties
methods:
[id(110)]
void OnConnectionState(enumConnectionState NewConnectionState);

[id(111)]
void OnConnectionError(long ErrorCode,
   BSTR Description,
   VARIANT ChannelName);

[id(112)]
void OnChannelState(Channel* Channel,
enumChannelState NewChannelState);

[id(113)]
void OnChannelError(Channel* Channel,
long ErrorCode,
BSTR Description);

[id(114)]
void OnBeginEnumeration(Channel* Channel,
enumEnumType EnumType);

[id(115)]
void OnEndEnumeration(Channel* Channel,
  enumEnumType EnumType);

[id(116)]
void OnAddMember(Channel* Channel,
 BSTR Nickname,
 ChatItems* MemberItems);

[id(117)]
void OnDelMember(Channel* Channel,
 BSTR Nickname);

[id(118)]
void OnUserProperty(ChatItems* UserItems);

[id(119)]
void OnUserPropertyChanged(BSTR ModifiedNickname,
   BSTR ModifierNickname,
   BSTR UserPropertyName,
   VARIANT OldUserProperty,
   VARIANT NewUserProperty);

[id(120)]
void OnMemberProperty(Channel* Channel, ChatItems* MemberItems);

[id(121)]
void OnMemberPropertyChanged(Channel* Channel,
 BSTR ModifiedNickname,
 BSTR ModifierNickname,
 BSTR MemberPropertyName,
 VARIANT OldMemberProperty,
 VARIANT NewMemberProperty);

[id(122)]
void OnMemberKicked(Channel* Channel,
BSTR KickedNickname,
BSTR KickerNickname,
BSTR Reason);

[id(123)]
void OnChannelProperty(Channel* Channel,
   ChatItems* ChannelItems);

[id(124)]
void OnChannelPropertyChanged(Channel* Channel,
  BSTR ModifierNickname,
  BSTR ChannelPropertyName,
  VARIANT OldChannelProperty,
  VARIANT NewChannelProperty);

[id(125)]
void OnServerProperty(ChatItems* ServerItems);

[id(126)]
void OnInvitation(BSTR ChannelName,
  ChatItems* InviterItems);

[id(127)]
void OnKnock(Channel* Channel,
 ChatItems* KnockerItems,
 long ErrorCode);

[id(128)]
void OnPrivateMessage(BSTR SenderNickname,
  long PrivateMessageType,
  VARIANT Message,
  VARIANT DataMessageTag);

[id(129)]
void OnServerTextMessage(enumServerMessageType ServerMessageType,
 BSTR Text);

[id(130)]
void OnMessage(Channel* Channel,
   BSTR SenderNickname,
   long MessageType,
   VARIANT Message,
   VARIANT RecipientNicknames,
   VARIANT DataMessageTag);

[id(131)]
void OnProtocolMessage(BSTR MessagePrefix,
   BSTR MessageCommand,
   VARIANT MessageParameters,
   OLE_ENABLEDEFAULTBOOL* EnableDefault);
};

[
        uuid(039397C0-46DF-11D0-99CB-00C04FD64497),
helpstring("Microsoft Chat Protocol Control")
]
coclass MsChatPr
{
[default] interface IMsChatPr;
[default, source] interface _IMsChatPrEvent;
};
};


MSCHATX.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       MsChatX.cpp
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

// Machine generated IDispatch wrapper class(es) created with ClassWizard

#include "stdafx.h"
#include "mschatx.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// IChatItems properties

/////////////////////////////////////////////////////////////////////////////
// IChatItems operations

CString IChatItems::GetAssociatedType()
{
CString result;
InvokeHelper(0x1, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void IChatItems::SetAssociatedType(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x1, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

VARIANT IChatItems::GetValidItems()
{
VARIANT result;
InvokeHelper(0x2, DISPATCH_PROPERTYGET, VT_VARIANT, (void*)&result, NULL);
return result;
}

long IChatItems::GetItemValid(LPCTSTR ItemName)
{
long result;
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x3, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, parms,
ItemName);
return result;
}

VARIANT IChatItems::GetItem(LPCTSTR ItemName)
{
VARIANT result;
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x4, DISPATCH_PROPERTYGET, VT_VARIANT, (void*)&result, parms,
ItemName);
return result;
}

void IChatItems::SetItem(LPCTSTR ItemName, const VARIANT& newValue)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x4, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 ItemName, &newValue);
}

void IChatItems::InvalidateItem(LPCTSTR ItemName)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x37, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ItemName);
}

/////////////////////////////////////////////////////////////////////////////
// IChannelObject properties

/////////////////////////////////////////////////////////////////////////////
// IChannelObject operations

long IChannelObject::GetIndex()
{
long result;
InvokeHelper(0xa, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

long IChannelObject::GetChannelState()
{
long result;
InvokeHelper(0xb, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

CString IChannelObject::GetChannelName()
{
CString result;
InvokeHelper(0xc, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void IChannelObject::SetChannelName(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0xc, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

CString IChannelObject::GetChannelTopic()
{
CString result;
InvokeHelper(0xd, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

long IChannelObject::GetMemberCount()
{
long result;
InvokeHelper(0xe, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

CString IChannelObject::GetMemberIdentity(const VARIANT& vMemberNickname)
{
CString result;
static BYTE parms[] =
VTS_VARIANT;
InvokeHelper(0xf, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, parms,
&vMemberNickname);
return result;
}

long IChannelObject::GetMemberModes(const VARIANT& vMemberNickname)
{
long result;
static BYTE parms[] =
VTS_VARIANT;
InvokeHelper(0x10, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, parms,
&vMemberNickname);
return result;
}

LPDISPATCH IChannelObject::GetChannelProperty(LPCTSTR ChannelPropertyName)
{
LPDISPATCH result;
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x11, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, parms,
ChannelPropertyName);
return result;
}

LPDISPATCH IChannelObject::GetMemberProperty(LPCTSTR MemberPropertyName, const VARIANT& vMemberNickname)
{
LPDISPATCH result;
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x12, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, parms,
MemberPropertyName, &vMemberNickname);
return result;
}

void IChannelObject::CreateChannel(const VARIANT& ChannelName, const VARIANT& ChannelKeyword, const VARIANT& ChannelModes, const VARIANT& MaxMemberCount)
{
static BYTE parms[] =
VTS_VARIANT VTS_VARIANT VTS_VARIANT VTS_VARIANT;
InvokeHelper(0x3c, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 &ChannelName, &ChannelKeyword, &ChannelModes, &MaxMemberCount);
}

void IChannelObject::JoinChannel(const VARIANT& ChannelName, const VARIANT& ChannelKeyword)
{
static BYTE parms[] =
VTS_VARIANT VTS_VARIANT;
InvokeHelper(0x3d, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 &ChannelName, &ChannelKeyword);
}

void IChannelObject::LeaveChannel()
{
InvokeHelper(0x3e, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void IChannelObject::SendMessage(long MessageType, const VARIANT& Message, const VARIANT& RecipientNicknames, const VARIANT& DataMessageTag)
{
static BYTE parms[] =
VTS_I4 VTS_VARIANT VTS_VARIANT VTS_VARIANT;
InvokeHelper(0x3f, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 MessageType, &Message, &RecipientNicknames, &DataMessageTag);
}

void IChannelObject::SendInvitation(LPCTSTR Nickname)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x40, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 Nickname);
}

void IChannelObject::ListMembers(LPDISPATCH MemberQueryItems)
{
static BYTE parms[] =
VTS_DISPATCH;
InvokeHelper(0x41, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 MemberQueryItems);
}

void IChannelObject::QueryMemberProperty(LPCTSTR MemberPropertyName, const VARIANT& MemberNickname)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x42, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 MemberPropertyName, &MemberNickname);
}

void IChannelObject::ChangeMemberProperty(LPCTSTR MemberPropertyName, const VARIANT& NewMemberProperty, const VARIANT& MemberNickname)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT VTS_VARIANT;
InvokeHelper(0x43, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 MemberPropertyName, &NewMemberProperty, &MemberNickname);
}

void IChannelObject::BanMembers(LPDISPATCH MemberItems, long Set)
{
static BYTE parms[] =
VTS_DISPATCH VTS_I4;
InvokeHelper(0x44, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 MemberItems, Set);
}

void IChannelObject::KickMember(LPCTSTR MemberNickname, const VARIANT& Reason)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x45, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 MemberNickname, &Reason);
}

void IChannelObject::QueryChannelProperty(LPCTSTR ChannelPropertyName)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x46, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ChannelPropertyName);
}

void IChannelObject::ChangeChannelProperty(LPCTSTR ChannelPropertyName, const VARIANT& NewChannelProperty)
{
static BYTE parms[] =
VTS_BSTR VTS_VARIANT;
InvokeHelper(0x47, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ChannelPropertyName, &NewChannelProperty);
}

/////////////////////////////////////////////////////////////////////////////
// IChannelsCollection properties

/////////////////////////////////////////////////////////////////////////////
// IChannelsCollection operations

LPDISPATCH IChannelsCollection::GetItem(const VARIANT& vIndex)
{
LPDISPATCH result;
static BYTE parms[] =
VTS_VARIANT;
InvokeHelper(0x0, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, parms,
&vIndex);
return result;
}

LPUNKNOWN IChannelsCollection::Get_NewEnum()
{
LPUNKNOWN result;
InvokeHelper(0xfffffffc, DISPATCH_PROPERTYGET, VT_UNKNOWN, (void*)&result, NULL);
return result;
}

long IChannelsCollection::GetCount()
{
long result;
InvokeHelper(0x1e, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

LPDISPATCH IChannelsCollection::Add(const VARIANT& vIndex, const VARIANT& vKey)
{
LPDISPATCH result;
static BYTE parms[] =
VTS_VARIANT VTS_VARIANT;
InvokeHelper(0x50, DISPATCH_METHOD, VT_DISPATCH, (void*)&result, parms,
&vIndex, &vKey);
return result;
}

void IChannelsCollection::Remove(const VARIANT& vIndex)
{
static BYTE parms[] =
VTS_VARIANT;
InvokeHelper(0x51, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 &vIndex);
}


MSCHATX.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       MsChatX.H
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __MSCHATX_H__
#define __MSCHATX_H__
// Machine generated IDispatch wrapper class(es) created with ClassWizard
/////////////////////////////////////////////////////////////////////////////
// IChatItems wrapper class

class IChatItems : public COleDispatchDriver
{
public:
IChatItems() {}// Calls COleDispatchDriver default constructor
IChatItems(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
IChatItems(const IChatItems& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

// Attributes
public:

// Operations
public:
CString GetAssociatedType();
void SetAssociatedType(LPCTSTR lpszNewValue);
VARIANT GetValidItems();
long GetItemValid(LPCTSTR ItemName);
VARIANT GetItem(LPCTSTR ItemName);
void SetItem(LPCTSTR ItemName, const VARIANT& newValue);
void InvalidateItem(LPCTSTR ItemName);
};
/////////////////////////////////////////////////////////////////////////////
// IChannelObject wrapper class

class IChannelObject : public COleDispatchDriver
{
public:
IChannelObject() {}// Calls COleDispatchDriver default constructor
IChannelObject(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
IChannelObject(const IChannelObject& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

// Attributes
public:

// Operations
public:
long GetIndex();
long GetChannelState();
CString GetChannelName();
void SetChannelName(LPCTSTR lpszNewValue);
CString GetChannelTopic();
long GetMemberCount();
CString GetMemberIdentity(const VARIANT& vMemberNickname);
long GetMemberModes(const VARIANT& vMemberNickname);
LPDISPATCH GetChannelProperty(LPCTSTR ChannelPropertyName);
LPDISPATCH GetMemberProperty(LPCTSTR MemberPropertyName, const VARIANT& vMemberNickname);
void CreateChannel(const VARIANT& ChannelName, const VARIANT& ChannelKeyword, const VARIANT& ChannelModes, const VARIANT& MaxMemberCount);
void JoinChannel(const VARIANT& ChannelName, const VARIANT& ChannelKeyword);
void LeaveChannel();
void SendMessage(long MessageType, const VARIANT& Message, const VARIANT& RecipientNicknames, const VARIANT& DataMessageTag);
void SendInvitation(LPCTSTR Nickname);
void ListMembers(LPDISPATCH MemberQueryItems);
void QueryMemberProperty(LPCTSTR MemberPropertyName, const VARIANT& MemberNickname);
void ChangeMemberProperty(LPCTSTR MemberPropertyName, const VARIANT& NewMemberProperty, const VARIANT& MemberNickname);
void BanMembers(LPDISPATCH MemberItems, long Set);
void KickMember(LPCTSTR MemberNickname, const VARIANT& Reason);
void QueryChannelProperty(LPCTSTR ChannelPropertyName);
void ChangeChannelProperty(LPCTSTR ChannelPropertyName, const VARIANT& NewChannelProperty);
};
/////////////////////////////////////////////////////////////////////////////
// IChannelsCollection wrapper class

class IChannelsCollection : public COleDispatchDriver
{
public:
IChannelsCollection() {}// Calls COleDispatchDriver default constructor
IChannelsCollection(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
IChannelsCollection(const IChannelsCollection& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

// Attributes
public:

// Operations
public:
LPDISPATCH GetItem(const VARIANT& vIndex);
LPUNKNOWN Get_NewEnum();
long GetCount();
LPDISPATCH Add(const VARIANT& vIndex, const VARIANT& vKey);
void Remove(const VARIANT& vIndex);
};
/////////////////////////////////////////////////////////////////////////////
// IMsChatPr wrapper class

class IMsChatPr : public COleDispatchDriver
{
public:
IMsChatPr() {}// Calls COleDispatchDriver default constructor
IMsChatPr(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
IMsChatPr(const IMsChatPr& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

// Attributes
public:

// Operations
public:
long GetConnectionState();
long GetConnectionType();
long GetOpeningChannels();
long GetPropertyAccessTimeOut();
void SetPropertyAccessTimeOut(long nNewValue);
long GetProcessProtocolMessages();
void SetProcessProtocolMessages(long nNewValue);
CString GetConvertedString(long ConversionType, long ConversionSource, LPCTSTR Source, long PostProcess);
LPDISPATCH GetChannels();
LPDISPATCH GetNewChatItems();
long GetMaxStringLength(long StringType);
CString GetServerName();
CString GetNickname();
LPDISPATCH GetUserProperty(LPCTSTR UserPropertyName, const VARIANT& Nickname);
LPDISPATCH GetChannelProperty(LPCTSTR ChannelPropertyName, const VARIANT& ChannelName);
LPDISPATCH GetServerProperty(LPCTSTR ServerPropertyName);
void AboutBox();
void Connect(LPCTSTR ServerName);
void Login(LPCTSTR ServerName, LPCTSTR Nickname, const VARIANT& UserName, const VARIANT& RealName, const VARIANT& Password, const VARIANT& SecurityPackages);
void Disconnect();
void CancelConnectLogin();
void ListChannels(LPDISPATCH ChannelQueryItems);
void QueryChannelProperty(LPCTSTR ChannelPropertyName, const VARIANT& ChannelName);
void KillChannel(LPCTSTR ChannelName, const VARIANT& Reason);
void ListUsers(LPDISPATCH UserQueryItems);
void QueryUserProperty(LPCTSTR UserPropertyName, const VARIANT& Nickname);
void ChangeUserProperty(LPCTSTR UserPropertyName, const VARIANT& NewUserProperty, const VARIANT& Nickname);
void IgnoreUsers(LPDISPATCH UserItems, long Set);
void BanUsers(LPDISPATCH UserItems, long Set, const VARIANT& Reason, const VARIANT& Duration);
void KickUser(LPCTSTR Nickname, const VARIANT& Reason);
void ChangeNickname(LPCTSTR NewNickname);
void SetAway(long Away, const VARIANT& TextMessage);
void QueryServerProperty(LPCTSTR ServerPropertyName);
void SendInvitation(LPCTSTR Nickname, const VARIANT& ChannelName);
void SendPrivateMessage(long PrivateMessageType, const VARIANT& Message, const VARIANT& RecipientNicknames, const VARIANT& DataMessageTag);
void SendProtocolMessage(LPCTSTR TextMessage);
};
/////////////////////////////////////////////////////////////////////////////
// _IMsChatPrEvent wrapper class

class _IMsChatPrEvent : public COleDispatchDriver
{
public:
_IMsChatPrEvent() {}// Calls COleDispatchDriver default constructor
_IMsChatPrEvent(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
_IMsChatPrEvent(const _IMsChatPrEvent& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

// Attributes
public:

// Operations
public:
void OnConnectionState(long NewConnectionState);
void OnConnectionError(long ErrorCode, LPCTSTR Description, const VARIANT& ChannelName);
void OnChannelState(LPDISPATCH Channel, long NewChannelState);
void OnChannelError(LPDISPATCH Channel, long ErrorCode, LPCTSTR Description);
void OnBeginEnumeration(LPDISPATCH Channel, long EnumType);
void OnEndEnumeration(LPDISPATCH Channel, long EnumType);
void OnAddMember(LPDISPATCH Channel, LPCTSTR Nickname, LPDISPATCH MemberItems);
void OnDelMember(LPDISPATCH Channel, LPCTSTR Nickname);
void OnUserProperty(LPDISPATCH UserItems);
void OnUserPropertyChanged(LPCTSTR ModifiedNickname, LPCTSTR ModifierNickname, LPCTSTR UserPropertyName, const VARIANT& OldUserProperty, const VARIANT& NewUserProperty);
void OnMemberProperty(LPDISPATCH Channel, LPDISPATCH MemberItems);
void OnMemberPropertyChanged(LPDISPATCH Channel, LPCTSTR ModifiedNickname, LPCTSTR ModifierNickname, LPCTSTR MemberPropertyName, const VARIANT& OldMemberProperty, const VARIANT& NewMemberProperty);
void OnMemberKicked(LPDISPATCH Channel, LPCTSTR KickedNickname, LPCTSTR KickerNickname, LPCTSTR Reason);
void OnChannelProperty(LPDISPATCH Channel, LPDISPATCH ChannelItems);
void OnChannelPropertyChanged(LPDISPATCH Channel, LPCTSTR ModifierNickname, LPCTSTR ChannelPropertyName, const VARIANT& OldChannelProperty, const VARIANT& NewChannelProperty);
void OnServerProperty(LPDISPATCH ServerItems);
void OnInvitation(LPCTSTR ChannelName, LPDISPATCH InviterItems);
void OnKnock(LPDISPATCH Channel, LPDISPATCH KnockerItems, long ErrorCode);
void OnPrivateMessage(LPCTSTR SenderNickname, long PrivateMessageType, const VARIANT& Message, const VARIANT& DataMessageTag);
void OnServerTextMessage(long ServerMessageType, LPCTSTR Text);
void OnMessage(LPDISPATCH Channel, LPCTSTR SenderNickname, long MessageType, const VARIANT& Message, const VARIANT& RecipientNicknames, const VARIANT& DataMessageTag);
void OnProtocolMessage(LPCTSTR MessagePrefix, LPCTSTR MessageCommand, const VARIANT& MessageParameters, BOOL* EnableDefault);
};

#endif //__MSCHATX_H__


PLTREE.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       PLTree.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"
#include "PLTree.h"
#include "Utility.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPartListTree

CPartListTree::CPartListTree()
{
m_iSelectedItem = NULL;
}

CPartListTree::~CPartListTree()
{
}

BEGIN_MESSAGE_MAP(CPartListTree, CTreeCtrl)
//{{AFX_MSG_MAP(CPartListTree)
ON_WM_SIZE()
ON_WM_RBUTTONDOWN()
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPartListTree message handlers
void CPartListTree::OnSize(UINT nType, int cx, int cy)
{
m_cx = cx + 8;

CTreeCtrl::OnSize(nType, cx, cy);

//TRACE1("CPartListTree::OnSize = %u\n", m_cx);

}

int CPartListTree::GetCx()
{
return m_cx;
}

void CPartListTree::DeleteItemFromText(LPCTSTR lpszItemText_p)
{
HTREEITEM hItem = FindItemFromText(lpszItemText_p);
//delete all children
HTREEITEM hChildItem = NULL;
while (hChildItem = GetChildItem(hItem))
{
DeleteItem (hChildItem);
}

DeleteItem (hItem);
}

HTREEITEM CPartListTree::FindItemFromText(LPCTSTR lpszItemText_p)
{

HTREEITEM hItem = GetFirstVisibleItem();

while (hItem)
{
CString szText =  GetItemText(hItem);
if (szText == lpszItemText_p)
{
return hItem;
}

 hItem = GetNextVisibleItem(hItem);
}
return NULL;

}

void CPartListTree::OnLButtonDown(UINT nFlags, CPoint point)
{
UINT iFlags;
m_iSelectedItem = HitTest(point, &iFlags);

HTREEITEM htTemp = GetParentItem(m_iSelectedItem);
m_iSelectedItem = (htTemp) ? htTemp : m_iSelectedItem;

CTreeCtrl::OnLButtonDown(nFlags, point);
}

void CPartListTree::OnRButtonDown(UINT nFlags, CPoint point)
{
CTreeCtrl::OnRButtonDown(nFlags, point);

CPoint pt(point);
ClientToScreen(&pt);
PopMenu(PART_LIST_MENU, pt);

}


PLTREE.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       PLTree.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************
#ifndef __PLTREE_H__
#define __PLTREE_H__

//////////////////////////////////////////////
// CPartListTree window

#define PART_LIST_MENU1

class CPartListTree : public CTreeCtrl
{
// Construction
public:
int GetCx();
CPartListTree();

// Attributes
private:
HTREEITEM m_iSelectedItem;

// Operations
public:
inline HTREEITEM GetSelItem() { return m_iSelectedItem;};

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CPartListTree)
//}}AFX_VIRTUAL

// Implementation
public:
HTREEITEM FindItemFromText(LPCSTR lpszItemText_p);
void DeleteItemFromText(LPCSTR lpszItemText_p);
virtual ~CPartListTree();

// Generated message map functions
protected:
//{{AFX_MSG(CPartListTree)
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
private:
int m_cx;
};

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

#endif //__PLTREE_H__


PWORDDLG.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       PWordDlg.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chappy.h"
#include "PWordDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CPWordDlg dialog

CPWordDlg::CPWordDlg(CWnd* pParent /*=NULL*/)
: CDialog(CPWordDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CPWordDlg)
m_szPassword = _T("");
//}}AFX_DATA_INIT
}

void CPWordDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CPWordDlg)
DDX_Text(pDX, IDC_PASSWORD, m_szPassword);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CPWordDlg, CDialog)
//{{AFX_MSG_MAP(CPWordDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPWordDlg message handlers


PWORDDLG.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       PWordDlg.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_PWORDDLG_H__723CE485_C028_11D0_AD49_00C04FC29DE5__INCLUDED_)
#define AFX_PWORDDLG_H__723CE485_C028_11D0_AD49_00C04FC29DE5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

/////////////////////////////////////////////////////////////////////////////
// CPWordDlg dialog

class CPWordDlg : public CDialog
{
// Construction
public:
CPWordDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CPWordDlg)
enum { IDD = IDD_PASSWORD };
CStringm_szPassword;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CPWordDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CPWordDlg)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_PWORDDLG_H__723CE485_C028_11D0_AD49_00C04FC29DE5__INCLUDED_)


BANDLG.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       BanDlg.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chappy.h"
#include "BanDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CBanDlg dialog

CBanDlg::CBanDlg(CWnd* pParent /*=NULL*/)
: CDialog(CBanDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CBanDlg)
m_iDuration = 0;
m_szReason = _T("");
m_szValue = _T("");
m_iOperation = -1;
m_iProperty = -1;
m_iBan = -1;
//}}AFX_DATA_INIT
}

void CBanDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CBanDlg)
DDX_Text(pDX, IDC_HOW_LONG, m_iDuration);
DDV_MinMaxLong(pDX, m_iDuration, 1, 2147483647);
DDX_Text(pDX, IDC_REASON, m_szReason);
DDX_Text(pDX, IDC_VALUE, m_szValue);
DDX_Radio(pDX, IDC_RB_NO_OP, m_iOperation);
DDX_Radio(pDX, IDC_RB_IP, m_iProperty);
DDX_Radio(pDX, IDC_RB_UNBAN, m_iBan);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CBanDlg, CDialog)
//{{AFX_MSG_MAP(CBanDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CBanDlg message handlers


REDIT.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       REdit.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chappy.h"
#include "REdit.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CREdit

CREdit::CREdit()
{
}

CREdit::~CREdit()
{
}

BEGIN_MESSAGE_MAP(CREdit, CRichEditCtrl)
//{{AFX_MSG_MAP(CREdit)
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// OnFocus(BOOL bFocus_p)
//  bFocus_p = TRUE we received focus
//bFocus_p = FALSE we lost focus
void CREdit::OnFocus(BOOL bFocus_p)
{
if (bFocus_p)
{
//we received focus
//show the window
ShowWindow(SW_SHOW);
}
else
{
//we lost focus
//hide the window
ShowWindow(SW_HIDE);
}
}

void CREdit::AddText(LPCSTR szTextIn_p, BOOL bBold_p)
{
TogleBoldText(bBold_p);
//m_ctrlHistoryWnd.SetSel(0,-1);
int iTextLength = GetWindowTextLength();
SetSel(iTextLength,iTextLength);
ReplaceSel(szTextIn_p);
//m_ctrlHistoryWnd.LineScroll(1);

}

void CREdit::AddText(CString &szTextIn_p, BOOL bBold_p)
{
//m_ctrlHistoryWnd.SetSel(0,-1);
AddText((LPCSTR) szTextIn_p, bBold_p);
}

void CREdit::TogleBoldText(BOOL bIsBold_p)
{
CHARFORMAT cf;

cf.cbSize = sizeof (CHARFORMAT);
cf.dwMask=CFM_BOLD;
cf.dwEffects = (bIsBold_p) ? CFE_BOLD : CFE_AUTOCOLOR;
//cf.yHeight;
//cf.yOffset;
//cf.crTextColor=RGB(250,0,0);
//cf.bCharSet;
//cf.bPitchAndFamily;
//sprintf(cf.szFaceName, "Arial");

SetSelectionCharFormat(cf);
}

void CREdit::TogleItalicText(BOOL bIsItalic_p)
{
CHARFORMAT cf;

cf.cbSize = sizeof (CHARFORMAT);
cf.dwMask=CFM_ITALIC;
cf.dwEffects = (bIsItalic_p) ? CFE_ITALIC : CFE_AUTOCOLOR;

SetSelectionCharFormat(cf);

}

void CREdit::SetTextColor(COLORREF crColor_p)
{
CHARFORMAT cf;

cf.cbSize = sizeof (CHARFORMAT);
cf.dwMask = CFM_COLOR;
cf.crTextColor=crColor_p;

SetSelectionCharFormat(cf);
}

int CREdit::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CRichEditCtrl::OnCreate(lpCreateStruct) == -1)
return -1;

//set the default font
CHARFORMAT cf;

cf.cbSize = sizeof (CHARFORMAT);
cf.dwMask = CFM_FACE ;
//cf.dwEffects;
//cf.yHeight;
//cf.yOffset;
//cf.crTextColor=RGB(250,0,0);
//cf.bCharSet;
//cf.bPitchAndFamily;
sprintf(cf.szFaceName, "Arial");

SetDefaultCharFormat(cf);

return 0;
}


REDIT.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       REdit.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************
#if !defined(AFX_REDIT_H__5C77B63F_BC3A_11D0_AD48_00C04FC29DE5__INCLUDED_)
#define AFX_REDIT_H__5C77B63F_BC3A_11D0_AD48_00C04FC29DE5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

/////////////////////////////////////////////////////////////////////////////
// CREdit window

class CREdit : public CRichEditCtrl
{
// Construction
public:
CREdit();

// Attributes
public:
void OnFocus(BOOL bFocus_p);
void TogleItalicText(BOOL bIsItalic_p);
void SetTextColor(COLORREF crColor_p);
void TogleBoldText(BOOL bIsBold_p = TRUE);
void AddText(CString &szTextIn_p, BOOL bBold_p = FALSE);
void AddText(LPCSTR szTextIn_p, BOOL bBold_p = FALSE);

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CREdit)
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CREdit();

// Generated message map functions
protected:
//{{AFX_MSG(CREdit)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_REDIT_H__5C77B63F_BC3A_11D0_AD48_00C04FC29DE5__INCLUDED_)


RESOURCE.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       resource.h
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Chappy.rc
//
#define IDD_ABOUTBOX                    100
#define IDR_MAINFRAME                   128
#define IDR_CHAPPYTYPE                  129
#define IDR_SEND_TOOLBAR                130
#define IDD_CHAT_CONNECT                130
#define IDR_POPUP                       134
#define IDD_LOG_IN_DLG                  138
#define IDD_PASSWORD                    142
#define IDI_ROOM_LIST                   143
#define IDI_SAY                         144
#define IDI_WHISPER                     146
#define IDI_DO                          147
#define IDI_IMPORTANT                   148
#define IDD_KICK                        149
#define IDI_EARTH                       154
#define IDD_BAN                         157
#define IDC_EDIT_ROOM                   1000
#define IDC_SERVER                      1000
#define IDC_EDIT_NICKNAME               1001
#define IDC_PASSWORD                    1001
#define IDC_EDIT_PASSWORD               1002
#define IDC_NICKNAME                    1002
#define IDC_ROOM_LIST_CHECK             1003
#define IDC_CREATE                      1004
#define IDC_DO_NOT_EXPOSE               1005
#define IDC_REASON                      1005
#define IDC_READ_ONLY                   1006
#define IDC_RB_NICKNAME                 1006
#define IDC_MIC_ONLY                    1007
#define IDC_RB_IP                       1007
#define IDC_AUTENTICATED_ONLY           1008
#define IDC_USER_NAME                   1008
#define IDC_AUTENTICATED_TALK           1009
#define IDC_VALUE                       1009
#define IDC_FLAGS                       1010
#define IDC_RB_CONTAINS                 1010
#define IDC_TYPE                        1011
#define IDC_RB_ENDS_WITH                1011
#define IDC_RB_STARTS_WITH              1012
#define IDC_RB_NO_OP                    1013
#define IDC_HOW_LONG                    1014
#define IDC_ANY_USER                    1016
#define IDC_RB_BAN                      1016
#define IDC_HOST_TOPIC                  1017
#define IDC_RB_UNBAN                    1017
#define IDC_LOGGED_ON_ONLY              1018
#define IDC_NO_WISPER                   1019
#define IDC_SHOW_HOST_ONLY              1020
#define IDC_SPECTATOR                   1021
#define IDS_TEXT_MSG_TYPE               20000
#define IDS_DATA_MSG_TYPE               20001
#define IDS_NORMAL_MSG_TYPE             20002
#define IDS_WHISPER_MSG_TYPE            20032
#define IDS_THOUGHT_MSG_TYPE            20064
#define IDS_DO_MSG_TYPE                 20128
#define IDS_SOUND_MSG_TYPE              20256
#define IDS_TCP_MSG_TYPE                20512
#define IDS_CS_DISCONNECTED             21000
#define IDS_CS_CONNECTING               21001
#define IDS_CS_CONNECTED                21002
#define IDS_CS_LOGGING                  21003
#define IDS_CS_LOGGED                   21004
#define IDS_CS_DISCONNECTING            21005
#define IDS_CT_NOTCONNECTED_YET         21009
#define IDS_CT_IRC                      21010
#define IDS_CT_IRCX                     21011
#define IDS_INFO_MSG_TYPE               21024
#define IDS_MNU_CANCEL_LOGGING          21100
#define IDS_MNU_LOGOUT                  21101
#define IDS_SMT_NORMAL                  22000
#define IDS_SMT_NOTIFICATION            22001
#define IDS_SMT_MESSAGE_OF_THE_DAY      22002
#define IDS_SMT_SERVER_INFO             22003
#define IDS_SMT_ERROR                   22004
#define IDS_ADD_MEMBER                  23000
#define IDS_DEL_MEMBER                  23001
#define IDS_KICKED_MSG                  23002
#define IDS_CHANNEL_ERROR_MSG           23003
#define IDS_CONNECTION_ERROR_MSG        23004
#define ID_BTN_SEND                     32772
#define IDS_NO_ROOM_LIST_MSG            32773
#define ID_BTN_WHISPER                  32775
#define IDS_CHAT_TAB                    32776
#define IDS_ROOM_LIST_TAB               32777
#define IDS_MESSAGES_TAB                32778
#define ID_HISTORY_CLEAR                32780
#define ID_PARTLIST_KICK                32781
#define ID_PARTLIST_BAN                 32782
#define ID_ROOM_CONNECT                 32783
#define ID_ROOM_DISCONNECT              32784
#define ID_ROOM_INFO                    32788
#define ID_BTN_THINK                    32790
#define ID_BTN_DO                       32791
#define IDM_SELECT_FONT                 32792
#define ID_ROOM_LOGIN                   32793
#define ID_ROOM_LOGOUT                  32794
#define ID_ROOM_LISTROOMS               32795
#define ID_ROOMLIST_ENTERROOM           32796
#define ID_SERVER_LOGIN                 32797
#define ID_SERVER_LOGOUT                32798
#define ID_SERVER_BANUSER               32799
#define ID_SERVER_KILLUSER              32800
#define ID_SERVER_KILLCHANNEL           32801
#define ID_PARTLIST_MAKEHOST            32802
#define ID_PARTLIST_MAKEPARTICIPANT     32803
#define IDS_FAIL_TO_LOAD_OCX            33000
#define IDS_URL_TO_CHAT                 33001
#define IDS_DEFAULT_CHAT_ROOM           33002
#define IDS_DEFAULT_NICKNAME            33003
#define IDS_DEFAULT_SERVER              33004
#define IDS_IP_ADDRESS_ITEM             34000
#define IDS_NICKNAME_ITEM               34001
#define IDS_USER_NAME_ITEM              34002
#define IDS_IP_ADDRESS_OP_NAME          34100
#define IDS_NICKNAME_OP_NAME            34101
#define IDS_USER_NAME_OP_NAME           34102
#define IDS_CONTAINS_OP                 34200
#define IDS_STARTS_WITH_OP              34201
#define IDS_ENDS_WITH                   34202
#define IDS_SEND                        57605
#define IDS_WHISPER                     57606
#define IDS_NO_MEMBER_SELECTED          61204

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_3D_CONTROLS                     1
#define _APS_NEXT_RESOURCE_VALUE        158
#define _APS_NEXT_COMMAND_VALUE         32804
#define _APS_NEXT_CONTROL_VALUE         1018
#define _APS_NEXT_SYMED_VALUE           103
#endif
#endif


RLISTCTR.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       RListCtrl.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chappy.h"
#include "RListCtr.h"
#include "Utility.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#include "ChppyVw.h"

#defineROOM_LIST_MENU5

/////////////////////////////////////////////////////////////////////////////
// CRoomListCtrl

CRoomListCtrl::CRoomListCtrl()
{
m_iSelectedItem = -1;
m_iNumOfItems= -1;
m_iNumOfColumns = 0;
}

CRoomListCtrl::~CRoomListCtrl()
{
}

BEGIN_MESSAGE_MAP(CRoomListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CRoomListCtrl)
ON_WM_RBUTTONDOWN()
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CRoomListCtrl message handlers

void CRoomListCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
CListCtrl::OnRButtonDown(nFlags, point);

CPoint pt(point);
ClientToScreen(&pt);
PopMenu(ROOM_LIST_MENU, pt);

}

void CRoomListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
m_iSelectedItem = HitTest(point);

CListCtrl::OnLButtonDown(nFlags, point);
}

void CRoomListCtrl::InsertNextItem(CString &szItem_p)
{
m_iNumOfItems++;
m_iNumOfItems = InsertItem(m_iNumOfItems, szItem_p);
ASSERT (-1 != m_iNumOfItems);
}

void CRoomListCtrl::AddSubItem(long iSubItemIndex_p, CString &szItem_p)
{
LV_ITEM ItemData;
ItemData.mask= LVIF_TEXT;
ItemData.iItem= m_iNumOfItems;
ItemData.iSubItem= iSubItemIndex_p;
ItemData.pszText= (LPSTR) (LPCSTR) szItem_p;
BOOL ret = SetItem (&ItemData);
ASSERT (ret) ;
}

void CRoomListCtrl::ClearList()
{
DeleteAllItems();
m_iNumOfItems = -1;

for (; m_iNumOfColumns >= 0; m_iNumOfColumns--)
{
DeleteColumn(m_iNumOfColumns);
}
m_iNumOfColumns = 0;
}

void CRoomListCtrl::AddColumn(CString & szColumnName_p)
{
//set the colums
LV_COLUMN ColData;
ColData.mask= LVCF_TEXT | LVCF_FMT | LVCF_WIDTH;

ColData.fmt= LVCFMT_LEFT;
ColData.cx= 200;
ColData.pszText= (LPSTR) (LPCSTR) szColumnName_p;

int iCol =InsertColumn(m_iNumOfColumns, &ColData);
ASSERT (m_iNumOfColumns == iCol);
m_iNumOfColumns++;

}


RLISTCTR.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       RListCtrl.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_RLISTCTRL_H__3F0A490F_B78C_11D0_AD47_00C04FC29DE5__INCLUDED_)
#define AFX_RLISTCTRL_H__3F0A490F_B78C_11D0_AD47_00C04FC29DE5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

/////////////////////////////////////////////////////////////////////////////
// CRoomListCtrl window

class CRoomListCtrl : public CListCtrl
{
// Construction
public:
CRoomListCtrl();

// Attributes
private:
long m_iSelectedItem;
long m_iNumOfItems;
long m_iNumOfColumns;

// Operations
public:
inline long GetNumOfColumns() { return m_iNumOfColumns;};
inline long GetSelectedItem() { return m_iSelectedItem;};
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CRoomListCtrl)
//}}AFX_VIRTUAL

// Implementation
public:
void AddColumn(CString &szColumnName_p);
void ClearList();
void AddSubItem (long iSubItemIndex_p, CString &szItem_p);
void InsertNextItem (CString &szItem_p);

virtual ~CRoomListCtrl();

// Generated message map functions
protected:
//{{AFX_MSG(CRoomListCtrl)
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_RLISTCTRL_H__3F0A490F_B78C_11D0_AD47_00C04FC29DE5__INCLUDED_)


ROOM.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       Room.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"
#include "Room.h"
#include "Globals.h"
#include "kickDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CRoom::CRoom()
{
}

CRoom::~CRoom()
{
if (m_IChannels.m_lpDispatch)
{
m_IChannels.ReleaseDispatch();
m_IChannels = NULL;
}
}

//|bool CRoom::operator== ( const CRoom &src ) const
//|{
//|if ((m_IChannel== src.m_IChannel))
//|{
//|return TRUE;
//|}
//|else
//|{
//|return FALSE;
//|}
//|}
//|
//|void CRoom::operator= ( const CRoom& src )
//|{
//|m_IChannel= src.m_IChannel;
//|}

BOOL CRoom::Create(CWnd &wndParent_p, LPDISPATCH lpIChannels_p)
{
//ASSERT (!m_ctrlHistoryWnd);  //if we already have a control something is wrong
ASSERT (&wndParent_p);
ASSERT (lpIChannels_p);

m_IChannels.AttachDispatch(lpIChannels_p, TRUE);

//create the input rich edit control
RECT rec;
rec.left= 100;
rec.top= 100;
rec.right= 200;
rec.bottom= 200;

int bRet = m_ctrlHistoryWnd.Create(WS_CHILD | WS_BORDER | WS_VSCROLL |
ES_MULTILINE | ES_AUTOVSCROLL| WS_EX_NOPARENTNOTIFY,
rec,
&wndParent_p,
IDC_HISTORY_RICHEDIT);
ASSERT (bRet);

//create the list of participant control
bRet = m_ctrlPartListWnd.Create(WS_CHILD | WS_BORDER | WS_VSCROLL | WS_TABSTOP | WS_THICKFRAME |
TVS_HASLINES | TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_LINESATROOT,
rec,
wndParent_p.GetParent(),
IDC_PART_LIST_TREE);
ASSERT (bRet);

//we do not have a channel yet
m_IChannel= NULL;
m_iIndex= 0;

//|//create the Channel item
//|//to instaciate the OCX you can get the CLSID directly usign MIDL or use CLSIDFromProgID
//|const CLSID CLSID_MsChatPr = {0x039397c0,0x46df,0x11d0,{0x99,0xcb,0x00,0xc0,0x4f,0xd6,0x44,0x97}};
//|//const CLSID CLSID_Channels = {0x011eee42,0x46df,0x11d0,{0x99,0xcb,0x00,0xc0,0x4f,0xd6,0x44,0x97}};
//|//const CLSID CLSID_Channel = {0x011eee44,0x46df,0x11d0,{0x99,0xcb,0x00,0xc0,0x4f,0xd6,0x44,0x97}};
//|
//|// create the chat object that we'll drive through OLE automation
//|COleException e;
//|CLSID clsid = CLSID_MsChatPr;
//|
//|if (CLSIDFromProgID(OLESTR("MsChatX.Microsoft Chat Protocol Control.2"), &clsid) != NOERROR)
//|{
//|AfxMessageBox("Failed to create");
//|//EndDialog(IDABORT);
//|return FALSE;
//|}
//|
//|// try to get the active calculator before creating a new one
//|LPUNKNOWN lpUnk;
//|LPDISPATCH lpDispatch;
//|if (GetActiveObject(clsid, NULL, &lpUnk) == NOERROR)
//|{
//|HRESULT hr = lpUnk->QueryInterface(IID_IDispatch,
//|(LPVOID*)&lpDispatch);
//|lpUnk->Release();
//|if (hr == NOERROR)
//|m_IChannel.AttachDispatch(lpDispatch, TRUE);
//|}
//|
//|// if not dispatch ptr attached yet, need to create one
//|if (m_IChannel.m_lpDispatch == NULL &&
//|!m_IChannel.CreateDispatch(clsid, &e))
//|{
//|AfxMessageBox("CreateDispatch() failed\n");
//|return FALSE;
//|}
//|
//|COleVariant vChannel(_T("#TestAR"));
//|COleVariant vKey(_T(""));
//|
//|m_IChannel.JoinChannel(vChannel,vKey);

return TRUE;
}

void CRoom::OnFocus(BOOL bFocus_p)
{
m_ctrlHistoryWnd.ShowWindow(bFocus_p ? SW_SHOW : SW_HIDE);
m_ctrlPartListWnd.ShowWindow(bFocus_p ? SW_SHOW : SW_HIDE);
}

void CRoom::SendMessage(LPCTSTR SenderNickname_p, long iMsgType_p, CString & szMsgTxt_p)
{
TRACE1("Send Messge : '%s'\n", szMsgTxt_p);

if (!m_IChannel) return;

COleVariant vMsgTxt(szMsgTxt_p);
COleVariant vRecipients("");
COleVariant vDataMessageTag;
vDataMessageTag.Clear();

vRecipients.Clear();
if (msgtWhisper == iMsgType_p )
{
vRecipients = GetSelectedMember();
if (vRecipients == COleVariant(""))
{
AfxMessageBox(IDS_NO_MEMBER_SELECTED);
return;
}
}

m_IChannel.SendMessage(iMsgType_p, vMsgTxt, vRecipients, vDataMessageTag);
m_ctrlHistoryWnd.UpdateMessageUI(SenderNickname_p, iMsgType_p, szMsgTxt_p);
}

void CRoom::LeaveChannel()
{
//exit if we do not have a channel object (not in a room)
if (!m_IChannel) return;

//leave the channel
if (chsClosed != m_IChannel.GetChannelState())
{
m_IChannel.LeaveChannel();
}
//m_ctrlPartListWnd.DeleteAllItems();
}

//call this function async
void CRoom::DestroyRoom()
{
if (m_ctrlPartListWnd)
{
m_ctrlPartListWnd.DeleteAllItems();
}

//release the IChannel object
m_IChannel.ReleaseDispatch();
m_IChannel = NULL;

//remove the channel from the collection
m_IChannels.Remove(COleVariant(m_szKey));

}

BOOL CRoom::EnterRoom(long iIndex_p, CString &szRoomName_p, CString &szRoomKey_p)
{

ASSERT (!m_IChannel); //if we already have a channel something is wrong
ASSERT (!szRoomName_p.IsEmpty());

m_iIndex = iIndex_p;
m_szKey.Format("%s%i",szRoomName_p,iIndex_p);

//Add a channel to the collection
//the ocx takes care of pushing in the new channel if you pass an index that
//already exists. If you pass in an invalid index, on the other hand, the OCX complains
COleVariant vIndex(1L);
COleVariant vKey(m_szKey);

LPDISPATCH lpIChannel = NULL;
lpIChannel = m_IChannels.Add (vIndex, vKey);
m_IChannel.AttachDispatch(lpIChannel, TRUE);
ASSERT (m_IChannel);

//enter the room
COleVariant vChannel (szRoomName_p) ;
COleVariant vChannelKey(szRoomKey_p);
m_IChannel.JoinChannel(vChannel,vChannelKey);

return TRUE;
}

CString CRoom::GetSelectedMember()
{
HTREEITEM iSel = m_ctrlPartListWnd.GetSelItem();
return ((iSel) ?  m_ctrlPartListWnd.GetItemText(iSel) : "");
}

void CRoom::KickMember(CString & szReason_p)
{
{
CString szMember = GetSelectedMember();
TRACE2("-- Kick: %s Reason: %s",szMember, szReason_p);
m_IChannel.KickMember((LPCSTR)szMember, COleVariant(szReason_p));
}
}

void CRoom::BanMember(CChatItems &IChatItem_p)
{
ASSERT (IChatItem_p);
IChatItem_p.SetAssociatedType("Query");
IChatItem_p.SetItem ("Nickname", COleVariant(GetSelectedMember()));
m_IChannel.BanMembers(IChatItem_p, TRUE);
}

void CRoom::MakeHost(BOOL bFlag)
{
m_IChannel.ChangeMemberProperty ("Modes", COleVariant((long)(bFlag ? mmHost : mmNotHost)),COleVariant(GetSelectedMember()));
}


ROOM.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       Room.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************
#ifndef __ROOM_H__
#define __ROOM_H__

#define IDC_OCX_CHAT2000

#include "RoomHstr.h"
#include "MSChatx.h"
#include "PLTree.h"
#include "chtitems.h"

/////////////////////////////////////////////////////////////////////////////
// CRoom thread

class CRoom
{

// Attributes
private:
long m_iIndex;
CString m_szKey;

IChannelsCollection m_IChannels;
IChannelObjectm_IChannel;

public:
CRoomHistorym_ctrlHistoryWnd;
CPartListTreem_ctrlPartListWnd;

// Operations
public:
void MakeHost(BOOL bFlag);
void BanMember(CChatItems & IChatItem_p);
void KickMember(CString & szReason_p);
CString GetSelectedMember();
//constructor and distructor
CRoom();
~CRoom();

inline IChannelObjectGetChannelObj(){ return m_IChannel;};
inline LPDISPATCHGetChannel(){ return m_IChannel.m_lpDispatch;};
inline longGetIndex(){ return m_iIndex;};
inline CRoomHistory*GetRoomHistoryCtrlPtr(){ return &m_ctrlHistoryWnd;};

//void operator =( const CRoom& src );
//bool operator== ( const CRoom &src ) const;

BOOLCreate(CWnd &wndParent_p, LPDISPATCH lpIChannels_p);
BOOLEnterRoom(long iIndex_p, CString &szRoomName_p, CString &szRoomKey_p);
voidLeaveChannel();
voidDestroyRoom();
voidSendMessage(LPCTSTR SenderNickname_p, long iMsgType_p, CString &szMsgTxt_p);

voidOnFocus(BOOL bFocus_p);

};

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

#endif //__ROOM_H__


ROOMHSTR.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       RoomHstr.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chappy.h"
#include "RoomHstr.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CRoomHistory

CRoomHistory::CRoomHistory()
{
}

CRoomHistory::~CRoomHistory()
{
}

BEGIN_MESSAGE_MAP(CRoomHistory, CRichEditCtrl)
//{{AFX_MSG_MAP(CRoomHistory)
ON_WM_RBUTTONDOWN()
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CRoomHistory message handlers

void CRoomHistory::OnRButtonDown(UINT nFlags, CPoint point)
{
{
CMenu menu;
CMenu *submenu;
//Load the menu.
menu.LoadMenu(IDR_POPUP);
//Get the pop-up menu.
submenu = menu.GetSubMenu(ROOM_HISTORY_MENU);

CPoint pt(point);
//Convert to screen coordinates.
ClientToScreen(&pt);
//Post the menu.
submenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,
pt.x,    pt.y,
AfxGetApp()->m_pMainWnd,NULL);
}

CRichEditCtrl::OnRButtonDown(nFlags, point);
}

int CRoomHistory::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CHistory::OnCreate(lpCreateStruct) == -1)
return -1;

return 0;
}


ROOMHSTR.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       RoomHstr.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_ROOMHSTR_H__5C77B642_BC3A_11D0_AD48_00C04FC29DE5__INCLUDED_)
#define AFX_ROOMHSTR_H__5C77B642_BC3A_11D0_AD48_00C04FC29DE5__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#include "History.h"

/////////////////////////////////////////////////////////////////////////////
// CRoomHistory window

#define ROOM_HISTORY_MENU4

class CRoomHistory : public CHistory
{
// Construction
public:
CRoomHistory();

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CRoomHistory)
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CRoomHistory();

// Generated message map functions
protected:
//{{AFX_MSG(CRoomHistory)
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_ROOMHSTR_H__5C77B642_BC3A_11D0_AD48_00C04FC29DE5__INCLUDED_)


RTAB.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       RTab.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"
#include "RTab.h"
#include "globals.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CRoomsTab

CRoomsTab::CRoomsTab()
{
}

CRoomsTab::~CRoomsTab()
{
}

BEGIN_MESSAGE_MAP(CRoomsTab, CTabCtrl)
//{{AFX_MSG_MAP(CRoomsTab)
ON_WM_RBUTTONDOWN()
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CRoomsTab message handlers

int CRoomsTab::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CTabCtrl::OnCreate(lpCreateStruct) == -1)
return -1;

m_ImageList.Create(16,16,TRUE,0,6);

m_ImageList.Add (LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_EARTH)));
m_ImageList.Add (LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_IMPORTANT)));
m_ImageList.Add (LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_SAY)));
m_ImageList.Add (LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_WHISPER)));
m_ImageList.Add (LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_DO)));
m_ImageList.Add (LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ROOM_LIST)));

SetImageList ( &m_ImageList);

return 0;
}

void CRoomsTab::OnRButtonDown(UINT nFlags, CPoint point)
{
CTabCtrl::OnRButtonDown(nFlags, point);

}

void CRoomsTab::RenameTab(long iTab_p, CString & szName_p)
{
TC_ITEM tcItem;
tcItem.mask = TCIF_TEXT;

char szTabTitle[256];
sprintf(szTabTitle, "%s",(LPCTSTR) szName_p);
tcItem.pszText = szTabTitle;

int ret = SetItem(iTab_p,&tcItem);
ASSERT (ret);

}

void CRoomsTab::ChangeImage(long iTab_p, long iImageIndex_p)
{
TC_ITEM tcItem;
tcItem.mask = TCIF_IMAGE;

tcItem.iImage = iImageIndex_p;

int ret = SetItem(iTab_p,&tcItem);
ASSERT (ret);

}

void CRoomsTab::SetCurrentTab(long iTab_p)
{
SetCurSel (iTab_p);
//since SetCurSel does not notify us that we changed the tab...

m_nmhdr.hwndFrom= GetSafeHwnd();
m_nmhdr.idFrom= IDC_MESSAGE_TAB;
m_nmhdr.code= TCN_SELCHANGE;

SendMessage(WM_NOTIFY, IDC_MESSAGE_TAB, (LPARAM) &m_nmhdr);
}


RTAB.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       RTab.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __RTAB_H__
#define __RTAB_H__

/////////////////////////////////////////////////////////////////////////////
// CRoomsTab window

#define ROOM_MENU3

class CRoomsTab : public CTabCtrl
{
// Construction
public:
CRoomsTab();

// Attributes
public:
NMHDR m_nmhdr;
CImageListm_ImageList;

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CRoomsTab)
//}}AFX_VIRTUAL

// Implementation
public:
void ChangeImage(long iTab_p, long iImageIndex_p);
void SetCurrentTab(long iTab_p);
void RenameTab (long iTab_p, CString &szName_p);
virtual ~CRoomsTab();

// Generated message map functions
protected:
//{{AFX_MSG(CRoomsTab)
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

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

#endif //__RTAB_H__


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//Chappy.pch will be the pre-compiled header
//stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"


STDAFX.H

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#define VC_EXTRALEAN// Exclude rarely-used stuff from Windows headers

#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions
#include <afxdisp.h>        // MFC OLE automation classes
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>// MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT


TB.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       TB.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chappy.h"
#include "TB.h"
#include "ChppyVw.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CTB

CTB::CTB()
{

}

CTB::~CTB()
{
}

BEGIN_MESSAGE_MAP(CTB, CFlatToolbar)
//{{AFX_MSG_MAP(CTB)
ON_UPDATE_COMMAND_UI(ID_BTN_SEND, OnUpdateBtnSend)
ON_UPDATE_COMMAND_UI(ID_BTN_WHISPER, OnUpdateBtnWhisper)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/*ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
*/

/////////////////////////////////////////////////////////////////////////////
// CTB message handlers

void CTB::OnUpdateBtnSend(CCmdUI* pCmdUI)
{
pCmdUI->Enable(TRUE);

}

void CTB::OnUpdateBtnWhisper(CCmdUI* pCmdUI)
{
pCmdUI->Enable(TRUE);

}


TB.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       TB.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __TB_H__
#define __TB_H__
#include "FlatTB.h"

/////////////////////////////////////////////////////////////////////////////
// CTB window

class CTB : public CFlatToolbar
{
// Construction
public:
CTB();

// Attributes
public:

// Operations
public:

//BOOL OnToolTipNotify( UINT id, NMHDR * pNMHDR, LRESULT * pResult );
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTB)
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CTB();

// Generated message map functions
protected:
//{{AFX_MSG(CTB)
afx_msg void OnUpdateBtnSend(CCmdUI* pCmdUI);
afx_msg void OnUpdateBtnWhisper(CCmdUI* pCmdUI);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

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

#endif //__TB_H__


UTILITY.CPP

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       Utility.cpp
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chappy.h"
#include "utility.h"

void PopMenu (long iSubMenu_p, CPoint pt)
{
CMenu menu;
CMenu *submenu;
//Load the menu.
menu.LoadMenu(IDR_POPUP);
//Get the pop-up menu.
submenu = menu.GetSubMenu(iSubMenu_p);

//Convert to screen coordinates.
//Post the menu.
submenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,
pt.x,    pt.y,
AfxGetApp()->m_pMainWnd,NULL);
}


UTILITY.H

//****************************************************************************
//  Module:     Chappy.EXE
//  File:       Utility.h
//
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
// 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.
//****************************************************************************

#ifndef __UTILITY_H__
#define __UTILITY_H__

void PopMenu (long iSubMenu_p, CPoint point);

#endif //__UTILITY_H__


ABSTRACT.TXT

Chat2000 is a sample app that demonstrate how to use the Chat OCX inside an MDI MFC application. The
goal of this sample is to clearly show how to:
* Create multiple OCX instances dynamically
* Use OCX methods
* Set and Get OCX properties
* Handle OCX events

The Sample does not fully implement its own UI but rather extends the standard OCX one with the
following features:
* Multiple chat sessions.  Join as many chat rooms as you want.
* Content sensitive right-click pop up menu
* Kick and Ban participants
* Set your preferences for every Chat session you join or create
* Clear or Save history in .rtf file
* Status info displayed in Status bar and MDI title
* Get MSChat info

The Sample uses the Document/view architecture of the Microsoft Foundation Classes.  The application
was generated by the AppWizard as an MDI-type application.  The support for OLE controls was added
manually.

The CMSChat class was generated by the ClassWizard using the Insert Component menu.  Some
methods were changed to facilitate the development process but no changes are necessary to use the Class.

The OCXes are created at runtime in the view method CChat2000View::OnCreate().  The view owns the
OCX because is not possible to implement multiple view of the same chat room without re-implementing
the UI completely.  The view exports the only data needed by the document using the wrapper function
CChat2000View::GetHistory().   The OCX Methods are generally called within appropriate command
handler functions.   The view is also responsible for updating the status bar and the MDI title.  OCX events
are trapped using the MFC standard event sink mechanism (BEGIN_EVENTSINK_MAP /
END_EVENTSINK_MAP).  Unfortunately the ClassWizard does not help generate the sink map, so this
code needs to be added manually.

This example also illustrates how to use MFC CList template how to implement a pop-up menu for a
control.  The application uses the CList template to implement a list of participants (CParticipant object).
The list is used to verify the status of  this participant  and to fill up the Drop-down in the  Ban  dialog.
The Pop-up menu is created in the CChat2000View::OnParentNotify() function.  This function wraps up
the WM_PARENTNOTIFY window message used by child controls to notify the parent of events such as
creation, destruction and mouse clicks.  Since the OCX covers all the view the WM_FOCUS message gets
to the view after the WM_PARENTNOTIFY.  This leaves the pop up menu in an incorrect state.  To
alleviate the problem the view keeps track of its own activation/deactivation giving a chance to the MFC
OnUpdate functions to take care of the menu items states.


BANDLG.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       BanDlg.cpp
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chat2000.h"
#include "BanDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CBanDlg dialog

CBanDlg::CBanDlg(CWnd* pParent /*=NULL*/)
: CDialog(CBanDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CBanDlg)
m_Set = -1;
m_Name = _T("");
//}}AFX_DATA_INIT

//the pointer to the list is NULL on costruction
m_pListOfPart = NULL;
}

void CBanDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CBanDlg)
DDX_Radio(pDX, IDC_BANKICK, m_Set);
DDX_CBString(pDX, IDC_NAME, m_Name);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CBanDlg, CDialog)
//{{AFX_MSG_MAP(CBanDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CBanDlg message handlers

BOOL CBanDlg::OnInitDialog()
{
CComboBox *NameCombo = (CComboBox *) GetDlgItem(IDC_NAME);

CString ParticipantName = GetParticipantAlias();
while (!ParticipantName.IsEmpty())
{
int res = NameCombo->InsertString (-1,(LPCSTR)ParticipantName);
ParticipantName = GetParticipantAlias();
}

UpdateData(FALSE); //put the data into the controls

return CDialog::OnInitDialog();  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}

CString CBanDlg::GetParticipantAlias()
{
static POSITION CurrentPos = m_pListOfPart->GetHeadPosition();

if (CurrentPos != NULL)
{
//return the alias, set the position to the next element of the list
return (m_pListOfPart->GetNext(CurrentPos)).GetAlias();
}
else
{
//we reached the end of the list or the list is empty
//    go back to the head
CurrentPos = m_pListOfPart->GetHeadPosition();
return _T("");
}
}


BANDLG.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       BanDlg.h
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "partic.h"

/////////////////////////////////////////////////////////////////////////////
// CBanDlg dialog

class CBanDlg : public CDialog
{
// Construction
public:
CList <CParticipant, CParticipant&> *m_pListOfPart;

CBanDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CBanDlg)
enum { IDD = IDD_BAN };
intm_Set;
CStringm_Name;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CBanDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

CStringGetParticipantAlias();

// Generated message map functions
//{{AFX_MSG(CBanDlg)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};


C2DOC.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       C2Doc.cpp : implementation of the CChat2000Doc class
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chat2000.h"

#include "C2Doc.h"
#include "C2View.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CChat2000Doc

IMPLEMENT_DYNCREATE(CChat2000Doc, CDocument)

BEGIN_MESSAGE_MAP(CChat2000Doc, CDocument)
//{{AFX_MSG_MAP(CChat2000Doc)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChat2000Doc construction/destruction

CChat2000Doc::CChat2000Doc()
{
// TODO: add one-time construction code here

}

CChat2000Doc::~CChat2000Doc()
{
}

BOOL CChat2000Doc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;

// TODO: add reinitialization code here
// (SDI documents will reuse this document)

return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CChat2000Doc serialization

void CChat2000Doc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
POSITION pos = GetFirstViewPosition();
//get the first (and only) view
CChat2000View *pView =(CChat2000View *) GetNextView( pos );
ASSERT (pView);
//write the ocx History
ar << pView->GetHistory();
}
else
{
// TODO: add loading code here
}
}

/////////////////////////////////////////////////////////////////////////////
// CChat2000Doc diagnostics

#ifdef _DEBUG
void CChat2000Doc::AssertValid() const
{
CDocument::AssertValid();
}

void CChat2000Doc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG


C2DOC.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       C2Doc.h : interface of the CChat2000Doc class
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

class CChat2000Doc : public CDocument
{
protected: // create from serialization only
CChat2000Doc();
DECLARE_DYNCREATE(CChat2000Doc)

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CChat2000Doc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CChat2000Doc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
//{{AFX_MSG(CChat2000Doc)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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


C2VIEW.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       C2View.cpp : implementation of the CChat2000View class
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chat2000.h"

#include "C2Doc.h"
#include "C2View.h"
#include "MainFrm.h"

#include "EnterDlg.h"
#include "KickBan.h"
#include "BanDlg.h"
#include "InfoDlg.h"
#include "TopicDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CChat2000View

IMPLEMENT_DYNCREATE(CChat2000View, CView)

BEGIN_MESSAGE_MAP(CChat2000View, CView)
//{{AFX_MSG_MAP(CChat2000View)
ON_WM_CREATE()
ON_COMMAND(IDM_CHAT_ENTERROOM, OnChatEnterRoom)
ON_COMMAND(IDM_CHAT_EXITROOM, OnChatExitRoom)
ON_UPDATE_COMMAND_UI(IDM_CHAT_ENTERROOM, OnUpdateChatEnterRoom)
ON_UPDATE_COMMAND_UI(IDM_CHAT_EXITROOM, OnUpdateChatExitRoom)
ON_WM_SIZE()
ON_WM_PARENTNOTIFY()
ON_COMMAND(ID_CHAT_ABOUTMSCHAT, OnChatAboutmschat)
ON_COMMAND(ID_CHAT_KICKPARTICIPANT, OnChatKickParticipant)
ON_UPDATE_COMMAND_UI(ID_CHAT_KICKPARTICIPANT, OnUpdateChatKickparticipant)
ON_COMMAND(ID_CHAT_CLEARHISTORY, OnChatClearhistory)
ON_COMMAND(ID_CHAT_BANPARTICIPANT, OnChatBanparticipant)
ON_UPDATE_COMMAND_UI(ID_CHAT_BANPARTICIPANT, OnUpdateChatBanparticipant)
ON_COMMAND(ID_CHAT_SHOWINFO, OnChatShowinfo)
ON_COMMAND(ID_CHAT_SETROOMTOPIC, OnChatSetroomtopic)
ON_UPDATE_COMMAND_UI(ID_CHAT_SETROOMTOPIC, OnUpdateChatSetroomtopic)
//}}AFX_MSG_MAP
// Standard printing commands
//ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
//ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
//ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChat2000View construction/destruction

CChat2000View::CChat2000View()
{
m_iXppli = m_iYppli = 0;
m_posThisParticipant = NULL;
m_bActive = FALSE;
}

CChat2000View::~CChat2000View()
{
}

BOOL CChat2000View::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
//  the CREATESTRUCT cs

return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CChat2000View drawing

void CChat2000View::OnDraw(CDC* pDC)
{
CChat2000Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

// TODO: add draw code for native data here
//Call the CWnd method for Redrawing the OCX.
m_ocxChat.RedrawWindow();
}

/////////////////////////////////////////////////////////////////////////////
// CChat2000View diagnostics

#ifdef _DEBUG
void CChat2000View::AssertValid() const
{
CView::AssertValid();
}

void CChat2000View::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}

CChat2000Doc* CChat2000View::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CChat2000Doc)));
return (CChat2000Doc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CChat2000View message handlers

int CChat2000View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;

//enable OCX embeding
AfxEnableControlContainer();

//Get pixels per logical inch, used to calculate the size of the control
CClientDC MyCDC(NULL);
m_iXppli = MyCDC.GetDeviceCaps(LOGPIXELSX);
m_iYppli = MyCDC.GetDeviceCaps(LOGPIXELSY);

//Create the ChatOCX and initialize the UI
RECT rect;

rect.left= 1;
rect.top= 1;
rect.right= 5000;//lpCreateStruct->cx;
rect.bottom= 5000;//lpCreateStruct->cy;

if (!m_ocxChat.Create(NULL, WS_VISIBLE | WS_BORDER, rect, this, IDC_OCX_CHAT))
{
CString szFailMessage;
szFailMessage.LoadString(IDS_FAIL_TO_LOAD_OCX);
if (IDYES == AfxMessageBox (szFailMessage,MB_YESNO ) )
{
CString szURLtoChat;
szURLtoChat.LoadString(IDS_URL_TO_CHAT);
//start default web browser to load the chat OCX
HINSTANCE hResults = ShellExecute (NULL, "open", szURLtoChat, "",NULL,SW_SHOWNORMAL);
//if ((int) hResult <= 32) // error starting the browser
//TODO handle the ShellExecute faillure
}
return -1;
}

//resize the OCX so that the UI looks proportionate
m_ocxChat.SetWidth(MAP_PIX_TO_LOGHIM (1000,m_iXppli));
m_ocxChat.SetHeight(MAP_PIX_TO_LOGHIM (1000,m_iYppli));
m_ocxChat.SetUIOption(UI_OPT_ALL - UI_OPT_DISPLAY_ERRORMESSAGES - UI_OPT_ALLOW_RESIZING);

//set the Status UI
UpdateStatusUI();

return 0;
}

void CChat2000View::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);

//TRACE ("OnSize\n");

if (m_ocxChat && cx && cy) //do not resize to 0
{
m_ocxChat.SetWidth(MAP_PIX_TO_LOGHIM (cx,m_iXppli));
m_ocxChat.SetHeight(MAP_PIX_TO_LOGHIM (cy,m_iYppli));
}
}

void CChat2000View::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
m_bActive = bActivate;
CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
UpdateStatusUI();
}

void CChat2000View::OnParentNotify(UINT message, LPARAM lParam)
{
//to trap the WM_PARENTNOTIFY message we need to set the 'Message Filter' dropdown
//in the 'advaced options' section on the 'Class Info' tab of the 'Class Wizard' to
//Window'.  The View is originally set as 'Child Window'

CView::OnParentNotify(message, lParam);

//We might not have focus yet. We risk to pop up the wrong menu
//To avoid that we check if the view got activated

if ( (message == WM_RBUTTONDOWN) && (m_bActive) )
{
CMenu menu;
CMenu *submenu;
//Load the menu.
menu.LoadMenu(IDR_CHAT20TYPE);
//Get the pop-up menu.
submenu = menu.GetSubMenu(POPUPMENU);

CPoint pt(LOWORD(lParam),HIWORD(lParam));
//Convert to screen coordinates.
ClientToScreen(&pt);
//Post the menu.
submenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,
pt.x,    pt.y,
AfxGetApp()->m_pMainWnd,NULL);
}
}

//-----------------------------------------------------------------------------
//Command handlers
void CChat2000View::OnChatEnterRoom()
{
ASSERT(m_ocxChat);

CEnterDlgdlgEnter;

int iDlgRet = dlgEnter.DoModal();

if (iDlgRet == IDOK)
{

TRACE1("Room URL: %s",(LPCTSTR) dlgEnter.m_szRoom);
m_ocxChat.EnterRoom((LPCTSTR) dlgEnter.m_szRoom, dlgEnter.m_szPassword, dlgEnter.m_szNickName,_T("NTLM;ALL"), dlgEnter.m_iFlags, dlgEnter.m_iTypes);
}
else
{
if (iDlgRet != IDCANCEL)
{
AfxMessageBox ("Error Displaying the dialog: %i", iDlgRet);
}
}

}

void CChat2000View::OnChatExitRoom()
{
ASSERT(m_ocxChat);

//exit or cancel
int iState = m_ocxChat.GetState();
ASSERT(iState != STATE_DISCONNECTED);

if ( iState == STATE_CONNECTED)
{
m_ocxChat.ExitRoom();
}
else
{
m_ocxChat.CancelEntering();
}
}

void CChat2000View::OnChatAboutmschat()
{
ASSERT(m_ocxChat);

m_ocxChat.AboutBox();
}

void CChat2000View::OnChatKickParticipant()
{
ASSERT(m_ocxChat);

CKickBanDlgdlgKickBan;

int iDlgRet = dlgKickBan.DoModal();

if (iDlgRet == IDOK)
{
m_ocxChat.KickParticipant(-1,dlgKickBan.m_Comment);
}
else
{
if (iDlgRet != IDCANCEL)
{
AfxMessageBox ("Error Displaying the dialog: %i", iDlgRet);
}
}
}

void CChat2000View::OnChatClearhistory()
{
ASSERT(m_ocxChat);
m_ocxChat.ClearHistory();
}

void CChat2000View::OnChatBanparticipant()
{
ASSERT(m_ocxChat);

CBanDlgdlgBan;

//set the control values
dlgBan.m_Set = 0; //ban
dlgBan.m_pListOfPart = &m_ListOfPart;

int iDlgRet = dlgBan.DoModal();

if ( (iDlgRet == IDOK) && (!dlgBan.m_Name.IsEmpty()) )
{
m_ocxChat.BanParticipant(dlgBan.m_Name,(dlgBan.m_Set==0));
}
}

void CChat2000View::OnChatSetroomtopic()
{
ASSERT(m_ocxChat);

CTopicDlgdlgTopic;

int iDlgRet = dlgTopic.DoModal();

if ( iDlgRet == IDOK )
{
m_ocxChat.SetRoomTopic(dlgTopic.m_szTopic);
}

}

void CChat2000View::OnChatShowinfo()
{
ASSERT(m_ocxChat);

CInfoDlgdlgInfo;

dlgInfo.m_szMaxNumOfMembers.Format("%u", m_ocxChat.GetMaxMembers());
dlgInfo.m_szNumberOfMembers.Format("%u",m_ocxChat.GetMemberCount());
dlgInfo.m_szAlias = m_ocxChat.GetThisParticipantAlias();
dlgInfo.m_szID.Format("%u", m_ocxChat.GetThisParticipantID());
dlgInfo.m_szPath = m_ocxChat.GetRoomPath();
dlgInfo.m_szRatings = m_ocxChat.GetRating();
dlgInfo.m_szTopic = m_ocxChat.GetRoomTopic();

dlgInfo.DoModal();
}

//-----------------------------------------------------------------------------
//UI Update functions
void CChat2000View::OnUpdateChatEnterRoom(CCmdUI* pCmdUI)
{
ASSERT(m_ocxChat);
pCmdUI->Enable((m_ocxChat.GetState() == STATE_DISCONNECTED));

}

void CChat2000View::OnUpdateChatExitRoom(CCmdUI* pCmdUI)
{
ASSERT(m_ocxChat);
short nState = m_ocxChat.GetState();

//enable/disable the menu
pCmdUI->Enable(nState != STATE_DISCONNECTED);

if (nState != STATE_DISCONNECTED)
{
CString szNewMenuText;

if (nState == STATE_CONNECTING)
{
szNewMenuText.LoadString(IDS_MNU_CANCEL_ENTERING);
}
else
{
szNewMenuText.LoadString(IDS_MNU_EXIT_ROOM);
}

pCmdUI->SetText(szNewMenuText);
}
}

void CChat2000View::OnUpdateChatKickparticipant(CCmdUI* pCmdUI)
{
if (IsValidPos(m_posThisParticipant))
{
//enable the menu only if we are hosts
pCmdUI->Enable( GetParticipantPos(m_posThisParticipant)->GetStatus() == PARTICIPANT_STATE_HOST);
}
else
{
pCmdUI->Enable(FALSE);
}

}

void CChat2000View::OnUpdateChatBanparticipant(CCmdUI* pCmdUI)
{

if (IsValidPos(m_posThisParticipant))
{
//enable the menu only if we are hosts
pCmdUI->Enable( GetParticipantPos(m_posThisParticipant)->GetStatus() == PARTICIPANT_STATE_HOST);
}
else
{
pCmdUI->Enable(FALSE);
}
}

void CChat2000View::OnUpdateChatSetroomtopic(CCmdUI* pCmdUI)
{
ASSERT(m_ocxChat);
short nState = m_ocxChat.GetState();

pCmdUI->Enable(nState != STATE_DISCONNECTED);
}

//-----------------------------------------------------------------------------
//Helper functions
void CChat2000View::UpdateStatusUI()
{
ASSERT(m_ocxChat);

UpdateStatusUI(m_ocxChat.GetState());

}

void CChat2000View::UpdateStatusUI(short NewState_p)
{
ASSERT(m_ocxChat);

CString szOut;
szOut.LoadString(IDS_MDI_TITLE+NewState_p);

//add the room info
szOut +=  ((NewState_p != STATE_DISCONNECTED) ? (_T("[")+m_ocxChat.GetRoomPath()+_T("]")) : _T(""));

if ( (NewState_p == STATE_CONNECTED) && (IsValidPos(m_posThisParticipant)) )
{
//add the user alias
szOut = m_ocxChat.GetThisParticipantAlias() + _T(" ") + szOut;
}

CMainFrame *MainFrame = (CMainFrame *)GetParentFrame()->GetParentFrame();
ASSERT (MainFrame);
MainFrame->m_wndStatusBar.SetPaneText(0,(LPCTSTR) szOut,TRUE);
MainFrame->GetActiveFrame()->SetWindowText((LPCTSTR) szOut);
}

CString CChat2000View::GetHistory()
{
ASSERT(m_ocxChat);
//wrap this because it is used by the Doc class
return m_ocxChat.GetHistory();
}

//-----------------------------------------------------------------------------
//Events from the OCX
//since we created the OCX dinamically, the ClassWizard does not generate this code automatically
//Added manually.
//An easy way to get this code is to create a project and insert the OCX in a dialog, let the Wizard generate the code,
//Cut and paste in here and change the class references.

BEGIN_EVENTSINK_MAP(CChat2000View, CView)
    //{{AFX_EVENTSINK_MAP(CChat2000View)
ON_EVENT(CChat2000View, IDC_OCX_CHAT, 111 /* OnStateChanged */, OnOnStateChangedOcxChat, VTS_I2)
ON_EVENT(CChat2000View, IDC_OCX_CHAT, 106 /* OnParticipantStatusChanged */, OnOnParticipantStatusChangedOcxChat, VTS_I4 VTS_I2)
ON_EVENT(CChat2000View, IDC_OCX_CHAT, 101 /* OnBeginEnumeration */, OnOnBeginEnumerationOcxChat, VTS_NONE)
ON_EVENT(CChat2000View, IDC_OCX_CHAT, 102 /* OnEndEnumeration */, OnOnEndEnumerationOcxChat, VTS_NONE)
ON_EVENT(CChat2000View, IDC_OCX_CHAT, 104 /* OnExitParticipant */, OnOnExitParticipantOcxChat, VTS_I4)
ON_EVENT(CChat2000View, IDC_OCX_CHAT, 103 /* OnEnterParticipant */, OnOnEnterParticipantOcxChat, VTS_I4 VTS_BSTR VTS_I2)
ON_EVENT(CChat2000View, IDC_OCX_CHAT, 114 /* OnError */, OnOnErrorOcxChat, VTS_I2 VTS_BSTR)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

void CChat2000View::OnOnStateChangedOcxChat(short NewState)
{
TRACE("CChat2000View::OnOnStateChangedOcxChat\n");
UpdateStatusUI(NewState);

//remove all participants from the list
if (NewState == STATE_DISCONNECTED)
{
DelAll();
}
}

void CChat2000View::OnOnParticipantStatusChangedOcxChat(long ParticipantID, short Status)
{
TRACE("CChat2000View::OnOnParticipantStatusChangedOcxChat\n");

ASSERT(ParticipantID);
//set the participant status
GetParticipantID((short)ParticipantID)->SetStatus (Status);
}

void CChat2000View::OnOnBeginEnumerationOcxChat()
{
TRACE("CChat2000View::OnOnBeginEnumerationOcxChat\n");
}

void CChat2000View::OnOnEndEnumerationOcxChat()
{
TRACE("CChat2000View::OnOnEndEnumerationOcxChat\n");

//The OCX is done enumerating, now we can find out who we are
m_posThisParticipant = FindParticipantID((short)m_ocxChat.GetThisParticipantID());

//we entered a room, set the UI
UpdateStatusUI();
}

void CChat2000View::OnOnExitParticipantOcxChat(long ParticipantID)
{
TRACE("CChat2000View::OnOnExitParticipantOcxChat\n");

DelParticipantID((short)ParticipantID);
}

void CChat2000View::OnOnEnterParticipantOcxChat(long ParticipantID, LPCTSTR Alias, short Status)
{
TRACE("CChat2000View::OnOnEnterParticipantOcxChat\n");

AddParticipant (ParticipantID, Alias, Status);
}

void CChat2000View::OnOnErrorOcxChat(short ErrorCode, LPCTSTR Description)
{
//catch those asyncronous errors
AfxMessageBox(Description,MB_ICONSTOP | MB_OK);
}

//-----------------------------------------------------------------------------
//participant list manager

inline void CChat2000View::AddParticipant(CParticipant &Part_p)
{
m_ListOfPart.AddTail(Part_p);
}

inline void CChat2000View::AddParticipant(long ParticipantID, LPCTSTR Alias, short Status)
{
CParticipant Part(ParticipantID, Alias, Status);
m_ListOfPart.AddTail(Part);
}

inline void CChat2000View::DelParticipantID(short iID_p)
{
CParticipant Part(iID_p);
m_ListOfPart.RemoveAt(m_ListOfPart.Find(Part));
if (iID_p == m_ocxChat.GetThisParticipantID())
{
m_posThisParticipant = NULL;
}
}

inline void CChat2000View::DelAll()
{
m_ListOfPart.RemoveAll();
m_posThisParticipant = NULL;
}

inline POSITION CChat2000View::FindParticipantID(short iID_p)
{
CParticipant Part(iID_p);
return m_ListOfPart.Find(Part);
}

inline CParticipant *CChat2000View::GetParticipantPos(POSITION Pos_p)
{
return &m_ListOfPart.GetAt(Pos_p);
}

inline CParticipant *CChat2000View::GetParticipantID(short iID_p)
{
return GetParticipantPos(FindParticipantID(iID_p));
}

inline BOOL CChat2000View::IsValidPos(POSITION Pos_p)
{
return (Pos_p != NULL);
//we could check if the pos falls between the head and tail of the list
//but we will rely on the fact that we keep a consistent list
}

inline CString CChat2000View::GetParticipantAlias()
{
static POSITION CurrentPos = m_ListOfPart.GetHeadPosition();

if (IsValidPos (CurrentPos))
{
//return the alias, set the position to the next element of the list
return (m_ListOfPart.GetNext(CurrentPos)).GetAlias();
}
else
{
//we reached the end of the list or the list is empty
//go back to the head
CurrentPos = m_ListOfPart.GetHeadPosition();
return "";
}
}


C2VIEW.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       C2View.h : interface of the CChat2000View class
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

//{{AFX_INCLUDES()
#include "mschat.h"
//}}AFX_INCLUDES

#include "partic.h"

#define POPUPMENU3

class CChat2000View : public CView
{
protected: // create from serialization only
CChat2000View();
DECLARE_DYNCREATE(CChat2000View)

// Attributes
public:
CString GetHistory();
CChat2000Doc* GetDocument();

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CChat2000View)
public:
virtual void OnDraw(CDC* pDC);  // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual void OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView);
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CChat2000View();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:
//the OCX !
CMSChatm_ocxChat;

//OCX does not expose the list of participants, we have to do the job
//I did not put the list into the OCX itself to avoid an not-so-elegant
//passing back and forth notifications from the VIEW to the OCX
CList <CParticipant, CParticipant&> m_ListOfPart;
POSITION m_posThisParticipant;

//used to keep the video device caps to resize the OCX
intm_iXppli;
intm_iYppli;

//track if the view has focus or not. Useful to pop up menus
BOOLm_bActive;

//participant list manager
voidAddParticipant(CParticipant &Part_p);
voidAddParticipant(long ParticipantID, LPCTSTR Alias, short Status);
voidDelParticipantID(short iID_p);
voidDelAll();
POSITIONFindParticipantID(short iID_p);
CParticipant*GetParticipantPos(POSITION Pos_p);
CParticipant*GetParticipantID(short iID_p);
BOOLIsValidPos(POSITION Pos_p);
CStringGetParticipantAlias();

// Generated message map functions
protected:
//{{AFX_MSG(CChat2000View)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnChatEnterRoom();
afx_msg void OnChatExitRoom();
afx_msg void OnUpdateChatEnterRoom(CCmdUI* pCmdUI);
afx_msg void OnUpdateChatExitRoom(CCmdUI* pCmdUI);
afx_msg void OnOnStateChangedOcxChat(short NewState);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnParentNotify(UINT message, LPARAM lParam);
afx_msg void OnChatAboutmschat();
afx_msg void OnChatKickParticipant();
afx_msg void OnUpdateChatKickparticipant(CCmdUI* pCmdUI);
afx_msg void OnOnParticipantStatusChangedOcxChat(long ParticipantID, short Status);
afx_msg void OnOnBeginEnumerationOcxChat();
afx_msg void OnOnEndEnumerationOcxChat();
afx_msg void OnOnExitParticipantOcxChat(long ParticipantID);
afx_msg void OnOnEnterParticipantOcxChat(long ParticipantID, LPCTSTR Alias, short Status);
afx_msg void OnChatClearhistory();
afx_msg void OnChatBanparticipant();
afx_msg void OnUpdateChatBanparticipant(CCmdUI* pCmdUI);
afx_msg void OnChatShowinfo();
afx_msg void OnOnErrorOcxChat(short ErrorCode, LPCTSTR Description);
afx_msg void OnChatSetroomtopic();
afx_msg void OnUpdateChatSetroomtopic(CCmdUI* pCmdUI);
DECLARE_EVENTSINK_MAP() //needed to trap events from the OCX
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

void UpdateStatusUI(short NewState) ;
void UpdateStatusUI() ;
};

#ifndef _DEBUG  // debug version in C2View.cpp
inline CChat2000Doc* CChat2000View::GetDocument()
   { return (CChat2000Doc*)m_pDocument; }
#endif

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


CHAT2000.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       Chat2000.cpp : Defines the class behaviors for the application.
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chat2000.h"

#include "MainFrm.h"
#include "ChildFrm.h"
#include "C2Doc.h"
#include "C2View.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CChat2000App

BEGIN_MESSAGE_MAP(CChat2000App, CWinApp)
//{{AFX_MSG_MAP(CChat2000App)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// NOTE - the ClassWizard will add and remove mapping macros here.
//    DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Standard file based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChat2000App construction

CChat2000App::CChat2000App()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CChat2000App object

CChat2000App theApp;

/////////////////////////////////////////////////////////////////////////////
// CChat2000App initialization

BOOL CChat2000App::InitInstance()
{
// Standard initialization
// If you are not using these features and wish to reduce the size
//  of your final executable, you should remove from the following
//  the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls();// Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic();// Call this when linking to MFC statically
#endif

LoadStdProfileSettings();  // Load standard INI file options (including MRU)

// Register the application's document templates.  Document templates
//  serve as the connection between documents, frame windows and views.

CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_CHAT20TYPE,
RUNTIME_CLASS(CChat2000Doc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CChat2000View));
AddDocTemplate(pDocTemplate);

// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;

// The main window has been initialized, so show and update it.
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();

return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
CAboutDlg();

// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
// No message handlers
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CChat2000App::OnAppAbout()
{
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}


CHAT2000.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       Chat2000.h : main header file for the CHAT2000 application
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// CChat2000App:
// See Chat2000.cpp for the implementation of this class
//

class CChat2000App : public CWinApp
{
public:
CChat2000App();

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CChat2000App)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL

// Implementation

//{{AFX_MSG(CChat2000App)
afx_msg void OnAppAbout();
// NOTE - the ClassWizard will add and remove member functions here.
//    DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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


CHILDFRM.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       ChildFrm.cpp : implementation of the CChildFrame class
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chat2000.h"

#include "ChildFrm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CChildFrame

IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd)

BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CChildFrame)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChildFrame construction/destruction

CChildFrame::CChildFrame()
{
// TODO: add member initialization code here

}

CChildFrame::~CChildFrame()
{
}

BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
//  the CREATESTRUCT cs

return CMDIChildWnd::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CChildFrame diagnostics

#ifdef _DEBUG
void CChildFrame::AssertValid() const
{
CMDIChildWnd::AssertValid();
}

void CChildFrame::Dump(CDumpContext& dc) const
{
CMDIChildWnd::Dump(dc);
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CChildFrame message handlers


CHILDFRM.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       ChildFrm.h : interface of the CChildFrame class
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

class CChildFrame : public CMDIChildWnd
{
DECLARE_DYNCREATE(CChildFrame)
public:
CChildFrame();

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CChildFrame)
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CChildFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

// Generated message map functions
protected:
//{{AFX_MSG(CChildFrame)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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


ENTERDLG.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       EnterDlg.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chat2000.h"
#include "EnterDlg.h"

#include "globals.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CEnterDlg dialog

CEnterDlg::CEnterDlg(CWnd* pParent /*=NULL*/)
: CDialog(CEnterDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CEnterDlg)
m_szNickName = _T("");
m_szPassword = _T("");
m_szRoom = _T("");
//}}AFX_DATA_INIT

m_iFlags = 0x00000;
m_iTypes = 0x00000;

}

void CEnterDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CEnterDlg)
DDX_Text(pDX, IDC_EDIT_NICKNAME, m_szNickName);
DDX_Text(pDX, IDC_EDIT_PASSWORD, m_szPassword);
DDX_Text(pDX, IDC_EDIT_ROOM, m_szRoom);
//}}AFX_DATA_MAP

intiControl = 0;
if (pDX->m_bSaveAndValidate)
{
//we are getting the data from the controls

m_iFlags = 0; //clean the variable from old settings
//get the checked flags
for (iControl = IDC_CREATE; iControl <= IDC_AUTENTICATED_TALK; iControl++)
{
m_iFlags |= ((CButton *)GetDlgItem(iControl))->GetCheck() << (iControl - IDC_CREATE);
}

m_iTypes = 0; //clean the variable from old settings
//get the checked types
for (iControl = IDC_ANY_USER; iControl <= IDC_SPECTATOR; iControl++)
{
m_iTypes |= ((CButton *)GetDlgItem(iControl))->GetCheck() << (iControl - IDC_ANY_USER);
}
}
else
{
//we are putting the data in the controls
short iRest= m_iFlags;

//set the checked flags
for (iControl = IDC_CREATE; iControl <= IDC_AUTENTICATED_TALK; iControl++)
{
((CButton *)GetDlgItem(iControl))->SetCheck(iRest&0x1);
iRest >>= 1; //pop the last decimal
}
//set the checked types
iRest= m_iTypes;
for (iControl = IDC_ANY_USER; iControl <= IDC_SPECTATOR; iControl++)
{
((CButton *)GetDlgItem(iControl))->SetCheck(iRest&0x1);
iRest >>= 1; //pop the last decimal
}
}
}

BEGIN_MESSAGE_MAP(CEnterDlg, CDialog)
//{{AFX_MSG_MAP(CEnterDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEnterDlg message handlers

BOOL CEnterDlg::OnInitDialog()
{
CDialog::OnInitDialog();

//default values for the controls
m_szRoom.LoadString(IDS_DEFAULT_CHAT_ROOM);
m_szNickName.LoadString(IDS_DEFAULT_NICKNAME);
m_iFlags = ENTER_FLAG_JOIN_CREATE;
m_iTypes = ENTER_TYPE_ANY_USER;

UpdateData(FALSE); //put the data into the controls

return TRUE;  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}


ENTERDLG.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       EnterDlg.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

/////////////////////////////////////////////////////////////////////////////
// CEnterDlg dialog

class CEnterDlg : public CDialog
{
// Construction
public:
CEnterDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CEnterDlg)
enum { IDD = IDD_CHAT_CONNECT };
CStringm_szNickName;
CStringm_szPassword;
CStringm_szRoom;
//}}AFX_DATA

//we are going to handle the check button manually
shortm_iFlags;
shortm_iTypes;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CEnterDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CEnterDlg)
virtual BOOL OnInitDialog();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};


GLOBALS.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       Globals.h
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#ifndef _GLOBALS_
#define _GLOBALS_

#define IDC_OCX_CHAT1000

//UI options
#define UI_OPT_SEND_BUTTON0x0001
#defineUI_OPT_WHISPER_BUTTON0x0002
#defineUI_OPT_PARTICIPANT_LISTBOX0x0004
#defineUI_OPT_RECEIVE_TEXTBOX0x0008
#defineUI_OPT_SEND_TEXTBOX0x0010
#defineUI_OPT_INFORM_JOIN0x0020
#defineUI_OPT_INFORM_LEAVE0x0040
#defineUI_OPT_INFORM_MEMCHG0x0080
#defineUI_OPT_INSERT_BLANK0x0100
#defineUI_OPT_DISPLAY_ERRORMESSAGES0x0200
#defineUI_OPT_ALLOW_RESIZING0x0400
#defineUI_OPT_SENSITIVE_URLS0x0800
#define UI_OPT_ALL0x0FFF

//Flags for EnterRoom
#define ENTER_FLAG_JOIN_CREATE0x0001
#defineENTER_FLAG_DO_NOT_EXPOSE0x0002
#defineENTER_FLAG_READ_ONLY0x0004
#defineENTER_FLAG_MIC_ONLY0x0008
#defineENTER_FLAG_AUTHENTICATED_ONLY0x0010
#defineENTER_FLAG_AUTHENTICATED_CAN_TALK0x0020

//Type for EnterRoom
#define ENTER_TYPE_ANY_USER0x0001
#defineENTER_TYPE_HOST_CAN_CHANGE_TOPIC0x0002
#defineENTER_TYPE_LOGGED_USERS_ONLY0x0004
#defineENTER_TYPE_DISABLE_WHISPERING0x0008
#defineENTER_TYPE_STRONG_HOST0x0010
#defineENTER_TYPE_SPECTATOR_DEFAULT0x0020

//States
#define STATE_DISCONNECTED0x0001
#defineSTATE_CONNECTING0x0002
#defineSTATE_CONNECTED0x0003

//Participant states
#define PARTICIPANT_STATE_HOST0x0001
#definePARTICIPANT_STATE_PARTICIPANT0x0002
#definePARTICIPANT_STATE_SPECTATOR0x0004
#definePARTICIPANT_STATE_NO_WISPER0x0008
#definePARTICIPANT_STATE_IGNORED0x0010

//metrix convertions macros
#define HIMETRIC_PER_INCH   2540
#define MAP_PIX_TO_LOGHIM(x,ppli)   ( (HIMETRIC_PER_INCH*(x) + ((ppli)>>1)) / (ppli) )
#define MAP_LOGHIM_TO_PIX(x,ppli)   ( ((ppli)*(x) + HIMETRIC_PER_INCH/2) / HIMETRIC_PER_INCH )

#endif _GLOBALS_


INFODLG.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       InfoDlg.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chat2000.h"
#include "InfoDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CInfoDlg dialog

CInfoDlg::CInfoDlg(CWnd* pParent /*=NULL*/)
: CDialog(CInfoDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CInfoDlg)
m_szMaxNumOfMembers = _T("");
m_szNumberOfMembers = _T("");
m_szAlias = _T("");
m_szID = _T("");
m_szPath = _T("");
m_szRatings = _T("");
m_szTopic = _T("");
//}}AFX_DATA_INIT
}

void CInfoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CInfoDlg)
DDX_Text(pDX, IDC_MAX_NUMBER_OF_MEMBERS, m_szMaxNumOfMembers);
DDX_Text(pDX, IDC_NUMBER_OF_MEMBERS, m_szNumberOfMembers);
DDX_Text(pDX, IDC_PARTICIPANT_ALIAS, m_szAlias);
DDX_Text(pDX, IDC_PARTICIPANT_ID, m_szID);
DDX_Text(pDX, IDC_PATH, m_szPath);
DDX_Text(pDX, IDC_Ratings, m_szRatings);
DDX_Text(pDX, IDC_TOPIC, m_szTopic);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CInfoDlg, CDialog)
//{{AFX_MSG_MAP(CInfoDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CInfoDlg message handlers


INFODLG.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       InfoDlg.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

/////////////////////////////////////////////////////////////////////////////
// CInfoDlg dialog

class CInfoDlg : public CDialog
{
// Construction
public:
CInfoDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CInfoDlg)
enum { IDD = IDD_INFO };
CStringm_szMaxNumOfMembers;
CStringm_szNumberOfMembers;
CStringm_szAlias;
CStringm_szID;
CStringm_szPath;
CStringm_szRatings;
CStringm_szTopic;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CInfoDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CInfoDlg)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};


KICKBAN.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       CKickBanDlg.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chat2000.h"
#include "KickBan.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CKickBanDlg dialog

CKickBanDlg::CKickBanDlg(CWnd* pParent /*=NULL*/)
: CDialog(CKickBanDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CKickBanDlg)
m_Comment = _T("");
//}}AFX_DATA_INIT
}

void CKickBanDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CKickBanDlg)
DDX_Text(pDX, IDC_COMMENT, m_Comment);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CKickBanDlg, CDialog)
//{{AFX_MSG_MAP(CKickBanDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CKickBanDlg message handlers


KICKBAN.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       CKickBanDlg.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

/////////////////////////////////////////////////////////////////////////////
// CKickBanDlg dialog

class CKickBanDlg : public CDialog
{
// Construction
public:
CKickBanDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CKickBanDlg)
enum { IDD = IDD_KICK_BAN };
CStringm_Comment;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CKickBanDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CKickBanDlg)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};


MAINFRM.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       MainFrm.cpp : implementation of the CMainFrame class
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "Chat2000.h"

#include "MainFrm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMainFrame

IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

static UINT indicators[] =
{
ID_SEPARATOR,           // status line indicator
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};

/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
// TODO: add member initialization code here

}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;

if (!m_wndToolBar.Create(this) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1;      // fail to create
}

if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
  sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1;      // fail to create
}

// TODO: Remove this if you don't want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

// TODO: Delete these three lines if you don't want the toolbar to
//  be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);

return 0;
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
//  the CREATESTRUCT cs

return CMDIFrameWnd::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CMDIFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
CMDIFrameWnd::Dump(dc);
}

#endif //_DEBUG


MAINFRM.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       MainFrm.h : interface of the CMainFrame class
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

class CMainFrame : public CMDIFrameWnd
{
DECLARE_DYNAMIC(CMainFrame)
public:
CMainFrame();

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMainFrame)
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CMainFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

//protected:  // control bar embedded members
public:  // public to update from the view
CStatusBar  m_wndStatusBar;
CToolBar    m_wndToolBar;

// Generated message map functions
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

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


MSCHAT.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       MsChat.Cpp
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.

#include "stdafx.h"
#include "mschat.h"

/////////////////////////////////////////////////////////////////////////////
// CMSChat

IMPLEMENT_DYNCREATE(CMSChat, CWnd)

/////////////////////////////////////////////////////////////////////////////
// CMSChat properties

/////////////////////////////////////////////////////////////////////////////
// CMSChat operations

unsigned long CMSChat::AddRef()
{
unsigned long result;
InvokeHelper(0x60000001, DISPATCH_METHOD, VT_I4, (void*)&result, NULL);
return result;
}

unsigned long CMSChat::Release()
{
unsigned long result;
InvokeHelper(0x60000002, DISPATCH_METHOD, VT_I4, (void*)&result, NULL);
return result;
}

unsigned long CMSChat::GetBackColor()
{
unsigned long result;
InvokeHelper(DISPID_BACKCOLOR, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CMSChat::SetBackColor(unsigned long newValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(DISPID_BACKCOLOR, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 newValue);
}

short CMSChat::GetWidth()
{
short result;
InvokeHelper(0x3, DISPATCH_PROPERTYGET, VT_I2, (void*)&result, NULL);
return result;
}

void CMSChat::SetWidth(short nNewValue)
{
static BYTE parms[] =
VTS_I2;
InvokeHelper(0x3, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

short CMSChat::GetHeight()
{
short result;
InvokeHelper(0x4, DISPATCH_PROPERTYGET, VT_I2, (void*)&result, NULL);
return result;
}

void CMSChat::SetHeight(short nNewValue)
{
static BYTE parms[] =
VTS_I2;
InvokeHelper(0x4, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

short CMSChat::GetUIOption()
{
short result;
InvokeHelper(0x1, DISPATCH_PROPERTYGET, VT_I2, (void*)&result, NULL);
return result;
}

void CMSChat::SetUIOption(short nNewValue)
{
static BYTE parms[] =
VTS_I2;
InvokeHelper(0x1, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

short CMSChat::GetState()
{
short result;
InvokeHelper(0x2, DISPATCH_PROPERTYGET, VT_I2, (void*)&result, NULL);
return result;
}

void CMSChat::SetState(short nNewValue)
{
static BYTE parms[] =
VTS_I2;
InvokeHelper(0x2, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

short CMSChat::GetBorderStyle()
{
short result;
InvokeHelper(DISPID_BORDERSTYLE, DISPATCH_PROPERTYGET, VT_I2, (void*)&result, NULL);
return result;
}

void CMSChat::SetBorderStyle(short nNewValue)
{
static BYTE parms[] =
VTS_I2;
InvokeHelper(DISPID_BORDERSTYLE, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

short CMSChat::GetAppearance()
{
short result;
InvokeHelper(DISPID_APPEARANCE, DISPATCH_PROPERTYGET, VT_I2, (void*)&result, NULL);
return result;
}

void CMSChat::SetAppearance(short nNewValue)
{
static BYTE parms[] =
VTS_I2;
InvokeHelper(DISPID_APPEARANCE, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

short CMSChat::GetMaxMessageLength()
{
short result;
InvokeHelper(0x5, DISPATCH_PROPERTYGET, VT_I2, (void*)&result, NULL);
return result;
}

void CMSChat::SetMaxMessageLength(short nNewValue)
{
static BYTE parms[] =
VTS_I2;
InvokeHelper(0x5, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

long CMSChat::GetMaxHistoryLength()
{
long result;
InvokeHelper(0x6, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CMSChat::SetMaxHistoryLength(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0x6, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

long CMSChat::GetMemberCount()
{
long result;
InvokeHelper(0x7, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CMSChat::SetMemberCount(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0x7, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

long CMSChat::GetMaxMembers()
{
long result;
InvokeHelper(0x1c, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CMSChat::SetMaxMembers(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0x1c, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

CString CMSChat::GetRating()
{
CString result;
InvokeHelper(0x8, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CMSChat::SetRating(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x8, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

long CMSChat::GetThisParticipantID()
{
long result;
InvokeHelper(0x9, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
return result;
}

void CMSChat::SetThisParticipantID(long nNewValue)
{
static BYTE parms[] =
VTS_I4;
InvokeHelper(0x9, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 nNewValue);
}

CString CMSChat::GetThisParticipantName()
{
CString result;
InvokeHelper(0xa, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CMSChat::SetThisParticipantName(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0xa, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

CString CMSChat::GetThisParticipantAlias()
{
CString result;
InvokeHelper(0xb, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CMSChat::SetThisParticipantAlias(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0xb, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

CString CMSChat::GetRoomPath()
{
CString result;
InvokeHelper(0xc, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CMSChat::SetRoomPath(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0xc, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

CString CMSChat::GetRoomTopic()
{
CString result;
InvokeHelper(0xd, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CMSChat::SetRoomTopic(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0xd, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

CString CMSChat::GetLastMessageSent()
{
CString result;
InvokeHelper(0xe, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CMSChat::SetLastMessageSent(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0xe, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

CString CMSChat::GetLastMessageReceived()
{
CString result;
InvokeHelper(0xf, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CMSChat::SetLastMessageReceived(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0xf, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

CString CMSChat::GetHistory()
{
CString result;
InvokeHelper(0x10, DISPATCH_PROPERTYGET, VT_BSTR, (void*)&result, NULL);
return result;
}

void CMSChat::SetHistory(LPCTSTR lpszNewValue)
{
static BYTE parms[] =
VTS_BSTR;
InvokeHelper(0x10, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms,
 lpszNewValue);
}

void CMSChat::GetParticipantRealName(long ParticipantID, LPCTSTR Alias, BOOL Synchronous, BSTR* RealName)
{
static BYTE parms[] =
VTS_I4 VTS_BSTR VTS_BOOL VTS_PBSTR;
InvokeHelper(0x11, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ParticipantID, Alias, Synchronous, RealName);
}

void CMSChat::SelectParticipants(short Selection)
{
static BYTE parms[] =
VTS_I2;
InvokeHelper(0x12, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 Selection);
}

void CMSChat::MoveSplitBar()
{
InvokeHelper(0x13, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CMSChat::ClearHistory()
{
InvokeHelper(0x14, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CMSChat::EnterRoom(LPCTSTR URL, LPCTSTR RoomPassword, LPCTSTR ThisParticipantAlias, LPCTSTR SecurityPackage, short Flags, short Type)
{
static BYTE parms[] =
VTS_BSTR VTS_BSTR VTS_BSTR VTS_BSTR VTS_I2 VTS_I2;
InvokeHelper(0x15, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 URL, RoomPassword, ThisParticipantAlias, SecurityPackage, Flags, Type);
}

void CMSChat::CancelEntering()
{
InvokeHelper(0x16, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CMSChat::ExitRoom()
{
InvokeHelper(0x17, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

void CMSChat::SetParticipantStatus(long ParticipantID, short Mask, short Status)
{
static BYTE parms[] =
VTS_I4 VTS_I2 VTS_I2;
InvokeHelper(0x18, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ParticipantID, Mask, Status);
}

void CMSChat::KickParticipant(long ParticipantID, LPCTSTR Comment)
{
static BYTE parms[] =
VTS_I4 VTS_BSTR;
InvokeHelper(0x19, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 ParticipantID, Comment);
}

void CMSChat::BanParticipant(LPCTSTR Alias, BOOL Set)
{
static BYTE parms[] =
VTS_BSTR VTS_BOOL;
InvokeHelper(0x1a, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 Alias, Set);
}

void CMSChat::SendMessage(const VARIANT& DestList, short Count, const VARIANT& Message)
{
static BYTE parms[] =
VTS_VARIANT VTS_I2 VTS_VARIANT;
InvokeHelper(0x1b, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
 &DestList, Count, &Message);
}

void CMSChat::AboutBox()
{
InvokeHelper(0xfffffdd8, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}


MSCHAT.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       MsChat.H
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#ifndef __MSCHAT_H__
#define __MSCHAT_H__

#include "globals.h"

// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.

/////////////////////////////////////////////////////////////////////////////
// CMSChat wrapper class

class CMSChat : public CWnd
{
protected:
DECLARE_DYNCREATE(CMSChat)
public:
CLSID const& GetClsid()
{
static CLSID const clsid
= { 0xd6526fe0, 0xe651, 0x11cf, { 0x99, 0xcb, 0x0, 0xc0, 0x4f, 0xd6, 0x44, 0x97 } };
return clsid;
}
virtual BOOL Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd, UINT nID,
CCreateContext* pContext = NULL)
{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); }

    BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID,
CFile* pPersist = NULL, BOOL bStorage = FALSE,
BSTR bstrLicKey = NULL)
{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID,
pPersist, bStorage, bstrLicKey); }

// Attributes
public:

// Operations
public:
// method 'QueryInterface' not emitted because of invalid return type or parameter type
unsigned long AddRef();
unsigned long Release();
// method 'GetTypeInfoCount' not emitted because of invalid return type or parameter type
// method 'GetTypeInfo' not emitted because of invalid return type or parameter type
// method 'GetIDsOfNames' not emitted because of invalid return type or parameter type
// method 'Invoke' not emitted because of invalid return type or parameter type
unsigned long GetBackColor();
void SetBackColor(unsigned long newValue);
short GetWidth();
void SetWidth(short nNewValue);
short GetHeight();
void SetHeight(short nNewValue);
short GetUIOption();
void SetUIOption(short nNewValue);
short GetState();
void SetState(short nNewValue);
short GetBorderStyle();
void SetBorderStyle(short nNewValue);
short GetAppearance();
void SetAppearance(short nNewValue);
short GetMaxMessageLength();
void SetMaxMessageLength(short nNewValue);
long GetMaxHistoryLength();
void SetMaxHistoryLength(long nNewValue);
long GetMemberCount();
void SetMemberCount(long nNewValue);
long GetMaxMembers();
void SetMaxMembers(long nNewValue);
CString GetRating();
void SetRating(LPCTSTR lpszNewValue);
long GetThisParticipantID();
void SetThisParticipantID(long nNewValue);
CString GetThisParticipantName();
void SetThisParticipantName(LPCTSTR lpszNewValue);
CString GetThisParticipantAlias();
void SetThisParticipantAlias(LPCTSTR lpszNewValue);
CString GetRoomPath();
void SetRoomPath(LPCTSTR lpszNewValue);
CString GetRoomTopic();
void SetRoomTopic(LPCTSTR lpszNewValue);
CString GetLastMessageSent();
void SetLastMessageSent(LPCTSTR lpszNewValue);
CString GetLastMessageReceived();
void SetLastMessageReceived(LPCTSTR lpszNewValue);
CString GetHistory();
void SetHistory(LPCTSTR lpszNewValue);
void GetParticipantRealName(long ParticipantID, LPCTSTR Alias, BOOL Synchronous, BSTR* RealName);
void SelectParticipants(short Selection);
void MoveSplitBar();
void ClearHistory();
//AR modified to have default parms
void EnterRoom(LPCTSTR URL, LPCTSTR RoomPassword = _T(""), LPCTSTR ThisParticipantAlias = _T("anonymous"), LPCTSTR SecurityPackage = _T("ANON"), short Flags = ENTER_FLAG_JOIN_CREATE, short Type = ENTER_TYPE_ANY_USER);
void CancelEntering();
void ExitRoom();
void SetParticipantStatus(long ParticipantID, short Mask, short Status);
void KickParticipant(long ParticipantID, LPCTSTR Comment);
void BanParticipant(LPCTSTR Alias, BOOL Set);
void SendMessage(const VARIANT& DestList, short Count, const VARIANT& Message);
void AboutBox();
};

#endif // __MSCHAT_H__


PARTIC.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       Partic.cpp
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "partic.h"

CParticipant::CParticipant()
{
}

CParticipant::CParticipant(long ParticipantID, LPCTSTR Alias, short Status)
{
m_iID = (short) ParticipantID;
//m_szName = Name;
m_szAlias = Alias;
m_iStatus = Status;
}

bool CParticipant::operator== ( const CParticipant &src ) const
{
if ((m_iID== src.m_iID))//&&
//(szName== src.szName)&&
//(szAlias== src.szAlias) )
{
return TRUE;
}
else
{
return FALSE;
}
}

void CParticipant::operator= ( const CParticipant& src )
{
m_iID= src.m_iID;
m_szName= src.m_szName;
m_szAlias= src.m_szAlias;
m_iStatus= src.m_iStatus;
}

//Set Attributes
void CParticipant::SetAlias(LPCTSTR Alias_p)
{
m_szAlias = Alias_p;
}

void CParticipant::SetStatus (short Status_p)
{
m_iStatus = Status_p;
}

void CParticipant::SetName(LPCTSTR Name_p)
{
m_szName = Name_p;
}

//get attributes
short CParticipant::GetID ()
{
return m_iID;
}

CString CParticipant::GetAlias()
{
return m_szAlias;
}

short CParticipant::GetStatus ()
{
return m_iStatus;
}

CString CParticipant::GetName()
{
return m_szName;
}


PARTIC.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       Partic.h
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#ifndef _PARTIC_H_
#define _PARTIC_H_

#include "globals.h"

//class participant
class CParticipant : public CObject
{
private:
shortm_iID;
CStringm_szName;
CStringm_szAlias;
shortm_iStatus;

public:
CParticipant();
CParticipant(long ParticipantID,
LPCTSTR Alias=NULL,
short Status=PARTICIPANT_STATE_PARTICIPANT);
void operator =( const CParticipant& src );
bool operator== ( const CParticipant &src ) const;

//Set Attributes
void SetAlias(LPCTSTR Alias_p);
void SetStatus (short Status_p);
void SetName(LPCTSTR Name_p);
//get attributes
shortGetID ();
CString GetAlias();
shortGetStatus ();
CString GetName();
};

#endif _PARTIC_H_


RESOURCE.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       Resource.H
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Chat2000.rc
//
#define IDD_ABOUTBOX                    100
#define IDR_MAINFRAME                   128
#define IDR_CHAT20TYPE                  129
#define IDD_CHAT_CONNECT                130
#define IDD_KICK_BAN                    131
#define IDD_BAN                         132
#define IDD_INFO                        138
#define IDD_ROOM_TOPIC                  139
#define IDC_EDIT_ROOM                   1000
#define IDC_EDIT_NICKNAME               1001
#define IDC_EDIT_PASSWORD               1002
#define IDC_COMMENT                     1003
#define IDC_CREATE                      1004
#define IDC_DO_NOT_EXPOSE               1005
#define IDC_READ_ONLY                   1006
#define IDC_MIC_ONLY                    1007
#define IDC_AUTENTICATED_ONLY           1008
#define IDC_AUTENTICATED_TALK           1009
#define IDC_FLAGS                       1010
#define IDC_TYPE                        1011
#define IDC_BANKICK                     1015
#define IDC_ANY_USER                    1016
#define IDC_HOST_TOPIC                  1017
#define IDC_LOGGED_ON_ONLY              1018
#define IDC_REINSTATE                   1018
#define IDC_NO_WISPER                   1019
#define IDC_SET                         1019
#define IDC_SHOW_HOST_ONLY              1020
#define IDC_NAME                        1020
#define IDC_SPECTATOR                   1021
#define IDC_PATH                        1026
#define IDC_TOPIC                       1027
#define IDC_Ratings                     1028
#define IDC_NUMBER_OF_MEMBERS           1029
#define IDC_MAX_NUMBER_OF_MEMBERS       1030
#define IDC_PARTICIPANT_ID              1031
#define IDC_PARTICIPANT_ALIAS           1033
#define IDS_MDI_TITLE                   5000
#define IDS_APP_DISCONNECTED            5001
#define IDS_APP_CONNECTING              5002
#define IDS_APP_CONNECTED               5003
#define IDS_FAIL_TO_LOAD_OCX            5004
#define IDS_URL_TO_CHAT                 5005
#define IDS_ERROR_LOADING_FILE          5006
#define IDS_MNU_CANCEL_ENTERING         5007
#define IDS_MNU_EXIT_ROOM               5008
#define IDS_DEFAULT_CHAT_ROOM           6000
#define IDS_DEFAULT_NICKNAME            6001
#define IDM_CHAT_ENTERROOM              32771
#define IDM_CHAT_EXITROOM               32772
#define ID_CHAT_SHOWINFO                32773
#define ID_CHAT_CLEARHISTORY            32775
#define ID_CHAT_ABOUTMSCHAT             32776
#define ID_CHAT_KICKPARTICIPANT         32781
#define ID_CHAT_BANPARTICIPANT          32782
#define ID_CHAT_SETROOMTOPIC            32785

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_3D_CONTROLS                     1
#define _APS_NEXT_RESOURCE_VALUE        140
#define _APS_NEXT_COMMAND_VALUE         32786
#define _APS_NEXT_CONTROL_VALUE         1035
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


STDAFX.CPP

// stdafx.cpp : source file that includes just the standard includes
//Chat2000.pch will be the pre-compiled header
//stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"


STDAFX.H

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#define VC_EXTRALEAN// Exclude rarely-used stuff from Windows headers

#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions
#include <afxdisp.h>        // MFC OLE automation classes
#include <afxtempl.h>// MFC support for collection classes <CList, CArray...>
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>// MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT


TOPICDLG.CPP

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       TopicDlg.cpp : implementation file
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "stdafx.h"
#include "chat2000.h"
#include "TopicDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CTopicDlg dialog

CTopicDlg::CTopicDlg(CWnd* pParent /*=NULL*/)
: CDialog(CTopicDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CTopicDlg)
m_szTopic = _T("");
//}}AFX_DATA_INIT
}

void CTopicDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CTopicDlg)
DDX_Text(pDX, IDC_TOPIC, m_szTopic);
DDV_MaxChars(pDX, m_szTopic, 95);  //room topic max size is 95 chars
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTopicDlg, CDialog)
//{{AFX_MSG_MAP(CTopicDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTopicDlg message handlers


TOPICDLG.H

//****************************************************************************
//  Module:     Chat2000.EXE
//  File:       TopicDlg.h : header file
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

/////////////////////////////////////////////////////////////////////////////
// CTopicDlg dialog

class CTopicDlg : public CDialog
{
// Construction
public:
CTopicDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
//{{AFX_DATA(CTopicDlg)
enum { IDD = IDD_ROOM_TOPIC };
CStringm_szTopic;
//}}AFX_DATA

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTopicDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:

// Generated message map functions
//{{AFX_MSG(CTopicDlg)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};


README.TXT

------------------------------------------------------------------
    Microsoft Chat SDK 1.1 ChatAd Readme File
                         August 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE CHATAD SAMPLE
BUILDING THE CHATAD SAMPLE

OVERVIEW OF THE CHATAD SAMPLE
==============================
This is a simple Visual Basic Application which provides monitoring
capability for administrators.

After building the sample, you can enter a chat session by clicking the Enter
Room button. You will need to enter a chat server, room name and alias name in
the Enter Room dialog box.

This sample demonstrates the usage of the following functionality of the
Chat Control:

* Assigning limits to how many people can chat in the room
* Setting the topic of the room
* Changing user settings to spectator, host and participant
* Ignoring an user
* Kicking, banning and reinstating an user
* Clearing the history of the chat
* Denying whispers in the room
* Getting the real name of a user
* Changing the user interface options of the control

BUILDING THE CHATAD SAMPLE
===========================

The chatad sample can be built with Visual Basic 4.0.

1. Select the Open project command from the File menu.

2. Open the admin.vbp file and build the application.


ENTEROPT.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     CHATAD.EXE
'  File:       EnterOpt.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form EnterOptions
   BackColor       =   &H00C0FFC0&
   BorderStyle     =   3  'Fixed Dialog
   Caption         =   "Enter Room Options"
   ClientHeight    =   5775
   ClientLeft      =   1785
   ClientTop       =   3300
   ClientWidth     =   6870
   Height          =   6180
   Left            =   1725
   LinkTopic       =   "EnterOptions"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   ScaleHeight     =   5775
   ScaleWidth      =   6870
   ShowInTaskbar   =   0   'False
   Top             =   2955
   Width           =   6990
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Default         =   -1  'True
      Height          =   375
      Left            =   4320
      TabIndex        =   26
      Top             =   5280
      Width           =   1095
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   5640
      TabIndex        =   25
      Top             =   5280
      Width           =   1095
   End
   Begin VB.ComboBox ComboSecPackages
      Height          =   315
      Left            =   4800
      TabIndex        =   24
      Top             =   1200
      Width           =   1935
   End
   Begin VB.Frame Frame2
      BackColor       =   &H00C0FFC0&
      Height          =   2415
      Left            =   120
      TabIndex        =   11
      Top             =   2640
      Width           =   6615
      Begin VB.CheckBox chkAuthTalk
         BackColor       =   &H00C0FFC0&
         Caption         =   "Only Authenticated Members Can Talk?"
         Height          =   195
         Left            =   2880
         TabIndex        =   23
         Top             =   2040
         Width           =   3615
      End
      Begin VB.CheckBox chkModerated
         BackColor       =   &H00C0FFC0&
         Caption         =   "Moderated Room?"
         Height          =   195
         Left            =   2880
         TabIndex        =   22
         Top             =   1320
         Width           =   3615
      End
      Begin VB.CheckBox chkAuthOnly
         BackColor       =   &H00C0FFC0&
         Caption         =   "Accept Authenticated Members Only?"
         Height          =   195
         Left            =   2880
         TabIndex        =   21
         Top             =   1680
         Width           =   3615
      End
      Begin VB.CheckBox chkAllowWhispers
         BackColor       =   &H00C0FFC0&
         Caption         =   "Allow Whispers in Room?"
         Height          =   195
         Left            =   2880
         TabIndex        =   20
         Top             =   600
         Value           =   1  'Checked
         Width           =   3615
      End
      Begin VB.CheckBox chkTopicRO
         BackColor       =   &H00C0FFC0&
         Caption         =   "Topic read only for non-hosts?"
         Height          =   195
         Left            =   120
         TabIndex        =   19
         Top             =   960
         Width           =   2655
      End
      Begin VB.CheckBox chkLocal
         BackColor       =   &H00C0FFC0&
         Caption         =   "Local Room?"
         Height          =   195
         Left            =   120
         TabIndex        =   18
         Top             =   1680
         Width           =   2655
      End
      Begin VB.CheckBox chkNoRemote
         BackColor       =   &H00C0FFC0&
         Caption         =   "NoRemote Room?"
         Height          =   195
         Left            =   120
         TabIndex        =   17
         Top             =   1320
         Width           =   2655
      End
      Begin VB.CheckBox chkMicOnly
         BackColor       =   &H00C0FFC0&
         Caption         =   "MIC Only Room?"
         Height          =   195
         Left            =   120
         TabIndex        =   16
         Top             =   240
         Width           =   2655
      End
      Begin VB.CheckBox chkPublic
         BackColor       =   &H00C0FFC0&
         Caption         =   "Public Room?"
         Height          =   195
         Left            =   120
         TabIndex        =   15
         Top             =   600
         Width           =   2655
      End
      Begin VB.CheckBox chkFeed
         BackColor       =   &H00C0FFC0&
         Caption         =   "Feed Room?"
         Height          =   195
         Left            =   120
         TabIndex        =   14
         Top             =   2040
         Width           =   2655
      End
      Begin VB.CheckBox chkCreateJoin
         BackColor       =   &H00C0FFC0&
         Caption         =   "Join if room exists?"
         Height          =   195
         Left            =   2880
         TabIndex        =   13
         Top             =   240
         Value           =   1  'Checked
         Width           =   3615
      End
      Begin VB.CheckBox chkAuditorium
         BackColor       =   &H00C0FFC0&
         Caption         =   "Auditorium Room?"
         Height          =   195
         Left            =   2880
         TabIndex        =   12
         Top             =   960
         Width           =   3615
      End
   End
   Begin VB.TextBox txtTopic
      Height          =   285
      Left            =   120
      TabIndex        =   9
      Top             =   2040
      Width           =   3975
   End
   Begin VB.CommandButton cmdGetTopic
      Caption         =   "Get Topic"
      Height          =   255
      Left            =   2160
      TabIndex        =   8
      Top             =   1800
      Width           =   975
   End
   Begin VB.CommandButton cmdSetTopic
      Caption         =   "Set Topic"
      Height          =   255
      Left            =   3120
      TabIndex        =   7
      Top             =   1800
      Width           =   975
   End
   Begin VB.TextBox txtRoomPwd
      Height          =   285
      Left            =   4800
      TabIndex        =   2
      Top             =   360
      Width           =   1935
   End
   Begin VB.ComboBox ComboRoomPath
      Height          =   315
      Left            =   120
      TabIndex        =   1
      Top             =   1200
      Width           =   3975
   End
   Begin VB.ComboBox ComboAlias
      Height          =   315
      Left            =   120
      TabIndex        =   0
      Top             =   360
      Width           =   3975
   End
   Begin VB.Label Label13
      BackColor       =   &H00C0FFC0&
      Caption         =   "Type the chat room topic:"
      Height          =   255
      Left            =   120
      TabIndex        =   10
      Top             =   1800
      Width           =   2175
   End
   Begin VB.Label Label7
      BackColor       =   &H00C0FFC0&
      Caption         =   "Select or type your Alias:"
      Height          =   255
      Left            =   120
      TabIndex        =   6
      Top             =   120
      Width           =   3975
   End
   Begin VB.Label Label8
      BackColor       =   &H00C0FFC0&
      Caption         =   "Select or type the chat room:"
      Height          =   255
      Left            =   120
      TabIndex        =   5
      Top             =   960
      Width           =   3975
   End
   Begin VB.Label lblSecurityPackage
      BackColor       =   &H00C0FFC0&
      Caption         =   "Select or type the security packages to use:"
      Height          =   495
      Left            =   4800
      TabIndex        =   4
      Top             =   800
      Width           =   1935
   End
   Begin VB.Label lblRoomPwd
      BackColor       =   &H00C0FFC0&
      Caption         =   "Type the optional host key:"
      Height          =   255
      Left            =   4800
      TabIndex        =   3
      Top             =   120
      Width           =   1935
   End
End
Attribute VB_Name = "EnterOptions"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit

' Channel creation modes
Const OCX_CHANNEL_CREATE_JOIN = 1

Const OCX_CHANNEL_FLAG_LOCAL = 2     ' channel not exposed to other servers on net
Const OCX_CHANNEL_FLAG_FEED = 4      ' news feeds, stock quotes..
Const OCX_CHANNEL_FLAG_MICONLY = 8   ' NO IRC - NO NO NO IRC
Const OCX_CHANNEL_FLAG_AUTHONLY = 16 ' permit authenticated members only
Const OCX_CHANNEL_FLAG_AUTHTALK = 32 ' permit only authenticated members to talk

Const OCX_CHANNEL_PUBLIC = 1         ' open to anybody and anything
Const OCX_CHANNEL_TOPICOP = 2        ' topic can be changed only by Host
Const OCX_CHANNEL_NOREMOTE = 4       ' only allow users who logged into THIS server..to enter chat
Const OCX_CHANNEL_NOWHISPER = 8      ' no whispers or \msg allowed
Const OCX_CHANNEL_AUDITORIUM = 16    ' auditorium chat room
Const OCX_CHANNEL_MODERATED = 32     ' join as spectator by default

Dim nFlags As Integer
Dim nType As Integer

Private Sub cmdCancel_Click()
    Hide
End Sub

Private Sub cmdOK_Click()

    On Error Resume Next

    SetFlagsAndType

    cmdSetTopic_Click

    Main.lblAlias.Caption = ComboAlias.Text
    Main.lblRoomPath.Caption = ComboRoomPath.Text
    Main.MSChat1.EnterRoom ComboRoomPath.Text, txtRoomPwd.Text, ComboAlias.Text, ComboSecPackages.Text, nFlags, nType

    Hide
End Sub

Sub SetFlagsAndType()
    nFlags = 0
    nType = 0
    If (chkCreateJoin.Value = 1) Then
        nFlags = nFlags + OCX_CHANNEL_CREATE_JOIN
    End If
    If (chkLocal.Value = 1) Then
        nFlags = nFlags + OCX_CHANNEL_FLAG_LOCAL
    End If
    If (chkFeed.Value = 1) Then
        nFlags = nFlags + OCX_CHANNEL_FLAG_FEED
    End If
    If (chkMicOnly.Value = 1) Then
        nFlags = nFlags + OCX_CHANNEL_FLAG_MICONLY
    End If
    If (chkAuthOnly.Value = 1) Then
        nFlags = nFlags + OCX_CHANNEL_FLAG_AUTHONLY
    End If
    If (chkAuthTalk.Value = 1) Then
        nFlags = nFlags + OCX_CHANNEL_FLAG_AUTHTALK
    End If

    If (chkPublic.Value = 1) Then
        nType = nType + OCX_CHANNEL_PUBLIC
    End If
    If (chkTopicRO.Value = 1) Then
        nType = nType + OCX_CHANNEL_TOPICOP
    End If
    If (chkNoRemote.Value = 1) Then
        nType = nType + OCX_CHANNEL_NOREMOTE
    End If
    If (chkAllowWhispers.Value = 0) Then
        nType = nType + OCX_CHANNEL_NOWHISPER
    End If
    If (chkAuditorium.Value = 1) Then
        nType = nType + OCX_CHANNEL_AUDITORIUM
    End If
    If (chkModerated.Value = 1) Then
        nType = nType + OCX_CHANNEL_MODERATED
    End If
End Sub

Private Sub cmdSetTopic_Click()
    On Error Resume Next

    Main.MSChat1.RoomTopic = txtTopic.Text
End Sub

Private Sub Form_Load()
    ComboRoomPath.AddItem "//ComicSrv1.Microsoft.Com/#Comic_Chat"
    ComboRoomPath.AddItem "//ComicSrv1.Microsoft.Com/#Francais"
    ComboRoomPath.AddItem "//ComicSrv1.Microsoft.Com/#Newbies"
    ComboRoomPath.AddItem "//ComicSrv1.Microsoft.Com/#Singles_Chat"

    ComboRoomPath.AddItem "//irc.dal.net/#newbies"
    ComboRoomPath.AddItem "//irc.mtv.com/#newbies"
    ComboRoomPath.AddItem "//irc.eskimo.com/#newbies"
    ComboRoomPath.AddItem "//irc.univ-lyon1.fr/#newbies"

    ComboRoomPath.ListIndex = 0

    ComboAlias.AddItem "Nickname1"
    ComboAlias.AddItem "Nickname2"

    ComboAlias.ListIndex = 0

    ComboSecPackages.AddItem "ANON"
    ComboSecPackages.AddItem "ALL"
    ComboSecPackages.AddItem "MSN;DPA;NTLM;ANON"
    ComboSecPackages.AddItem "MSN;DPA;ANON"
    ComboSecPackages.AddItem "MSN;DPA"
    ComboSecPackages.AddItem "NTLM;ANON"
    ComboSecPackages.AddItem "NTLM"

    ComboSecPackages.ListIndex = 0
End Sub


KICKBAN.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     CHATAD.EXE
'  File:       KickBan.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form KickBan
   BackColor       =   &H00C0FFC0&
   Caption         =   "Kick, Ban and Re-instate Participant"
   ClientHeight    =   6690
   ClientLeft      =   2595
   ClientTop       =   1905
   ClientWidth     =   5745
   Height          =   7095
   Left            =   2535
   LinkTopic       =   "KickBan"
   ScaleHeight     =   6690
   ScaleWidth      =   5745
   Top             =   1560
   Width           =   5865
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Height          =   375
      Left            =   4440
      TabIndex        =   14
      Top             =   6240
      Width           =   1215
   End
   Begin VB.CommandButton cmdReinstate
      Caption         =   "Re-instate Participant"
      Height          =   375
      Left            =   3720
      TabIndex        =   13
      Top             =   5640
      Width           =   1935
   End
   Begin VB.TextBox txtAlias
      Height          =   405
      Left            =   120
      TabIndex        =   12
      Top             =   5040
      Width           =   5535
   End
   Begin VB.CommandButton cmdBanParticipant
      Caption         =   "Ban Participant"
      Height          =   375
      Left            =   1560
      TabIndex        =   10
      Top             =   5640
      Width           =   1935
   End
   Begin VB.TextBox txtKickReason
      Height          =   375
      Left            =   120
      TabIndex        =   5
      Top             =   2040
      Width           =   5535
   End
   Begin VB.CommandButton cmdKickParticipant
      Caption         =   "Kick Participant"
      Height          =   375
      Left            =   3720
      TabIndex        =   0
      Top             =   2640
      Width           =   1935
   End
   Begin VB.Line Line2
      X1              =   5640
      X2              =   120
      Y1              =   6120
      Y2              =   6120
   End
   Begin VB.Label Label9
      BackColor       =   &H00C0FFC0&
      Caption         =   "Participant Alias:"
      Height          =   255
      Left            =   120
      TabIndex        =   11
      Top             =   4800
      Width           =   1335
   End
   Begin VB.Label Label8
      BackColor       =   &H00C0FFC0&
      Caption         =   "2. Click the 'Ban Participant' or 'Re-instate Participant' button."
      Height          =   255
      Left            =   120
      TabIndex        =   9
      Top             =   4320
      Width           =   5535
   End
   Begin VB.Label Label7
      BackColor       =   &H00C0FFC0&
      Caption         =   "1. Type the alias of the participant to ban or re-instate."
      Height          =   255
      Left            =   120
      TabIndex        =   8
      Top             =   3840
      Width           =   5535
   End
   Begin VB.Label Label6
      BackColor       =   &H00C0FFC0&
      Caption         =   "To ban or re-instate a participant:"
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   700
         size            =   8.25
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   255
      Left            =   120
      TabIndex        =   7
      Top             =   3360
      Width           =   5535
   End
   Begin VB.Label Label5
      BackColor       =   &H00C0FFC0&
      Caption         =   "To kick a participant:"
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   700
         size            =   8.25
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   255
      Left            =   120
      TabIndex        =   6
      Top             =   120
      Width           =   5535
   End
   Begin VB.Line Line1
      X1              =   5640
      X2              =   120
      Y1              =   3120
      Y2              =   3120
   End
   Begin VB.Label Label4
      BackColor       =   &H00C0FFC0&
      Caption         =   "Kick Reason:"
      Height          =   255
      Left            =   120
      TabIndex        =   4
      Top             =   1800
      Width           =   1095
   End
   Begin VB.Label Label3
      BackColor       =   &H00C0FFC0&
      Caption         =   "3. Click the 'Kick Participant' button."
      Height          =   255
      Left            =   120
      TabIndex        =   3
      Top             =   1320
      Width           =   5535
   End
   Begin VB.Label Label2
      BackColor       =   &H00C0FFC0&
      Caption         =   "2. Enter the optinal kick reason."
      Height          =   255
      Left            =   120
      TabIndex        =   2
      Top             =   960
      Width           =   5535
   End
   Begin VB.Label Label1
      BackColor       =   &H00C0FFC0&
      Caption         =   "1. Select the participant you want to kick in the control's member list box."
      Height          =   255
      Left            =   120
      TabIndex        =   1
      Top             =   600
      Width           =   5535
   End
End
Attribute VB_Name = "KickBan"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Private Sub cmdBanParticipant_Click()
    On Error GoTo errBan
    If (txtAlias.Text <> "") Then
        Main.MSChat1.BanParticipant txtAlias.Text, True
    Else
        MsgBox "Please enter the alias of the participant to ban and try again."
    End If
    Exit Sub

errBan:
    Hide
End Sub

Private Sub cmdKickParticipant_Click()
    On Error GoTo errKick
    Main.MSChat1.KickParticipant -1, txtKickReason.Text
    Exit Sub

errKick:
    Hide
End Sub

Private Sub cmdOK_Click()
    Hide
End Sub

Private Sub Command1_Click()

End Sub

Private Sub cmdReinstate_Click()
    On Error GoTo errReinstate
    If (txtAlias.Text <> "") Then
        Main.MSChat1.BanParticipant txtAlias.Text, False
    Else
        MsgBox "Please enter the alias of the participant to re-instate and try again."
    End If
    Exit Sub

errReinstate:
    Hide
End Sub


ADMIN.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     CHATAD.EXE
'  File:       Admin.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form Main
   BackColor       =   &H00FFFFC0&
   Caption         =   "MS Chat Monitoring Tool"
   ClientHeight    =   8415
   ClientLeft      =   1125
   ClientTop       =   1845
   ClientWidth     =   11655
   Height          =   8820
   Left            =   1065
   LinkTopic       =   "Main"
   ScaleHeight     =   8415
   ScaleWidth      =   11655
   Top             =   1500
   Width           =   11775
   Begin VB.CommandButton cmdClearErr
      Caption         =   "Clear Err."
      Height          =   255
      Left            =   120
      TabIndex        =   47
      Top             =   8040
      Width           =   855
   End
   Begin VB.Frame Frame5
      BackColor       =   &H00FFFFC0&
      Caption         =   "Enter/Exit Room"
      Height          =   735
      Left            =   6600
      TabIndex        =   46
      Top             =   6120
      Width           =   4935
      Begin VB.CommandButton cmdExitRoom
         Caption         =   "Exit Room"
         Height          =   255
         Left            =   3360
         TabIndex        =   2
         Top             =   360
         Width           =   1455
      End
      Begin VB.CommandButton cmdCancelEntering
         Caption         =   "Cancel Entering"
         Height          =   255
         Left            =   1680
         TabIndex        =   1
         Top             =   360
         Width           =   1575
      End
      Begin VB.CommandButton cmdEnterRoom
         Caption         =   "Enter Room"
         Default         =   -1  'True
         Height          =   255
         Left            =   120
         TabIndex        =   0
         Top             =   360
         Width           =   1455
      End
   End
   Begin VB.Frame Frame4
      BackColor       =   &H00FFFFC0&
      Caption         =   "Ignore Participant"
      Height          =   975
      Left            =   6600
      TabIndex        =   44
      Top             =   4920
      Width           =   4935
      Begin VB.CommandButton cmdDoNotIgnore
         Caption         =   "Don't Ignore"
         Height          =   255
         Left            =   2520
         TabIndex        =   22
         Top             =   600
         Width           =   2175
      End
      Begin VB.CommandButton cmdIgnore
         Caption         =   "Ignore"
         Height          =   255
         Left            =   240
         TabIndex        =   21
         Top             =   600
         Width           =   2175
      End
      Begin VB.Label Label5
         BackColor       =   &H00FFFFC0&
         Caption         =   "Ignore or not the selected member."
         Height          =   255
         Left            =   1320
         TabIndex        =   45
         Top             =   240
         Width           =   2535
      End
   End
   Begin VB.Frame Frame3
      BackColor       =   &H00FFFFC0&
      Caption         =   "Participant Privileges"
      Height          =   1095
      Left            =   6600
      TabIndex        =   42
      Top             =   3600
      Width           =   4935
      Begin VB.CommandButton cmdMakeHost
         Caption         =   "Make Host"
         Height          =   255
         Left            =   3360
         TabIndex        =   20
         Top             =   720
         Width           =   1455
      End
      Begin VB.CommandButton cmdMakeParticipant
         Caption         =   "Make Participant"
         Height          =   255
         Left            =   1800
         TabIndex        =   19
         Top             =   720
         Width           =   1455
      End
      Begin VB.CommandButton cmdMakeSpectator
         Caption         =   "Make Spectator"
         Height          =   255
         Left            =   120
         TabIndex        =   18
         Top             =   720
         Width           =   1575
      End
      Begin VB.Label Label4
         BackColor       =   &H00FFFFC0&
         Caption         =   "Change the status of the selected member."
         Height          =   255
         Left            =   1080
         TabIndex        =   43
         Top             =   360
         Width           =   3255
      End
   End
   Begin VB.Frame Frame1
      BackColor       =   &H00FFFFC0&
      Caption         =   "Participant Real Name"
      Height          =   2295
      Left            =   6600
      TabIndex        =   36
      Top             =   1080
      Width           =   4935
      Begin VB.CommandButton cmdGetRealName
         Caption         =   "Get Real Name"
         Height          =   975
         Left            =   4080
         TabIndex        =   17
         Top             =   480
         Width           =   735
      End
      Begin VB.Frame Frame2
         BackColor       =   &H00FFFFC0&
         Caption         =   "Get Real Name from"
         Height          =   1095
         Left            =   120
         TabIndex        =   37
         Top             =   360
         Width           =   3735
         Begin VB.TextBox txtAlias
            Enabled         =   0   'False
            Height          =   285
            Left            =   1320
            TabIndex        =   16
            Top             =   720
            Width           =   2295
         End
         Begin VB.OptionButton optAlias
            BackColor       =   &H00FFFFC0&
            Caption         =   "This Alias"
            Height          =   255
            Left            =   120
            TabIndex        =   15
            Top             =   720
            Width           =   1095
         End
         Begin VB.OptionButton optSelection
            BackColor       =   &H00FFFFC0&
            Caption         =   "Selected member"
            Height          =   255
            Left            =   120
            TabIndex        =   14
            Top             =   360
            Value           =   -1  'True
            Width           =   1815
         End
      End
      Begin VB.Label lblAsyncRealName
         Height          =   255
         Left            =   1680
         TabIndex        =   41
         Top             =   1920
         Width           =   3135
      End
      Begin VB.Label lblAsyncAlias
         Height          =   255
         Left            =   120
         TabIndex        =   40
         Top             =   1920
         Width           =   1335
      End
      Begin VB.Label Label2
         BackColor       =   &H00FFFFC0&
         Caption         =   "Real Name:"
         Height          =   255
         Left            =   1680
         TabIndex        =   39
         Top             =   1680
         Width           =   1215
      End
      Begin VB.Label Label1
         BackColor       =   &H00FFFFC0&
         Caption         =   "Alias:"
         Height          =   255
         Left            =   120
         TabIndex        =   38
         Top             =   1680
         Width           =   735
      End
   End
   Begin VB.CommandButton cmdKickBan
      Caption         =   "Kick, Ban and Re-instate Participant"
      Height          =   255
      Left            =   6600
      TabIndex        =   6
      Top             =   7560
      Width           =   3255
   End
   Begin VB.CommandButton cmdChangeControlUI
      Caption         =   "Change Control UI"
      Height          =   255
      Left            =   9840
      TabIndex        =   5
      Top             =   7080
      Width           =   1695
   End
   Begin VB.CommandButton cmdSetMaxMembers
      Caption         =   "Set MaxMembers"
      Height          =   255
      Left            =   3240
      TabIndex        =   12
      Top             =   1800
      Width           =   1455
   End
   Begin VB.CommandButton cmdGetMaxMembers
      Caption         =   "Get MaxMembers"
      Height          =   255
      Left            =   4800
      TabIndex        =   13
      Top             =   1800
      Width           =   1455
   End
   Begin VB.TextBox txtMaxMembers
      Height          =   285
      Left            =   1680
      TabIndex        =   11
      Top             =   1800
      Width           =   975
   End
   Begin VB.TextBox txtError
      BackColor       =   &H000080FF&
      Height          =   285
      Left            =   1080
      TabIndex        =   31
      Top             =   8040
      Width           =   10455
   End
   Begin VB.TextBox txtState
      BackColor       =   &H000000C0&
      Height          =   285
      Left            =   10440
      TabIndex        =   30
      Text            =   "Disconnected"
      Top             =   360
      Width           =   1095
   End
   Begin VB.CommandButton cmdGetMemberCount
      Caption         =   "Get MemberCount"
      Height          =   255
      Left            =   4800
      TabIndex        =   10
      Top             =   1440
      Width           =   1455
   End
   Begin VB.TextBox txtMemberCount
      Height          =   285
      Left            =   1680
      TabIndex        =   9
      Top             =   1440
      Width           =   975
   End
   Begin VB.CommandButton cmdWhisperNoWhisper
      Caption         =   "No/Whisper"
      Height          =   255
      Left            =   6600
      TabIndex        =   3
      Top             =   7080
      Width           =   1455
   End
   Begin VB.CommandButton cmdSetTopic
      Caption         =   "Set Topic"
      Height          =   255
      Left            =   4800
      TabIndex        =   8
      Top             =   1080
      Width           =   1455
   End
   Begin VB.TextBox txtTopic
      Height          =   285
      Left            =   1680
      TabIndex        =   7
      Top             =   1080
      Width           =   3015
   End
   Begin VB.CommandButton cmdClearHistory
      Caption         =   "Clear History"
      Height          =   255
      Left            =   8280
      TabIndex        =   4
      Top             =   7080
      Width           =   1335
   End
   Begin VB.CommandButton cmdExit
      Caption         =   "Exit"
      Height          =   255
      Left            =   10080
      TabIndex        =   23
      Top             =   7560
      Width           =   1455
   End
   Begin MSChatObjectsCtl.MSChat MSChat1
      Height          =   5775
      Left            =   120
      TabIndex        =   48
      Top             =   2160
      Width           =   6375
      _ExtentX        =   11245
      _ExtentY        =   10186
      BorderStyle     =   0
      UIOption        =   4095
      BackColor       =   192
   End
   Begin VB.Label lblRoomPath
      Height          =   255
      Left            =   4800
      TabIndex        =   35
      Top             =   360
      Width           =   5175
   End
   Begin VB.Label lblUserName
      Height          =   255
      Left            =   2520
      TabIndex        =   34
      Top             =   360
      Width           =   1815
   End
   Begin VB.Label lblAlias
      Height          =   255
      Left            =   120
      TabIndex        =   33
      Top             =   360
      Width           =   1935
   End
   Begin VB.Label lblMaxMembers
      Alignment       =   1  'Right Justify
      BackColor       =   &H00FFFFC0&
      Caption         =   "MaxMembers"
      Height          =   255
      Left            =   480
      TabIndex        =   32
      Top             =   1800
      Width           =   975
   End
   Begin VB.Label Label18
      BackColor       =   &H00FFFFC0&
      Caption         =   "State"
      Height          =   255
      Left            =   10440
      TabIndex        =   29
      Top             =   120
      Width           =   615
   End
   Begin VB.Label Label15
      Alignment       =   1  'Right Justify
      BackColor       =   &H00FFFFC0&
      Caption         =   "Member Count"
      Height          =   255
      Left            =   360
      TabIndex        =   28
      Top             =   1440
      Width           =   1095
   End
   Begin VB.Label Label13
      Alignment       =   1  'Right Justify
      BackColor       =   &H00FFFFC0&
      Caption         =   "Chat Room Topic"
      Height          =   255
      Left            =   120
      TabIndex        =   27
      Top             =   1080
      Width           =   1335
   End
   Begin VB.Label Label10
      BackColor       =   &H00FFFFC0&
      Caption         =   "Name"
      Height          =   255
      Left            =   2520
      TabIndex        =   26
      Top             =   120
      Width           =   1815
   End
   Begin VB.Label Label8
      BackColor       =   &H00FFFFC0&
      Caption         =   "Room Path"
      Height          =   255
      Left            =   4800
      TabIndex        =   25
      Top             =   120
      Width           =   5175
   End
   Begin VB.Label Label7
      BackColor       =   &H00FFFFC0&
      Caption         =   "Alias "
      Height          =   255
      Left            =   120
      TabIndex        =   24
      Top             =   120
      Width           =   1935
   End
End
Attribute VB_Name = "Main"
Attribute VB_Creatable = False
Attribute VB_Exposed = False

Option Explicit

Const MASK_PRIVILEGES = 1
Const MASK_NOWHISPER = 2
Const MASK_IGNORED = 4

Const STAT_HOST = 1
Const STAT_PARTICIPANT = 2
Const STAT_SPECTATOR = 4
Const STAT_NOWHISPER = 8
Const STAT_IGNORED = 16

Const CAP_NOWHISPER = "Deny Whispers"
Const CAP_WHISPER = "Accept Whispers"

Sub SetState(NewState As Integer)
    Select Case NewState
    Case 1:
        cmdEnterRoom.Enabled = True
        cmdCancelEntering.Enabled = False
        cmdExitRoom.Enabled = False
        txtState.Text = "Disconnected"
        txtState.BackColor = &HC0&
        MSChat1.BackColor = &HC0&
    Case 2:
        cmdEnterRoom.Enabled = False
        cmdCancelEntering.Enabled = True
        cmdExitRoom.Enabled = False
        txtState.Text = "Connecting..."
        txtState.BackColor = &H80FF&
        MSChat1.BackColor = &H80FF&
    Case 3:
        cmdEnterRoom.Enabled = False
        cmdCancelEntering.Enabled = False
        cmdExitRoom.Enabled = True
        txtState.Text = "Connected"
        txtState.BackColor = &HC000&
        MSChat1.BackColor = &HC000&
    End Select
End Sub

Private Sub cmdChangeControlUI_Click()
    UISettings.Show
End Sub

Private Sub cmdClearErr_Click()
    txtError.Text = ""
End Sub

Private Sub cmdGetMaxMembers_Click()
    On Error Resume Next
    txtMaxMembers.Text = CStr(MSChat1.MaxMembers)
End Sub

Private Sub cmdGetRealName_Click()
    On Error Resume Next

    Dim txtReal As String

    lblAsyncAlias.Caption = ""
    lblAsyncRealName.Caption = ""

    If (optSelection.Value) Then
        MSChat1.GetParticipantRealName -1, "", False, txtReal
    Else
        MSChat1.GetParticipantRealName 0, txtAlias.Text, False, txtReal
    End If
End Sub

Private Sub cmdGetMemberCount_Click()
    On Error Resume Next
    txtMemberCount.Text = CStr(MSChat1.MemberCount)
End Sub

Private Sub cmdKickBan_Click()
    KickBan.Show
End Sub

Private Sub cmdMakeHost_Click()
    On Error Resume Next
    MSChat1.SetParticipantStatus -1, MASK_PRIVILEGES, STAT_HOST
End Sub

Private Sub cmdMakeParticipant_Click()
    On Error Resume Next
    MSChat1.SetParticipantStatus -1, MASK_PRIVILEGES, STAT_PARTICIPANT
End Sub

Private Sub cmdMakeSpectator_Click()
    On Error Resume Next
    MSChat1.SetParticipantStatus -1, MASK_PRIVILEGES, STAT_SPECTATOR
End Sub

Private Sub cmdSetMaxMembers_Click()
    On Error Resume Next
    MSChat1.MaxMembers = CLng(txtMaxMembers.Text)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Unload UISettings
    Unload EnterOptions
    Unload KickBan
End Sub

Private Sub MSChat1_OnParticipantRealName(ByVal Alias As String, ByVal RealName As String)
    On Error Resume Next
    lblAsyncAlias.Caption = Alias
    lblAsyncRealName.Caption = RealName
End Sub

Private Sub MSChat1_OnRoomTopicChanged(ByVal NewRoomTopic As String)
    On Error Resume Next
    txtTopic.Text = NewRoomTopic
End Sub

Private Sub MSChat1_OnStateChanged(ByVal NewState As Integer)
    SetState NewState
End Sub

Private Sub cmdCancelEntering_Click()
    On Error Resume Next
    MSChat1.CancelEntering
End Sub

Private Sub cmdClearHistory_Click()
    On Error Resume Next
    MSChat1.ClearHistory
End Sub

Private Sub cmdDoNotIgnore_Click()
    On Error Resume Next
    MSChat1.SetParticipantStatus -1, MASK_IGNORED, 0
End Sub

Private Sub cmdEnterRoom_Click()
    EnterOptions.Show
End Sub

Private Sub cmdExit_Click()
    End
End Sub

Private Sub cmdExitRoom_Click()
    On Error Resume Next
    MSChat1.ExitRoom
End Sub

Private Sub cmdIgnore_Click()
    On Error Resume Next
    MSChat1.SetParticipantStatus -1, MASK_IGNORED, STAT_IGNORED
End Sub

Private Sub cmdSetTopic_Click()
    On Error Resume Next
    MSChat1.RoomTopic = txtTopic.Text
End Sub

Private Sub cmdWhisperNoWhisper_Click()
    On Error Resume Next
    If (cmdWhisperNoWhisper.Caption = CAP_NOWHISPER) Then
        MSChat1.SetParticipantStatus MSChat1.ThisParticipantID, MASK_NOWHISPER, STAT_NOWHISPER
        cmdWhisperNoWhisper.Caption = CAP_WHISPER
    Else
        MSChat1.SetParticipantStatus MSChat1.ThisParticipantID, MASK_NOWHISPER, 0
        cmdWhisperNoWhisper.Caption = CAP_NOWHISPER
    End If
End Sub
Private Sub Form_Load()
    cmdWhisperNoWhisper.Caption = CAP_NOWHISPER
End Sub

Private Sub MSChat1_OnEndEnumeration()

    On Error Resume Next
    txtTopic.Text = MSChat1.RoomTopic
    lblUserName.Caption = MSChat1.ThisParticipantName
    lblRoomPath.Caption = MSChat1.RoomPath

    cmdGetMemberCount_Click
    cmdGetMaxMembers_Click
End Sub

Private Sub MSChat1_OnError(ByVal ErrorCode As Integer, ByVal Description As String)
    On Error Resume Next
    txtError.Text = "MSChat1 error " + CStr(ErrorCode) + ", Desc: " + Description
End Sub

Private Sub optAlias_Click()
    txtAlias.Enabled = True
End Sub

Private Sub optSelection_Click()
    txtAlias.Enabled = False
End Sub


UISET.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     CHATAD.EXE
'  File:       UISet.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form UISettings
   BackColor       =   &H00C0FFC0&
   Caption         =   "MSChat Control UI Setting"
   ClientHeight    =   5070
   ClientLeft      =   6840
   ClientTop       =   2730
   ClientWidth     =   4800
   Height          =   5475
   Left            =   6780
   LinkTopic       =   "UISettings"
   ScaleHeight     =   5070
   ScaleWidth      =   4800
   Top             =   2385
   Width           =   4920
   Begin VB.CommandButton cmdApply
      Caption         =   "Apply"
      Height          =   375
      Left            =   3480
      TabIndex        =   14
      Top             =   4560
      Width           =   1215
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   2160
      TabIndex        =   13
      Top             =   4560
      Width           =   1215
   End
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Default         =   -1  'True
      Height          =   375
      Left            =   840
      TabIndex        =   0
      Top             =   4560
      Width           =   1215
   End
   Begin VB.Frame Frame1
      BackColor       =   &H00C0FFC0&
      Height          =   4215
      Left            =   120
      TabIndex        =   1
      Top             =   120
      Width           =   4575
      Begin VB.CheckBox chkBut
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Send Button?"
         Height          =   255
         Left            =   120
         TabIndex        =   12
         TabStop         =   0   'False
         Top             =   3480
         Width           =   4335
      End
      Begin VB.CheckBox chkMem
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Member ListBox?"
         Height          =   255
         Left            =   120
         TabIndex        =   11
         TabStop         =   0   'False
         Top             =   2400
         Width           =   4335
      End
      Begin VB.CheckBox chkRec
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Receive TextBox?"
         Height          =   255
         Left            =   120
         TabIndex        =   10
         TabStop         =   0   'False
         Top             =   2760
         Width           =   4335
      End
      Begin VB.CheckBox chkSen
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Send TextBox?"
         Height          =   255
         Left            =   120
         TabIndex        =   9
         TabStop         =   0   'False
         Top             =   3840
         Width           =   4335
      End
      Begin VB.CheckBox chkDisplayError
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display error messages?"
         Height          =   255
         Left            =   120
         TabIndex        =   8
         Top             =   1680
         Width           =   4335
      End
      Begin VB.CheckBox chkSensitiveURLs
         BackColor       =   &H00C0FFC0&
         Caption         =   "Sensitive URLs in history window?"
         Height          =   255
         Left            =   120
         TabIndex        =   7
         Top             =   2040
         Width           =   4335
      End
      Begin VB.CheckBox chkMemChg
         BackColor       =   &H00C0FFC0&
         Caption         =   "Inform when privilege, alias changed or member kicked?"
         Height          =   255
         Left            =   120
         TabIndex        =   6
         Top             =   1320
         Width           =   4335
      End
      Begin VB.CheckBox chkBlank
         BackColor       =   &H00C0FFC0&
         Caption         =   "Insert blank line between revd messages?"
         Height          =   255
         Left            =   120
         TabIndex        =   5
         TabStop         =   0   'False
         Top             =   240
         Width           =   4335
      End
      Begin VB.CheckBox chkEnter
         BackColor       =   &H00C0FFC0&
         Caption         =   "Inform when participant enters the room?"
         Height          =   255
         Left            =   120
         TabIndex        =   4
         TabStop         =   0   'False
         Top             =   600
         Width           =   4335
      End
      Begin VB.CheckBox chkLeave
         BackColor       =   &H00C0FFC0&
         Caption         =   "Inform when participant leaves the room?"
         Height          =   255
         Left            =   120
         TabIndex        =   3
         TabStop         =   0   'False
         Top             =   960
         Width           =   4335
      End
      Begin VB.CheckBox chkWhi
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Whisper Button?"
         Height          =   255
         Left            =   120
         TabIndex        =   2
         TabStop         =   0   'False
         Top             =   3120
         Width           =   4335
      End
   End
End
Attribute VB_Name = "UISettings"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit

Const SEND_BUTTON = 1
Const WHISPER_BUTTON = 2
Const PARTICIPANT_LISTBOX = 4
Const RECEIVE_TEXTBOX = 8
Const SEND_TEXTBOX = 16
Const INFORM_JOIN = 32
Const INFORM_LEAVE = 64
Const INFORM_MEMCHG = 128
Const INSERT_BLANK = 256
Const DISPLAY_ERRORMESSAGES = 512
Const ALLOW_RESIZING = 1024
Const SENSITIVE_URLS = 2048

Dim nUIOption As Integer
Dim nOriginalUIOption As Integer
Sub UpdateUIOption()

    On Error GoTo UpdateUIOptionErr

    nUIOption = chkBut.Value * SEND_BUTTON + chkWhi.Value * WHISPER_BUTTON + chkMem.Value * PARTICIPANT_LISTBOX + chkRec.Value * RECEIVE_TEXTBOX + chkSen.Value * SEND_TEXTBOX + chkEnter.Value * INFORM_JOIN + chkLeave.Value * INFORM_LEAVE + chkMemChg.Value * INFORM_MEMCHG + chkBlank.Value * INSERT_BLANK + chkDisplayError.Value * DISPLAY_ERRORMESSAGES + chkSensitiveURLs.Value * SENSITIVE_URLS
    Exit Sub

UpdateUIOptionErr:
    MsgBox Err.Description
End Sub

Private Sub chkBlank_Click()
    UpdateUIOption
End Sub

Private Sub chkBut_Click()
    UpdateUIOption
End Sub

Private Sub chkDisplayError_Click()
    UpdateUIOption
End Sub

Private Sub chkEnter_Click()
    UpdateUIOption
End Sub

Private Sub chkLeave_Click()
    UpdateUIOption
End Sub

Private Sub chkMem_Click()
    UpdateUIOption
End Sub

Private Sub chkMemChg_Click()
    UpdateUIOption
End Sub

Private Sub chkRec_Click()
    UpdateUIOption
End Sub

Private Sub chkSen_Click()
    UpdateUIOption
End Sub

Private Sub chkSensitiveURLs_Click()
    UpdateUIOption
End Sub

Private Sub chkWhi_Click()
    UpdateUIOption
End Sub

Private Sub cmdApply_Click()
    On Error GoTo errApply_Click
    Main.MSChat1.UIOption = nUIOption
    Exit Sub

errApply_Click:
    Main.MSChat1.UIOption = nOriginalUIOption
End Sub

Private Sub cmdCancel_Click()
    Main.MSChat1.UIOption = nOriginalUIOption
    Hide
End Sub

Private Sub cmdOK_Click()
    On Error GoTo errOK_Click
    Main.MSChat1.UIOption = nUIOption
    Hide
    Exit Sub

errOK_Click:
    Main.MSChat1.UIOption = nOriginalUIOption
End Sub

Private Sub Form_Load()

    nOriginalUIOption = Main.MSChat1.UIOption
    If (nOriginalUIOption And SEND_BUTTON) Then
        chkBut.Value = 1
    Else
        chkBut.Value = 0
    End If
    If (nOriginalUIOption And WHISPER_BUTTON) Then
        chkWhi.Value = 1
    Else
        chkWhi.Value = 0
    End If
    If (nOriginalUIOption And PARTICIPANT_LISTBOX) Then
        chkMem.Value = 1
    Else
        chkMem.Value = 0
    End If
    If (nOriginalUIOption And RECEIVE_TEXTBOX) Then
        chkRec.Value = 1
    Else
        chkRec.Value = 0
    End If
    If (nOriginalUIOption And SEND_TEXTBOX) Then
        chkSen.Value = 1
    Else
        chkSen.Value = 0
    End If
    If (nOriginalUIOption And INFORM_JOIN) Then
        chkEnter.Value = 1
    Else
        chkEnter.Value = 0
    End If
    If (nOriginalUIOption And INFORM_LEAVE) Then
        chkLeave.Value = 1
    Else
        chkLeave.Value = 0
    End If
    If (nOriginalUIOption And INFORM_MEMCHG) Then
        chkMemChg.Value = 1
    Else
        chkMemChg.Value = 0
    End If
    If (nOriginalUIOption And INSERT_BLANK) Then
        chkBlank.Value = 1
    Else
        chkBlank.Value = 0
    End If
    If (nOriginalUIOption And DISPLAY_ERRORMESSAGES) Then
        chkDisplayError.Value = 1
    Else
        chkDisplayError.Value = 0
    End If
    If (nOriginalUIOption And SENSITIVE_URLS) Then
        chkSensitiveURLs.Value = 1
    Else
        chkSensitiveURLs.Value = 0
    End If
End Sub


README.TXT

------------------------------------------------------------------
    Microsoft Chat SDK 1.1 Checkrs Readme File
                         August 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE CHECKRS SAMPLE
BUILDING THE CHECKRS SAMPLE

OVERVIEW OF THE CHECKRS SAMPLE
==============================
This is a Visual Basic Application which enables you to play Checkers
game with the people you are chatting with.

After building the sample, you can enter a chat session by clicking on the Enter
Room button. You will need to enter a chat server, room name and alias name in the
Enter Room dialog box.

BUILDING THE CHECKRS SAMPLE
===========================

The checkrs sample can be built with Visual Basic 4.0.

1. Select the Open project command from the File menu.

2. Open the checker.vbp file and build the application.


CHECKER.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     CHECKER.EXE
'  File:       Checker.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form Main
   BackColor       =   &H00FFFFC0&
   Caption         =   "MS Chat Monitoring Tool"
   ClientHeight    =   8760
   ClientLeft      =   135
   ClientTop       =   1740
   ClientWidth     =   11850
   Height          =   9165
   Left            =   75
   LinkTopic       =   "Main"
   ScaleHeight     =   8760
   ScaleWidth      =   11850
   Top             =   1395
   Width           =   11970
   Begin VB.Frame Frame5
      BackColor       =   &H00FFFFC0&
      Caption         =   "Enter/Exit Room"
      Height          =   735
      Left            =   6840
      TabIndex        =   30
      Top             =   4560
      Width           =   4935
      Begin VB.CommandButton cmdExitRoom
         Caption         =   "Exit Room"
         Height          =   255
         Left            =   3360
         TabIndex        =   2
         Top             =   360
         Width           =   1455
      End
      Begin VB.CommandButton cmdCancelEntering
         Caption         =   "Cancel Entering"
         Height          =   255
         Left            =   1680
         TabIndex        =   1
         Top             =   360
         Width           =   1575
      End
      Begin VB.CommandButton cmdEnterRoom
         Caption         =   "Enter Room"
         Default         =   -1  'True
         Height          =   255
         Left            =   120
         TabIndex        =   0
         Top             =   360
         Width           =   1455
      End
   End
   Begin VB.Frame Frame4
      BackColor       =   &H00FFFFC0&
      Caption         =   "Ignore Participant"
      Height          =   975
      Left            =   6840
      TabIndex        =   28
      Top             =   3480
      Width           =   4935
      Begin VB.CommandButton cmdDoNotIgnore
         Caption         =   "Don't Ignore"
         Height          =   255
         Left            =   2520
         TabIndex        =   12
         Top             =   600
         Width           =   2175
      End
      Begin VB.CommandButton cmdIgnore
         Caption         =   "Ignore"
         Height          =   255
         Left            =   240
         TabIndex        =   11
         Top             =   600
         Width           =   2175
      End
      Begin VB.Label Label5
         BackColor       =   &H00FFFFC0&
         Caption         =   "Ignore or not the selected member."
         Height          =   255
         Left            =   1320
         TabIndex        =   29
         Top             =   240
         Width           =   2535
      End
   End
   Begin VB.Frame Frame1
      BackColor       =   &H00FFFFC0&
      Caption         =   "Participant Real Name"
      Height          =   2295
      Left            =   6840
      TabIndex        =   22
      Top             =   1080
      Width           =   4935
      Begin VB.CommandButton cmdGetRealName
         Caption         =   "Get Real Name"
         Height          =   975
         Left            =   4080
         TabIndex        =   10
         Top             =   480
         Width           =   735
      End
      Begin VB.Frame Frame2
         BackColor       =   &H00FFFFC0&
         Caption         =   "Get Real Name from"
         Height          =   1095
         Left            =   120
         TabIndex        =   23
         Top             =   360
         Width           =   3735
         Begin VB.TextBox txtAlias
            Enabled         =   0   'False
            Height          =   285
            Left            =   1320
            TabIndex        =   9
            Top             =   720
            Width           =   2295
         End
         Begin VB.OptionButton optAlias
            BackColor       =   &H00FFFFC0&
            Caption         =   "This Alias"
            Height          =   255
            Left            =   120
            TabIndex        =   8
            Top             =   720
            Width           =   1095
         End
         Begin VB.OptionButton optSelection
            BackColor       =   &H00FFFFC0&
            Caption         =   "Selected member"
            Height          =   255
            Left            =   120
            TabIndex        =   7
            Top             =   360
            Value           =   -1  'True
            Width           =   1815
         End
      End
      Begin VB.Label lblAsyncRealName
         Height          =   255
         Left            =   1680
         TabIndex        =   27
         Top             =   1920
         Width           =   3135
      End
      Begin VB.Label lblAsyncAlias
         Height          =   255
         Left            =   120
         TabIndex        =   26
         Top             =   1920
         Width           =   1335
      End
      Begin VB.Label Label2
         BackColor       =   &H00FFFFC0&
         Caption         =   "Real Name:"
         Height          =   255
         Left            =   1680
         TabIndex        =   25
         Top             =   1680
         Width           =   1215
      End
      Begin VB.Label Label1
         BackColor       =   &H00FFFFC0&
         Caption         =   "Alias:"
         Height          =   255
         Left            =   120
         TabIndex        =   24
         Top             =   1680
         Width           =   735
      End
   End
   Begin VB.CommandButton cmdWhisperNoWhisper
      Caption         =   "No/Whisper"
      Height          =   255
      Left            =   6840
      TabIndex        =   3
      Top             =   5400
      Width           =   1455
   End
   Begin VB.CommandButton cmdSetTopic
      Caption         =   "Set Topic"
      Height          =   255
      Left            =   4800
      TabIndex        =   6
      Top             =   960
      Width           =   1455
   End
   Begin VB.TextBox txtTopic
      Height          =   285
      Left            =   1680
      TabIndex        =   5
      Top             =   960
      Width           =   3015
   End
   Begin VB.CommandButton cmdClearHistory
      Caption         =   "Clear History"
      Height          =   255
      Left            =   8640
      TabIndex        =   4
      Top             =   5400
      Width           =   1335
   End
   Begin VB.CommandButton cmdExit
      Caption         =   "Exit"
      Height          =   255
      Left            =   10320
      TabIndex        =   13
      Top             =   5400
      Width           =   1455
   End
   Begin MSChatObjectsCtl.MSChat MSChat1
      Height          =   2775
      Left            =   120
      TabIndex        =   70
      Top             =   5880
      Width           =   11655
      _ExtentX        =   20558
      _ExtentY        =   4895
      Appearance      =   0
      BorderStyle     =   0
      UIOption        =   3839
      BackColor       =   192
   End
   Begin VB.Label lblState
      Alignment       =   2  'Center
      BackColor       =   &H000000C0&
      Caption         =   "Disconnected"
      Height          =   255
      Left            =   10680
      TabIndex        =   69
      Top             =   360
      Width           =   1095
   End
   Begin VB.Label lblYourTurn
      Alignment       =   2  'Center
      BackColor       =   &H00C0FFFF&
      Caption         =   "Up to you!"
      Height          =   255
      Left            =   9000
      TabIndex        =   68
      Top             =   360
      Width           =   1335
   End
   Begin VB.Label Label12
      BackColor       =   &H00FFFFC0&
      Caption         =   "Your turn to play?"
      Height          =   255
      Left            =   9000
      TabIndex        =   67
      Top             =   120
      Width           =   1335
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   31
      Left            =   3370
      TabIndex        =   66
      Top             =   5240
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   30
      Left            =   2290
      TabIndex        =   65
      Top             =   5240
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   29
      Left            =   1210
      TabIndex        =   64
      Top             =   5240
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   28
      Left            =   140
      TabIndex        =   63
      Top             =   5240
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   27
      Left            =   3910
      TabIndex        =   62
      Top             =   4690
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   26
      Left            =   2840
      TabIndex        =   61
      Top             =   4690
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   25
      Left            =   1760
      TabIndex        =   60
      Top             =   4690
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   24
      Left            =   680
      TabIndex        =   59
      Top             =   4690
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   23
      Left            =   3370
      TabIndex        =   58
      Top             =   4160
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   22
      Left            =   2290
      TabIndex        =   57
      Top             =   4160
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   21
      Left            =   1210
      TabIndex        =   56
      Top             =   4160
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   20
      Left            =   140
      TabIndex        =   55
      Top             =   4160
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   19
      Left            =   3910
      TabIndex        =   54
      Top             =   3610
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   18
      Left            =   2840
      TabIndex        =   53
      Top             =   3610
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   17
      Left            =   1760
      TabIndex        =   52
      Top             =   3610
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   16
      Left            =   675
      TabIndex        =   51
      Top             =   3610
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   15
      Left            =   3370
      TabIndex        =   50
      Top             =   3070
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   14
      Left            =   2290
      TabIndex        =   49
      Top             =   3070
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   13
      Left            =   1210
      TabIndex        =   48
      Top             =   3070
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   12
      Left            =   140
      TabIndex        =   47
      Top             =   3070
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   11
      Left            =   3910
      TabIndex        =   46
      Top             =   2540
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   10
      Left            =   2840
      TabIndex        =   45
      Top             =   2540
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   9
      Left            =   1760
      TabIndex        =   44
      Top             =   2540
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   8
      Left            =   675
      TabIndex        =   43
      Top             =   2540
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   7
      Left            =   3370
      TabIndex        =   42
      Top             =   2000
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   6
      Left            =   2290
      TabIndex        =   41
      Top             =   2000
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   5
      Left            =   1210
      TabIndex        =   40
      Top             =   2000
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   4
      Left            =   140
      TabIndex        =   39
      Top             =   2000
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   3
      Left            =   3910
      TabIndex        =   38
      Top             =   1460
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   2
      Left            =   2840
      TabIndex        =   37
      Top             =   1460
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   525
      Index           =   1
      Left            =   1760
      TabIndex        =   36
      Top             =   1460
      Width           =   525
   End
   Begin VB.Label Label11
      BackStyle       =   0  'Transparent
      Height          =   530
      Index           =   0
      Left            =   680
      TabIndex        =   35
      Top             =   1460
      Width           =   530
   End
   Begin VB.Label Label9
      BackStyle       =   0  'Transparent
      Height          =   615
      Left            =   4440
      TabIndex        =   34
      Top             =   3600
      Width           =   2175
   End
   Begin VB.Label Label6
      BackStyle       =   0  'Transparent
      Height          =   615
      Left            =   4440
      TabIndex        =   33
      Top             =   1440
      Width           =   2175
   End
   Begin VB.Label Label4
      Alignment       =   2  'Center
      BackColor       =   &H00FFFFFF&
      Caption         =   "Dead White Checkers"
      Height          =   255
      Left            =   4560
      TabIndex        =   32
      Top             =   3840
      Width           =   1935
   End
   Begin VB.Label Label3
      Alignment       =   2  'Center
      BackColor       =   &H00000000&
      Caption         =   "Dead Black Checkers"
      ForeColor       =   &H00FFFFFF&
      Height          =   255
      Left            =   4560
      TabIndex        =   31
      Top             =   1680
      Width           =   1935
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   11
      Left            =   4080
      Picture         =   "checker.frx":0000
      Tag             =   "1"
      Top             =   4200
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   10
      Left            =   3000
      Picture         =   "checker.frx":061A
      Tag             =   "1"
      Top             =   4200
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   9
      Left            =   1920
      Picture         =   "checker.frx":0C34
      Tag             =   "1"
      Top             =   4200
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   8
      Left            =   840
      Picture         =   "checker.frx":124E
      Tag             =   "1"
      Top             =   4200
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   7
      Left            =   3480
      Picture         =   "checker.frx":1868
      Tag             =   "1"
      Top             =   4800
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   6
      Left            =   2400
      Picture         =   "checker.frx":1E82
      Tag             =   "1"
      Top             =   4800
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   5
      Left            =   1320
      Picture         =   "checker.frx":249C
      Tag             =   "1"
      Top             =   4800
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   4
      Left            =   240
      Picture         =   "checker.frx":2AB6
      Tag             =   "1"
      Top             =   4800
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   3
      Left            =   4080
      Picture         =   "checker.frx":30D0
      Tag             =   "1"
      Top             =   5280
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   2
      Left            =   3000
      Picture         =   "checker.frx":36EA
      Tag             =   "1"
      Top             =   5280
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   1
      Left            =   1920
      Picture         =   "checker.frx":3D04
      Tag             =   "1"
      Top             =   5280
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   11
      Left            =   3480
      Picture         =   "checker.frx":431E
      Tag             =   "0"
      Top             =   2640
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   10
      Left            =   2400
      Picture         =   "checker.frx":4938
      Tag             =   "0"
      Top             =   2640
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   9
      Left            =   1320
      Picture         =   "checker.frx":4F52
      Tag             =   "0"
      Top             =   2640
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   8
      Left            =   240
      Picture         =   "checker.frx":556C
      Tag             =   "0"
      Top             =   2640
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   7
      Left            =   3960
      Picture         =   "checker.frx":5B86
      Tag             =   "0"
      Top             =   2160
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   6
      Left            =   2880
      Picture         =   "checker.frx":61A0
      Tag             =   "0"
      Top             =   2160
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   5
      Left            =   1920
      Picture         =   "checker.frx":67BA
      Tag             =   "0"
      Top             =   2160
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   4
      Left            =   720
      Picture         =   "checker.frx":6DD4
      Tag             =   "0"
      Top             =   2040
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   3
      Left            =   3480
      Picture         =   "checker.frx":73EE
      Tag             =   "0"
      Top             =   1560
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   2
      Left            =   2400
      Picture         =   "checker.frx":7A08
      Tag             =   "0"
      Top             =   1560
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   1
      Left            =   1320
      Picture         =   "checker.frx":8022
      Tag             =   "0"
      Top             =   1560
      Width           =   330
   End
   Begin VB.Image imgWhite
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   0
      Left            =   840
      Picture         =   "checker.frx":863C
      Tag             =   "1"
      Top             =   5280
      Width           =   330
   End
   Begin VB.Image imgBlack
      DragMode        =   1  'Automatic
      Height          =   330
      Index           =   0
      Left            =   240
      Picture         =   "checker.frx":8C56
      Tag             =   "0"
      Top             =   1560
      Width           =   330
   End
   Begin VB.Image imgBackGround
      Height          =   4335
      Left            =   120
      Picture         =   "checker.frx":9270
      Top             =   1440
      Width           =   6465
   End
   Begin VB.Label lblRoomPath
      Height          =   255
      Left            =   4080
      TabIndex        =   21
      Top             =   360
      Width           =   4575
   End
   Begin VB.Label lblUserName
      Height          =   255
      Left            =   2160
      TabIndex        =   20
      Top             =   360
      Width           =   1695
   End
   Begin VB.Label lblAlias
      Height          =   255
      Left            =   120
      TabIndex        =   19
      Top             =   360
      Width           =   1815
   End
   Begin VB.Label Label18
      BackColor       =   &H00FFFFC0&
      Caption         =   "State"
      Height          =   255
      Left            =   10680
      TabIndex        =   18
      Top             =   120
      Width           =   615
   End
   Begin VB.Label Label13
      Alignment       =   1  'Right Justify
      BackColor       =   &H00FFFFC0&
      Caption         =   "Chat Room Topic"
      Height          =   255
      Left            =   120
      TabIndex        =   17
      Top             =   960
      Width           =   1335
   End
   Begin VB.Label Label10
      BackColor       =   &H00FFFFC0&
      Caption         =   "Name"
      Height          =   255
      Left            =   2160
      TabIndex        =   16
      Top             =   120
      Width           =   1815
   End
   Begin VB.Label Label8
      BackColor       =   &H00FFFFC0&
      Caption         =   "Room Path"
      Height          =   255
      Left            =   4080
      TabIndex        =   15
      Top             =   120
      Width           =   4575
   End
   Begin VB.Label Label7
      BackColor       =   &H00FFFFC0&
      Caption         =   "Alias "
      Height          =   255
      Left            =   120
      TabIndex        =   14
      Top             =   120
      Width           =   1815
   End
End
Attribute VB_Name = "Main"
Attribute VB_Creatable = False
Attribute VB_Exposed = False

Option Explicit

Const MASK_NOWHISPER = 2
Const MASK_IGNORED = 4

Const STAT_NOWHISPER = 8
Const STAT_IGNORED = 16

Const CAP_NOWHISPER = "Deny Whispers"
Const CAP_WHISPER = "Accept Whispers"

Const CAP_YOURTURN = "Yes"
Const CAP_NOTYOURTURN = "No"

Const COLUMNS = 8
Const ROWS = 8
Const CHK_PER_ROW = COLUMNS / 2
Const ROW_PER_TEAM = 3

Const TYPE_MOVE = 0
Const TYPE_ASKFORPOS = 1
Const TYPE_GIVEPOS = 2

Dim nDeadBlack As Integer
Dim nDeadWhite As Integer
Dim fGotPositions As Boolean
Dim lgParticipantID1 As Long
Dim lgParticipantID2 As Long

Sub BroadcastMove(Tag As Integer, Index As Integer, Left As Integer, Top As Integer)
    On Error Resume Next
    If (MSChat1.State = 3) Then
        Dim rgInt(0 To 4) As Integer
        Dim vMess As Variant

        rgInt(0) = TYPE_MOVE
        rgInt(1) = Tag
        rgInt(2) = Index
        rgInt(3) = Left
        rgInt(4) = Top

        MSChat1.SendMessage -1, 0, rgInt

        lblYourTurn.Caption = CAP_NOTYOURTURN
        lblYourTurn.BackColor = &HC0&
    End If
End Sub
Sub InitializeChkPositions()
    Dim X, Y As Integer

    For Y = 0 To ROW_PER_TEAM - 1
        For X = 0 To CHK_PER_ROW - 1
            imgBlack(Y * CHK_PER_ROW + X).Left = imgBackGround.Left + 120 + X * 1080 + (Y Mod 2) * 540

imgBlack(Y * CHK_PER_ROW + X).Top = imgBackGround.Top + 120 + Y * 540
        Next X
    Next Y

    For Y = 0 To ROW_PER_TEAM - 1
        For X = 0 To CHK_PER_ROW - 1
            imgWhite(Y * CHK_PER_ROW + X).Left = imgBackGround.Left + 120 + (X + 1) * 1080 - ((Y Mod 2) + 1) * 540
            imgWhite(Y * CHK_PER_ROW + X).Top = imgBackGround.Top + imgBackGround.Height - 120 - Y * 540 - imgWhite(0).Height
        Next X
    Next Y
End Sub

Sub SendPositions(ParticipantID As Long)
    On Error Resume Next
    If (MSChat1.State = 3) Then
        Dim rgInt(0 To CHK_PER_ROW * ROW_PER_TEAM * 4) As Integer
        Dim X As Integer

        rgInt(0) = TYPE_GIVEPOS

        For X = 0 To CHK_PER_ROW * ROW_PER_TEAM - 1
            rgInt(2 * X + 1) = imgBlack(X).Left
            rgInt(2 * (X + 1)) = imgBlack(X).Top
        Next X

        For X = 0 To CHK_PER_ROW * ROW_PER_TEAM - 1
            rgInt(2 * X + 1 + CHK_PER_ROW * ROW_PER_TEAM * 2) = imgWhite(X).Left
            rgInt(2 * (X + 1) + CHK_PER_ROW * ROW_PER_TEAM * 2) = imgWhite(X).Top
        Next X

        MSChat1.SendMessage ParticipantID, 1, rgInt
    End If
End Sub

Sub SetState(NewState As Integer)
    Select Case NewState
    Case 1:
        cmdEnterRoom.Enabled = True
        cmdCancelEntering.Enabled = False
        cmdExitRoom.Enabled = False
        lblState.Caption = "Disconnected"
        lblState.BackColor = &HC0&
        MSChat1.BackColor = &HC0&
    Case 2:
        cmdEnterRoom.Enabled = False
        cmdCancelEntering.Enabled = True
        cmdExitRoom.Enabled = False
        lblState.Caption = "Connecting..."
        lblState.BackColor = &H80FF&
        MSChat1.BackColor = &H80FF&
    Case 3:
        cmdEnterRoom.Enabled = False
        cmdCancelEntering.Enabled = False
        cmdExitRoom.Enabled = True
        lblState.Caption = "Connected"
        lblState.BackColor = &HC000&
        MSChat1.BackColor = &HC000&
    End Select
End Sub

Private Sub cmdGetRealName_Click()
    On Error Resume Next

    Dim txtReal As String

    lblAsyncAlias.Caption = ""
    lblAsyncRealName.Caption = ""

    If (optSelection.Value) Then
        MSChat1.GetParticipantRealName -1, "", False, txtReal
    Else
        MSChat1.GetParticipantRealName 0, txtAlias.Text, False, txtReal
    End If
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Unload EnterOptions
End Sub

Private Sub imgBackGround_DragDrop(Source As Control, X As Single, Y As Single)
    Source.Left = (Int(X / 540) + 0.5) * 540 + imgBackGround.Left - Source.Width / 2
    Source.Top = (Int(Y / 540) + 0.5) * 540 + imgBackGround.Top - Source.Height / 2

    BroadcastMove Source.Tag, Source.Index, Source.Left, Source.Top
End Sub

Private Sub MSChat1_OnBeginEnumeration()
    lgParticipantID1 = 0
    lgParticipantID2 = 0
    fGotPositions = False
End Sub

Private Sub MSChat1_OnEnterParticipant(ByVal ParticipantID As Long, ByVal Alias As String, ByVal Status As Integer)
    If (Not fGotPositions) Then
        lgParticipantID1 = lgParticipantID2
        lgParticipantID2 = ParticipantID
    End If
End Sub

Private Sub MSChat1_OnParticipantRealName(ByVal Alias As String, ByVal RealName As String)
    On Error Resume Next
    lblAsyncAlias.Caption = Alias
    lblAsyncRealName.Caption = RealName
End Sub

Private Sub MSChat1_OnRoomTopicChanged(ByVal NewRoomTopic As String)
    On Error Resume Next
    txtTopic.Text = NewRoomTopic
End Sub

Private Sub MSChat1_OnStateChanged(ByVal NewState As Integer)
    SetState NewState
End Sub

Private Sub cmdCancelEntering_Click()
    On Error Resume Next
    MSChat1.CancelEntering
End Sub

Private Sub cmdClearHistory_Click()
    On Error Resume Next
    MSChat1.ClearHistory
End Sub

Private Sub cmdDoNotIgnore_Click()
    On Error Resume Next
    MSChat1.SetParticipantStatus -1, MASK_IGNORED, 0
End Sub

Private Sub cmdEnterRoom_Click()
    EnterOptions.Show
End Sub

Private Sub cmdExit_Click()
    End
End Sub

Private Sub cmdExitRoom_Click()
    On Error Resume Next
    MSChat1.ExitRoom
End Sub

Private Sub cmdIgnore_Click()
    On Error Resume Next
    MSChat1.SetParticipantStatus -1, MASK_IGNORED, STAT_IGNORED
End Sub

Private Sub cmdSetTopic_Click()
    On Error Resume Next
    MSChat1.RoomTopic = txtTopic.Text
End Sub

Private Sub cmdWhisperNoWhisper_Click()
    On Error Resume Next
    If (cmdWhisperNoWhisper.Caption = CAP_NOWHISPER) Then
        MSChat1.SetParticipantStatus MSChat1.ThisParticipantID, MASK_NOWHISPER, STAT_NOWHISPER
        cmdWhisperNoWhisper.Caption = CAP_WHISPER
    Else
        MSChat1.SetParticipantStatus MSChat1.ThisParticipantID, MASK_NOWHISPER, 0
        cmdWhisperNoWhisper.Caption = CAP_NOWHISPER
    End If
End Sub
Private Sub Form_Load()

    cmdWhisperNoWhisper.Caption = CAP_NOWHISPER
    InitializeChkPositions
    nDeadBlack = 0
    nDeadWhite = 0

End Sub

Private Sub MSChat1_OnEndEnumeration()
    On Error Resume Next
    txtTopic.Text = MSChat1.RoomTopic
    lblUserName.Caption = MSChat1.ThisParticipantName
    lblRoomPath.Caption = MSChat1.RoomPath

    fGotPositions = True
    If (MSChat1.MemberCount > 1) Then
        If (lgParticipantID1 <> MSChat1.ThisParticipantID) Then
            MSChat1.SendMessage lgParticipantID1, 1, TYPE_ASKFORPOS
        Else
            MSChat1.SendMessage lgParticipantID2, 1, TYPE_ASKFORPOS
        End If
    End If
End Sub
Private Sub MSChat1_OnMessage(ByVal SenderID As Long, ByVal Message As Variant, ByVal MessageType As Integer)
    On Error Resume Next

    If (VarType(Message) = vbInteger + vbArray) Then
        Select Case Message(0)
            Case TYPE_MOVE:
                If (Message(1) = 0) Then  'black checker was moved
                    imgBlack(Message(2)).Left = Message(3)
                    imgBlack(Message(2)).Top = Message(4)
                Else                      'white checker was moved
                    imgWhite(Message(2)).Left = Message(3)
                    imgWhite(Message(2)).Top = Message(4)
                End If
                lblYourTurn.Caption = CAP_YOURTURN
                lblYourTurn.BackColor = &HC000&

            Case TYPE_GIVEPOS:
                Dim X As Integer

                For X = 0 To CHK_PER_ROW * ROW_PER_TEAM - 1
                    imgBlack(X).Left = Message(X * 2 + 1)
                    imgBlack(X).Top = Message((X + 1) * 2)
                Next X
                For X = 0 To CHK_PER_ROW * ROW_PER_TEAM - 1
                    imgWhite(X).Left = Message(X * 2 + 1 + CHK_PER_ROW * ROW_PER_TEAM * 2)
                    imgWhite(X).Top = Message((X + 1) * 2 + CHK_PER_ROW * ROW_PER_TEAM * 2)
                Next X
        End Select
    End If

    If (VarType(Message) = vbInteger) Then
        Select Case Message
            Case TYPE_ASKFORPOS:
                SendPositions SenderID
            End Select
    End If
End Sub

Private Sub optAlias_Click()
    txtAlias.Enabled = True
End Sub

Private Sub optSelection_Click()
    txtAlias.Enabled = False
End Sub


CHKENTER.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     CHECKER.EXE
'  File:       Chkenter.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form EnterOptions
   BackColor       =   &H00C0FFC0&
   BorderStyle     =   3  'Fixed Dialog
   Caption         =   "Join Game Options"
   ClientHeight    =   3060
   ClientLeft      =   1785
   ClientTop       =   3300
   ClientWidth     =   4245
   Height          =   3465
   Left            =   1725
   LinkTopic       =   "EnterOptions"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   ScaleHeight     =   3060
   ScaleWidth      =   4245
   ShowInTaskbar   =   0   'False
   Top             =   2955
   Width           =   4365
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Default         =   -1  'True
      Height          =   375
      Left            =   1680
      TabIndex        =   9
      Top             =   2520
      Width           =   1095
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   3000
      TabIndex        =   8
      Top             =   2520
      Width           =   1095
   End
   Begin VB.TextBox txtTopic
      Height          =   285
      Left            =   120
      TabIndex        =   6
      Top             =   2040
      Width           =   3975
   End
   Begin VB.CommandButton cmdGetTopic
      Caption         =   "Get Topic"
      Height          =   255
      Left            =   2160
      TabIndex        =   5
      Top             =   1800
      Width           =   975
   End
   Begin VB.CommandButton cmdSetTopic
      Caption         =   "Set Topic"
      Height          =   255
      Left            =   3120
      TabIndex        =   4
      Top             =   1800
      Width           =   975
   End
   Begin VB.ComboBox ComboRoomPath
      Height          =   315
      Left            =   120
      TabIndex        =   1
      Top             =   1200
      Width           =   3975
   End
   Begin VB.ComboBox ComboAlias
      Height          =   315
      Left            =   120
      TabIndex        =   0
      Top             =   360
      Width           =   3975
   End
   Begin VB.Label Label13
      BackColor       =   &H00C0FFC0&
      Caption         =   "Type the chat room topic:"
      Height          =   255
      Left            =   120
      TabIndex        =   7
      Top             =   1800
      Width           =   2175
   End
   Begin VB.Label Label7
      BackColor       =   &H00C0FFC0&
      Caption         =   "Select or type your Alias:"
      Height          =   255
      Left            =   120
      TabIndex        =   3
      Top             =   120
      Width           =   3975
   End
   Begin VB.Label Label8
      BackColor       =   &H00C0FFC0&
      Caption         =   "Select or type the chat room:"
      Height          =   255
      Left            =   120
      TabIndex        =   2
      Top             =   960
      Width           =   3975
   End
End
Attribute VB_Name = "EnterOptions"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit

' Channel creation modes
Const OCX_CHANNEL_CREATE_JOIN = 1

Const OCX_CHANNEL_TOPICOP = 2        ' topic can be changed only by Host

Const SEC_PACKAGE = "ANON;ALL"
Private Sub cmdCancel_Click()
    Hide
End Sub

Private Sub cmdGetTopic_Click()
    On Error Resume Next
    txtTopic.Text = Main.MSChat1.RoomTopic
End Sub

Private Sub cmdOK_Click()

    On Error Resume Next

    cmdSetTopic_Click

    Main.lblAlias.Caption = ComboAlias.Text
    Main.lblRoomPath.Caption = ComboRoomPath.Text
    Main.MSChat1.EnterRoom ComboRoomPath.Text, "", ComboAlias.Text, SEC_PACKAGE, OCX_CHANNEL_CREATE_JOIN, OCX_CHANNEL_TOPICOP

    Hide
End Sub

Private Sub cmdSetTopic_Click()
    On Error Resume Next

    Main.MSChat1.RoomTopic = txtTopic.Text
End Sub

Private Sub Form_Load()
    ComboRoomPath.AddItem "//ComicSrv1.Microsoft.Com/#Checkers"
    ComboRoomPath.AddItem "//ComicSrv2.Microsoft.Com/#Checkers"

    ComboRoomPath.ListIndex = 0

    ComboAlias.AddItem "Player1"
    ComboAlias.AddItem "Player2"

    ComboAlias.ListIndex = 0
End Sub


README.TXT

------------------------------------------------------------------
    Microsoft Chat SDK 1.1 Execchat Readme File
                         August 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE EXECCHAT SAMPLE
BUILDING THE EXECCHAT SAMPLE

OVERVIEW OF THE EXECCHAT SAMPLE
==============================
ExecChat contains a VB application for hosting Executive Chat Sessions.
This application can be used in three modes: Host, Moderator and Special
Guest.

After building the sample, you can enter a chat session by clicking on the Enter
Room button. You will need to enter a chat server, room name and alias name in the
Enter Room dialog box.

This is a chat tool that is very useful for special guest chats. The sample is setup
so that the Host gets all the questions from the users, while the special guest and
the moderator do not see the questions posted. The host can then send the selected
question to the host with the whisper function and the moderator can then send
the question to the special guest and the users.

BUILDING THE EXECCHAT SAMPLE
===========================

The execchat sample can be built with Visual Basic 4.0.

1. Select the Open project command from the File menu.

2. Open the execc.vbp file and build the application.


ECKICBAN.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     ExecChat.EXE
'  File:       ECKicBan.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form KickBan
   BackColor       =   &H00C0FFC0&
   Caption         =   "Kick, Ban and Re-instate Participant"
   ClientHeight    =   6690
   ClientLeft      =   1125
   ClientTop       =   1620
   ClientWidth     =   5745
   Height          =   7095
   Left            =   1065
   LinkTopic       =   "KickBan"
   ScaleHeight     =   6690
   ScaleWidth      =   5745
   Top             =   1275
   Width           =   5865
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Default         =   -1  'True
      Height          =   375
      Left            =   4440
      TabIndex        =   5
      Top             =   6240
      Width           =   1215
   End
   Begin VB.CommandButton cmdReinstate
      Caption         =   "Re-instate Participant"
      Height          =   375
      Left            =   3720
      TabIndex        =   4
      Top             =   5640
      Width           =   1935
   End
   Begin VB.TextBox txtAlias
      Height          =   405
      Left            =   120
      TabIndex        =   2
      Top             =   5040
      Width           =   5535
   End
   Begin VB.CommandButton cmdBanParticipant
      Caption         =   "Ban Participant"
      Height          =   375
      Left            =   1560
      TabIndex        =   3
      Top             =   5640
      Width           =   1935
   End
   Begin VB.TextBox txtKickReason
      Height          =   375
      Left            =   120
      TabIndex        =   0
      Top             =   2040
      Width           =   5535
   End
   Begin VB.CommandButton cmdKickParticipant
      Caption         =   "Kick Participant"
      Height          =   375
      Left            =   3720
      TabIndex        =   1
      Top             =   2640
      Width           =   1935
   End
   Begin VB.Line Line2
      X1              =   5640
      X2              =   120
      Y1              =   6120
      Y2              =   6120
   End
   Begin VB.Label Label9
      BackColor       =   &H00C0FFC0&
      Caption         =   "Participant Alias:"
      Height          =   255
      Left            =   120
      TabIndex        =   14
      Top             =   4800
      Width           =   1335
   End
   Begin VB.Label Label8
      BackColor       =   &H00C0FFC0&
      Caption         =   "2. Click the 'Ban Participant' or 'Re-instate Participant' button."
      Height          =   255
      Left            =   120
      TabIndex        =   13
      Top             =   4320
      Width           =   5535
   End
   Begin VB.Label Label7
      BackColor       =   &H00C0FFC0&
      Caption         =   "1. Type the alias of the participant to ban or re-instate."
      Height          =   255
      Left            =   120
      TabIndex        =   12
      Top             =   3840
      Width           =   5535
   End
   Begin VB.Label Label6
      BackColor       =   &H00C0FFC0&
      Caption         =   "To ban or re-instate a participant:"
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   700
         size            =   8.25
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   255
      Left            =   120
      TabIndex        =   11
      Top             =   3360
      Width           =   5535
   End
   Begin VB.Label Label5
      BackColor       =   &H00C0FFC0&
      Caption         =   "To kick a participant:"
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   700
         size            =   8.25
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   255
      Left            =   120
      TabIndex        =   10
      Top             =   120
      Width           =   5535
   End
   Begin VB.Line Line1
      X1              =   5640
      X2              =   120
      Y1              =   3120
      Y2              =   3120
   End
   Begin VB.Label Label4
      BackColor       =   &H00C0FFC0&
      Caption         =   "Kick Reason:"
      Height          =   255
      Left            =   120
      TabIndex        =   9
      Top             =   1800
      Width           =   1095
   End
   Begin VB.Label Label3
      BackColor       =   &H00C0FFC0&
      Caption         =   "3. Click the 'Kick Participant' button."
      Height          =   255
      Left            =   120
      TabIndex        =   8
      Top             =   1320
      Width           =   5535
   End
   Begin VB.Label Label2
      BackColor       =   &H00C0FFC0&
      Caption         =   "2. Enter the optinal kick reason."
      Height          =   255
      Left            =   120
      TabIndex        =   7
      Top             =   960
      Width           =   5535
   End
   Begin VB.Label Label1
      BackColor       =   &H00C0FFC0&
      Caption         =   "1. Select the participant you want to kick in the control's member list box."
      Height          =   255
      Left            =   120
      TabIndex        =   6
      Top             =   600
      Width           =   5535
   End
End
Attribute VB_Name = "KickBan"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit
Private Sub cmdBanParticipant_Click()
    On Error GoTo errBanPart
    If (txtAlias.Text <> "") Then
        ExecC.MSChat.BanParticipant txtAlias.Text, True
    Else
        MsgBox "Please enter the alias of the participant to ban and try again."
    End If
    Exit Sub

errBanPart:
    Hide
End Sub

Private Sub cmdKickParticipant_Click()
    On Error GoTo errKickPart
    ExecC.MSChat.KickParticipant -1, txtKickReason.Text
    Exit Sub

errKickPart:
    Hide
End Sub

Private Sub cmdOK_Click()
    Hide
End Sub

Private Sub cmdReinstate_Click()
    On Error GoTo errReinstate
    If (txtAlias.Text <> "") Then
        ExecC.MSChat.BanParticipant txtAlias.Text, False
    Else
        MsgBox "Please enter the alias of the participant to re-instate and try again."
    End If
    Exit Sub

errReinstate:
    Hide
End Sub


ECUISET.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     ExecChat.EXE
'  File:       ECUISet.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form UISettings
   BackColor       =   &H00C0FFC0&
   Caption         =   "MSChat Control UI Setting"
   ClientHeight    =   5070
   ClientLeft      =   1125
   ClientTop       =   3225
   ClientWidth     =   4800
   Height          =   5475
   Left            =   1065
   LinkTopic       =   "UISettings"
   ScaleHeight     =   5070
   ScaleWidth      =   4800
   Top             =   2880
   Width           =   4920
   Begin VB.CommandButton cmdApply
      Caption         =   "Apply"
      Height          =   375
      Left            =   3480
      TabIndex        =   14
      Top             =   4560
      Width           =   1215
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   2160
      TabIndex        =   13
      Top             =   4560
      Width           =   1215
   End
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Default         =   -1  'True
      Height          =   375
      Left            =   840
      TabIndex        =   0
      Top             =   4560
      Width           =   1215
   End
   Begin VB.Frame Frame1
      BackColor       =   &H00C0FFC0&
      Height          =   4215
      Left            =   120
      TabIndex        =   1
      Top             =   120
      Width           =   4575
      Begin VB.CheckBox chkBut
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Send Button?"
         Height          =   255
         Left            =   120
         TabIndex        =   12
         TabStop         =   0   'False
         Top             =   3480
         Width           =   4335
      End
      Begin VB.CheckBox chkMem
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Member ListBox?"
         Height          =   255
         Left            =   120
         TabIndex        =   11
         TabStop         =   0   'False
         Top             =   2400
         Width           =   4335
      End
      Begin VB.CheckBox chkRec
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Receive TextBox?"
         Height          =   255
         Left            =   120
         TabIndex        =   10
         TabStop         =   0   'False
         Top             =   2760
         Width           =   4335
      End
      Begin VB.CheckBox chkSen
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Send TextBox?"
         Height          =   255
         Left            =   120
         TabIndex        =   9
         TabStop         =   0   'False
         Top             =   3840
         Width           =   4335
      End
      Begin VB.CheckBox chkDisplayError
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display error messages?"
         Height          =   255
         Left            =   120
         TabIndex        =   8
         Top             =   1680
         Width           =   4335
      End
      Begin VB.CheckBox chkSensitiveURLs
         BackColor       =   &H00C0FFC0&
         Caption         =   "Sensitive URLs in history window?"
         Height          =   255
         Left            =   120
         TabIndex        =   7
         Top             =   2040
         Width           =   4335
      End
      Begin VB.CheckBox chkMemChg
         BackColor       =   &H00C0FFC0&
         Caption         =   "Inform when privilege, alias changed or member kicked?"
         Height          =   255
         Left            =   120
         TabIndex        =   6
         Top             =   1320
         Width           =   4335
      End
      Begin VB.CheckBox chkBlank
         BackColor       =   &H00C0FFC0&
         Caption         =   "Insert blank line between revd messages?"
         Height          =   255
         Left            =   120
         TabIndex        =   5
         TabStop         =   0   'False
         Top             =   240
         Width           =   4335
      End
      Begin VB.CheckBox chkEnter
         BackColor       =   &H00C0FFC0&
         Caption         =   "Inform when participant enters the room?"
         Height          =   255
         Left            =   120
         TabIndex        =   4
         TabStop         =   0   'False
         Top             =   600
         Width           =   4335
      End
      Begin VB.CheckBox chkLeave
         BackColor       =   &H00C0FFC0&
         Caption         =   "Inform when participant leaves the room?"
         Height          =   255
         Left            =   120
         TabIndex        =   3
         TabStop         =   0   'False
         Top             =   960
         Width           =   4335
      End
      Begin VB.CheckBox chkWhi
         BackColor       =   &H00C0FFC0&
         Caption         =   "Display Whisper Button?"
         Height          =   255
         Left            =   120
         TabIndex        =   2
         TabStop         =   0   'False
         Top             =   3120
         Width           =   4335
      End
   End
End
Attribute VB_Name = "UISettings"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit

Const SEND_BUTTON = 1
Const WHISPER_BUTTON = 2
Const PARTICIPANT_LISTBOX = 4
Const RECEIVE_TEXTBOX = 8
Const SEND_TEXTBOX = 16
Const INFORM_JOIN = 32
Const INFORM_LEAVE = 64
Const INFORM_MEMCHG = 128
Const INSERT_BLANK = 256
Const DISPLAY_ERRORMESSAGES = 512
Const ALLOW_RESIZING = 1024
Const SENSITIVE_URLS = 2048

Dim nUIOption As Integer
Dim nOriginalUIOption As Integer
Sub UpdateUIOption()

    On Error GoTo errUpdateUIOption

    nUIOption = chkBut.Value * SEND_BUTTON + chkWhi.Value * WHISPER_BUTTON + chkMem.Value * PARTICIPANT_LISTBOX + chkRec.Value * RECEIVE_TEXTBOX + chkSen.Value * SEND_TEXTBOX + chkEnter.Value * INFORM_JOIN + chkLeave.Value * INFORM_LEAVE + chkMemChg.Value * INFORM_MEMCHG + chkBlank.Value * INSERT_BLANK + chkDisplayError.Value * DISPLAY_ERRORMESSAGES + chkSensitiveURLs.Value * SENSITIVE_URLS
    Exit Sub

errUpdateUIOption:
    DisplayError "UpdateUIOption"
End Sub

Private Sub chkBlank_Click()
    UpdateUIOption
End Sub

Private Sub chkBut_Click()
    UpdateUIOption
End Sub

Private Sub chkDisplayError_Click()
    UpdateUIOption
End Sub

Private Sub chkEnter_Click()
    UpdateUIOption
End Sub

Private Sub chkLeave_Click()
    UpdateUIOption
End Sub

Private Sub chkMem_Click()
    UpdateUIOption
End Sub

Private Sub chkMemChg_Click()
    UpdateUIOption
End Sub

Private Sub chkRec_Click()
    UpdateUIOption
End Sub

Private Sub chkSen_Click()
    UpdateUIOption
End Sub

Private Sub chkSensitiveURLs_Click()
    UpdateUIOption
End Sub

Private Sub chkWhi_Click()
    UpdateUIOption
End Sub

Private Sub cmdApply_Click()
    On Error GoTo errApply_Click
    ExecC.MSChat.UIOption = nUIOption
    Exit Sub

errApply_Click:
    ExecC.MSChat.UIOption = nOriginalUIOption
End Sub

Private Sub cmdCancel_Click()
    ExecC.MSChat.UIOption = nOriginalUIOption
    Hide
End Sub

Private Sub cmdOK_Click()
    On Error GoTo errOK_Click
    ExecC.MSChat.UIOption = nUIOption
    Hide
    Exit Sub

errOK_Click:
    ExecC.MSChat.UIOption = nOriginalUIOption
End Sub

Private Sub Form_Load()

    nOriginalUIOption = ExecC.MSChat.UIOption
    If (nOriginalUIOption And SEND_BUTTON) Then
        chkBut.Value = 1
    Else
        chkBut.Value = 0
    End If
    If (nOriginalUIOption And WHISPER_BUTTON) Then
        chkWhi.Value = 1
    Else
        chkWhi.Value = 0
    End If
    If (nOriginalUIOption And PARTICIPANT_LISTBOX) Then
        chkMem.Value = 1
    Else
        chkMem.Value = 0
    End If
    If (nOriginalUIOption And RECEIVE_TEXTBOX) Then
        chkRec.Value = 1
    Else
        chkRec.Value = 0
    End If
    If (nOriginalUIOption And SEND_TEXTBOX) Then
        chkSen.Value = 1
    Else
        chkSen.Value = 0
    End If
    If (nOriginalUIOption And INFORM_JOIN) Then
        chkEnter.Value = 1
    Else
        chkEnter.Value = 0
    End If
    If (nOriginalUIOption And INFORM_LEAVE) Then
        chkLeave.Value = 1
    Else
        chkLeave.Value = 0
    End If
    If (nOriginalUIOption And INFORM_MEMCHG) Then
        chkMemChg.Value = 1
    Else
        chkMemChg.Value = 0
    End If
    If (nOriginalUIOption And INSERT_BLANK) Then
        chkBlank.Value = 1
    Else
        chkBlank.Value = 0
    End If
    If (nOriginalUIOption And DISPLAY_ERRORMESSAGES) Then
        chkDisplayError.Value = 1
    Else
        chkDisplayError.Value = 0
    End If
    If (nOriginalUIOption And SENSITIVE_URLS) Then
        chkSensitiveURLs.Value = 1
    Else
        chkSensitiveURLs.Value = 0
    End If
End Sub


EXECC.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     ExecChat.EXE
'  File:       Execc.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form ExecC
   BackColor       =   &H00FFFFC0&
   Caption         =   "Executive Chat Monitoring Tool"
   ClientHeight    =   8310
   ClientLeft      =   990
   ClientTop       =   405
   ClientWidth     =   8850
   Height          =   8715
   Left            =   930
   LinkTopic       =   "ExecC"
   ScaleHeight     =   8310
   ScaleWidth      =   8850
   Top             =   60
   Width           =   8970
   Begin VB.CheckBox chkAutoPaste
      BackColor       =   &H00FFFFC0&
      Caption         =   "Paste Clipboard Content into Send text boxes?"
      Height          =   615
      Left            =   3840
      TabIndex        =   27
      Top             =   720
      Width           =   2055
   End
   Begin VB.ListBox lstPartID
      Enabled         =   0   'False
      Height          =   255
      Left            =   7920
      TabIndex        =   22
      Top             =   120
      Visible         =   0   'False
      Width           =   855
   End
   Begin VB.Frame frmGuest
      BackColor       =   &H00FFFFC0&
      Caption         =   "Special Guest Functions"
      Enabled         =   0   'False
      Height          =   615
      Left            =   120
      TabIndex        =   17
      Top             =   3600
      Width           =   8655
      Begin VB.CommandButton cmdGWhisperToHosts
         Caption         =   "Whisper To Hosts"
         Enabled         =   0   'False
         Height          =   255
         Left            =   4560
         TabIndex        =   29
         Top             =   240
         Width           =   1935
      End
      Begin VB.CommandButton cmdGWhisperToModerator
         Caption         =   "Whisper To Moderator"
         Enabled         =   0   'False
         Height          =   255
         Left            =   6600
         TabIndex        =   23
         Top             =   240
         Width           =   1935
      End
   End
   Begin VB.Frame frmModerator
      BackColor       =   &H00FFFFC0&
      Caption         =   "Moderator Functions"
      Enabled         =   0   'False
      Height          =   975
      Left            =   120
      TabIndex        =   16
      Top             =   2520
      Visible         =   0   'False
      Width           =   8655
      Begin VB.CommandButton cmdMWhisperToHosts
         Caption         =   "Whisper To Hosts"
         Enabled         =   0   'False
         Height          =   255
         Left            =   6720
         TabIndex        =   28
         Top             =   240
         Width           =   1815
      End
      Begin VB.CommandButton cmdMSendToEntireRoom
         Caption         =   "Send To Entire Room"
         Enabled         =   0   'False
         Height          =   615
         Left            =   4680
         TabIndex        =   21
         Top             =   240
         Width           =   1935
      End
      Begin VB.CommandButton cmdWhisperToGuest
         Caption         =   "Whisper To Guest"
         Enabled         =   0   'False
         Height          =   255
         Left            =   6720
         TabIndex        =   20
         Top             =   600
         Width           =   1815
      End
      Begin VB.CommandButton cmdSetGuestID
         Caption         =   "Identify Special Guest"
         Enabled         =   0   'False
         Height          =   255
         Left            =   120
         TabIndex        =   18
         Top             =   240
         Width           =   1815
      End
      Begin VB.Label lblGuestAlias
         BackColor       =   &H00FFFF00&
         Caption         =   "<Special Guest Alias>"
         Height          =   255
         Left            =   2040
         TabIndex        =   19
         Top             =   240
         Width           =   2415
      End
   End
   Begin VB.Frame frmHost
      BackColor       =   &H00FFFFC0&
      Caption         =   "Host Functions"
      Enabled         =   0   'False
      Height          =   975
      Left            =   120
      TabIndex        =   12
      Top             =   1440
      Visible         =   0   'False
      Width           =   8655
      Begin VB.CommandButton cmdHWhisperToHosts
         Caption         =   "Whisper To Hosts"
         Enabled         =   0   'False
         Height          =   255
         Left            =   6720
         TabIndex        =   26
         Top             =   240
         Width           =   1815
      End
      Begin VB.CheckBox chkSpeakerView
         BackColor       =   &H00FFFFC0&
         Caption         =   "Enable Send and Whisper to Room Features?"
         Height          =   255
         Left            =   120
         TabIndex        =   25
         Top             =   600
         Width           =   3855
      End
      Begin VB.CommandButton cmdShowHostPanel
         Caption         =   "Show Host Panel"
         Enabled         =   0   'False
         Height          =   255
         Left            =   6720
         TabIndex        =   24
         Top             =   600
         Width           =   1815
      End
      Begin VB.CommandButton cmdHWhisperToModerator
         Caption         =   "Whisper To Moderator"
         Enabled         =   0   'False
         Height          =   615
         Left            =   4680
         TabIndex        =   15
         Top             =   240
         Width           =   1935
      End
      Begin VB.CommandButton cmdSetModeratorID
         Caption         =   "Identify Moderator"
         Enabled         =   0   'False
         Height          =   255
         Left            =   120
         TabIndex        =   13
         Top             =   240
         Width           =   1575
      End
      Begin VB.Label lblModeratorAlias
         BackColor       =   &H00FFFF00&
         Caption         =   "<Moderator Alias>"
         Height          =   255
         Left            =   1800
         TabIndex        =   14
         Top             =   240
         Width           =   2175
      End
   End
   Begin VB.CommandButton cmdClearHistory
      Caption         =   "Clear History"
      Height          =   375
      Left            =   6000
      TabIndex        =   11
      Top             =   840
      Width           =   1215
   End
   Begin VB.Frame Frame1
      BackColor       =   &H00FFFF00&
      Caption         =   "User Mode"
      Height          =   615
      Left            =   120
      TabIndex        =   7
      Top             =   720
      Width           =   3630
      Begin VB.OptionButton optGuest
         BackColor       =   &H00FFFF00&
         Caption         =   "Special Guest"
         Height          =   255
         Left            =   2160
         TabIndex        =   10
         Top             =   240
         Value           =   -1  'True
         Width           =   1320
      End
      Begin VB.OptionButton optModerator
         BackColor       =   &H00FFFF00&
         Caption         =   "Moderator"
         Height          =   255
         Left            =   960
         TabIndex        =   9
         Top             =   240
         Width           =   1455
      End
      Begin VB.OptionButton optHost
         BackColor       =   &H00FFFF00&
         Caption         =   "Host"
         Height          =   255
         Left            =   120
         TabIndex        =   8
         Top             =   240
         Width           =   735
      End
   End
   Begin VB.CommandButton cmdFlux
      Caption         =   "Enter Chat"
      Height          =   375
      Left            =   7320
      TabIndex        =   6
      Top             =   840
      Width           =   1455
   End
   Begin ComctlLib.StatusBar StatusBar
      Align           =   2  'Align Bottom
      Height          =   255
      Left            =   0
      TabIndex        =   31
      Top             =   8055
      Width           =   8850
      _Version        =   65536
      _ExtentX        =   15610
      _ExtentY        =   450
      _StockProps     =   68
      AlignSet        =   -1  'True
      SimpleText      =   ""
      NumPanels       =   3
      i1              =   "execc.frx":0000
      i2              =   "execc.frx":00FD
      i3              =   "execc.frx":0202
   End
   Begin MSChatObjectsCtl.MSChat MSChat
      Height          =   3615
      Left            =   120
      TabIndex        =   30
      Top             =   4320
      Width           =   8655
      _ExtentX        =   15266
      _ExtentY        =   6376
      BorderStyle     =   0
      UIOption        =   4093
      BackColor       =   192
   End
   Begin VB.Label lblRoomTopic
      BackColor       =   &H00FFFF00&
      Height          =   255
      Left            =   3840
      TabIndex        =   5
      Top             =   360
      Width           =   4935
   End
   Begin VB.Label lblUserName
      BackColor       =   &H00FFFF00&
      Height          =   255
      Left            =   2040
      TabIndex        =   4
      Top             =   360
      Width           =   1695
   End
   Begin VB.Label lblAlias
      BackColor       =   &H00FFFF00&
      Height          =   255
      Left            =   120
      TabIndex        =   3
      Top             =   360
      Width           =   1815
   End
   Begin VB.Label Label10
      BackColor       =   &H00FFFFC0&
      Caption         =   "Name"
      Height          =   255
      Left            =   2040
      TabIndex        =   2
      Top             =   120
      Width           =   1695
   End
   Begin VB.Label Label8
      BackColor       =   &H00FFFFC0&
      Caption         =   "Room Topic"
      Height          =   255
      Left            =   3840
      TabIndex        =   1
      Top             =   120
      Width           =   4935
   End
   Begin VB.Label Label7
      BackColor       =   &H00FFFFC0&
      Caption         =   "Alias"
      Height          =   255
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   1815
   End
End
Attribute VB_Name = "ExecC"
Attribute VB_Creatable = False
Attribute VB_Exposed = False

Option Explicit

Private Sub chkSpeakerView_Click()
    On Error GoTo errSpeakerView
    If (chkSpeakerView.Value = 1) Then
        MSChat.UIOption = MSChat.UIOption + 19
    Else
        MSChat.UIOption = MSChat.UIOption - 19
    End If
    Exit Sub

errSpeakerView:
    DisplayError "chkSpeakerView_Click"
End Sub

Private Sub cmdFlux_Click()
    On Error GoTo errFlux

    Select Case (MSChat.State)
        Case 1: 'disconnected
            EnterOptions.Show
        Case 2: 'connecting
            MSChat.CancelEntering
            WriteLogFile "Cancelling EnterRoom operation..."
        Case 3: 'connected
            MSChat.ExitRoom
            WriteLogFile "Exiting Room."
    End Select
    Exit Sub

errFlux:
    DisplayError "Flux_Click"
End Sub

Sub GetMemberCount()
    On Error Resume Next
    StatusBar.Panels(1).Text = CStr(MSChat.MemberCount) + " Members"
End Sub

Sub SetState(NewState As Integer)
    On Error GoTo errSetState

    Select Case NewState
    Case 1:
        lModeratorID = 0
        WriteLogFile "lModeratorID set to 0 in SetState(1)"
        lGuestID = 0
        WriteLogFile "lGuestID set to 0 in SetState(1)"
        nRealNameType = 0

        lstPartID.Clear
        WhispToHosts.lstHosts.Clear

        lblModeratorAlias.Caption = "<Moderator Alias>"
        lblGuestAlias.Caption = "<Special Guest Alias>"

        cmdFlux.Caption = "Enter Room"
        StatusBar.Panels(1).Text = "0 Members"
        StatusBar.Panels(2).Text = "Disconnected"
        StatusBar.Panels(3).Text = "Not in a room"
        lblAlias.Caption = ""
        lblUserName.Caption = ""
        lblRoomTopic.Caption = ""

        MSChat.BackColor = &HC0&

        optHost.Enabled = True
        optModerator.Enabled = True
        optGuest.Enabled = True

        frmHost.Enabled = False
        frmModerator.Enabled = False
        frmGuest.Enabled = False

        cmdSetModeratorID.Enabled = False
        cmdSetGuestID.Enabled = False

        cmdGWhisperToHosts.Enabled = False
        cmdMWhisperToHosts.Enabled = False
        cmdHWhisperToHosts.Enabled = False

        cmdGWhisperToModerator.Enabled = False
        cmdHWhisperToModerator.Enabled = False
        cmdWhisperToGuest.Enabled = False

        cmdMSendToEntireRoom.Enabled = False
        cmdShowHostPanel.Enabled = False
        WriteLogFile ""

    Case 2:
        cmdFlux.Caption = "Cancel entering"
        StatusBar.Panels(2).Text = "Connecting..."
        MSChat.BackColor = &H80FF&
        optHost.Enabled = False
        optModerator.Enabled = False
        optGuest.Enabled = False

    Case 3:
        cmdFlux.Caption = "Leave Room"
        StatusBar.Panels(2).Text = "Connected"
        MSChat.BackColor = &HC000&

        frmHost.Enabled = True
        frmModerator.Enabled = True
        frmGuest.Enabled = True

        cmdSetModeratorID.Enabled = True
        cmdSetGuestID.Enabled = True

        cmdGWhisperToHosts.Enabled = True
        cmdMWhisperToHosts.Enabled = True
        cmdHWhisperToHosts.Enabled = True

        cmdMSendToEntireRoom.Enabled = True
        cmdShowHostPanel.Enabled = True
    End Select

    Exit Sub

errSetState:
    DisplayError "SetState"
End Sub

Private Sub cmdGWhisperToHosts_Click()
    WhispToHosts.Show
    If (chkAutoPaste.Value = 1) Then
        WhispToHosts.txtMessage.Text = Clipboard.GetText()
    End If
End Sub

Private Sub cmdGWhisperToModerator_Click()
    On Error Resume Next
    WhispToMod.Show
    If (chkAutoPaste.Value = 1) Then
        WhispToMod.txtMessage.Text = Clipboard.GetText()
    End If
End Sub

Private Sub cmdHWhisperToHosts_Click()
    WhispToHosts.Show
    If (chkAutoPaste.Value = 1) Then
        WhispToHosts.txtMessage.Text = Clipboard.GetText()
    End If
End Sub

Private Sub cmdMSendToEntireRoom_Click()
    SendToRoom.Show
    If (chkAutoPaste.Value = 1) Then
        SendToRoom.txtMessage.Text = Clipboard.GetText()
    End If
End Sub

Private Sub cmdMWhisperToHosts_Click()
    WhispToHosts.Show
    If (chkAutoPaste.Value = 1) Then
        WhispToHosts.txtMessage.Text = Clipboard.GetText()
    End If
End Sub

Private Sub cmdSetGuestID_Click()
    Dim txtRealName As String

    On Error GoTo errSetGuestID

    nRealNameType = RN_GUEST
    MSChat.GetParticipantRealName -1, "", False, txtRealName
    Exit Sub

errSetGuestID:
    DisplayError "cmdSetGuestID_Click"
End Sub

Private Sub cmdSetModeratorID_Click()
    Dim txtRealName As String

    On Error GoTo errSetModeratorID

    nRealNameType = RN_MODERATOR
    MSChat.GetParticipantRealName -1, "", False, txtRealName
    Exit Sub

errSetModeratorID:
    DisplayError "cmdSetModeratorID"
End Sub

Private Sub cmdShowHostPanel_Click()
    On Error Resume Next
    HostPanel.Show
End Sub

Private Sub cmdWhisperToGuest_Click()
    WhispToGuest.Show
    If (chkAutoPaste.Value = 1) Then
        WhispToGuest.txtMessage.Text = Clipboard.GetText()
    End If
End Sub

Private Sub cmdHWhisperToModerator_Click()
    On Error Resume Next
    WhispToMod.Show
    If (chkAutoPaste.Value = 1) Then
        WhispToMod.txtMessage.Text = Clipboard.GetText()
    End If
End Sub

Private Sub Form_Load()
    MSChat.Top = MSCHAT_TOP

    frmHost.Top = FUNCTIONS_TOP
    frmModerator.Top = FUNCTIONS_TOP
    frmGuest.Top = FUNCTIONS_TOP
    lModeratorID = 0
    lGuestID = 0
    nRealNameType = 0
    Load WhispToHosts

    OpenLogFile
End Sub

Private Sub Form_Resize()
    On Error Resume Next
    MSChat.Height = ExecC.Height - StatusBar.Height - MSChat.Top - 500
    MSChat.Width = ExecC.Width - 350
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Unload EnterOptions
    Unload HostPanel
    Unload KickBan
    Unload UISettings
    Unload WhispToMod
    Unload WhispToGuest
    Unload WhispToHosts
    Unload SendToRoom

    CloseLogFile
End Sub

Private Sub cmdClearHistory_Click()
    On Error Resume Next
    MSChat.ClearHistory
End Sub

Private Sub MSChat_OnEndEnumeration()
    On Error GoTo errOnEndEnumeration

    ' For the Moderator and the Guest, ignore all none-host members
    If (Not optHost.Value) Then
        Dim nCnt As Integer
        Dim nStatus As Integer
        Dim nAliasLen As Integer

        For nCnt = 0 To lstPartID.ListCount - 1
            nAliasLen = InStr(lstPartID.List(nCnt), ",") - 1
            nStatus = CInt(Mid(lstPartID.List(nCnt), nAliasLen + 2))
            If (((nStatus And STAT_PARTICIPANT) Or (nStatus And STAT_SPECTATOR)) And (lstPartID.ItemData(nCnt) <> MSChat.ThisParticipantID)) Then
                MSChat.SetParticipantStatus lstPartID.ItemData(nCnt), MASK_IGNORED, STAT_IGNORED
            End If
        Next nCnt
    End If

    lblRoomTopic.Caption = MSChat.RoomTopic
    lblUserName.Caption = MSChat.ThisParticipantName
    StatusBar.Panels(3).Text = MSChat.RoomPath

    GetMemberCount
    Exit Sub

errOnEndEnumeration:
    DisplayError "OnEndEnumeration"
End Sub

Private Sub MSChat_OnEnterParticipant(ByVal ParticipantID As Long, ByVal Alias As String, ByVal Status As Integer)
    On Error GoTo errOnEnterParticipant

    GetMemberCount

    lstPartID.AddItem Alias + "," + CStr(Status)
    lstPartID.ItemData(lstPartID.NewIndex) = ParticipantID

    If (Status And STAT_HOST) Then
        WhispToHosts.lstHosts.AddItem Alias
        WhispToHosts.lstHosts.ItemData(WhispToHosts.lstHosts.NewIndex) = ParticipantID
    End If

    If ((MSChat.ThisParticipantID <> 0) And (optGuest.Value Or optModerator.Value) And ((Status And STAT_PARTICIPANT) Or (Status And STAT_SPECTATOR))) Then
        ' Guest and Moderator ignore anybody that is not a host
        MSChat.SetParticipantStatus ParticipantID, MASK_IGNORED, STAT_IGNORED
    End If

    Exit Sub

errOnEnterParticipant:
    DisplayError "OnEnterParticipant"
End Sub

Private Sub MSChat_OnExitParticipant(ByVal ParticipantID As Long)

    On Error GoTo errOnExitPart

    GetMemberCount

    Dim nLstPos As Integer

    nLstPos = nFindParticipantIndex(ParticipantID)
    If nLstPos >= 0 Then
        lstPartID.RemoveItem nLstPos
    Else
        WriteLogFile "Couldn't find participant in OnExitParticipant(" + CStr(ParticipantID) + ")"
    End If

    nLstPos = nFindHostIndex(ParticipantID)
    If nLstPos >= 0 Then
        WhispToHosts.lstHosts.RemoveItem nLstPos
    End If

    If lModeratorID = ParticipantID Then
        lModeratorID = 0
        WriteLogFile "lModeratorID set to 0 in OnExitParticipant because Moderator left the room."
        lblModeratorAlias.Caption = "<Moderator Alias>"
        cmdHWhisperToModerator.Enabled = False
        cmdGWhisperToModerator.Enabled = False
    End If
    If lGuestID = ParticipantID Then
        lGuestID = 0
        WriteLogFile "lGuestID set to 0 in OnExitParticipant because Guest left the room."
        lblGuestAlias.Caption = "<Guest Alias>"
        cmdWhisperToGuest.Enabled = False
    End If

    Exit Sub

errOnExitPart:
    DisplayError "OnExitParticipant"
End Sub

Private Function nFindParticipantIndex(ParticipantID As Long) As Integer
    Dim nCnt As Integer

    On Error GoTo errFindPartIndex

    nFindParticipantIndex = -1
    For nCnt = 0 To lstPartID.ListCount - 1
        If lstPartID.ItemData(nCnt) = ParticipantID Then
            nFindParticipantIndex = nCnt
            Exit For
        End If
    Next nCnt
    Exit Function

errFindPartIndex:
    DisplayError "nFindPArticipantIndex"
End Function

Private Function nFindHostIndex(ParticipantID As Long) As Integer
    Dim nCnt As Integer

    On Error GoTo errFindHostIndex

    nFindHostIndex = -1
    For nCnt = 0 To WhispToHosts.lstHosts.ListCount - 1
        If WhispToHosts.lstHosts.ItemData(nCnt) = ParticipantID Then
            nFindHostIndex = nCnt
            Exit For
        End If
    Next nCnt
    Exit Function

errFindHostIndex:
    DisplayError "nFindHostIndex"
End Function

Private Function lFindParticipantID(Alias As String) As Long
    Dim nCnt As Integer
    Dim nAliasLen As Integer

    On Error GoTo errFindPartID

    lFindParticipantID = 0
    For nCnt = 0 To lstPartID.ListCount - 1
        nAliasLen = InStr(lstPartID.List(nCnt), ",") - 1
        If Left(lstPartID.List(nCnt), nAliasLen) = Alias Then
            lFindParticipantID = lstPartID.ItemData(nCnt)
            Exit For
        End If
    Next nCnt
    Exit Function

errFindPartID:
    DisplayError "lFindParticipantID"
End Function

Private Function nFindStatus(Alias As String) As Integer
    Dim nCnt As Integer
    Dim nAliasLen As Integer

    On Error GoTo errFindStatus

    nFindStatus = 0
    For nCnt = 0 To lstPartID.ListCount - 1
        nAliasLen = InStr(lstPartID.List(nCnt), ",") - 1
        If Left(lstPartID.List(nCnt), nAliasLen) = Alias Then
            nFindStatus = CInt(Mid(lstPartID.List(nCnt), nAliasLen + 2))
            Exit For
        End If
    Next nCnt
    Exit Function

errFindStatus:
    DisplayError "nFindStatus"
End Function
Private Sub MSChat_OnMessage(ByVal SenderID As Long, ByVal Message As Variant, ByVal MessageType As Integer)
    On Error GoTo errOnMessage

    If (optGuest.Value) Then
        If (VarType(Message) = vbInteger) Then
            If (Message = 1984) Then
                lModeratorID = SenderID
                WriteLogFile "lModeratorID set to " + CStr(SenderID) + " in OnMessage handler."
                cmdGWhisperToModerator.Enabled = True
            End If
        End If
    End If

    Exit Sub

errOnMessage:
    DisplayError "OnMessage"
End Sub

Private Sub MSChat_OnParticipantAliasChanged(ByVal ParticipantID As Long, ByVal OldAlias As String, ByVal NewAlias As String)
    On Error GoTo errOnPartAliasChanged

    Dim nLstPos As Integer

    nLstPos = nFindParticipantIndex(ParticipantID)
    If (nLstPos <> -1) Then
        lstPartID.List(nLstPos) = NewAlias + "," + Mid(lstPartID.List(nLstPos), Len(OldAlias) + 2)
    Else
        WriteLogFile "Couldn't find participant in OnParticipantAliasChanged(" + CStr(ParticipantID) + "," + OldAlias + "," + NewAlias + ")"
    End If

    nLstPos = nFindHostIndex(ParticipantID)
    If nLstPos >= 0 Then
        WhispToHosts.lstHosts.RemoveItem nLstPos
        WhispToHosts.lstHosts.AddItem NewAlias
        WhispToHosts.lstHosts.ItemData(WhispToHosts.lstHosts.NewIndex) = ParticipantID
    End If

    Exit Sub

errOnPartAliasChanged:
    DisplayError "OnParticipantAliasChanged"
End Sub

Private Sub MSChat_OnParticipantRealName(ByVal Alias As String, ByVal RealName As String)

    On Error GoTo errOnPartRealName

    Select Case nRealNameType
    Case RN_MODERATOR:
        lModeratorID = lFindParticipantID(Alias)
        If (lModeratorID <> 0) Then
            If (lModeratorID <> MSChat.ThisParticipantID) Then
                WriteLogFile "lModeratorID set to " + CStr(lModeratorID) + " (" + Alias + ") in OnParticipantRealName handler."
                lblModeratorAlias.Caption = Alias
                cmdHWhisperToModerator.Enabled = True
            Else
                MsgBox "You cannot select yourself as the Moderator. Please select another member.", vbOKOnly, "Executive Chat Monitoring Tool"
                lModeratorID = 0
            End If
        Else
            WriteLogFile "Couldn't find Moderator in OnParticipantRealName(" + Alias + "," + RealName + ")"
        End If

    Case RN_GUEST:
        lGuestID = lFindParticipantID(Alias)
        If (lGuestID <> 0) Then
            If (lGuestID <> MSChat.ThisParticipantID) Then
                cmdWhisperToGuest.Enabled = True
                lblGuestAlias.Caption = Alias
                MSChat.SendMessage lGuestID, 1, CVar(1984)
            Else
                MsgBox "You cannot select yourself as the Special Guest. Please select another member.", vbOKOnly, "Executive Chat Monitoring Tool"
                lGuestID = 0
            End If
        Else
            WriteLogFile "Couldn't find Guest in OnParticipantRealName(" + Alias + "," + RealName + ")"
        End If

    Case RN_PARTICIPANT:
        HostPanel.lblAsyncAlias.Caption = Alias
        HostPanel.lblAsyncRealName.Caption = RealName
    End Select

    Exit Sub

errOnPartRealName:
    DisplayError "OnParticipantRealName"
End Sub

Private Sub MSChat_OnParticipantStatusChanged(ByVal ParticipantID As Long, ByVal Status As Integer)

    On Error GoTo errOnPartStatusChanged

    Dim nLstPos As Integer
    Dim nAliasLen As Integer
    Dim strAlias As String

    nLstPos = nFindParticipantIndex(ParticipantID)
    If nLstPos >= 0 Then
        nAliasLen = InStr(lstPartID.List(nLstPos), ",") - 1
        If (nAliasLen) Then
            strAlias = Left(lstPartID.List(nLstPos), nAliasLen)
            lstPartID.List(nLstPos) = strAlias + "," + CStr(Status)
        End If
    Else
        WriteLogFile "Couldn't find participant in OnParticipantStatusChanged(" + CStr(ParticipantID) + "," + CStr(Status) + ")"
    End If

    nLstPos = nFindHostIndex(ParticipantID)
    If (nLstPos >= 0) And (Not (Status And STAT_HOST)) Then
        WhispToHosts.lstHosts.RemoveItem nLstPos
    End If

    If (Status And STAT_HOST) And (nLstPos = -1) Then
        WhispToHosts.lstHosts.AddItem strAlias
        WhispToHosts.lstHosts.ItemData(WhispToHosts.lstHosts.NewIndex) = ParticipantID
    End If
    Exit Sub

errOnPartStatusChanged:
    DisplayError "OnParticipantStatusChanged"
End Sub

Private Sub MSChat_OnRoomTopicChanged(ByVal NewRoomTopic As String)
    On Error Resume Next
    lblRoomTopic.Caption = NewRoomTopic
End Sub

Private Sub MSChat_OnStateChanged(ByVal NewState As Integer)
    SetState NewState
End Sub

Private Sub optGuest_Click()
    On Error Resume Next
    HostPanel.Hide
    MSChat.UIOption = UIOPT_GUEST
    MSChat.Top = frmGuest.Top + frmGuest.Height + 100
    MSChat.Height = ExecC.Height - StatusBar.Height - MSChat.Top - 500
    frmHost.Visible = False
    frmModerator.Visible = False
    frmGuest.Visible = True
End Sub

Private Sub optHost_Click()
    On Error Resume Next
    HostPanel.Show
    MSChat.UIOption = UIOPT_HOST
    MSChat.Top = frmHost.Top + frmHost.Height + 100
    MSChat.Height = ExecC.Height - StatusBar.Height - MSChat.Top - 500
    frmHost.Visible = True
    frmModerator.Visible = False
    frmGuest.Visible = False
End Sub

Private Sub optModerator_Click()
    On Error Resume Next
    HostPanel.Hide
    MSChat.UIOption = UIOPT_MODERATOR
    MSChat.Top = frmModerator.Top + frmModerator.Height + 100
    MSChat.Height = ExecC.Height - StatusBar.Height - MSChat.Top - 500
    frmHost.Visible = False
    frmModerator.Visible = True
    frmGuest.Visible = False
End Sub


GLOBALS.BAS

'****************************************************************************
'
'  Module:     ExecChat.EXE
'  File:       Globals.bas
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Attribute VB_Name = "Globals"
Option Explicit

Global Const MASK_PRIVILEGES = 1
Global Const MASK_IGNORED = 4

Global Const STAT_HOST = 1
Global Const STAT_PARTICIPANT = 2
Global Const STAT_SPECTATOR = 4
Global Const STAT_IGNORED = 16

Global Const RN_MODERATOR = 1
Global Const RN_GUEST = 2
Global Const RN_PARTICIPANT = 3

Global Const APP_TITLE = "Executive Chat Monitoring Tool"

Global Const UIOPT_HOST = 3596
Global Const UIOPT_MODERATOR = 3596
Global Const UIOPT_GUEST = 3865

Global Const MSCHAT_TOP = 2160
Global Const FUNCTIONS_TOP = 1440

Global nLogFile As Integer
Global nRealNameType As Integer
Global lModeratorID As Long
Global lGuestID As Long

Declare Function GetPrivateProfileStringA Lib "Kernel32" (ByVal lpApplicationName As String, lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer

Sub CloseLogFile()
    On Error Resume Next
    WriteLogFile "End of Log"
    Close #nLogFile   ' Close file.
End Sub

Sub DisplayError(strProcedure As String)
    If (Err.Number < 1000 Or Err.Number > 1090) Then
        MsgBox Err.Description, vbOKOnly, "Error " + CStr(Err.Number) + " in " + strProcedure
    End If
    WriteLogFile Err.Description + ", Error " + CStr(Err.Number) + " in " + strProcedure
End Sub

Sub OpenLogFile()

    On Error Resume Next

    Dim nSuffix As Integer

    nLogFile = FreeFile   ' Get unused file number.
    nSuffix = 0

    Do
        Err.Clear
        Open "ExecChat" & CStr(nSuffix) & ".Log" For Output Lock Write As #nLogFile ' Create filename.
        nSuffix = nSuffix + 1
    Loop Until (Err.Number = 0 Or nSuffix > 32)

    If (Err.Number = 0) Then
        Write #nLogFile, "ExecChat Log File"  ' Output text.
    End If
End Sub

Function VBGetPrivateProfileString(sAppName$, sKeyName$, sDefault$, sFileName$) As String
Static sProfileBuf As String * 127
Dim nret%

  On Error GoTo errVBGetPrivateProfileString

 'nret% = GetPrivateProfileString(sAppName$, ByVal sKeyName$, sDefault$, sProfileBuf$, Len(sProfileBuf$), sIniDir$ + sFileName$)
  nret% = GetPrivateProfileStringA(sAppName$, ByVal sKeyName$, sDefault$, sProfileBuf$, Len(sProfileBuf$), sFileName$)
  If nret% = 0 Then
    VBGetPrivateProfileString = ""
  Else
    VBGetPrivateProfileString = Left$(sProfileBuf$, nret%)
  End If
  Exit Function

errVBGetPrivateProfileString:
    DisplayError "VBGetPrivateProfileString"
    VBGetPrivateProfileString = ""
End Function

Sub WriteLogFile(strLog As String)
    On Error Resume Next
    Write #nLogFile, strLog  ' Output text.
End Sub


HOSTPAN.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     ExecChat.EXE
'  File:       HostPan.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form HostPanel
   BackColor       =   &H00C0FFC0&
   Caption         =   "Host Monitoring Panel"
   ClientHeight    =   2985
   ClientLeft      =   1095
   ClientTop       =   5325
   ClientWidth     =   9765
   Height          =   3390
   Left            =   1035
   LinkTopic       =   "HostPanel"
   ScaleHeight     =   2985
   ScaleWidth      =   9765
   Top             =   4980
   Width           =   9885
   Begin VB.Frame Frame1
      BackColor       =   &H00C0FFC0&
      Caption         =   "Privileges of the Selected Member"
      Height          =   615
      Left            =   4440
      TabIndex        =   22
      Top             =   120
      Width           =   5295
      Begin VB.CommandButton cmdMakeSpectator
         Caption         =   "Make Spectator"
         Height          =   255
         Left            =   120
         TabIndex        =   25
         Top             =   240
         Width           =   1695
      End
      Begin VB.CommandButton cmdMakeParticipant
         Caption         =   "Make Participant"
         Height          =   255
         Left            =   1920
         TabIndex        =   24
         Top             =   240
         Width           =   1695
      End
      Begin VB.CommandButton cmdMakeHost
         Caption         =   "Make Host"
         Height          =   255
         Left            =   3720
         TabIndex        =   23
         Top             =   240
         Width           =   1455
      End
   End
   Begin VB.Frame Frame2
      BackColor       =   &H00C0FFC0&
      Caption         =   "Filtering of the Selected Member"
      Height          =   615
      Left            =   4440
      TabIndex        =   19
      Top             =   840
      Width           =   5295
      Begin VB.CommandButton cmdIgnore
         Caption         =   "Ignore Member"
         Height          =   255
         Left            =   120
         TabIndex        =   21
         Top             =   240
         Width           =   2415
      End
      Begin VB.CommandButton cmdDoNotIgnore
         Caption         =   "Don't Ignore Member"
         Height          =   255
         Left            =   2640
         TabIndex        =   20
         Top             =   240
         Width           =   2535
      End
   End
   Begin VB.Frame Frame3
      BackColor       =   &H00C0FFC0&
      Caption         =   "Set New Room Topic"
      ForeColor       =   &H00000000&
      Height          =   615
      Left            =   120
      TabIndex        =   16
      Top             =   1560
      Width           =   4215
      Begin VB.TextBox txtRoomTopic
         Height          =   285
         Left            =   120
         TabIndex        =   18
         Top             =   240
         Width           =   3015
      End
      Begin VB.CommandButton cmdSetTopic
         Caption         =   "Set Topic"
         Height          =   285
         Left            =   3240
         TabIndex        =   17
         Top             =   240
         Width           =   855
      End
   End
   Begin VB.Frame Frame4
      BackColor       =   &H00C0FFC0&
      Caption         =   "Room Capacity"
      Height          =   615
      Left            =   4440
      TabIndex        =   12
      Top             =   1560
      Width           =   5295
      Begin VB.TextBox txtMaxMembers
         Height          =   285
         Left            =   120
         TabIndex        =   15
         Top             =   240
         Width           =   975
      End
      Begin VB.CommandButton cmdSetMaxMembers
         Caption         =   "Set New Capacity"
         Height          =   285
         Left            =   1200
         TabIndex        =   14
         Top             =   240
         Width           =   1695
      End
      Begin VB.CommandButton cmdGetMaxMembers
         Caption         =   "Get Current Capacity"
         Height          =   285
         Left            =   3000
         TabIndex        =   13
         Top             =   240
         Width           =   2175
      End
   End
   Begin VB.Frame Frame5
      BackColor       =   &H00C0FFC0&
      Caption         =   "Police Functions"
      Height          =   615
      Left            =   120
      TabIndex        =   10
      Top             =   2280
      Width           =   4215
      Begin VB.CommandButton cmdKickBan
         Caption         =   "Kick, Ban or Re-instate the Selected Member"
         Height          =   255
         Left            =   120
         TabIndex        =   11
         Top             =   240
         Width           =   3975
      End
   End
   Begin VB.Frame Frame7
      BackColor       =   &H00C0FFC0&
      Caption         =   "Control UI Settings"
      Height          =   615
      Left            =   4440
      TabIndex        =   7
      Top             =   2280
      Width           =   5295
      Begin VB.CommandButton cmdClearHistory
         Caption         =   "Clear History"
         Height          =   255
         Left            =   120
         TabIndex        =   9
         Top             =   240
         Width           =   2535
      End
      Begin VB.CommandButton cmdChangeControlUI
         Caption         =   "Change Control UI"
         Height          =   255
         Left            =   2760
         TabIndex        =   8
         Top             =   240
         Width           =   2415
      End
   End
   Begin VB.Frame Frame6
      BackColor       =   &H00C0FFC0&
      Caption         =   "Real Name of"
      Height          =   1335
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   4215
      Begin VB.CommandButton cmdGetRealName
         Caption         =   "Get    Real Name"
         Height          =   640
         Left            =   3240
         TabIndex        =   4
         Top             =   240
         Width           =   855
      End
      Begin VB.OptionButton optSelection
         BackColor       =   &H00C0FFC0&
         Caption         =   "the Selected Member"
         Height          =   255
         Left            =   120
         TabIndex        =   3
         Top             =   240
         Value           =   -1  'True
         Width           =   3015
      End
      Begin VB.OptionButton optAlias
         BackColor       =   &H00C0FFC0&
         Caption         =   "this Alias"
         Height          =   255
         Left            =   120
         TabIndex        =   2
         Top             =   500
         Width           =   1095
      End
      Begin VB.TextBox txtAlias
         Height          =   285
         Left            =   1200
         TabIndex        =   1
         Top             =   480
         Width           =   1935
      End
      Begin VB.Label lblAsyncAlias
         BackColor       =   &H00FFFFFF&
         Caption         =   "Alias appears here"
         BeginProperty Font
            name            =   "Arial Narrow"
            charset         =   0
            weight          =   400
            size            =   8.25
            underline       =   0   'False
            italic          =   0   'False
            strikethrough   =   0   'False
         EndProperty
         Height          =   255
         Left            =   120
         TabIndex        =   6
         Top             =   960
         Width           =   1335
      End
      Begin VB.Label lblAsyncRealName
         BackColor       =   &H00FFFFFF&
         Caption         =   "Real name appears here"
         BeginProperty Font
            name            =   "Arial Narrow"
            charset         =   0
            weight          =   400
            size            =   8.25
            underline       =   0   'False
            italic          =   0   'False
            strikethrough   =   0   'False
         EndProperty
         Height          =   255
         Left            =   1560
         TabIndex        =   5
         Top             =   960
         Width           =   2535
      End
   End
End
Attribute VB_Name = "HostPanel"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit

Sub cmdGetMaxMembers_Click()
    On Error Resume Next
    txtMaxMembers.Text = CStr(ExecC.MSChat.MaxMembers)
End Sub

Private Sub cmdChangeControlUI_Click()
    UISettings.Show
End Sub

Private Sub cmdDoNotIgnore_Click()
    On Error Resume Next
    ExecC.MSChat.SetParticipantStatus -1, MASK_IGNORED, 0
End Sub

Private Sub cmdGetRealName_Click()
    On Error GoTo errGetRealName

    Dim txtReal As String

    lblAsyncAlias.Caption = "Alias appears here"
    lblAsyncRealName.Caption = "Real name appears here"

    nRealNameType = RN_PARTICIPANT
    If (optSelection.Value) Then
        ExecC.MSChat.GetParticipantRealName -1, "", False, txtReal
    Else
        ExecC.MSChat.GetParticipantRealName 0, txtAlias.Text, False, txtReal
    End If

    Exit Sub

errGetRealName:
    DisplayError "cmdGetRealName_Click"
End Sub

Private Sub cmdIgnore_Click()
    On Error Resume Next
    ExecC.MSChat.SetParticipantStatus -1, MASK_IGNORED, STAT_IGNORED
End Sub

Private Sub cmdKickBan_Click()
    KickBan.Show
End Sub

Private Sub cmdMakeHost_Click()
    On Error Resume Next
    ExecC.MSChat.SetParticipantStatus -1, MASK_PRIVILEGES, STAT_HOST
End Sub

Private Sub cmdMakeParticipant_Click()
    On Error Resume Next
    ExecC.MSChat.SetParticipantStatus -1, MASK_PRIVILEGES, STAT_PARTICIPANT
End Sub

Private Sub cmdMakeSpectator_Click()
    On Error Resume Next
    ExecC.MSChat.SetParticipantStatus -1, MASK_PRIVILEGES, STAT_SPECTATOR
End Sub

Private Sub cmdSetMaxMembers_Click()
    On Error Resume Next
    ExecC.MSChat.MaxMembers = CLng(txtMaxMembers.Text)
End Sub

Private Sub cmdSetTopic_Click()
    On Error Resume Next
    ExecC.MSChat.RoomTopic = txtRoomTopic.Text
End Sub

Private Sub optAlias_Click()
    txtAlias.Enabled = True
End Sub

Private Sub optSelection_Click()
    txtAlias.Enabled = False
End Sub


ECENTER.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     ExecChat.Exe
'  File:       ECenter.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form EnterOptions
   BackColor       =   &H00FFFFC0&
   BorderStyle     =   3  'Fixed Dialog
   Caption         =   "Enter Room Options"
   ClientHeight    =   2325
   ClientLeft      =   1890
   ClientTop       =   1980
   ClientWidth     =   6945
   Height          =   2730
   Left            =   1830
   LinkTopic       =   "EnterOptions"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   ScaleHeight     =   2325
   ScaleWidth      =   6945
   ShowInTaskbar   =   0   'False
   Top             =   1635
   Width           =   7065
   Begin VB.CheckBox chkMicOnly
      BackColor       =   &H00FFFFC0&
      Caption         =   "MIC only room?"
      Height          =   255
      Left            =   120
      TabIndex        =   8
      Top             =   1800
      Width           =   1455
   End
   Begin VB.TextBox txtRoomPath
      Height          =   285
      Left            =   120
      TabIndex        =   2
      Top             =   1200
      Width           =   4095
   End
   Begin VB.TextBox txtAlias
      Height          =   285
      Left            =   120
      TabIndex        =   0
      Top             =   360
      Width           =   4095
   End
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Default         =   -1  'True
      Height          =   375
      Left            =   4440
      TabIndex        =   3
      Top             =   1800
      Width           =   1095
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   5640
      TabIndex        =   4
      Top             =   1800
      Width           =   1095
   End
   Begin VB.TextBox txtRoomPwd
      Height          =   285
      Left            =   4800
      TabIndex        =   1
      Top             =   360
      Width           =   1935
   End
   Begin VB.Label Label7
      BackColor       =   &H00FFFFC0&
      Caption         =   "Type your alias:"
      Height          =   255
      Left            =   120
      TabIndex        =   7
      Top             =   120
      Width           =   3975
   End
   Begin VB.Label Label8
      BackColor       =   &H00FFFFC0&
      Caption         =   "Type the chat room path:"
      Height          =   255
      Index           =   0
      Left            =   120
      TabIndex        =   6
      Top             =   960
      Width           =   3975
   End
   Begin VB.Label lblRoomPwd
      BackColor       =   &H00FFFFC0&
      Caption         =   "Type the optional host key:"
      Height          =   255
      Left            =   4800
      TabIndex        =   5
      Top             =   120
      Width           =   1935
   End
End
Attribute VB_Name = "EnterOptions"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit

' Channel creation modes
Const OCX_CHANNEL_CREATE_JOIN = 1

Const OCX_CHANNEL_FLAG_LOCAL = 2     ' channel not exposed to other servers on net
Const OCX_CHANNEL_FLAG_FEED = 4      ' news feeds, stock quotes..
Const OCX_CHANNEL_FLAG_MICONLY = 8   ' NO IRC - NO NO NO IRC
Const OCX_CHANNEL_FLAG_AUTHONLY = 16 ' permit authenticated members only
Const OCX_CHANNEL_FLAG_AUTHTALK = 32 ' permit only authenticated members to talk

Const OCX_CHANNEL_PUBLIC = 1         ' open to anybody and anything
Const OCX_CHANNEL_TOPICOP = 2        ' topic can be changed only by Host
Const OCX_CHANNEL_NOREMOTE = 4       ' only allow users who logged into THIS server..to enter chat
Const OCX_CHANNEL_NOWHISPER = 8      ' no whispers or \msg allowed
Const OCX_CHANNEL_AUDITORIUM = 16    ' auditorium chat room
Const OCX_CHANNEL_MODERATED = 32     ' join as spectator by default

Dim nFlags As Integer
Dim nType As Integer

Private Sub cmdCancel_Click()
    Hide
End Sub

Private Sub cmdOK_Click()

    On Error Resume Next

    Dim nFlags As Integer

    If (chkMicOnly.Value = 1) Then
        nFlags = 9  'Mic only room
    Else
        nFlags = 1  'Mic and Irc room
    End If

    ExecC.MSChat.EnterRoom txtRoomPath.Text, txtRoomPwd.Text, txtAlias.Text, "NTLM;ANON", nFlags, 0

    If (Err.Number = 0) Then
        ExecC.lblAlias.Caption = txtAlias.Text
        ExecC.StatusBar.Panels(3).Text = txtRoomPath.Text
        If (ExecC.optHost.Value) Then
            WriteLogFile "Entering room " + txtRoomPath.Text + " as host."
        Else
            If (ExecC.optModerator.Value) Then
                WriteLogFile "Entering room " + txtRoomPath.Text + " as moderator."
            Else
                WriteLogFile "Entering room " + txtRoomPath.Text + " as guest."
            End If
        End If
        WriteLogFile "Alias: " + txtAlias.Text
    End If

    Hide
End Sub

Private Sub Form_Load()

    txtRoomPath.Text = VBGetPrivateProfileString("Common Default Entries", "RoomPath", "//Comicsrv1.microsoft.com/#ExecChat", App.Path + "\ExecChat.ini")
    txtRoomPwd.Text = VBGetPrivateProfileString("Common Default Entries", "HostKey", "", App.Path + "\ExecChat.ini")

    If (ExecC.optHost.Value) Then
        txtAlias.Text = VBGetPrivateProfileString("Host Default Entries", "HostAlias", "Host", App.Path + "\ExecChat.ini")
    End If
    If (ExecC.optModerator.Value) Then
        txtAlias.Text = VBGetPrivateProfileString("Moderator Default Entries", "ModeratorAlias", "Moderator", App.Path + "\ExecChat.ini")
    End If
    If (ExecC.optGuest.Value) Then
        txtAlias.Text = VBGetPrivateProfileString("Guest Default Entries", "GuestAlias", "Guest", App.Path + "\ExecChat.ini")
    End If

    If (InStr(txtRoomPath.Text, "/#") Or InStr(txtRoomPath.Text, "\#")) Then
        chkMicOnly.Value = 0
    Else
        chkMicOnly.Value = 1
    End If
End Sub

Private Sub txtRoomPath_Change()
    If (InStr(txtRoomPath.Text, "/#") Or InStr(txtRoomPath.Text, "\#")) Then
        chkMicOnly.Value = 0
    Else
        chkMicOnly.Value = 1
    End If
End Sub


SENDTOR.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     ExecChat.EXE
'  File:       SendToR.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form SendToRoom
   BackColor       =   &H00FFFFC0&
   Caption         =   "Send To Entire Room"
   ClientHeight    =   3240
   ClientLeft      =   2040
   ClientTop       =   1995
   ClientWidth     =   6690
   Height          =   3645
   Left            =   1980
   LinkTopic       =   "SendToRoom"
   ScaleHeight     =   3240
   ScaleWidth      =   6690
   Top             =   1650
   Width           =   6810
   Begin VB.TextBox txtMessage
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   400
         size            =   9.75
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   2295
      Left            =   120
      MultiLine       =   -1  'True
      ScrollBars      =   2  'Vertical
      TabIndex        =   0
      Top             =   360
      Width           =   6495
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   5760
      TabIndex        =   2
      Top             =   2760
      Width           =   855
   End
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Height          =   375
      Left            =   4680
      TabIndex        =   1
      Top             =   2760
      Width           =   855
   End
   Begin VB.Label Label1
      BackColor       =   &H00FFFFC0&
      Caption         =   "Message to send:"
      Height          =   205
      Left            =   120
      TabIndex        =   3
      Top             =   120
      Width           =   1455
   End
End
Attribute VB_Name = "SendToRoom"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit
Private Sub cmdCancel_Click()
    Unload SendToRoom
End Sub

Private Sub cmdOK_Click()
    On Error Resume Next
    If (txtMessage.Text <> "") Then
        ExecC.MSChat.SendMessage -1, 0, Trim(txtMessage.Text)
    End If
    Unload SendToRoom
End Sub

Private Sub Form_Load()
    On Error Resume Next
    txtMessage.SetFocus
End Sub


WHISPTOG.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     ExecChat.EXE
'  File:       WhispToG.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form WhispToGuest
   BackColor       =   &H00FFFFC0&
   Caption         =   "Whisper to the Special Guest"
   ClientHeight    =   3240
   ClientLeft      =   2025
   ClientTop       =   1980
   ClientWidth     =   6690
   Height          =   3645
   Left            =   1965
   LinkTopic       =   "WhispToGuest"
   ScaleHeight     =   3240
   ScaleWidth      =   6690
   Top             =   1635
   Width           =   6810
   Begin VB.TextBox txtMessage
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   400
         size            =   9.75
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   2295
      Left            =   120
      MultiLine       =   -1  'True
      ScrollBars      =   2  'Vertical
      TabIndex        =   0
      Top             =   360
      Width           =   6495
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   5760
      TabIndex        =   2
      Top             =   2760
      Width           =   855
   End
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Height          =   375
      Left            =   4680
      TabIndex        =   1
      Top             =   2760
      Width           =   855
   End
   Begin VB.Label Label1
      BackColor       =   &H00FFFFC0&
      Caption         =   "Message to whisper:"
      Height          =   205
      Left            =   120
      TabIndex        =   3
      Top             =   120
      Width           =   1575
   End
End
Attribute VB_Name = "WhispToGuest"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit
Private Sub cmdCancel_Click()
    Unload WhispToGuest
End Sub

Private Sub cmdOK_Click()
    On Error GoTo errOK
    If (txtMessage.Text <> "") Then
        ExecC.MSChat.SendMessage lGuestID, 1, Trim(txtMessage.Text)
    End If
    Unload WhispToGuest
    Exit Sub

errOK:
    DisplayError "WhisperToGuest.cmdOK_Click"
End Sub

Private Sub Form_Load()
    On Error Resume Next
    txtMessage.SetFocus
End Sub


WHISPTOH.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     ExecChat.EXE
'  File:       WhispToH.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form WhispToHosts
   BackColor       =   &H00FFFFC0&
   Caption         =   "Whisper to Hosts"
   ClientHeight    =   3855
   ClientLeft      =   2055
   ClientTop       =   2010
   ClientWidth     =   7125
   Height          =   4260
   Left            =   1995
   LinkTopic       =   "Form1"
   ScaleHeight     =   3855
   ScaleWidth      =   7125
   Top             =   1665
   Width           =   7245
   Begin VB.TextBox txtMessage
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   400
         size            =   9.75
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   2895
      Left            =   2640
      MultiLine       =   -1  'True
      ScrollBars      =   2  'Vertical
      TabIndex        =   0
      Top             =   360
      Width           =   4335
   End
   Begin VB.CommandButton Cancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   6120
      TabIndex        =   3
      Top             =   3360
      Width           =   855
   End
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Height          =   375
      Left            =   5040
      TabIndex        =   2
      Top             =   3360
      Width           =   855
   End
   Begin VB.ListBox lstHosts
      Height          =   3375
      Left            =   120
      MultiSelect     =   2  'Extended
      TabIndex        =   1
      Top             =   360
      Width           =   2295
   End
   Begin VB.Label Label2
      BackColor       =   &H00FFFFC0&
      Caption         =   "Message To Whisper:"
      Height          =   210
      Left            =   2640
      TabIndex        =   5
      Top             =   120
      Width           =   1695
   End
   Begin VB.Label Label1
      BackColor       =   &H00FFFFC0&
      Caption         =   "Host List:"
      Height          =   205
      Left            =   120
      TabIndex        =   4
      Top             =   120
      Width           =   735
   End
End
Attribute VB_Name = "WhispToHosts"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit
Private Sub Cancel_Click()
    txtMessage.Text = ""
    Hide
End Sub

Private Sub cmdOK_Click()
    On Error Resume Next

    ' get the selected hosts and construct an array of longs
    Dim rglHostIDs(0 To 15) As Long
    Dim nCnt As Integer
    Dim nArrayIndex As Integer

    nArrayIndex = 0
    For nCnt = 0 To lstHosts.ListCount - 1
        If lstHosts.Selected(nCnt) Then
            If nArrayIndex > 15 Then
                MsgBox "Too many hosts are selected. You are limited to 10 hosts.", vbOKOnly, "Executive Chat Monitoring Tool"
                Exit For
            End If
            If (lstHosts.ItemData(nCnt) <> ExecC.MSChat.ThisParticipantID) Then
                rglHostIDs(nArrayIndex) = lstHosts.ItemData(nCnt)
                nArrayIndex = nArrayIndex + 1
            End If
        End If
    Next nCnt

    If (ExecC.MSChat.State = 3 And nArrayIndex > 0 And txtMessage.Text <> "") Then
        ExecC.MSChat.SendMessage rglHostIDs, nArrayIndex, Trim(txtMessage.Text)
    End If

    txtMessage.Text = ""
    Hide
End Sub

Private Sub Form_Activate()
    On Error Resume Next
    txtMessage.SetFocus
End Sub


WHISPTOM.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     ExecChat.EXE
'  File:       WhispToM.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form WhispToMod
   BackColor       =   &H00FFFFC0&
   Caption         =   "Whisper to the Moderator"
   ClientHeight    =   3240
   ClientLeft      =   2025
   ClientTop       =   1995
   ClientWidth     =   6690
   Height          =   3645
   Left            =   1965
   LinkTopic       =   "WhispToMod"
   ScaleHeight     =   3240
   ScaleWidth      =   6690
   Top             =   1650
   Width           =   6810
   Begin VB.TextBox txtMessage
      BeginProperty Font
         name            =   "MS Sans Serif"
         charset         =   0
         weight          =   400
         size            =   9.75
         underline       =   0   'False
         italic          =   0   'False
         strikethrough   =   0   'False
      EndProperty
      Height          =   2295
      Left            =   120
      MultiLine       =   -1  'True
      ScrollBars      =   2  'Vertical
      TabIndex        =   0
      Top             =   360
      Width           =   6495
   End
   Begin VB.CommandButton cmdCancel
      Caption         =   "Cancel"
      Height          =   375
      Left            =   5640
      TabIndex        =   2
      Top             =   2760
      Width           =   975
   End
   Begin VB.CommandButton cmdOK
      Caption         =   "OK"
      Height          =   375
      Left            =   4440
      TabIndex        =   1
      Top             =   2760
      Width           =   975
   End
   Begin VB.Label Label1
      BackColor       =   &H00FFFFC0&
      Caption         =   "Message to whisper:"
      Height          =   205
      Left            =   120
      TabIndex        =   3
      Top             =   120
      Width           =   1575
   End
End
Attribute VB_Name = "WhispToMod"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit
Private Sub cmdCancel_Click()
    Unload WhispToMod
End Sub

Private Sub cmdOK_Click()
    On Error Resume Next
    If (txtMessage.Text <> "") Then
        ExecC.MSChat.SendMessage lModeratorID, 1, Trim(txtMessage.Text)
    End If
    Unload WhispToMod
End Sub

Private Sub Form_Load()
    On Error Resume Next
    txtMessage.SetFocus
End Sub


PLAYBD.FRM

VERSION 5.00
'****************************************************************************
'
'  Module:     PlayBd.ocx
'  File:       PlayBd.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Object = "{A2BBC341-F930-11D0-99CB-00C04FD64497}#3.0#0"; "PlayBd.ocx"
Begin VB.Form Form1
   BackColor       =   &H00FFFF00&
   Caption         =   "Form1"
   ClientHeight    =   5100
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   9240
   LinkTopic       =   "Form1"
   ScaleHeight     =   5100
   ScaleWidth      =   9240
   StartUpPosition =   3  'Windows Default
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   23
      Left            =   8880
      TabIndex        =   43
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   22
      Left            =   8880
      TabIndex        =   42
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   21
      Left            =   8520
      TabIndex        =   41
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   20
      Left            =   8520
      TabIndex        =   40
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   19
      Left            =   8160
      TabIndex        =   39
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   18
      Left            =   8160
      TabIndex        =   38
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   17
      Left            =   7800
      TabIndex        =   37
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   16
      Left            =   7800
      TabIndex        =   36
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   15
      Left            =   7440
      TabIndex        =   35
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   14
      Left            =   7440
      TabIndex        =   34
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   13
      Left            =   7080
      TabIndex        =   33
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   12
      Left            =   7080
      TabIndex        =   32
      Top             =   4320
      Width           =   255
   End
   Begin VB.CheckBox chkWhite
      BackColor       =   &H00FFFF80&
      Caption         =   "White?"
      Height          =   255
      Left            =   6480
      TabIndex        =   31
      Top             =   3960
      Width           =   975
   End
   Begin VB.CommandButton cmdSetCheckerPositions
      Caption         =   "Set"
      Height          =   255
      Left            =   8400
      TabIndex        =   30
      Top             =   3960
      Width           =   735
   End
   Begin VB.CommandButton cmdGetCheckerPositions
      Caption         =   "Get"
      Height          =   255
      Left            =   7560
      TabIndex        =   29
      Top             =   3960
      Width           =   735
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   11
      Left            =   6720
      TabIndex        =   27
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   10
      Left            =   6720
      TabIndex        =   26
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   9
      Left            =   6360
      TabIndex        =   25
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   8
      Left            =   6360
      TabIndex        =   24
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   7
      Left            =   6000
      TabIndex        =   23
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   6
      Left            =   6000
      TabIndex        =   22
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   5
      Left            =   5640
      TabIndex        =   21
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   4
      Left            =   5640
      TabIndex        =   20
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   3
      Left            =   5280
      TabIndex        =   19
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   2
      Left            =   5280
      TabIndex        =   18
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   1
      Left            =   4920
      TabIndex        =   17
      Top             =   4680
      Width           =   255
   End
   Begin VB.TextBox txtCP
      Height          =   285
      Index           =   0
      Left            =   4920
      TabIndex        =   16
      Top             =   4320
      Width           =   255
   End
   Begin VB.TextBox txtEYD
      Height          =   285
      Left            =   8760
      TabIndex        =   14
      Top             =   2160
      Width           =   255
   End
   Begin VB.TextBox txtEXD
      Height          =   285
      Left            =   8400
      TabIndex        =   13
      Top             =   2160
      Width           =   255
   End
   Begin VB.TextBox txtEYO
      Height          =   285
      Left            =   7080
      TabIndex        =   12
      Top             =   2160
      Width           =   255
   End
   Begin VB.TextBox txtEXO
      Height          =   285
      Left            =   6720
      TabIndex        =   11
      Top             =   2160
      Width           =   255
   End
   Begin VB.CommandButton cmdResetCheckers
      Caption         =   "Reset Checkers"
      Height          =   255
      Left            =   6720
      TabIndex        =   9
      Top             =   600
      Width           =   1335
   End
   Begin VB.CommandButton cmdMoveTo
      Caption         =   "MoveTo"
      Height          =   255
      Left            =   7440
      TabIndex        =   8
      Top             =   1080
      Width           =   855
   End
   Begin VB.TextBox txtYD
      Height          =   285
      Left            =   8760
      TabIndex        =   7
      Text            =   "2"
      Top             =   1080
      Width           =   255
   End
   Begin VB.TextBox txtXD
      Height          =   285
      Left            =   8400
      TabIndex        =   6
      Text            =   "1"
      Top             =   1080
      Width           =   255
   End
   Begin VB.TextBox txtYO
      Height          =   285
      Left            =   7080
      TabIndex        =   5
      Text            =   "1"
      Top             =   1080
      Width           =   255
   End
   Begin VB.TextBox txtXO
      Height          =   285
      Left            =   6720
      TabIndex        =   4
      Text            =   "2"
      Top             =   1080
      Width           =   255
   End
   Begin VB.CommandButton cmdSetSize
      Caption         =   "Set"
      Height          =   255
      Left            =   8160
      TabIndex        =   2
      Top             =   120
      Width           =   735
   End
   Begin VB.TextBox txtBoardSize
      Height          =   285
      Left            =   7680
      TabIndex        =   1
      Text            =   "8"
      Top             =   120
      Width           =   375
   End
   Begin Project1.PlayBoard PlayBoard1
      Height          =   3135
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   4815
      _ExtentX        =   8493
      _ExtentY        =   5530
   End
   Begin VB.Label Label6
      BackStyle       =   0  'Transparent
      Caption         =   "Y"
      Height          =   255
      Left            =   4560
      TabIndex        =   45
      Top             =   4680
      Width           =   255
   End
   Begin VB.Label Label5
      BackStyle       =   0  'Transparent
      Caption         =   "X"
      Height          =   255
      Left            =   4560
      TabIndex        =   44
      Top             =   4320
      Width           =   255
   End
   Begin VB.Label Label4
      Alignment       =   2  'Center
      BackStyle       =   0  'Transparent
      Caption         =   "Chercher Positions"
      Height          =   255
      Left            =   6840
      TabIndex        =   28
      Top             =   3600
      Width           =   2055
   End
   Begin VB.Label Label3
      Alignment       =   2  'Center
      BackStyle       =   0  'Transparent
      Caption         =   "moved to"
      Height          =   255
      Left            =   7440
      TabIndex        =   15
      Top             =   2160
      Width           =   855
   End
   Begin VB.Label Label2
      BackStyle       =   0  'Transparent
      Caption         =   "Event feedback:"
      Height          =   255
      Left            =   6720
      TabIndex        =   10
      Top             =   1800
      Width           =   1335
   End
   Begin VB.Label Label1
      BackStyle       =   0  'Transparent
      Caption         =   "BoardSize:"
      Height          =   255
      Left            =   6720
      TabIndex        =   3
      Top             =   120
      Width           =   855
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit

Private Sub cmdGetCheckerPositions_Click()
    Dim vrgnPos As Variant
    Dim i As Integer

    vrgnPos = PlayBoard1.CheckerPositions(chkWhite.Value = 1)
    For i = 1 To (PlayBoard1.Size - 2) * PlayBoard1.Size / 2
        txtCP(i - 1).Text = vrgnPos(i)
    Next i
End Sub

Private Sub cmdMoveTo_Click()
    PlayBoard1.MoveChecker CInt(txtXO.Text), CInt(txtYO.Text), CInt(txtXD.Text), CInt(txtYD.Text)
End Sub

Private Sub cmdResetCheckers_Click()
    PlayBoard1.ResetCheckers
End Sub

Private Sub cmdSetCheckerPositions_Click()
    Dim vrgnPos() As Variant
    Dim i As Integer

    ReDim vrgnPos(1 To (PlayBoard1.Size - 2) * PlayBoard1.Size / 2)

    For i = 1 To (PlayBoard1.Size - 2) * PlayBoard1.Size / 2
        vrgnPos(i) = CInt(txtCP(i - 1).Text)
    Next i
    PlayBoard1.CheckerPositions(chkWhite.Value = 1) = vrgnPos
End Sub

Private Sub cmdSetSize_Click()
    PlayBoard1.Size = CInt(txtBoardSize.Text)
End Sub

Private Sub Form_Load()
    PlayBoard1.Left = 0
    PlayBoard1.Top = 0
    PlayBoard1.Height = 4000
    PlayBoard1.Width = 5500
End Sub

Private Sub PlayBoard1_OnCheckerMoved(FromMoveCheckerCall As Boolean, XO As Integer, YO As Integer, XD As Integer, YD As Integer)
    txtEXO.Text = CStr(XO)
    txtEYO.Text = CStr(YO)
    txtEXD.Text = CStr(XD)
    txtEYD.Text = CStr(YD)
    Beep
End Sub


README.TXT

------------------------------------------------------------------
    Microsoft Chat SDK 1.1 Trnscrpt Readme File
                         August 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE TRNSCRPT SAMPLE
BUILDING THE TRNSCRPT SAMPLE

OVERVIEW OF THE TRNSCRPT SAMPLE
==============================
Trnscrpt contains a VB Application which provides a transcript of the chat
session.

The Transcript sample allows you to record a chat session and participate
in the chat at the same time. The user can set the time interval the chat
should be recorded in the text file and also the sample will reconnect to the
room, if the chat server goes down.

TRNSCRPT THE TRNSCRPT SAMPLE
===========================

The trnscrpt sample can be built with Visual Basic 4.0.

1. Select the Open project command from the File menu.

2. Open the trnscrpt.vbp file and build the application.


FORM2.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     Trnscrpt.EXE
'  File:       Form2.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form Form2
   Caption         =   "Current Chat Transcript"
   ClientHeight    =   5688
   ClientLeft      =   1596
   ClientTop       =   1860
   ClientWidth     =   8784
   Height          =   6012
   Left            =   1548
   LinkTopic       =   "Form2"
   ScaleHeight     =   5688
   ScaleWidth      =   8784
   Top             =   1584
   Width           =   8880
   Begin RichtextLib.RichTextBox RichTextBox1
      Height          =   5412
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   8532
      _Version        =   65536
      _ExtentX        =   15050
      _ExtentY        =   9546
      _StockProps     =   69
      BackColor       =   -2147483643
      BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
         Name            =   "MS Sans Serif"
         Size            =   7.8
         Charset         =   0
         Weight          =   400
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      ScrollBars      =   2
      TextRTF         =   $"Form2.frx":0000
   End
End
Attribute VB_Name = "Form2"
Attribute VB_Creatable = False
Attribute VB_Exposed = False


TRNSCRPT.FRM

VERSION 4.00
'****************************************************************************
'
'  Module:     Trnscrpt.EXE
'  File:       Trnscrpt.frm
'
'  Copyright (c) Microsoft Corporation 1996-1997
'
' 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.
'****************************************************************************
Begin VB.Form Form1
   Caption         =   "Chat Transcripter"
   ClientHeight    =   6696
   ClientLeft      =   1356
   ClientTop       =   1488
   ClientWidth     =   7380
   FillColor       =   &H00FF0000&
   BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
      Name            =   "MS Sans Serif"
      Size            =   9.6
      Charset         =   0
      Weight          =   400
      Underline       =   0   'False
      Italic          =   0   'False
      Strikethrough   =   0   'False
   EndProperty
   ForeColor       =   &H000000FF&
   Height          =   7020
   Left            =   1308
   LinkTopic       =   "Form1"
   ScaleHeight     =   6696
   ScaleWidth      =   7380
   Top             =   1212
   Width           =   7476
   Begin VB.Frame Frame3
      Caption         =   "Chat Transcripter Session"
      Height          =   3252
      Left            =   120
      TabIndex        =   14
      Top             =   3240
      Width           =   7092
      Begin VB.Timer Timer2
         Enabled         =   0   'False
         Interval        =   1000
         Left            =   4440
         Top             =   240
      End
      Begin VB.Timer Timer1
         Interval        =   1000
         Left            =   5880
         Top             =   240
      End
      Begin MSChatObjectsCtl.MSChat MSChat1
         Height          =   2892
         Left            =   120
         TabIndex        =   15
         Top             =   240
         Width           =   6852
         _ExtentX        =   12086
         _ExtentY        =   5101
         UIOption        =   3980
         BackColor       =   255
      End
   End
   Begin VB.Frame Frame2
      Caption         =   "Chat Transcripter Commands"
      Height          =   732
      Left            =   120
      TabIndex        =   11
      Top             =   2400
      Width           =   7092
      Begin VB.CommandButton CurTranscript
         Caption         =   "View Current Transcript"
         Height          =   372
         Left            =   3960
         TabIndex        =   13
         Top             =   240
         Width           =   3012
      End
      Begin VB.CommandButton TranscriptCmd
         Caption         =   "Start Transcribing"
         Height          =   372
         Left            =   120
         TabIndex        =   12
         Top             =   240
         Width           =   3732
      End
   End
   Begin VB.Frame Frame1
      Caption         =   "Chat Transcripter Options"
      BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
         Name            =   "Times New Roman"
         Size            =   10.2
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   2292
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   7092
      Begin VB.TextBox Peak
         BackColor       =   &H00FF80FF&
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Arial"
            Size            =   7.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         ForeColor       =   &H00000000&
         Height          =   288
         Left            =   3480
         TabIndex        =   28
         Top             =   1838
         Visible         =   0   'False
         Width           =   852
      End
      Begin VB.TextBox Total
         BackColor       =   &H00FF80FF&
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Arial"
            Size            =   7.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         ForeColor       =   &H00FF0000&
         Height          =   288
         Left            =   3480
         TabIndex        =   26
         Top             =   1560
         Visible         =   0   'False
         Width           =   852
      End
      Begin VB.TextBox Current
         BackColor       =   &H00FF80FF&
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Arial"
            Size            =   7.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         ForeColor       =   &H0000FF00&
         Height          =   288
         Left            =   3480
         TabIndex        =   24
         Top             =   1320
         Visible         =   0   'False
         Width           =   852
      End
      Begin VB.TextBox HostKey
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.2
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   336
         Left            =   5640
         TabIndex        =   22
         Top             =   840
         Width           =   1212
      End
      Begin VB.CheckBox MembStatus
         Caption         =   "Allow Participation"
         Height          =   240
         Left            =   4680
         TabIndex        =   19
         Top             =   1320
         Width           =   2052
      End
      Begin VB.TextBox Interval
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.2
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   336
         Left            =   2040
         TabIndex        =   18
         Text            =   "1"
         Top             =   1320
         Width           =   372
      End
      Begin VB.CheckBox TextSpacing
         Caption         =   "Text Spacing"
         Height          =   252
         Left            =   4680
         TabIndex        =   16
         Top             =   1580
         Value           =   1  'Checked
         Width           =   1692
      End
      Begin VB.CheckBox Reconnect
         Caption         =   "AutoReconnect"
         Height          =   252
         Left            =   4680
         TabIndex        =   10
         Top             =   1860
         Width           =   1692
      End
      Begin VB.TextBox ChatRoom
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.2
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   336
         Left            =   1560
         TabIndex        =   9
         Text            =   "//ChatSrv1/#ChatRoom"
         Top             =   360
         Width           =   2292
      End
      Begin VB.CommandButton Browse
         Caption         =   "..."
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   372
         Left            =   3960
         TabIndex        =   8
         Top             =   840
         Width           =   372
      End
      Begin VB.TextBox TranscriptFile
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.2
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   336
         Left            =   1560
         TabIndex        =   7
         Top             =   840
         Width           =   2292
      End
      Begin VB.TextBox Alias
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.2
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   336
         Left            =   5640
         TabIndex        =   6
         Text            =   "Transcripter"
         Top             =   360
         Width           =   1212
      End
      Begin VB.TextBox Retries
         Enabled         =   0   'False
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.2
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   336
         Left            =   2040
         TabIndex        =   5
         Text            =   "2"
         Top             =   1800
         Width           =   372
      End
      Begin VB.Label Label10
         BackColor       =   &H00FF80FF&
         BorderStyle     =   1  'Fixed Single
         Caption         =   "Peak"
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Arial"
            Size            =   12
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         ForeColor       =   &H00000000&
         Height          =   288
         Left            =   2640
         TabIndex        =   27
         Top             =   1838
         Visible         =   0   'False
         Width           =   852
      End
      Begin VB.Label Label9
         BackColor       =   &H00FF80FF&
         BorderStyle     =   1  'Fixed Single
         Caption         =   "Total"
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Arial"
            Size            =   12
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         ForeColor       =   &H00FF0000&
         Height          =   288
         Left            =   2640
         TabIndex        =   25
         Top             =   1560
         Visible         =   0   'False
         Width           =   852
      End
      Begin VB.Label Label8
         BackColor       =   &H00FF80FF&
         BorderStyle     =   1  'Fixed Single
         Caption         =   "Current"
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Arial"
            Size            =   12
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         ForeColor       =   &H0000FF00&
         Height          =   288
         Left            =   2640
         TabIndex        =   23
         Top             =   1320
         Visible         =   0   'False
         Width           =   852
      End
      Begin VB.Label Label7
         Caption         =   "Host Key"
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   252
         Left            =   4680
         TabIndex        =   21
         Top             =   860
         Width           =   1452
      End
      Begin VB.Label Label6
         Alignment       =   2  'Center
         Caption         =   "(Minutes)"
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   7.8
            Charset         =   0
            Weight          =   400
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   204
         Left            =   480
         TabIndex        =   20
         Top             =   1560
         Width           =   732
      End
      Begin VB.Label Label5
         Caption         =   "Log Interval"
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   252
         Left            =   240
         TabIndex        =   17
         Top             =   1360
         Width           =   1212
      End
      Begin MSComDlg.CommonDialog CommonDialog1
         Left            =   3960
         Top             =   360
         _Version        =   65536
         _ExtentX        =   677
         _ExtentY        =   677
         _StockProps     =   0
         DefaultExt      =   ".txt"
         DialogTitle     =   "ChatTranscript File"
         Filter          =   "Text (*.txt)|*.txt|All (*.*)|*.*"
         FilterIndex     =   1
         InitDir         =   "."
      End
      Begin VB.Label Label4
         Caption         =   "Number of Retries"
         Enabled         =   0   'False
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   252
         Left            =   240
         TabIndex        =   4
         Top             =   1800
         Width           =   1812
      End
      Begin VB.Label Label3
         Caption         =   "Alias"
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   252
         Left            =   4680
         TabIndex        =   3
         Top             =   400
         Width           =   492
      End
      Begin VB.Label Label2
         Caption         =   "Log File"
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   252
         Left            =   240
         TabIndex        =   2
         Top             =   880
         Width           =   1452
      End
      Begin VB.Label Label1
         Caption         =   "Chat Room"
         BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
            Name            =   "Times New Roman"
            Size            =   10.8
            Charset         =   0
            Weight          =   700
            Underline       =   0   'False
            Italic          =   0   'False
            Strikethrough   =   0   'False
         EndProperty
         Height          =   252
         Left            =   240
         TabIndex        =   1
         Top             =   400
         Width           =   1212
      End
   End
End
Attribute VB_Name = "Form1"
Attribute VB_Creatable = False
Attribute VB_Exposed = False

Const INSERT_BLANK = &H100&
Const ALLOW_RESIZING = &H400&
Const SENSITIVE_URLS = &H800&

Const SEND_BUTTON = &H1&
Const WHISPER_BUTTON = &H2&
Const SEND_TEXTBOX = &H10&

Const INFORM_JOIN = &H20&
Const INFORM_LEAVE = &H40&

Const MASK_PRIVILEGES = &H1&
Const STAT_PARTICIPANT = &H2&
Const STAT_SPECTATOR = &H4&

Public TimerTicks As Integer
Public CurTicks As Integer
Public CancelFlag As Boolean

Public CurTry As Integer
Public TotalCount As Integer
Public MaxCount As Integer
Sub Enable_ChatOptions(ByVal flag As Boolean)
ChatRoom.Enabled = flag
TranscriptFile.Enabled = flag
Alias.Enabled = flag
Browse.Enabled = flag
HostKey.Enabled = flag
Label1.Enabled = flag
Label2.Enabled = flag
Label3.Enabled = flag
Label7.Enabled = flag
Timer1.Enabled = Not flag

If (HostKey.Text = "") Then
    current.Visible = False
    total.Visible = False
    Peak.Visible = False
    Label8.Visible = False
    Label9.Visible = False
    Label10.Visible = False
Else
    current.Visible = Not flag
    total.Visible = Not flag
    Peak.Visible = Not flag
    Label8.Visible = Not flag
    Label9.Visible = Not flag
    Label10.Visible = Not flag
End If

End Sub

Sub Set_FileName()
strFileName = ChatRoom.Text
Do
    nPos = InStr(strFileName, "/")
    If (nPos > 0) Then
        strTemp = Mid(strFileName, nPos + 1)
        strFileName = strTemp
    End If
Loop Until nPos = 0
TranscriptFile.Text = strFileName + ".txt"
If (HostKey.Text = "") Then
Else
TranscriptFile.Text = "Host_" + TranscriptFile.Text
End If
End Sub

Sub Update_File(ByVal Stats As Boolean)
'MsgBox "Update File Called", , "testing"
On Error GoTo ErrorHandler
CurHistory = MSChat1.History
If ((CurHistory <> "") Or (Stats)) Then
    CurTranscript.Enabled = False
    Open TranscriptFile.Text For Append Access Write As #1

    Print #1, CurHistory
    MSChat1.ClearHistory

    If (Stats And (HostKey.Text <> "")) Then
        TempStr = ":::::::::::::::::::::::::::::::::::::::::"
        Print #1, TempStr, TempStr, TempStr, TempStr
        Print #1, "    Chat Room:", ChatRoom.Text;
        Print #1, "      ClosingTime :", Time$
        Print #1, " "
        Print #1, "Total number of participants : ";
        Print #1, TotalCount
        Print #1, "Peak participation during session: ";
        Print #1, MaxCount
        Print #1, " "
        Print #1, TempStr, TempStr, TempStr, TempStr
'        Print #1, "Current number of participants : "
'        Print #1, current.Text
    End If

    Close #1
    CurTranscript.Enabled = True
End If

Exit Sub        ' Exit to avoid handler.
ErrorHandler:   ' Error-handling routine.
    Select Case Err.Number  ' Evaluate error number.
        Case 75
            Msg = "Error Saving Transcript File! Make Sure you have access privelages to the file"
            MsgBox Msg, , TranscriptFile.Text
            Err.Clear   ' Clear Err object fields
        Case Else
            ' Tell user what happened. Then clear the Err object.
            Msg = "There was an error attempting to open the OLE object!"
            MsgBox Msg, , Err.Number
            Err.Clear   ' Clear Err object fields

            ' Handle other situations here...
    End Select
    CurTranscript.Enabled = True

End Sub

Private Sub Browse_Click()
Rem CommonDialog1.DefaultExt = ".txt"
Rem CommonDialog1.Filter = "Text (*.txt)|*.txt|All (*.*)|*.*"
Rem CommonDialog1.FilterIndex = 1
Rem CommonDialog1.DialogTitle = "ChatTranscript File"
Rem CommonDialog1.DefaultExt = ".txt"
Rem CommonDialog1.DefaultExt = ".txt"
CommonDialog1.filename = TranscriptFile.Text
CommonDialog1.Flags = &H800& Or &H8000& Or &H2& Or &H200000 Or &H4&
CommonDialog1.InitDir = "."
'CommonDialog1.Action = 2
CommonDialog1.ShowSave
TranscriptFile.Text = CommonDialog1.filename
End Sub

Private Sub ChatRoom_Change()
'Call Set_FileName
End Sub

Private Sub ChatRoom_LostFocus()
Call Set_FileName
End Sub

Private Sub CurTranscript_Click()
    On Error GoTo ErrorHandler   ' Defer error trapping.
    Open TranscriptFile.Text For Input Access Read Shared As 2
    FileStr = Input(LOF(2), 2)
    Close #2
    Load Form2
    Form2.RichTextBox1.TextRTF = FileStr
    Form2.Visible = True
'    Else
'        Msg = "Unable to Access Transcript File"
'       MsgBox Msg, , TranscriptFile.Text
'  End If
    'Form2.RichTextBox1.LoadFile TranscriptFile.Text, rtfText
    Exit Sub        ' Exit to avoid handler.
ErrorHandler:   ' Error-handling routine.
    Select Case Err.Number  ' Evaluate error number.
        Case 53
            Msg = "Transcript File Doesn't exist"
            MsgBox Msg, , TranscriptFile.Text
            Err.Clear   ' Clear Err object fields

        Case 75
            Msg = "Error Reading Transcript File! Make Sure the file exists"
            MsgBox Msg, , TranscriptFile.Text
            Err.Clear   ' Clear Err object fields
        Case Else
            ' Tell user what happened. Then clear the Err object.
            Msg = "There was an error attempting to open the OLE object!"
            MsgBox Msg, , Err.Number
            Err.Clear   ' Clear Err object fields

            ' Handle other situations here...
    End Select

End Sub

Private Sub Form_Load()

Call Set_FileName

TimerTicks = Interval.Text
CurTicks = 0

CurTry = 0

End Sub

Private Sub HostKey_LostFocus()
    If (Alias.Text = "Transcripter") Then
        Alias.Text = "THost"
    End If

    If (InStr(TranscriptFile.Text, "Host_") = 0) Then
            TranscriptFile.Text = "Host_" + TranscriptFile.Text
    End If

End Sub

Private Sub Interval_Change()
    iVal = 1
    On Error Resume Next
    iVal = CInt(Interval.Text)
    Interval.Text = iVal
    TimerTicks = iVal
'    Timer1.Interval = 1000 * 60 * iVal ' Set interval.
End Sub

Private Sub MembStatus_Click()
On Error Resume Next
If (MembStatus.Value = 0) Then
'    If (MSChat1.State = 3) Then
'        MSChat1.SetParticipantStatus MSChat1.ThisParticipantID, MASK_PRIVILEGES, STAT_SPECTATOR
'    End If
    MSChat1.UIOption = MSChat1.UIOption And Not (SEND_TEXTBOX Or SEND_BUTTON Or WHISPER_BUTTON)
Else
'    If (MSChat1.State = 3) Then
'        MSChat1.SetParticipantStatus MSChat1.ThisParticipantID, MASK_PRIVILEGES, STAT_PARTICIPANT
'    End If
    MSChat1.UIOption = MSChat1.UIOption Or (SEND_TEXTBOX Or SEND_BUTTON Or WHISPER_BUTTON)
End If

End Sub

Private Sub MSChat1_OnBeginEnumeration()
    TotalCount = 0
    MaxCount = TotalCount
End Sub

Private Sub MSChat1_OnEnterParticipant(ByVal ParticipantID As Long, ByVal Alias As String, ByVal Status As Integer)
    current.Text = MSChat1.MemberCount
    TotalCount = TotalCount + 1
    total.Text = TotalCount
    If (MaxCount < MSChat1.MemberCount) Then
        MaxCount = MSChat1.MemberCount
        Peak.Text = MaxCount
    End If
End Sub

Private Sub MSChat1_OnExitParticipant(ByVal ParticipantID As Long)
    current.Text = MSChat1.MemberCount
End Sub

Private Sub MSChat1_OnHistoryFull(ByVal Percent As Integer)
Call Update_File(False)
End Sub

Private Sub MSChat1_OnStateChanged(ByVal NewState As Integer)
    If (NewState = 1) Then
        Update_File (True)

        If ((CancelFlag = True) Or (Reconnect.Value = 0) Or ((Reconnect.Value = 1) And (CurTry >= Retries.Text))) Then
            TranscriptCmd.Caption = "Start Transcribing"
            TranscriptCmd.Enabled = True
            MSChat1.BackColor = 255
            CurTry = 0
            Call Enable_ChatOptions(True)
        Else
            TranscriptCmd.Caption = "ReStarting Transcribing"
            TranscriptCmd.Enabled = False
            CurTry = CurTry + 1
            MSChat1.BackColor = &HFF0000
            Timer2.Interval = 30000
            Timer2.Enabled = True
        End If
    Else
        If (NewState = 2) Then
            TranscriptCmd.Caption = "Cancel Entering"
            MSChat1.BackColor = 33023
        Else
            On Error Resume Next
            If (NewState = 3) Then
                TranscriptCmd.Caption = "Stop Transcribing"
                MSChat1.BackColor = 49152
                CurTry = 0
                TranscriptCmd.Enabled = True
            End If
        End If
    End If
End Sub

Private Sub Reconnect_Click()
If Reconnect.Value = 0 Then
    Retries.Enabled = False
    Label4.Enabled = False
Else
    Retries.Enabled = True
    Label4.Enabled = True
End If
End Sub

Private Sub TextSpacing_Click()
If TextSpacing.Value = 0 Then
    MSChat1.UIOption = MSChat1.UIOption And Not INSERT_BLANK
Else
    MSChat1.UIOption = MSChat1.UIOption Or INSERT_BLANK
End If
End Sub

Private Sub Timer1_Timer()
CurTicks = CurTicks + 1
If (CurTicks >= TimerTicks) Then
    CurTicks = 0
    Call Update_File(False)
End If
End Sub

Private Sub Timer2_Timer()
If (MSChat1.State = 1) Then
    Call TranscriptCmd_Click
End If
End Sub

Private Sub TranscriptCmd_Click()

If (MSChat1.State = 1) Then
    Rem make sure the file is writable.
    On Error GoTo ErrorHandler
    Open TranscriptFile.Text For Append Access Write As #1
    Close #1
    Timer1.Interval = 60000  ' Set interval to be 1 minute.
    Timer2.Enabled = False

    CancelFlag = False
    MSChat1.EnterRoom ChatRoom.Text, HostKey.Text, Alias.Text, "ANON;ALL", 1, 1
    Call Enable_ChatOptions(False)
Else
    CancelFlag = True
    If (MSChat1.State = 2) Then
        TranscriptCmd.Enabled = False
        MSChat1.CancelEntering
        MSChat1.ClearHistory
    Else
        If (MSChat1.State = 3) Then
            TranscriptCmd.Enabled = False
            MSChat1.ExitRoom
            MSChat1.ClearHistory
        End If
    End If

    Call Enable_ChatOptions(True)
End If

Exit Sub        ' Exit to avoid handler.
ErrorHandler:   ' Error-handling routine.
    Select Case Err.Number  ' Evaluate error number.
        Case 75
            Msg = "Error Saving Transcript File! Make Sure you have access privelages to the file"
            MsgBox Msg, , TranscriptFile.Text
            Err.Clear   ' Clear Err object fields
        Case Else
            ' Tell user what happened. Then clear the Err object.
            Msg = "There was an error attempting to open the OLE object!"
            MsgBox Msg, , Err.Number
            Err.Clear   ' Clear Err object fields

            ' Handle other situations here...
    End Select

End Sub


README.TXT

------------------------------------------------------------------
    Microsoft Chat SDK 1.1 Webdemo Readme File
                         August 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE WEBDEMO SAMPLE

OVERVIEW OF THE WEBDEMO SAMPLE
==============================
Webdemo directory contains five web pages that demonstrate the usage of
the Chat control with Visual Basic Scripting.

Sample1.htm is a simple chat demo, where the user enters the chat
room path and the nick name and logs onto the chat.

Sample2.htm is like sample1.htm, but it also demonstrates the usage of no
whisper capability.

Sample3.htm is a web page, where the user can setup a chat, the chat topic
and the participant count of a chat room. The web page also allows the user
to ignore members, ignore whispers from a member, kick a member out of the
chat room and get the real name of a participant.

In addition to the functionality that sample3.htm has sample4.htm also
provides you with an additional IRC server name to logon and gives you
the option to logon anonymously or logon with NTLM authentication for NT
networks.

Sample5.htm is a sample web page, where you can automatically monitor
a room for words and automatically kick or ban the user if a certain word
has been used.


README.TXT

------------------------------------------------------------------
       Microsoft NetMeeting SDK 2.0 Samples Readme File
                          August 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

OVERVIEW OF THE NETMEETING SDK SAMPLES
======================================

The Samples directory consists of the following subdirectories, which
contain samples demonstrating the use of the NetMeeting ActiveX Control,
NetMeeting COM objects, Internet Locator COM Objects, and Codec
installation COM Objects.

NMCHAT
------
NMCHAT is a simple chat application that demonstrates the use of the new
NetMeeting 2.0 INm* COM interfaces.  In particular, this sample illustrates
the use of data channels to transmit information to peer applications in
a NetMeeting conference, as well as the basics of detecting new calls and
joining those calls.

NMUI
----
NMUI is a large sample that demonstrates the use of most of the new COM
interfaces.  A critical new feature demonstrated by NMUI is the ability
for an application to replace NetMeeting's User Interface.

In general you enable this sample to start its work by selecting the File,
Monitor menu item.  This kicks off the sample's processing.

Using this sample you can see how to replace NetMeeting's user interface
with that of your own application.   To see this, go to the View,Options...
dialog and select the NM_INIT_CONTROL *before* selecting Monitor.  This will
cause the NMUI sample, via the NetMeeting 2.0 API, to "take over" all UI from
NetMeeting.  Upon selecting Monitor, the NetMeeting UI will not appear and
you can drive everything from within the NMUI sample.

You can use the Call, User... menu to place calls, the Channel, Application
Sharing... menu to manipulate shared applications, The Call, Accept... menu
item to accept incoming calls, etc.

SKIPPY
------
SKIPPY demonstrates the use of the ILS COM interface to gain access to
the same LDAP directory services used in NetMeeting.  SKIPPY shows how to
register with a server, retrieve information from a server, and filter that
information for specific properties or content.

See README.TXT in the SKIPPY directory for more information.

INSTCODC
--------
INSTCODC demonstrates the use of the codec installation APIs in NetMeeting
2.0.  These APIs are of interest only to vendors of audio and video codecs
who wish to add their codec to the set of codecs available during NetMeeting
calls.

See README.TXT.TXT in the INSTCODC directory for more information.

HTML
----
The HTML directory contains two sample HTML Web pages that are the same
except one shows the use of the Control from VBScript, and the other
shows the use of the Control from JScript. These samples illustrate the
key Web-related functionality enabled by the Control, which is using a
Web page to bring together, in a conference, a community of users with
a similar interest.

See the README.TXT file in the HTML directory for more information.

VBCARD
------
VBCARD is a data conferencing application that leverages the AcitveX Control.

See the README.TXT file in the VBCARD directory for more information.

NMSHARE
-------
The NMSHARE directory contains a sample which demonstrates the application
sharing interfaces in the NetMeeting 2.0 COM API.  NMSHARE is particularly
focused on new app sharing state notifications built into the new 2.0 COM APIs.
Run NMSHARE, establish a call with NetMeeting, share an app using NetMeeting,
and watch the various state changes as they are tracked in NMSHARE.  You can use
the Share menu to set the collaboration state of application sharing.

NMFT
----
The NMFT directory contains a sample which demonstrates the file transfer
related interfaces in the NetMeeting 2.0 COM API.

To run the NMFT application you must already be in a NetMeeting call.
Once in a call, running the application will result in a "pick a file" dialog
box that allows you to choose the file to send.  A progress indicator is
displayed during the send.


README.TXT

------------------------------------------------------------------
         Microsoft NetMeeting SDK 2.1 AVPHONE Readme File
                           November 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE AVPHONE SAMPLE
BUILDING THE AVPHONE SAMPLE

OVERVIEW OF THE AVPHONE SAMPLE
==============================

AVPHONE is a Windows sample that demonstrates how to use the
Microsoft NetMeeting Audio & Video API.

BUILDING THE AVPHONE SAMPLE
===========================

Building the sample application requires VC++ Version 5.0 or later.

To build the sample from Visual C++, open the project makefile
(avphone.mak) as a Microsoft VC++ project, and build as usual.  The
NetMeeting SDK directories must be added to your VC++ environment.

If your DOS environment is correctly configured, you can build the sample
from the command line by typing "nmake".  The DOS path, include and lib
environment variables must point to the VC installation and the
NetMeeting SDK directories.

Notes:


CALLNTFY.CPP

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       CALLNTFY.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CCallNotify::CCallNotify()
{

}

CCallNotify::CCallNotify(Conf *pConf)
{
m_pConf = pConf;
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

CCallNotify::~CCallNotify()
{

}

//****************************************************************************
//
// methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CCallNotify::AddRef()
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CCallNotify::Release()
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CCallNotify::QueryInterface(REFIID riid, PVOID * ppvObject)
{
*ppvObject = NULL;

if (riid == IID_IUnknown)
*ppvObject = (PVOID *)this;
else if (riid == IID_INmCallNotify)
*ppvObject = (PVOID *)this;
else
return E_NOINTERFACE;

AddRef();
return S_OK;
}

//****************************************************************************
//
// Sink connection methods
//
//****************************************************************************

HRESULT CCallNotify::Connect(IUnknown * pUnkOuter)
{
return CNotify::Connect(pUnkOuter, IID_INmCallNotify, (IUnknown *)this);
}

HRESULT CCallNotify::Disconnect()
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Main sink notification methods
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CCallNotify::Failed(ULONG uError)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CCallNotify::Accepted(INmConference *pConference)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CCallNotify::NmUI(CONFN uNotify)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CCallNotify::StateChanged(NM_CALL_STATE uState)
{
switch (uState)
{
case NM_CALL_RING:

// Query whether to accept the call and notify my Conf object the result.
if (MessageBox(NULL,"Accept incoming call?","Call",MB_YESNO|MB_TASKMODAL)== IDYES)
m_pConf->AcceptCall();
else
m_pConf->RejectCall();
break;

case NM_CALL_REJECTED:

// Notify my Conf object that the call was rejected.
m_pConf->CallRejected();
break;

case NM_CALL_ACCEPTED:

// Notify my Conf object that the call was accepted
m_pConf->CallAccepted();
break;
}
return S_OK;
}


CALLNTFY.H

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       CALLNTFY.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_CALLNOTIFY_H__21170B81_2878_11D1_A79A_00A024AB19AE__INCLUDED_)
#define AFX_CALLNOTIFY_H__21170B81_2878_11D1_A79A_00A024AB19AE__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

//****************************************************************************
//
// CLASS CCallNotify
//
//****************************************************************************

class CCallNotify :
public INmCallNotify,
public RefCount,
public CNotify
{
private:
Conf *m_pConf; // Back pointer to Conf object
CCallNotify(); // prevent default construction
public:
CCallNotify(Conf *);
virtual ~CCallNotify();

// from IUnknown
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObject);

// Sink connection methods
HRESULT Connect(IUnknown *pUnkOuter);
HRESULT Disconnect(void);

// Main sink notification methods
HRESULT STDMETHODCALLTYPE StateChanged( NM_CALL_STATE uState);
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE Failed(ULONG uError);
HRESULT STDMETHODCALLTYPE Accepted(INmConference *pConference);
};

#endif // !defined(AFX_CALLNOTIFY_H__21170B81_2878_11D1_A79A_00A024AB19AE__INCLUDED_)


CLUTIL.CPP

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       CLUTIL.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

RefCount::RefCount(void)
{
m_cRef = 1;
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

RefCount::~RefCount(void)
{
}

//****************************************************************************
//
// ULONG STDMETHODCALLTYPE RefCount::AddRef(void)
//
//****************************************************************************

ULONG STDMETHODCALLTYPE RefCount::AddRef(void)
{
   ASSERT(m_cRef >= 0);

   InterlockedIncrement(&m_cRef);

   return (ULONG) m_cRef;
}

//****************************************************************************
//
// ULONG STDMETHODCALLTYPE RefCount::Release(void)
//
//****************************************************************************

ULONG STDMETHODCALLTYPE RefCount::Release(void)
{
if (0 == InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}

ASSERT(m_cRef > 0);
return (ULONG) m_cRef;
}

//****************************************************************************
//
// CLASS CNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CNotify::CNotify() :
m_pcnpcnt(NULL),
    m_pcnp(NULL),
    m_dwCookie(0),
    m_pUnk(NULL)
{
}

//****************************************************************************
//
// destructor
//
//****************************************************************************

CNotify::~CNotify()
{
Disconnect(); // Make sure we're disconnected
}

//****************************************************************************
//
// HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
//
// Connects the sink to the container
//
//****************************************************************************

HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
{
HRESULT hr;

ASSERT(0 == m_dwCookie);

// Get the connection container
hr = pUnk->QueryInterface(IID_IConnectionPointContainer, (void **)&m_pcnpcnt);
if (SUCCEEDED(hr))
{
// Find an appropriate connection point
hr = m_pcnpcnt->FindConnectionPoint(riid, &m_pcnp);
if (SUCCEEDED(hr))
{
ASSERT(NULL != m_pcnp);
// Connect the sink object
hr = m_pcnp->Advise((IUnknown *)pUnkN, &m_dwCookie);
}
}

if (FAILED(hr))
{
m_dwCookie = 0;
}
else
{
    m_pUnk = pUnk; // keep around for caller
    }

return hr;
}

//****************************************************************************
//
// HRESULT CNotify::Disconnect (void)
//
// Disconnects the sink from the container
//
//****************************************************************************

HRESULT CNotify::Disconnect (void)
{
    if (0 != m_dwCookie)
    {

        // Disconnect the sink object
        m_pcnp->Unadvise(m_dwCookie);
        m_dwCookie = 0;

        m_pcnp->Release();
        m_pcnp = NULL;

        m_pcnpcnt->Release();
        m_pcnpcnt = NULL;

        m_pUnk = NULL;
    }

    return S_OK;
}

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

// We don't support construction from an ANSI string in the Unicode build.
#if !defined(UNICODE)

BSTRING::BSTRING(LPCSTR lpcString)
{
m_bstr = NULL;

// Compute the length of the required BSTR, including the null
int cWC =  MultiByteToWideChar(CP_ACP, 0, lpcString, -1, NULL, 0);
if (cWC <= 0)
return;

// Allocate the BSTR, including the null
m_bstr = SysAllocStringLen(NULL, cWC - 1); // SysAllocStringLen adds another 1

ASSERT(NULL != m_bstr);
if (NULL == m_bstr)
{
return;
}

// Copy the string
MultiByteToWideChar(CP_ACP, 0, lpcString, -1, (LPWSTR) m_bstr, cWC);

// Verify that the string is null terminated
ASSERT(0 == m_bstr[cWC - 1]);
}

#endif // !defined(UNICODE)

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

BTSTR::BTSTR(BSTR bstr)
{
m_psz = PszFromBstr(bstr);
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

BTSTR::~BTSTR()
{
if (NULL != m_psz)
LocalFree(m_psz);
}

//****************************************************************************
//
// LPTSTR PszFromBstr(BSTR bstr)
//
// Converts a BSTR to a LPSTR
//
//****************************************************************************

LPTSTR PszFromBstr(BSTR bstr)
{
if (NULL == bstr)
return NULL;
int cch =  WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, NULL, 0, NULL, NULL);
if (cch <= 0)
return NULL;

LPTSTR psz = (char *)LocalAlloc(LPTR,cch);
if (NULL == psz)
return NULL;

WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, psz, cch+1, NULL, NULL);
return psz;
}


CLUTIL.H

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       CLUTIL.H
//  Content:    Utility Classes
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#ifndef _CL_UTIL_H_
#define _CL_UTIL_H_
#define ASSERT assert

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

class RefCount
{
private:
   LONG m_cRef;

public:
   RefCount();
   // Virtual destructor defers destruction to destructor of derived class.
   virtual ~RefCount();

   ULONG STDMETHODCALLTYPE AddRef(void);
   ULONG STDMETHODCALLTYPE Release(void);
};

//****************************************************************************
//
// CLASS CNotify
//
// Notification sink
//
//****************************************************************************

class CNotify
{
private:
    DWORD  m_dwCookie;
IUnknown * m_pUnk;
    IConnectionPoint           * m_pcnp;
    IConnectionPointContainer  * m_pcnpcnt;
public:
    CNotify(void);
    ~CNotify();

    HRESULT Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN);
    HRESULT Disconnect(void);

    IUnknown * GetPunk() {return m_pUnk;}
};

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

class BSTRING
{
private:
BSTR   m_bstr;

public:
// Constructors
BSTRING() {m_bstr = NULL;}

inline BSTRING(LPCWSTR lpcwString);

#if !defined(UNICODE)
// We don't support construction from an ANSI string in the Unicode build.
BSTRING(LPCSTR lpcString);
#endif // !defined(UNICODE)

// Destructor
inline ~BSTRING();

// Cast to BSTR
operator BSTR() {return m_bstr;}
inline LPBSTR GetLPBSTR(void);
};

BSTRING::BSTRING(LPCWSTR lpcwString)
{
if (NULL != lpcwString)
{
m_bstr = SysAllocString(lpcwString);
ASSERT(NULL != m_bstr);
}
else
{
m_bstr = NULL;
}
}

BSTRING::~BSTRING()
{
if (NULL != m_bstr)
{
SysFreeString(m_bstr);
}
}

inline LPBSTR BSTRING::GetLPBSTR(void)
{
ASSERT(NULL != m_bstr);

return &m_bstr;
}

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

class BTSTR
{
private:
LPTSTR m_psz;

public:
BTSTR(BSTR bstr);
~BTSTR();

// Cast to BSTR
operator LPTSTR() {return (NULL == m_psz) ? TEXT("<null>") : m_psz;}
};

LPTSTR PszFromBstr(BSTR bst);

#endif  // _CL_UTIL_H_


CNFNTFY.CPP

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       CNFNTFY.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// CLASS CConfNotify
//
//****************************************************************************

//****************************************************************************
//
// Construction/Destruction
//
//****************************************************************************

CConfNotify::CConfNotify()
{

}

CConfNotify::CConfNotify(Conf *pConf)
{
m_pConf = pConf;
}

CConfNotify::~CConfNotify()
{

}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

//****************************************************************************
//
// ULONG STDMETHODCALLTYPE CConfNotify::AddRef()
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CConfNotify::AddRef()
{
return RefCount::AddRef();
}

//****************************************************************************
//
// ULONG STDMETHODCALLTYPE CConfNotify::Release()
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CConfNotify::Release()
{
return RefCount::Release();
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CConfNotify::QueryInterface(REFIID riid, PVOID * ppvObject)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::QueryInterface(REFIID riid, PVOID * ppvObject)
{
*ppvObject = NULL;

if (riid == IID_IUnknown)
*ppvObject = (PVOID *)this;
else if (riid == IID_INmConferenceNotify)
*ppvObject = (PVOID *)this;
else
return E_NOINTERFACE;

AddRef();
return S_OK;
}

//****************************************************************************
//
// Sink connection methods
//
//****************************************************************************

//****************************************************************************
//
// HRESULT CConfNotify::Connect(IUnknown * pUnkOuter)
//
// Connects to container
//
//****************************************************************************

HRESULT CConfNotify::Connect(IUnknown * pUnkOuter)
{
return CNotify::Connect(pUnkOuter, IID_INmConferenceNotify, (IUnknown *)this);
}

HRESULT CConfNotify::Disconnect()
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Main sink notification methods
//
//****************************************************************************

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CConfNotify::ChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel * pChannel)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::ChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel * pChannel)
{
INmChannelVideo *pVidChannel;
ULONG uCh;
IID iid;
HRESULT hr;

pChannel->AddRef();

// When notified that a video channel has been added, display the video window.

if (uNotify == NM_CHANNEL_ADDED)
{
pChannel->GetNmch(&uCh);
if (uCh==NMCH_VIDEO)
{
pChannel->GetInterface(&iid);
hr = pChannel->QueryInterface(iid,(void **)&pVidChannel);

if (SUCCEEDED(hr))
{
pVidChannel->SetProperty(NM_VIDPROP_IMAGE_PREFERRED_SIZE,NM_VIDEO_SMALL);
pVidChannel->SetProperty(NM_VIDPROP_WINDOW_AUTO_SIZE,100);
pVidChannel->SetProperty(NM_VIDPROP_WINDOW_TOP_MOST,1);
pVidChannel->SetProperty(NM_VIDPROP_WINDOW_VISIBLE,1);

// if video channel is incoming, be sure to not pause by default
if (pVidChannel->IsIncoming()==S_OK)
pVidChannel->SetProperty(NM_VIDPROP_PAUSE,0);

pVidChannel->Release();
}
}
}

pChannel->Release();

return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CConfNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember * pMember)
//
// This function gets called when a member as changed
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember * pMember)
{
return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CConfNotify::NmUI(CONFN uNotify)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::NmUI(CONFN uNotify)
{
return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CConfNotify::StateChanged(NM_CONFERENCE_STATE uState)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::StateChanged(NM_CONFERENCE_STATE uState)
{
// Notify my Conf object that the remote hungup.
if (uState==NM_CONFERENCE_IDLE)
m_pConf->RemoteHungup();

return S_OK;
}


CNFNTFY.H

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       CNFNTFY.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_CONFNOTIFY_H__C19C97C1_2A15_11D1_A79A_00A024AB19AE__INCLUDED_)
#define AFX_CONFNOTIFY_H__C19C97C1_2A15_11D1_A79A_00A024AB19AE__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

class Conf; // forward declaration

//****************************************************************************
//
// CLASS CConfNotify
//
//****************************************************************************

class CConfNotify :  public INmConferenceNotify,  public RefCount,  public CNotify
{
private:
CConfNotify();  // Prevents default construction.
Conf *m_pConf;// Back pointer to Conf object
public:
CConfNotify(Conf *);
virtual ~CConfNotify();

ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObject);

HRESULT Connect(IUnknown *pUnkOuter);
HRESULT Disconnect(void);

HRESULT STDMETHODCALLTYPE StateChanged(NM_CONFERENCE_STATE uState);
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember);
HRESULT STDMETHODCALLTYPE ChannelChanged(NM_CHANNEL_NOTIFY uNotify,INmChannel *pChannel);

};

#endif // !defined(AFX_CONFNOTIFY_H__C19C97C1_2A15_11D1_A79A_00A024AB19AE__INCLUDED_)


CONF.CPP

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       CONF.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// CLASS Conf
//
//****************************************************************************

//****************************************************************************
//
// Construction/Destruction
//
//****************************************************************************

Conf::Conf(HWND hWnd)
{
// Create Sink Objects.
// During construction, AddRef occurs
m_pMgrNotify = new CMgrNotify(this);
m_pCallNotify = new CCallNotify(this);
m_pConfNotify = new CConfNotify(this);

// Initialize other member variables
m_pINmMgr=NULL;
m_pINmConf=NULL;
m_pINmCall=NULL;
m_bInConnection=FALSE;
m_hWnd=hWnd;
}

Conf::~Conf()
{
//clean up the notification sink objects
m_pMgrNotify->Release();
m_pCallNotify->Release();
m_pConfNotify->Release();
}

//****************************************************************************
//
// HRESULT Conf::Initialize()
//
// Initiate NetMeeting, obtain a pointer to INmManager, connect the MgrNotify
// sink object and initialize INmManager.
//
//****************************************************************************

HRESULT Conf::Initialize()
{
HRESULT hr=S_OK;

if (FAILED(CoCreateInstance(CLSID_NmManager,NULL,CLSCTX_INPROC_SERVER,IID_INmManager,(VOID **)&m_pINmMgr)))
return E_FAIL;

ULONG uOptions = NM_INIT_CONTROL; // Don't display the NetMeeting UI
ULONG uCaps = NMCH_AUDIO|NMCH_VIDEO;

hr =m_pMgrNotify->Connect(m_pINmMgr);
if (SUCCEEDED (hr))
hr=m_pINmMgr->Initialize(&uOptions,&uCaps);

return hr;
}

//****************************************************************************
//
// HRESULT Conf::Uninitialize()
//
// Disconnect MgrNotify sink object and release the interface INmManager
//
//****************************************************************************

HRESULT Conf::Uninitialize()
{

m_pMgrNotify->Disconnect();
if (m_pINmMgr)
m_pINmMgr->Release();

return S_OK;
}

//****************************************************************************
//
// HRESULT Conf::Call(LPSTR szMachineName)
//
// This function initiates an AV conference to the remote machine specified
// by szMachineName.
//
//****************************************************************************

HRESULT Conf::Call(LPSTR szMachineName)
{
HRESULT hr = S_OK;
BSTRING bstrName(szMachineName);
hr = m_pINmMgr->CreateCall(&m_pINmCall,NM_CALL_H323 ,NM_ADDR_MACHINENAME ,bstrName,NULL);

if (FAILED(hr))
{
// Call object sometimes needs to be released.
if (m_pINmCall)
{
m_pCallNotify->Disconnect();
m_pINmCall->Release();
}
}

return hr;
}

//****************************************************************************
//
// HRESULT Conf::HangUp()
//
// Leave the conference
//
//****************************************************************************

HRESULT Conf::HangUp()
{
HRESULT hr=S_OK;

// if call hungup before Call accepted or rejected, release INmCall and
// disconnect CallNotify sink
if (m_pINmCall)
{
hr = m_pINmCall->Cancel();
m_pCallNotify->Disconnect();
m_pINmCall->Release();
m_pINmCall=NULL;
}
else
if (m_pINmConf)
hr = m_pINmConf->Leave();

// Release INmConf and disconnect the ConfNotify sink
if (m_pINmConf)
{
m_pINmConf->Release();
m_pINmConf=NULL;
m_pConfNotify->Disconnect();
}

m_bInConnection=FALSE;

if (m_hWnd)
SendMessage(m_hWnd,WM_DISCONNECTED,0,0);

return hr;
}

//****************************************************************************
//
// HRESULT Conf::CallCreated(INmCall * pCall)
//
// Callback from the MgrNotify sink object
//
//****************************************************************************

HRESULT Conf::CallCreated(INmCall * pCall)
{
m_pINmCall = pCall;
m_pINmCall->AddRef();
m_pCallNotify->Connect(pCall);
m_bInConnection=TRUE;

if (m_hWnd)
SendMessage(m_hWnd,WM_CONNECTING,0,0);
return S_OK;
}

//****************************************************************************
//
// HRESULT Conf::AcceptCall()
//
// Accept the incoming call
//
//****************************************************************************

HRESULT Conf::AcceptCall()
{
return m_pINmCall->Accept();
}

//****************************************************************************
//
// HRESULT Conf::RejectCall()
//
// Reject the incoming call
//
//****************************************************************************

HRESULT Conf::RejectCall()
{
return m_pINmCall->Reject();
}

//****************************************************************************
//
// HRESULT Conf::ConferenceCreated(INmConference *pINmConf)
//
// Callback from the MgrNotify sink object that a new conference has been
// created
//
//****************************************************************************

HRESULT Conf::ConferenceCreated(INmConference *pINmConf)
{
//Add Conference notification sink to handle channels.
m_pINmConf=pINmConf;
m_pINmConf->AddRef();
m_pConfNotify->Connect(pINmConf);
m_bInConnection=TRUE;

if (m_hWnd)
SendMessage(m_hWnd,WM_CONNECTED,0,0);
return S_OK;
}

//****************************************************************************
//
// HRESULT Conf::CallAccepted()
//
// Callback from the CallNotify sink object that the new call has been accepted
//
//****************************************************************************

HRESULT Conf::CallAccepted()
{
// Release INmCall, and let send window message that a user has connected
if (m_pINmCall)
m_pINmCall->Release();

m_pINmCall=NULL;
m_pCallNotify->Disconnect();
m_bInConnection=TRUE;

return S_OK;
}

//****************************************************************************
//
// HRESULT Conf::CallRejected()
//
// Callback from the CallNotify sink object that the new call has been rejected
//
//****************************************************************************

HRESULT Conf::CallRejected()
{
// Release INmCall, INmConf, disconnect CallNotify and ConfNotify
// sink objects, and send message to main window that the user has
// disconnected.

if (m_pINmCall)
{
m_pCallNotify->Disconnect();
m_pINmCall->Release();
m_pINmCall=NULL;
}

if (m_pINmConf)
{
m_pINmConf->Release();
m_pINmConf=NULL;
}
m_pConfNotify->Disconnect();
m_bInConnection=FALSE;

if (m_hWnd)
SendMessage(m_hWnd,WM_DISCONNECTED,0,0);

return S_OK;
}

//****************************************************************************
//
// HRESULT Conf::RemoteHungup()
//
// Callback from the ConfNotify sink object that a member has left the
// conference
//
//****************************************************************************

HRESULT Conf::RemoteHungup()
{

// Release INmConf, disconnect ConfNotify sink object, and send message to
// main window that the remote node has disconnected.

m_pINmConf->Release();
m_pINmConf=NULL;
m_pConfNotify->Disconnect();
m_bInConnection=FALSE;

if (m_hWnd)
SendMessage(m_hWnd,WM_DISCONNECTED,0,0);

return S_OK;
}

//****************************************************************************
//
// BOOL Conf::InConference()
//
//****************************************************************************

BOOL Conf::InConnection()
{
return m_bInConnection;
}


CONF.H

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       CONF.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_CONF_H__688DCF62_255F_11D1_A79A_00A024AB19AE__INCLUDED_)
#define AFX_CONF_H__688DCF62_255F_11D1_A79A_00A024AB19AE__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#define WM_CONNECTINGWM_USER + 1
#define WM_CONNECTEDWM_USER + 2
#define WM_DISCONNECTED WM_USER +3

//****************************************************************************
//
// CLASS Conf
//
//****************************************************************************

class Conf
{
private:
CMgrNotify * m_pMgrNotify;
CCallNotify * m_pCallNotify;
CConfNotify *m_pConfNotify;
INmConference * m_pINmConf;
INmManager * m_pINmMgr;
INmCall *m_pINmCall;
HWND m_hWnd;
BOOL m_bInConnection;

public:
Conf(HWND);
virtual ~Conf();

HRESULT Initialize(void);
HRESULT Uninitialize(void);
HRESULT Call(LPSTR);
HRESULT CallCreated(INmCall *pCall);
HRESULT ConferenceCreated(INmConference *);
HRESULT CallRejected();
HRESULT CallAccepted();
HRESULT HangUp();
HRESULT RemoteHungup();
HRESULT RejectCall();
HRESULT AcceptCall();
BOOL InConnection();
};

#endif // !defined(AFX_CONF_H__688DCF62_255F_11D1_A79A_00A024AB19AE__INCLUDED_)


IMSCONF2.IDL

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       imsconf2.idl
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include <imsconf2.idl>


MAIN.CPP

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       MAIN.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//*******************
//GLOBALS
//*******************

#define MAX_SZ 200

HINSTANCE ghInst;

//*******************
//FUNCTION DECLARATIONS
//*******************

BOOL CALLBACK MainDlgProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam);

//****************************************************************************
//
//    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
//
//    PURPOSE: calls initialization function
//
//    COMMENTS:
//
//        Windows recognizes this function by name as the initial entry point
//        for the program.  This function calls the application initialization
//        routine.
//
//****************************************************************************

int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst, LPSTR szCmdLine, INT nCmdShow)
{
ghInst=hInst;

CoInitialize(NULL);
DialogBox(hInst,MAKEINTRESOURCE(IDD_MAIN),NULL,(DLGPROC)MainDlgProc);
CoUninitialize();
return 0;

}

//****************************************************************************
//
// BOOL CALLBACK MainDlgProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// This function is called when the Main Dialog initializes.  It sets up
// the initial interface pointers and updates the dialog accordingly
//
//****************************************************************************

BOOL CALLBACK MainDlgProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static Conf *pConf;

switch (uMsg)
{
case WM_INITDIALOG:
pConf=new Conf(hWnd);
if (FAILED(pConf->Initialize()))
{
MessageBox(NULL,"NetMeeting may not be installed","Initialization Failure",MB_OK);
EndDialog(hWnd,0);
return FALSE;
}
return TRUE;

case WM_CLOSE:

// Hangup if in a call
if (pConf->InConnection())
pConf->HangUp();

pConf->Uninitialize();
delete pConf;
EndDialog(hWnd,0);
return TRUE;

case WM_CONNECTING:
case WM_CONNECTED:

// Message from Conf object that a user is connected
SetDlgItemText(hWnd,IDC_CALLHANGUP,"H&ang Up");
return TRUE;

case WM_DISCONNECTED:

// Message from Conf object that a user disconnected or rejected call
SetDlgItemText(hWnd,IDC_CALLHANGUP,"C&all");
return TRUE;

case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_CALLHANGUP:

// If not in a call, then call szMachineName, otherwise hang up.
if (!pConf->InConnection())
{
char szMachineName[MAX_SZ];
GetDlgItemText(hWnd,IDC_MACHINENAME,szMachineName,MAX_SZ);
pConf->Call(szMachineName);
return TRUE;
}
else
{
pConf->HangUp();
return TRUE;
}

case ID_FILE_EXIT:
EndDialog(hWnd,0);
return TRUE;
}
}
break;
}
return FALSE;
}


MGRNTFY.CPP

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       MGRNTFY.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// CLASS CMgrNotify
//
//****************************************************************************

//****************************************************************************
//
// Construction/Destruction
//
//****************************************************************************

CMgrNotify::CMgrNotify()
{

}

CMgrNotify::CMgrNotify(Conf * pConf)
{
m_pConf = pConf;
}

CMgrNotify::~CMgrNotify()
{

}

//****************************************************************************
//
// Methods from IUknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CMgrNotify::AddRef()
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CMgrNotify::Release()
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CMgrNotify::QueryInterface(REFIID riid, PVOID * ppvObject)
{
*ppvObject = NULL;

if (riid == IID_IUnknown)
*ppvObject = (PVOID *)this;
else if (riid == IID_INmManagerNotify)
*ppvObject = (PVOID *)this;
else
return E_NOINTERFACE;

AddRef();
return S_OK;
}

//****************************************************************************
//
// Sink connection methods
//
//****************************************************************************

HRESULT CMgrNotify::Connect(IUnknown * pUnkOuter)
{
return CNotify::Connect(pUnkOuter, IID_INmManagerNotify, (IUnknown *)this);
}

HRESULT CMgrNotify::Disconnect()
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Main sink notification methods
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CMgrNotify::CallCreated(INmCall * pCall)
{
// Notify my Conf object that a Call object was created
m_pConf->CallCreated(pCall);
return S_OK;
}

HRESULT STDMETHODCALLTYPE CMgrNotify::ConferenceCreated(INmConference * pConference)
{
// Notify my Conf object that a Conference object was created
m_pConf->ConferenceCreated(pConference);
return S_OK;
}

HRESULT STDMETHODCALLTYPE CMgrNotify::NmUI(CONFN uNotify)
{
return S_OK;
}


MGRNTFY.H

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       MGRNTFY.H
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#if !defined(AFX_MGRNOTIFY_H__84DE9DA3_26AB_11D1_A79A_00A024AB19AE__INCLUDED_)
#define AFX_MGRNOTIFY_H__84DE9DA3_26AB_11D1_A79A_00A024AB19AE__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

class Conf; // forward declaration

//****************************************************************************
//
// CLASS CMgrNotify
//
//****************************************************************************

class CMgrNotify :  public INmManagerNotify,  public RefCount,  public CNotify
{
private:
CMgrNotify();// Prevent default construction
Conf *m_pConf;// Back pointer to Conf object

public:
CMgrNotify(Conf *);
virtual ~CMgrNotify();

ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObject);

HRESULT Connect(IUnknown *pUnkOuter);
HRESULT Disconnect(void);

HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE ConferenceCreated(INmConference *pConference);
HRESULT STDMETHODCALLTYPE CallCreated(INmCall *pCall);
};

#endif // !defined(AFX_MGRNOTIFY_H__84DE9DA3_26AB_11D1_A79A_00A024AB19AE__INCLUDED_)


PRECOMP.H

//****************************************************************************
//  Module:     AVPHONE.EXE
//  File:       PRECOMP.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include <windows.h>
#include <ocidl.h>
#include <assert.h>
#include "imsconf2.h"
#include "ClUtil.h"
#include "MgrNtfy.h"
#include "CallNtfy.h"
#include "CnfNtfy.h"
#include "Conf.h"
#include "resource.h"


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by main.rc
//
#define IDR_MAINMENU                    102
#define IDD_MAIN                        107
#define IDC_MACHINENAME                 1001
#define IDC_CALLHANGUP                  1004
#define ID_FILE_EXIT                    40001

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        104
#define _APS_NEXT_COMMAND_VALUE         40004
#define _APS_NEXT_CONTROL_VALUE         1005
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

------------------------------------------------------------------
         Microsoft NetMeeting SDK 2.1 Instcodc Readme File
                           November 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE SKIPPY! SAMPLE
BUILDING THE SKIPPY! SAMPLE

OVERVIEW OF THE INSTCODC SAMPLE
===============================

Instcodc demonstrates the addition, removal, and enumeration of an installed
audio NetMeeting codec, using the API provided for this purpose.

BUILDING THE INSTCODC SAMPLE
============================

Building the sample application requires VC++ Version 5.0 or later.

To build the sample from Visual C++, open the project makefile
(instcodc.mak) as a Microsoft VC++ project, and build as usual.
The NetMeeting SDK directories must be added to your VC++ environment.

If your DOS environment is correctly configured, you can build the sample
from the command line by typing "nmake".  The DOS path, include and lib
environment variables must point to the VC installation and the
NetMeeting SDK directories.

Notes:

1) INSTCODC.EXE must be executed in the directory where NetMeeting is
installed.

2) This sample should not be run while NetMeeting is running.


INSTCODC.CPP

//****************************************************************************
//  Module:      INSTCODC.EXE
//  File:        INSTCODC.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include <windows.h>
#include <windowsx.h>
#include <objbase.h>
#include <initguid.h>

#include "resource.h"
#include "codecs.h"

typedef enum {AUDIOCODEC, NONE} CODECTYPE;

//*******************
//GLOBALS
//*******************

const int MID_SZ = 128;
CODECTYPE gtCodec = AUDIOCODEC;  // Global depicting type of codec being displayed, installed,
 // removed, or reordered.

HINSTANCE ghInstance;

//Structures used for adding and removing codecs
WAVEFORMATEX *gpWFmt;
AUDCAP_INFO gWInfo;

//CAP_INFO lists
AUDCAP_INFO_LIST *gpsAudCapInfoList = NULL;  // Contains a list of the installed
 // audio codecs and their properties.
int *gpnAudCapOrderedIndex = NULL; // Contains, in installed order, indexes
 // of the structures in the AUDCAP_INFO_LIST.

//*******************
//GLOBAL Interfaces
//*******************

static IInstallAudioCodecs *pAudio=NULL;

//*******************
//MAIN UI FUNCTIONS
//*******************

LRESULT CALLBACK MainUIProc (HWND hwnd, UINT message, UINT wParam, LONG lParam);
LRESULT CALLBACK ReorderUIProc (HWND hwnd, UINT message, UINT wParam, LONG lParam);
BOOL InitAppInstance (HINSTANCE hInstance);
void DeinitAppInstance(HINSTANCE hInstance);

//*******************
//Misc. Functions
//*******************

void LoadInstalledCodecs(void);
void IndexInstalledCodecs(void);
void UpdateCapInfoListsFromIndex(void);
void DlgDisplayInstalledCodecs(HWND);

//*******************
//helper function  converts HRESULT to error message
//*******************

TCHAR gszHRError[MID_SZ];
inline void HRErrorToString(HRESULT hr)
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,(DWORD)hr,LANG_SYSTEM_DEFAULT,gszHRError,200,NULL);
}

//****************************************************************************
//
//    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
//
//    PURPOSE: calls initialization function
//
//    COMMENTS:
//
//        Windows recognizes this function by name as the initial entry point
//        for the program.  This function calls the application initialization
//        routine.
//
//****************************************************************************

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
BOOL fProcessed=InitAppInstance(hInstance);
DeinitAppInstance(hInstance);
return fProcessed;
}

//****************************************************************************
//
// BOOL InitAppInstance (HINSTANCE hInstance)
//
// This function initializes this instance of the application.
//
//****************************************************************************

BOOL InitAppInstance (HINSTANCE hInstance)
{
WNDCLASS wc;

ghInstance=hInstance;

//Register WNDCLASS for the app.

//This class enables the main dialog box to have additional properties
//such as an icon.
    wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
    wc.lpfnWndProc = DefDlgProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = DLGWINDOWEXTRA;
    wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_INSTCODC));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "CodecInstClass";

    if (!RegisterClass(&wc))
return FALSE;

//Setup audio codec structures
//This particular codec is: GSM 6.10, 8,000 Hz, Mono

BYTE gWFmtEx[2] = {0x40,0x01};

gpWFmt = (WAVEFORMATEX *)LocalAlloc(LPTR,sizeof(WAVEFORMATEX)+sizeof(gWFmtEx));

gpWFmt->wFormatTag = 49;
gpWFmt->nChannels = 1;
gpWFmt->nSamplesPerSec = 8000;
gpWFmt->nAvgBytesPerSec = 1625;
gpWFmt->nBlockAlign = 65;
gpWFmt->wBitsPerSample = 0;
gpWFmt->cbSize = sizeof(gWFmtEx);

CopyMemory((gpWFmt + 1),&gWFmtEx,gpWFmt->cbSize);

ZeroMemory(&gWInfo,sizeof(AUDCAP_INFO));
gWInfo.wFormatTag = 49;
gWInfo.uAvgBitrate = 13000;
gWInfo.wCPUUtilizationEncode = 50; //arbitrary
gWInfo.wCPUUtilizationDecode = 20; //artitrary
gWInfo.bSendEnabled = 1;
gWInfo.bRecvEnabled = 1;

//Setup COM support
if (FAILED(CoInitialize(NULL)))
return FALSE;

//Application is a Modal Dialog.
return DialogBox(hInstance,MAKEINTRESOURCE(IDD_MAINUI),0,(DLGPROC)MainUIProc);
}

//****************************************************************************
//
// void DeinitAppInstance (HINSTANCE hInstance)
//
// This function initializes this instance of the application.
//
//****************************************************************************

void DeinitAppInstance(HINSTANCE hInstance)
{
//End COM support
CoUninitialize();

//free the allocated memory
LocalFree (gpWFmt);
}

//****************************************************************************
//
// BOOL MainUI_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
//
// This function is called when the Main Dialog initializes.  It sets up
// the initial interface pointers and updates the dialog accordingly
//
//****************************************************************************

BOOL MainUI_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
HRESULT hr;
TCHAR szString[MID_SZ];

gtCodec=NONE;

//Get an interface to the audio codec methods
hr = CoCreateInstance(CLSID_InstallCodecs,
NULL,
CLSCTX_INPROC_SERVER,
IID_IInstallAudioCodecs,
(void **) &pAudio);

if (FAILED(hr))
{
pAudio=NULL;
HRErrorToString(hr);
LoadString(ghInstance,IDS_GETIAUDIO,szString,MID_SZ);
MessageBox(NULL,gszHRError,szString,MB_OK);
}
else
{
gtCodec = AUDIOCODEC;
}

// If no interfaces were available, disable everything.
if (gtCodec == NONE)
{
EnableWindow(GetDlgItem(hwnd,IDC_INSTALL),FALSE);
EnableWindow(GetDlgItem(hwnd,IDC_UNINSTALL),FALSE);
EnableWindow(GetDlgItem(hwnd,IDC_REORDER),FALSE);
EnableMenuItem(GetMenu(hwnd),0,MF_BYPOSITION|MF_GRAYED);
return TRUE;
}

//Load installed codec formats
LoadInstalledCodecs();

//display codecs
DlgDisplayInstalledCodecs(hwnd);
return TRUE;
}

//****************************************************************************
//
// void MainUI_OnDestroy(HWND hwnd)
//
//****************************************************************************

void MainUI_OnDestroy(HWND hwnd)
{

// Free CapInfo structures
if (gpsAudCapInfoList)
pAudio->FreeBuffer(gpsAudCapInfoList);

// Release the interfaces
if (pAudio)
pAudio->Release();

// Free CapInfoIndexs
if (gpnAudCapOrderedIndex)
LocalFree(gpnAudCapOrderedIndex);
}

//****************************************************************************
//
// void MainUI_OnClose(HWND hwnd)
//
//****************************************************************************

void MainUI_OnClose(HWND hwnd)
{
EndDialog(hwnd,0);
}

//****************************************************************************
//
// void MainUI_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
//
//****************************************************************************

void MainUI_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
TCHAR szString[MID_SZ];
TCHAR szString2[MID_SZ];

switch(id)
{
//Install the codec
case IDC_INSTALL:
{
if (gtCodec==AUDIOCODEC)
{
if (FAILED(pAudio->AddACMFormat(gpWFmt,&gWInfo)))
{
LoadString(ghInstance,IDS_IAFMT,szString,MID_SZ);
LoadString(ghInstance,IDS_AAFMT,szString2,MID_SZ);
MessageBox(hwnd,szString,szString2,MB_OK|MB_ICONEXCLAMATION);
}
}
LoadInstalledCodecs();
break;
}

// Uninstall the codec
case IDC_UNINSTALL:
{
if (gtCodec==AUDIOCODEC)
{
if (FAILED(pAudio->RemoveACMFormat(gpWFmt)))
{
LoadString(ghInstance,IDS_UAFMT,szString,MID_SZ);
LoadString(ghInstance,IDS_RAFMT,szString2,MID_SZ);
MessageBox(hwnd,szString,szString2,MB_OK|MB_ICONEXCLAMATION);
}
}

LoadInstalledCodecs();
break;
}

// Reorder the codecs
case IDC_REORDER:
{
DialogBox(ghInstance,MAKEINTRESOURCE(IDD_REORDER),hwnd,(DLGPROC)ReorderUIProc);
break;
}
}

DlgDisplayInstalledCodecs(hwnd);
}

//****************************************************************************
//
// LRESULT CALLBACK MainUIProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
//
//****************************************************************************

LRESULT CALLBACK MainUIProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{

LRESULT fProcessed=TRUE;

switch(message)
{
HANDLE_MSG(hwnd,WM_INITDIALOG,MainUI_OnInitDialog);
HANDLE_MSG(hwnd,WM_CLOSE,MainUI_OnClose);
HANDLE_MSG(hwnd,WM_DESTROY,MainUI_OnDestroy);
HANDLE_MSG(hwnd,WM_COMMAND,MainUI_OnCommand);

default:
fProcessed=FALSE;
break;
}
return (fProcessed);
}

//****************************************************************************
//
// BOOL ReorderUI_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
//
//****************************************************************************

BOOL ReorderUI_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{

DlgDisplayInstalledCodecs(hwnd);
return TRUE;
}

//****************************************************************************
//
// void ReorderUI_OnDestroy(HWND hwnd)
//
//****************************************************************************

void ReorderUI_OnDestroy(HWND hwnd)
{
EndDialog(hwnd,TRUE);
}

//****************************************************************************
//
// void ReorderUI_OnClose(HWND hwnd)
//
//****************************************************************************

void ReorderUI_OnClose(HWND hwnd)
{
IndexInstalledCodecs();
ReorderUI_OnDestroy(hwnd);
}

//****************************************************************************
//
// void ReorderUI_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
//
//****************************************************************************

void ReorderUI_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{

HWND hListBox;
int tmp;

switch(id)
{

// Move the selected codec up in the list box, and update the CapInfoIndex
case IDC_UP:
{

hListBox = GetDlgItem(hwnd,IDC_LBINSTALLEDCODECS);

//get text of current selection
int index =SendMessage(hListBox,LB_GETCURSEL,0,0);

//if wasn't able to get selection
if (index==0 || index==LB_ERR)
return;

SendMessage(hListBox,LB_DELETESTRING,index,0);
SendMessage(hListBox,LB_INSERTSTRING,index-1,(LPARAM)(LPCSTR)gpsAudCapInfoList->aFormats[gpnAudCapOrderedIndex[index]].szFormat);

tmp = gpnAudCapOrderedIndex[index-1];
gpnAudCapOrderedIndex[index-1] = gpnAudCapOrderedIndex[index];
gpnAudCapOrderedIndex[index] = tmp;

SendMessage(hListBox,LB_SETCURSEL,index-1,0);
break;
}

// Move the selected codec down in the list box, and update the CapInfoIndex
case IDC_DOWN:
{

hListBox = GetDlgItem(hwnd,IDC_LBINSTALLEDCODECS);

//get text of current selection
int unsigned index =SendMessage(hListBox,LB_GETCURSEL,0,0);

if (index==gpsAudCapInfoList->cFormats-1 || index==LB_ERR)
return;

SendMessage(hListBox,LB_DELETESTRING,index,0);
SendMessage(hListBox,LB_INSERTSTRING,index+1,(LPARAM)(LPCSTR)gpsAudCapInfoList->aFormats[gpnAudCapOrderedIndex[index]].szFormat);

tmp = gpnAudCapOrderedIndex[index+1];
gpnAudCapOrderedIndex[index+1] = gpnAudCapOrderedIndex[index];
gpnAudCapOrderedIndex[index] = tmp;

SendMessage(hListBox,LB_SETCURSEL,index+1,0);
break;
}

// Update the CapInfoList from the CapInfoIndex
// Call ReorderFormats to reorder the codecs in NetMtg
// Reload the CapInfoList w/ the new info.
case IDC_OK:
{
UpdateCapInfoListsFromIndex();

pAudio->ReorderFormats(gpsAudCapInfoList);

//refresh display
LoadInstalledCodecs();
EndDialog(hwnd,TRUE);
break;
}

// Cancel changes by reinitializing the CapInfoIndexs from the
// CapInfoLists and ending the dialog.
case IDC_CANCEL:
{
IndexInstalledCodecs();
EndDialog(hwnd,TRUE);
break;
}
}
}

//****************************************************************************
//
// LRESULT CALLBACK ReorderUIProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
//
//****************************************************************************

LRESULT CALLBACK ReorderUIProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
LRESULT fProcessed=TRUE;

switch(message)
{
HANDLE_MSG(hwnd,WM_INITDIALOG,ReorderUI_OnInitDialog);
HANDLE_MSG(hwnd,WM_CLOSE,ReorderUI_OnClose);
HANDLE_MSG(hwnd,WM_DESTROY,ReorderUI_OnDestroy);
HANDLE_MSG(hwnd,WM_COMMAND,ReorderUI_OnCommand);

default:
fProcessed=FALSE;
break;
}
return (fProcessed);
}

//****************************************************************************
//
// void LoadInstalledCodecs(void)
//
// Loads a list of the installed codecs w/ their properties.
//
//****************************************************************************

void LoadInstalledCodecs(void)
{

//free any existing info
if (gtCodec==AUDIOCODEC)
{
if (gpsAudCapInfoList)
pAudio->FreeBuffer(gpsAudCapInfoList);

//Get Installed Audio Codecs
if (FAILED(pAudio->EnumFormats(&gpsAudCapInfoList)))
gpsAudCapInfoList=NULL;
else
{
if (gpnAudCapOrderedIndex)
LocalFree(gpnAudCapOrderedIndex);
gpnAudCapOrderedIndex = (int *)LocalAlloc(LPTR,sizeof(int) * gpsAudCapInfoList->cFormats);
}
}

//Structures returned by EnumFormats not guarenteed to be inorder.
IndexInstalledCodecs();
}

//****************************************************************************
//
// void IndexInstalledCodecs(void)
//
// Updates the CapOrderedIndexes to contain lists of indexes to the codecs
// represented in the CapInfoLists in installed order.
//
//****************************************************************************

void IndexInstalledCodecs(void)
{
unsigned int i;

if (gtCodec==AUDIOCODEC && gpsAudCapInfoList)
for (i=0;i<gpsAudCapInfoList->cFormats;i++)
gpnAudCapOrderedIndex[gpsAudCapInfoList->aFormats[i].wSortIndex]=i;

}

//****************************************************************************
//
// void UpdateCapInfoListsFromIndex(void)
//
// Updates the CapInfoLists with the installed order represended in the CapOrderedIndexes
//
//****************************************************************************

void UpdateCapInfoListsFromIndex(void)
{
unsigned int i;

if (gtCodec==AUDIOCODEC)
for (i=0;i<gpsAudCapInfoList->cFormats;i++)
gpsAudCapInfoList->aFormats[gpnAudCapOrderedIndex[i]].wSortIndex=i;

}

//****************************************************************************
//
// void DlgDisplayInstalledCodecs(HWND hwnd)
//
// Displays the installed codecs in the order specified by the CapOrderedIndexes.
//
//****************************************************************************

void DlgDisplayInstalledCodecs(HWND hwnd)
{
unsigned int i;

//Display Installed Codecs
HWND hListBox = GetDlgItem(hwnd,IDC_LBINSTALLEDCODECS);
SendMessage(hListBox,LB_RESETCONTENT,0,0);

if ((gtCodec==AUDIOCODEC)&& gpsAudCapInfoList)
for (i=0;i<gpsAudCapInfoList->cFormats;i++)
SendMessage(hListBox,LB_ADDSTRING,0,(LPARAM)(LPCSTR)gpsAudCapInfoList->aFormats[gpnAudCapOrderedIndex[i]].szFormat);

}

//*****************************************************************************


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by instcodc.rc
//
#define IDS_GETIAUDIO                   2
#define IDS_RAFMT                       13
#define IDS_AAFMT                       14
#define IDS_IAFMT                       16
#define IDS_UAFMT                       17
#define IDD_MAINUI                      101
#define IDD_REORDER                     106
#define IDI_INSTCODC                    108
#define IDC_OK                          1002
#define IDC_CANCEL                      1003
#define IDC_INSTALL                     1004
#define IDC_DOWN                        1005
#define IDC_UNINSTALL                   1006
#define IDC_REORDER                     1020
#define IDC_UP                          1025
#define IDC_LBINSTALLEDCODECS           1043

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        109
#define _APS_NEXT_COMMAND_VALUE         40017
#define _APS_NEXT_CONTROL_VALUE         1045
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


README.TXT

------------------------------------------------------------------
         Microsoft NetMeeting SDK 2.1 NMCHAT Readme File
                           November 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE NMCHAT SAMPLE
BUILDING THE NMCHAT SAMPLE

OVERVIEW OF THE NMCHAT SAMPLE
=============================

NMCHAT is a Windows utility application that allows the user to
send text messages to other NmChat users existing NetMeeting connection.

BUILDING THE NMCHAT SAMPLE
==========================

Building the sample application requires VC++ Version 5.0 or later.

To build the sample from Visual C++, open the project makefile (nmchat.mak)
as a Microsoft VC++ project, and build as usual.  The NetMeeting SDK
directories must be added to your VC++ environment.

If your DOS environment is correctly configured, you can build the sample
from the command line by typing "nmake".  The DOS path, include and lib
environment variables must point to the VC installation and the
NetMeeting SDK directories.

Notes:
The chat sample uses an existing NetMeeting connection.  There is no way
to launch NetMeeting from the NmChat UI.

NmChat displays three windows.  The upper left displays the call roster and
allows the user to specify the recipient of a message.  The upper right
displays the status messages and time stamps.  Across the bottom is the
text entry area for composing your messages.

NmChat can only exchange data with another instance of NmChat.  This sample
should be running on all machines in the conference.


CCONF.CPP

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CCONF.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

// Global Varaiables
INmManager * g_pMgr  = NULL;             // The Conference Manager
CMgrNotify * g_pMgrNotify = NULL;        // Notifications for the Manager
INmConference * g_pConference = NULL;    // The Current Conference
CConfNotify * g_pConferenceNotify =NULL; // Notifications for the Conference

// Local Application Guid : {D29A8C50-774F-11d0-8B1D-00A0C91BC90E}
const GUID g_guidApp =
{ 0xd29a8c51, 0x774f, 0x11d0, { 0x8b, 0x1d, 0x0, 0xa0, 0xc9, 0x1b, 0xc9, 0xe} };

// NetMeeting 2.0 chat guid: {340F3A60-7067-11D0-A041-444553540000}
const GUID g_guidNM2Chat =
{ 0x340f3a60, 0x7067, 0x11d0, { 0xa0, 0x41, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0 } };

//****************************************************************************
//
// CLASS CMgrNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CMgrNotify::CMgrNotify() : RefCount(), CNotify()
{
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

CMgrNotify::~CMgrNotify()
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CMgrNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CMgrNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CMgrNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
}
else if (riid == IID_INmManagerNotify)
{
*ppvObject = (INmManagerNotify *)this;
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// HRESULT InitConfMgr(void)
//
//****************************************************************************

HRESULT InitConfMgr(void)
{
HRESULT hr;
LPCLASSFACTORY pcf;

    // Notify the system we want to use the conferencing services
    // by creating a conference manager object
    hr = CoGetClassObject(CLSID_NmManager,
                          CLSCTX_INPROC,
                          NULL,
                          IID_IClassFactory,
                          (void**)&pcf);
    if (SUCCEEDED(hr))
    {
        // Get the conference manager object
        hr = pcf->CreateInstance(NULL, IID_INmManager, (void**)&g_pMgr);
    if (SUCCEEDED(hr))
        {
            // Connect to the conference manager object
            g_pMgrNotify = new CMgrNotify();
            if (NULL != g_pMgrNotify)
            {
                hr = g_pMgrNotify->Connect(g_pMgr);
    if (SUCCEEDED(hr))
                {
                ULONG uchCaps = NMCH_ALL;
                ULONG uOptions = 0;
hr = g_pMgr->Initialize(&uOptions, &uchCaps);
}
            }
        }

        pcf->Release();
    }

return hr;
}

//****************************************************************************
//
// VOID FreeConfMgr(void)
//
//****************************************************************************

VOID FreeConfMgr(void)
{
// Release conference manager notify
if (NULL != g_pMgrNotify)
{
g_pMgrNotify->Disconnect();
g_pMgrNotify->Release();
g_pMgrNotify = NULL;
}

// Release conference manager
if (NULL != g_pMgr)
{
g_pMgr->Release();
g_pMgr = NULL;
}
}

//****************************************************************************
//
// VOID FreeConference(void)
//
//****************************************************************************

VOID FreeConference(void)
{
if (NULL != g_pConferenceNotify)
{
g_pConferenceNotify->Disconnect();
g_pConferenceNotify->Release();
g_pConferenceNotify = NULL;
}

if (NULL != g_pConference)
{
g_pConference->Release();
g_pConference = NULL;
}
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CMgrNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmManagerNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CMgrNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from INmManagerNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CMgrNotify::NmUI(CONFN confn)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CMgrNotify::CallCreated(INmCall *pCall)
{
return S_OK;
}

// As soon as a conference is available, create the data channel
HRESULT STDMETHODCALLTYPE CMgrNotify::ConferenceCreated(INmConference *pConference)
{
ASSERT(NULL == g_pConference);
ASSERT(NULL == g_pChannelData);

HookConference(pConference);

return S_OK;
}

//****************************************************************************
//
// HRESULT HookConference(INmConference * pConference)
//
//****************************************************************************

HRESULT HookConference(INmConference * pConference)
{
HRESULT hr;

ASSERT(NULL != pConference);
ASSERT(NULL == g_pConference);
g_pConference = pConference;

pConference->AddRef();

    // Connect to the conference object
    ASSERT(NULL == g_pConferenceNotify)
    g_pConferenceNotify = new CConfNotify();
    if (NULL == g_pConferenceNotify)
    {
        hr = E_OUTOFMEMORY;
    }
    else
    {
hr = g_pConferenceNotify->Connect(pConference);
if (FAILED(hr))
{
g_pConferenceNotify->Release();
g_pConferenceNotify = NULL;
}
}

return hr;
}

//****************************************************************************
//
// CLASS CConfNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CConfNotify::CConfNotify() : RefCount(), CNotify()
{
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

CConfNotify::~CConfNotify()
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CConfNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CConfNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CConfNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
}
else if (riid == IID_INmConferenceNotify)
{
*ppvObject = (INmConferenceNotify *)this;
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmConferenceNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CConfNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from IConfNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::NmUI(CONFN uNotify)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CConfNotify::StateChanged(NM_CONFERENCE_STATE uState)
{
if (NULL == g_pConference)
return S_OK; // weird

switch (uState)
{
case NM_CONFERENCE_ACTIVE:
{
HRESULT hr;
INmChannelData * pChannelData;

#ifdef NM2_CHAT_PROTOCOL
hr = g_pConference->CreateDataChannel(&pChannelData, g_guidNM2Chat);
#else
hr = g_pConference->CreateDataChannel(&pChannelData, g_guidApp);
#endif /* NM2_CHAT_PROTOCOL */
if (S_OK == hr)
{
hr = HookData(pChannelData);  // Real hook is g_pChannelData
}
DisplaySysMsg((S_OK == hr) ? TEXT("Chat Started") : TEXT("Chat failed to start!"), NULL);
break;
}

case NM_CONFERENCE_INITIALIZING:
break; // can't do anything just yet

case NM_CONFERENCE_WAITING:
FreeDataChannel();
DisplaySysMsg(TEXT("Waiting in Host Mode"), NULL);
break;

case NM_CONFERENCE_IDLE:
FreeDataChannel();
FreeConference();
DisplaySysMsg(TEXT("No conference"), NULL);
break;
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE CConfNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CConfNotify::ChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel *pChannel)
{
return S_OK;
}


CCONF.H

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CCONF.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CMgrNotify
//
//****************************************************************************

class CMgrNotify : public RefCount, public CNotify, public INmManagerNotify
{
public:
CMgrNotify();
~CMgrNotify();

// INmManagerNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN confn);
HRESULT STDMETHODCALLTYPE ConferenceCreated(INmConference *pConference);
HRESULT STDMETHODCALLTYPE CallCreated(INmCall *pCall);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

//****************************************************************************
//
// CLASS CConfNotify
//
//****************************************************************************

class CConfNotify : public RefCount, public CNotify, public INmConferenceNotify
{
public:
CConfNotify();
~CConfNotify();

// INmConferenceNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE StateChanged(NM_CONFERENCE_STATE uState);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pfMember);
HRESULT STDMETHODCALLTYPE ChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel *pChannel);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

// Global Variables
extern INmManager * g_pMgr;
extern INmConference * g_pConference;

// Global Functions
HRESULT InitConfMgr(void);
VOID FreeConfMgr(void);
VOID FreeConference(void);
HRESULT HookConference(INmConference * pConference);


CDATA.CPP

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CDATA.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

INmChannelData * g_pChannelData;
CDataNotify * g_pNotifyData;

//****************************************************************************
//
// VOID DisplayMsg(LPTSTR pszMsg, INmMember * pMember)
//
//****************************************************************************

VOID DisplayMsg(LPTSTR pszMsg, INmMember * pMember)
{
#ifdef NM2_CHAT_PROTOCOL
#ifndef UNICODE
// Convert UNICODE to ANSI
char sz[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, (LPWSTR) pszMsg, -1, sz, MAX_PATH, NULL, NULL);
pszMsg = sz;
#endif /* UNICODE */
#endif /* NM2_CHAT_PROTOCOL */

DisplaySysMsg(pszMsg, pMember);
}

//****************************************************************************
//
// VOID DisplaySysMsg(LPTSTR pszMsg, INmMember * pMember)
//
//****************************************************************************

VOID DisplaySysMsg(LPTSTR pszMsg, INmMember * pMember)
{
LPTSTR pszUser = NULL;

if (NULL != pMember)
{
PUSER pUser = PUserFromINmMember(pMember);
if (NULL != pUser)
pszUser = pUser->pszName;
}

if (NULL == pszUser)
{
pszUser = TEXT("");
}

DisplayMessage(pszMsg, pszUser);
}

//****************************************************************************
//
// VOID DisplayMessage(LPTSTR pszMsg, LPTSTR pszUser)
//
//****************************************************************************

VOID DisplayMessage(LPTSTR pszMsg, LPTSTR pszUser)
{
int iItem;
LV_ITEM  lvItem;

ClearStruct(&lvItem);
lvItem.iItem = 0x0FFFFFFF;
lvItem.mask = LVIF_PARAM | LVIF_TEXT;
lvItem.pszText = pszUser;
lvItem.cchTextMax = lstrlen(lvItem.pszText);
iItem = ListView_InsertItem(ghwndMsg, &lvItem);

{
TCHAR sz[MAX_PATH];
SYSTEMTIME sysTime;
GetLocalTime(&sysTime);
wsprintf(sz, "%02d:%02d:%02d", sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
ListView_SetItemText(ghwndMsg, iItem, ILV_TIME, sz);
}

ListView_SetItemText(ghwndMsg, iItem, ILV_TEXT, pszMsg);
}

//****************************************************************************
//
// HRESULT HookData(INmChannelData * pChannelData)
//
//****************************************************************************

HRESULT HookData(INmChannelData * pChannelData)
{
HRESULT hr;
CDataNotify * pNotify;

ASSERT(NULL != pChannelData);
ASSERT(NULL == g_pChannelData);

g_pChannelData = pChannelData;

    // Connect to the channel object
    pNotify = new CDataNotify();
    if (pNotify == NULL)
    {
        hr = E_OUTOFMEMORY;
    }
    else
    {
hr = pNotify->Connect(pChannelData);
if (SUCCEEDED(hr))
{
g_pNotifyData = pNotify;
}
}

if (FAILED(hr))
{
FreeDataChannel();
}

return hr;
}

//****************************************************************************
//
// VOID FreeDataChannel(void)
//
//****************************************************************************

VOID FreeDataChannel(void)
{
// Release all notify
if (NULL != g_pNotifyData)
{
g_pNotifyData->Disconnect();
g_pNotifyData->Release();
g_pNotifyData = NULL;
}

if (NULL != g_pChannelData)
{
g_pChannelData->Release();
g_pChannelData = NULL;
}
}

//****************************************************************************
//
// CLASS CDataNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CDataNotify::CDataNotify() : RefCount(), CNotify()
{
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

CDataNotify::~CDataNotify()
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CDataNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CDataNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CDataNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
}
else if (riid == IID_INmChannelDataNotify)
{
*ppvObject = (INmChannelDataNotify *)this;
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CDataNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmChannelDataNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CDataNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from INmChannelNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CDataNotify::NmUI(CONFN uNotify)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CDataNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
if (NM_MEMBER_ADDED == uNotify)
{
AddMember(pMember);
}
else if (NM_MEMBER_REMOVED == uNotify)
{
RemoveMember(pMember);
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE CDataNotify::DataSent(INmMember *pMember, ULONG uSize, LPBYTE pb)
{
LPTSTR psz;

#ifdef NM2_CHAT_PROTOCOL
ASSERT(uSize > CB_NM2_HDR);
psz = (LPTSTR) (pb + *(LPDWORD) pb);  // skip past header
#else
psz = (LPTSTR) pb;
#endif /* NM2_CHAT_PROTOCOL */

DisplayMsg(psz, g_pMemberLocal);

return S_OK;
}

HRESULT STDMETHODCALLTYPE CDataNotify::DataReceived(INmMember *pMember, ULONG uSize, LPBYTE pb, ULONG dwFlags)
{
LPTSTR psz;

// Warning : Doesn't handle broken packets
// TODO: Check dwFlags & NM_DF_SEGMENT_END

#ifdef NM2_CHAT_PROTOCOL
ASSERT(uSize > CB_NM2_HDR);
psz = (LPTSTR) (pb + *(LPDWORD) pb);  // skip past header
#else
psz = (LPTSTR) pb;
#endif /* NM2_CHAT_PROTOCOL */

DisplayMsg(psz, pMember);

return S_OK;
}


CDATA.H

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CDATA.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CDataNotify
//
//****************************************************************************

class CDataNotify : public RefCount, public CNotify, public INmChannelDataNotify
{
public:
CDataNotify();
~CDataNotify();

// INmChannelDataNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember * pMember);
HRESULT STDMETHODCALLTYPE DataSent(INmMember *pMember, ULONG uSize, LPBYTE pb);
HRESULT STDMETHODCALLTYPE DataReceived(INmMember *pMember, ULONG uSize, LPBYTE pb, ULONG dwFlags);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

// Global Variables
extern INmChannelData * g_pChannelData;
extern CDataNotify    * g_pNotifyData;

// Global Routines
HRESULT HookData(INmChannelData * pChannel);
VOID DoCreateDataChannel(INmConference *pConference);
VOID FreeDataChannel(void);


CLUTIL.CPP

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CLUTIL.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

RefCount::RefCount(void)
{
m_cRef = 1;
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

RefCount::~RefCount(void)
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE RefCount::AddRef(void)
{
   ASSERT(m_cRef >= 0);

   InterlockedIncrement(&m_cRef);

   return (ULONG) m_cRef;
}

ULONG STDMETHODCALLTYPE RefCount::Release(void)
{
if (0 == InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}

ASSERT(m_cRef > 0);
return (ULONG) m_cRef;
}

//****************************************************************************
//
// CLASS CNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CNotify::CNotify() :
m_pcnpcnt(NULL),
    m_pcnp(NULL),
    m_dwCookie(0),
    m_pUnk(NULL)
{
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

CNotify::~CNotify()
{
Disconnect(); // Make sure we're disconnected
}

//****************************************************************************
//
// HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
//
//****************************************************************************

HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
{
HRESULT hr;

ASSERT(0 == m_dwCookie);

// Get the connection container
hr = pUnk->QueryInterface(IID_IConnectionPointContainer, (void **)&m_pcnpcnt);
if (SUCCEEDED(hr))
{
// Find an appropriate connection point
hr = m_pcnpcnt->FindConnectionPoint(riid, &m_pcnp);
if (SUCCEEDED(hr))
{
ASSERT(NULL != m_pcnp);
// Connect the sink object
hr = m_pcnp->Advise((IUnknown *)pUnkN, &m_dwCookie);
}
}

if (FAILED(hr))
{
m_dwCookie = 0;
}
else
{
    m_pUnk = pUnk; // keep around for caller
    }

return hr;
}

//****************************************************************************
//
// HRESULT CNotify::Disconnect (void)
//
//****************************************************************************

HRESULT CNotify::Disconnect (void)
{
    if (0 != m_dwCookie)
    {
        // Disconnect the sink object
        m_pcnp->Unadvise(m_dwCookie);
        m_dwCookie = 0;

        m_pcnp->Release();
        m_pcnp = NULL;

        m_pcnpcnt->Release();
        m_pcnpcnt = NULL;

        m_pUnk = NULL;
    }

    return S_OK;
}

//****************************************************************************
//
// CLASS COBLIST
//
//****************************************************************************

//****************************************************************************
//
// Destructor
//
//****************************************************************************

COBLIST::~COBLIST()
{
    ASSERT(IsEmpty());
}

#ifdef DEBUG
VOID* COBLIST::GetHead()
{
ASSERT(m_pHead);

return m_pHead->pItem;
}

VOID* COBLIST::GetTail()
{
ASSERT(m_pTail);

return m_pTail->pItem;
}
#endif /* DEBUG */

//****************************************************************************
//
// VOID* COBLIST::GetNext(POSITION& rPos)
//
//****************************************************************************

VOID* COBLIST::GetNext(POSITION& rPos)
{
ASSERT(rPos);

VOID* pReturn = rPos->pItem;
rPos = rPos->pNext;

return pReturn;
}

//****************************************************************************
//
// VOID* COBLIST::RemoveAt(POSITION Pos)
//
//****************************************************************************

VOID* COBLIST::RemoveAt(POSITION Pos)
{
VOID* pReturn = NULL;

if (m_pHead)
{
if (m_pHead == Pos)
{
// Removing the first element in the list

m_pHead = Pos->pNext;
pReturn = Pos->pItem;
delete Pos;
m_cItem--;
ASSERT(0 <= m_cItem);

if (NULL == m_pHead)
{
// Removing the only element!
m_pTail = NULL;
}
}
else
{
POSITION pCur = m_pHead;

while (pCur && pCur->pNext)
{
if (pCur->pNext == Pos)
{
// Removing

pCur->pNext = Pos->pNext;
if (m_pTail == Pos)
{
m_pTail = pCur;
}
pReturn = Pos->pItem;
delete Pos;

m_cItem--;
ASSERT(0 <= m_cItem);
}

pCur = pCur->pNext;
}
}
}

return pReturn;
}

//****************************************************************************
//
// POSITION COBLIST::AddTail(VOID* pItem)
//
//****************************************************************************

POSITION COBLIST::AddTail(VOID* pItem)
{
POSITION posRet = NULL;

if (m_pTail)
{
if (m_pTail->pNext = new COBNODE)
{
m_pTail = m_pTail->pNext;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
m_cItem++;
}
}
else
{
ASSERT(!m_pHead);
if (m_pHead = new COBNODE)
{
m_pTail = m_pHead;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
m_cItem++;
}
}

return m_pTail;
}

//****************************************************************************
//
// void COBLIST::EmptyList()
//
//****************************************************************************

void COBLIST::EmptyList()
{
    while (!IsEmpty()) {
        RemoveAt(GetHeadPosition());
    }
}

#ifdef DEBUG
VOID* COBLIST::RemoveTail()
{
ASSERT(m_pHead);
ASSERT(m_pTail);

return RemoveAt(m_pTail);
}

VOID* COBLIST::RemoveHead()
{
ASSERT(m_pHead);
ASSERT(m_pTail);

return RemoveAt(m_pHead);
}

void * COBLIST::GetFromPosition(POSITION Pos)
{
    void * Result = SafeGetFromPosition(Pos);
ASSERT(Result);
return Result;
}
#endif /* DEBUG */

//****************************************************************************
//
// POSITION COBLIST::GetPosition(void* _pItem)
//
//****************************************************************************

POSITION COBLIST::GetPosition(void* _pItem)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Position->pItem == _pItem) {
            break;
        }
GetNext(Position);
    }
    return Position;
}

//****************************************************************************
//
// POSITION COBLIST::Lookup(void* pComparator)
//
//****************************************************************************

POSITION COBLIST::Lookup(void* pComparator)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Compare(Position->pItem, pComparator)) {
            break;
        }
GetNext(Position);
    }
    return Position;
}

//****************************************************************************
//
// void * COBLIST::SafeGetFromPosition(POSITION Pos)
//
//****************************************************************************

void * COBLIST::SafeGetFromPosition(POSITION Pos)
{
// Safe way to validate that an entry is still in the list,
// which ensures bugs that would reference deleted memory,
// reference a NULL pointer instead
// (e.g. an event handler fires late/twice).
// Note that versioning on entries would provide an additional
// safeguard against re-use of a position.
// Walklist to find entry.

POSITION PosWork = m_pHead;

while (PosWork) {
if (PosWork == Pos) {
return Pos->pItem;
}
GetNext(PosWork);
}
return NULL;
}

//****************************************************************************
//
// COBLIST Utility routines
//
//****************************************************************************

//****************************************************************************
//
// POSITION AddNode(PVOID pv, COBLIST ** ppList)
//
// Add a node to a list.
// Initializes the ObList, if necessary.
// Returns the position in the list or NULL if there was a problem.
//
//****************************************************************************

POSITION AddNode(PVOID pv, COBLIST ** ppList)
{
ASSERT(NULL != ppList);
if (NULL == *ppList)
{
*ppList = new COBLIST();
if (NULL == *ppList)
return NULL;
}

return (*ppList)->AddTail(pv);
}

//****************************************************************************
//
// PVOID RemoveNode(POSITION * pPos, COBLIST *pList)
//
// Remove a node from a list.
// Sets pPos to NULL
//
//****************************************************************************

PVOID RemoveNode(POSITION * pPos, COBLIST *pList)
{
if ((NULL == pList) || (NULL == pPos))
return NULL;

PVOID pv = pList->RemoveAt(*pPos);
*pPos = NULL;
return pv;
}

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
// We don't support construction from an ANSI string in the Unicode build.
//
//****************************************************************************

#if !defined(UNICODE)

BSTRING::BSTRING(LPCSTR lpcString)
{
m_bstr = NULL;

// Compute the length of the required BSTR, including the null
int cWC =  MultiByteToWideChar(CP_ACP, 0, lpcString, -1, NULL, 0);
if (cWC <= 0)
return;

// Allocate the BSTR, including the null
m_bstr = SysAllocStringLen(NULL, cWC - 1); // SysAllocStringLen adds another 1

ASSERT(NULL != m_bstr);
if (NULL == m_bstr)
{
return;
}

// Copy the string
MultiByteToWideChar(CP_ACP, 0, lpcString, -1, (LPWSTR) m_bstr, cWC);

// Verify that the string is null terminated
ASSERT(0 == m_bstr[cWC - 1]);
}

#endif // !defined(UNICODE)

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

BTSTR::BTSTR(BSTR bstr)
{
m_psz = PszFromBstr(bstr);
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

BTSTR::~BTSTR()
{
if (NULL != m_psz)
LocalFree(m_psz);
}

//****************************************************************************
//
// LPTSTR PszFromBstr(BSTR bstr)
//
// Converts a BSTR to a LPSTR
//
//****************************************************************************

LPTSTR PszFromBstr(BSTR bstr)
{
if (NULL == bstr)
return NULL;
int cch =  WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, NULL, 0, NULL, NULL);
if (cch <= 0)
return NULL;

LPTSTR psz = PszAlloc(cch);
if (NULL == psz)
return NULL;

WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, psz, cch+1, NULL, NULL);
return psz;
}


CLUTIL.H

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CLUTIL.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#ifndef _CL_UTIL_H_
#define _CL_UTIL_H_

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

class RefCount
{
private:
   LONG m_cRef;

public:
   RefCount();
   // Virtual destructor defers to destructor of derived class.
   virtual ~RefCount();

   // IUnknown methods

   ULONG STDMETHODCALLTYPE AddRef(void);
   ULONG STDMETHODCALLTYPE Release(void);
};

//****************************************************************************
//
// CLASS CNotify
//
// Notification sink
//
//****************************************************************************

class CNotify
{
private:
    DWORD  m_dwCookie;
IUnknown * m_pUnk;
    IConnectionPoint           * m_pcnp;
    IConnectionPointContainer  * m_pcnpcnt;
public:
    CNotify(void);
    ~CNotify();

    HRESULT Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN);
    HRESULT Disconnect(void);

    IUnknown * GetPunk() {return m_pUnk;}
};

//****************************************************************************
//
// CLASS COBLIST
//
//****************************************************************************

#define POSITION COBNODE*

struct COBNODE
{
POSITIONpNext;
void*pItem;
};

class COBLIST
{
protected:
POSITION m_pHead;
POSITION m_pTail;
int      m_cItem;
    virtual BOOL Compare(void* pItemToCompare, void* pComparator)
                       { return(pItemToCompare == pComparator); }
public:
COBLIST() : m_pHead(NULL), m_pTail(NULL), m_cItem(0) { }
    virtual         ~COBLIST();

virtual void *  RemoveAt(POSITION rPos);
    void            EmptyList();
POSITION    AddTail(void* pItem);
void *    GetNext(POSITION& rPos);
    void *          SafeGetFromPosition(POSITION rPos);
    POSITION        GetPosition(void* pItem);
    POSITION        Lookup(void* pComparator);
POSITION    GetHeadPosition()  { return (m_pHead); }
POSITION    GetTailPosition()  { return (m_pTail); }
BOOL    IsEmpty()          { return (!m_pHead); }
int             GetItemCount()     { return (m_cItem); }
#ifdef DEBUG
void *    GetHead();
void *    GetTail();
void *    RemoveHead();
void *        RemoveTail();
void *    GetFromPosition(POSITION rPos);
#else
void *    GetHead()          { return GetFromPosition(GetHeadPosition());}
void *          GetTail()          { return m_pTail->pItem;}
void *    RemoveHead()       { return RemoveAt(m_pHead); }
void *        RemoveTail()       { return RemoveAt(m_pTail); }
void *    GetFromPosition(POSITION rPos){return(rPos->pItem);}
#endif
};

//****************************************************************************
//
// Utility Functions
//
//****************************************************************************

POSITION AddNode(PVOID pv, COBLIST ** ppList);
PVOID RemoveNode(POSITION * pPos, COBLIST *pList);

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

class BSTRING
{
private:
BSTR   m_bstr;

public:
// Constructors
BSTRING() {m_bstr = NULL;}

inline BSTRING(LPCWSTR lpcwString);

#if !defined(UNICODE)
// We don't support construction from an ANSI string in the Unicode build.
BSTRING(LPCSTR lpcString);
#endif // !defined(UNICODE)

// Destructor
inline ~BSTRING();

// Cast to BSTR
operator BSTR() {return m_bstr;}
inline LPBSTR GetLPBSTR(void);
};

BSTRING::BSTRING(LPCWSTR lpcwString)
{
if (NULL != lpcwString)
{
m_bstr = SysAllocString(lpcwString);
ASSERT(NULL != m_bstr);
}
else
{
m_bstr = NULL;
}
}

BSTRING::~BSTRING()
{
if (NULL != m_bstr)
{
SysFreeString(m_bstr);
}
}

inline LPBSTR BSTRING::GetLPBSTR(void)
{
ASSERT(NULL == m_bstr);

return &m_bstr;
}

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

class BTSTR
{
private:
LPTSTR m_psz;

public:
BTSTR(BSTR bstr);
~BTSTR();

// Cast to BSTR
operator LPTSTR() {return (NULL == m_psz) ? TEXT("<null>") : m_psz;}
};

LPTSTR PszFromBstr(BSTR bst);

#endif  // _CL_UTIL_H_


CMD.CPP

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CMD.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// VOID CmdOptions(void)
//
//****************************************************************************

VOID CmdOptions(void)
{
}

//****************************************************************************
//
// VOID CmdClear(void)
//
// Clear the message window
//
//****************************************************************************

VOID CmdClear(void)
{
ClearText();
}

//****************************************************************************
//
// VOID CmdExit(void)
//
// Shutdown the application.
//
//****************************************************************************

VOID CmdExit(void)
{
DestroyWindow(ghwndMain);
}

//****************************************************************************
//
// VOID CmdHelp(void)
//
// Bring up help for the application
//
//****************************************************************************

VOID CmdHelp(void)
{
}

//****************************************************************************
//
// VOID CmdFont(void)
//
// Change the font for the message window
//
//****************************************************************************

VOID CmdFont(void)
{
#ifdef NOTUSED
CHOOSEFONT cf;
LOGFONT    lf;
HFONT      hfont;

lf = gPref.lf;

ClearStruct(&cf);
cf.lStructSize = sizeof(CHOOSEFONT);
cf.hwndOwner = ghwndMain;
cf.lpLogFont = &lf;
cf.iPointSize = 10;
cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT;

if (ChooseFont(&cf) != TRUE)
return;

hfont= CreateFontIndirect(cf.lpLogFont);
if (hfont == hfontNil)
return;

MaybeDeleteObject((HGDIOBJ *) &ghfontEntry);
ghfontEntry = hfont;
SendMessage(ghwndMsg, WM_SETFONT,
(WPARAM) ghfontEntry, MAKELPARAM(TRUE, 0));

gPref.lf = lf;
#endif
}

//****************************************************************************
//
// VOID CmdAbout(void)
//
// Display the about box
//
//****************************************************************************

VOID CmdAbout(void)
{
TCHAR szVersion[MAX_PATH];

LoadString(ghInst, IDS_VERSION, szVersion, CCHMAX(szVersion));

ShellAbout(ghwndMain, szVersion, NULL, LoadIcon(ghInst, MAKEINTRESOURCE(ICO_MAIN)));
}

//****************************************************************************
//
// Main window command table definition.
//
//****************************************************************************

static CMD _rgcmdMain[] =
{
{IDM_EXIT,                CmdExit},

{IDM_VIEW_CLEAR,          CmdClear},
{IDM_VIEW_FONT,           CmdFont},
{IDM_OPTIONS,             CmdOptions},

{IDM_HELP,                CmdHelp},
{IDM_ABOUT,               CmdAbout},

{0, NULL}  // MUST be last entry in the table
};

//****************************************************************************
//
// LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
//
// Handle a WM_COMMAND message
//
//****************************************************************************

LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
{
UINT wCmd = (UINT) GET_WM_COMMAND_ID(wparam, lparam);

for (LPCMD lpcmd = _rgcmdMain; 0 != lpcmd->wCmd; lpcmd++)
{
if (wCmd == lpcmd->wCmd)
{
lpcmd->pfncmd();
break;
}
}

return 0L;
}


CMEMB.CPP

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CMEMB.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

COBLIST * g_pListUser = (COBLIST *) NULL;

INmMember * g_pMemberLocal = NULL;

//****************************************************************************
//
// PUSER PUserFromINmMember(INmMember * pMember)
//
//****************************************************************************

PUSER PUserFromINmMember(INmMember * pMember)
{
if (NULL != g_pListUser)
{
POSITION pos = g_pListUser->GetHeadPosition();
while (NULL != pos)
{
PUSER pUser = (PUSER) g_pListUser->GetNext(pos);
if (pMember == pUser->pMember)
{
ASSERT(pMember == pUser->pMember);
return pUser;
}
}
}

return NULL;
}

//****************************************************************************
//
// int ItemFromPUser(PUSER pUser)
//
//****************************************************************************

int ItemFromPUser(PUSER pUser)
{
if (NULL == ghwndUser)
return -1;

LV_FINDINFO findInfo;
ClearStruct(&findInfo);
findInfo.flags = LVFI_PARAM;
    findInfo.lParam = (LPARAM) pUser;
    return ListView_FindItem(ghwndUser, -1, &findInfo);
}

//****************************************************************************
//
// PUSER AllocUser(INmMember * pMember)
//
//****************************************************************************

PUSER AllocUser(INmMember * pMember)
{
HRESULT hr;
BSTR bstr;
INmMember * pMemberTop;

PUSER pUser = new USER;
if (NULL == pUser)
{
return NULL;
}
ClearStruct(pUser);
pUser->pMember = pMember;
pMember->AddRef();

hr = pMember->GetName(&bstr);
if (SUCCEEDED(hr))
{
pUser->pszName = PszFromBstr(bstr);
SysFreeString(bstr);
}

hr = pMember->GetNmVersion(&pUser->uVersion);

pUser->fLocal = S_OK == pMember->IsSelf();
pUser->fMcu = S_OK == pMember->IsMCU();

pUser->fTop = FALSE;
if (SUCCEEDED(g_pConference->GetTopProvider(&pMemberTop)))
{
pUser->fTop = (pMember == pMemberTop);
pMemberTop->Release();
}

if (pUser->fLocal)
g_pMemberLocal = pMember;

return pUser;
}

//****************************************************************************
//
// VOID FreeUser(PUSER pUser)
//
//****************************************************************************

VOID FreeUser(PUSER pUser)
{
ASSERT(NULL != pUser);

if (pUser->fLocal)
{
g_pMemberLocal = NULL;
}

FreePlpv(&pUser->pszName);

if (NULL != pUser->pMember)
{
pUser->pMember->Release();
}

delete pUser;
}

//****************************************************************************
//
// VOID FreeAllUsers(void)
//
//****************************************************************************

VOID FreeAllUsers(void)
{
if (NULL == g_pListUser)
return;

while (!g_pListUser->IsEmpty())
{
USER * pUser = (USER *) g_pListUser->RemoveHead();
ASSERT(NULL != pUser);
FreeUser(pUser);
}

delete g_pListUser;
g_pListUser = NULL;
}

//****************************************************************************
//
// VOID AddMember(INmMember * pMember)
//
//****************************************************************************

VOID AddMember(INmMember * pMember)
{
PUSER pUser = AllocUser(pMember);
if (NULL == pUser)
{
return;
}

AddNode(pUser, &g_pListUser);

LV_ITEM  lvItem;
ClearStruct(&lvItem);
lvItem.iItem = g_pListUser->GetItemCount();
lvItem.mask = LVIF_PARAM | LVIF_TEXT;
lvItem.pszText = (S_OK == pMember->IsSelf()) ? TEXT("everyone") : pUser->pszName;
lvItem.cchTextMax = lstrlen(pUser->pszName);
lvItem.lParam = (LPARAM) pUser;
ListView_InsertItem(ghwndUser, &lvItem);

DisplaySysMsg(TEXT("<joined>"), pMember);
}

//****************************************************************************
//
// VOID RemoveMember(INmMember * pMember)
//
//****************************************************************************

VOID RemoveMember(INmMember * pMember)
{
if (NULL == g_pListUser)
return;

POSITION pos = g_pListUser->GetHeadPosition();
while (NULL != pos)
{
POSITION posSav = pos;
PUSER pUser = (PUSER) g_pListUser->GetNext(pos);
if (pMember == pUser->pMember)
{
ASSERT(pMember == pUser->pMember);
DisplaySysMsg(TEXT("<left>"), pMember);

ListView_DeleteItem(ghwndUser, ItemFromPUser(pUser));
FreeUser(pUser);
g_pListUser->RemoveAt(posSav);
return;
}
}
}

//****************************************************************************
//
// INmMember * GetSelectedMember(void)
//
//****************************************************************************

INmMember * GetSelectedMember(void)
{
int iItem;

if (NULL == g_pListUser)
return NULL;

iItem = ListView_GetNextItem(ghwndUser, -1, LVNI_ALL | LVNI_SELECTED);
if (-1 == iItem)
return NULL;

LV_ITEM lv;
lv.mask = LVIF_PARAM;
lv.iItem = iItem;
ListView_GetItem(ghwndUser, &lv);

return ((PUSER) lv.lParam)->pMember;
}


CMEMB.H

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CMEMB.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

typedef struct tagUser {
INmMember * pMember;
LPTSTR pszName;
ULONG  uVersion;
BOOL   fLocal;
BOOL   fMcu;
BOOL   fTop;
} USER;
typedef USER * PUSER;

extern COBLIST * g_pListUser;
extern INmMember * g_pMemberLocal;

// from cMemb.cpp
VOID AddMember(INmMember * pMember);
VOID RemoveMember(INmMember * pMember);
VOID FreeAllUsers(void);
VOID FreeUser(PUSER pUser);
PUSER PUserFromINmMember(INmMember * pMember);
INmMember * GetSelectedMember(void);


DEBUG.CPP

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       DEBUG.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

#ifdef DEBUG /* The whole file! */

//****************************************************************************
//
// VOID FAR PASCAL AssertProc(LPSTR lpszMsg, LPSTR lpszAssert, LPSTR lpszFile, UINT line)
//
//****************************************************************************

VOID FAR PASCAL AssertProc(LPSTR lpszMsg, LPSTR lpszAssert, LPSTR lpszFile, UINT line)
{
int  id;
TCHAR szBuffer[1024]; // a big buffer

wsprintf((LPSTR)szBuffer,
"Assert: %s\nFile %s, Line %d",
(lpszMsg && *lpszMsg) ? lpszMsg  : lpszAssert,
lpszFile ? lpszFile : "", line);

OutputDebugString(szBuffer);

do {
id = MessageBox(NULL, (LPSTR)szBuffer, TEXT("NmChat Assert"),
MB_ICONHAND | MB_ABORTRETRYIGNORE | MB_SYSTEMMODAL);

switch (id)
{
case IDRETRY:
DebugBreak();
break;
case IDABORT:
FatalAppExit(0, (LPSTR)szBuffer);
break;
case IDIGNORE:
default:
break;
}
} while (id == IDRETRY);
}

#endif /* DEBUG - the whole file! */


GLOBAL.H

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       GLOBAL.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#ifndef GLOBAL_H
#define GLOBAL_H

// #define NM2_CHAT_PROTOCOL  // Define this to use the NM 2.0 chat protocol
#ifdef NM2_CHAT_PROTOCOL
#define CB_NM2_HDR   8     // NetMeeting 2.0 chat protocol has an 8 byte header
#endif

//-------------------------------------------------------
// Useful constants

#define chNull   ('\0')
#define lpNil    (NULL)

#define hwndNil      ((HWND) NULL)
#define hfontNil     ((HFONT) NULL)
#define hgdiNil      ((HGDIOBJ) NULL)

//-------------------------------------------------------
// Useful macros

#define ClearBytes(lpv, cb)ZeroMemory((LPVOID) (lpv), (cb))
#define ClearStruct(lpv)ZeroMemory((LPVOID) (lpv), sizeof(*(lpv)))
#define InitStruct(lpv)        {ClearStruct(lpv); (* (LPDWORD) lpv) = sizeof(*(lpv));}
#define CopyStruct(pDest, pSrc)  CopyMemory(pDest, pSrc, sizeof(*(pDest)))

#define SetEmptySz(sz)( *(sz) = _T('\0') )
#define FEmptySz(sz)            (((sz) == NULL) || (*(sz) == _T('\0')))

#define ARRAY_ELEMENTS(rg)      (sizeof(rg) / sizeof(rg[0]))
#define CCHMAX(sz)              (sizeof(sz) / sizeof(TCHAR))

#ifdef DEBUG
VOID FAR PASCAL AssertProc(LPTSTR lpszMsg, LPTSTR lpszAssert, LPTSTR lpszFile, UINT line);

#define DEBASSERT(exp,szMsg)  \
if (!(exp))                                                      \
{                                                                \
static char _szAssert[] = #exp ;   \
static char _szMsg[]    = szMsg;   \
AssertProc(_szMsg,_szAssert,__FILE__,__LINE__); \
}

#define ASSERT(f)       DEBASSERT(f, "(" #f ")")
#define AssertSz(f,sz)  DEBASSERT(f, sz)
#define NotReached()    DEBASSERT(FALSE, "NotReached declaration was reached!")
#else
#define ASSERT(f)
#define AssertSz(f, sz)
#define NotReached()
#endif

//****************************************************************************
//
// Functions for handling main window messages.  The message-dispatching
// mechanism expects all message-handling functions to have the following
// prototype:
//
// Function pointer prototype for message handling functions:
// LRESULT FunctionName(HWND, UINT, WPARAM, LPARAM);
//
//****************************************************************************

typedef LRESULT (*PFNMSG)(HWND, UINT, WPARAM, LPARAM);

// This structure maps messages to message handling functions.
typedef struct _MSD
{
UINT   uMsg;
PFNMSG pfnmsg;
} MSD;                 // MeSsage Dispatch structure
typedef MSD * LPMSD;

//****************************************************************************
//
// Functions for handling main window commands--ie. functions for
// processing WM_COMMAND messages based on the wParam value.
// The message-dispatching mechanism expects all command-handling
// functions to have the following prototype:
//
//****************************************************************************

typedef VOID (*PFNCMD)(VOID);

// This structure maps command IDs to command handling functions.
typedef struct _CMD
{
UINT     wCmd;
PFNCMD pfncmd;
} CMD;                 // CoMmand Dispatch structure
typedef CMD * LPCMD;

//****************************************************************************
//
// Special resource constants
//
//****************************************************************************

#define IDW_MSG   100
#define IDW_USER  101
#define IDW_EDIT  102

// See _rgColMember
#define ILV_NAME    0
#define ILV_TIME    1
#define ILV_TEXT    2

const int DYP_EDIT   = 30;  // Fixed Height of Edit control
const int DXP_USER   = 80;  // Fixed Width of User List
const int YP_TOP     = 3;
const int XP_LEFT    = 0;

//****************************************************************************
//
// Global Function Prototypes.
//
//****************************************************************************

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// from util.cpp
LPVOID LpvAlloc(int cb);
LPTSTR PszAlloc(int cch);
VOID FreePlpv(LPVOID plpv);
VOID MaybeDeleteObject(HGDIOBJ * phgdi);
VOID SizeMsgWindow(int dx, int dy);
VOID RecalcMsgWindow(void);

VOID SendText(void);
VOID ClearText(void);
VOID DisplayMsg(LPTSTR pszMsg, INmMember * pMember);
VOID DisplaySysMsg(LPTSTR pszMsg, INmMember * pMember);
VOID DisplayMessage(LPTSTR pszMsg, LPTSTR pszUser);

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

extern HINSTANCE ghInst;          // The current instance handle
extern HACCEL    ghAccelTable;    // Menu accelerators
extern HWND      ghwndMain;       // Main Window
extern HMENU     ghMenu;          // Main Menu
extern HWND      ghwndMsg;        // Message Window
extern HWND      ghwndUser;       // User List
extern HWND      ghwndEdit;       // Edit control
extern HFONT     ghfontEntry;     // Font for message Window

extern const GUID g_guidApp;      // Our NmChat Guid
extern const GUID g_guidNM2Chat;  // NM 2.0 Chat guid

#endif /* GLOBAL_H */


IMSCONF2.IDL

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       imsconf2.idl
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include <imsconf2.idl>


MAIN.CPP

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       MAIN.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

HINSTANCE ghInst       = NULL;          // Current Instance
HACCEL    ghAccelTable = NULL;          // Menu accelerators
HMENU     ghMenu       = NULL;          // Main Menu
HWND      ghwndMain    = NULL;          // Main Window
HWND      ghwndMsg     = hwndNil;       // Message window
HWND      ghwndEdit    = hwndNil;       // Edit Control
HWND      ghwndUser    = hwndNil;       // User List
HFONT     ghfontMsg    = hfontNil;      // Font for messages

TCHAR gszAppTitle[]     = TEXT("NetMeeting Chat Test Application");

// string constants (no change for international)
TCHAR gszAppClassName[] = TEXT("NMCHAT");
TCHAR gszAppName[]      = TEXT("NmChat");

// List column information

typedef struct _tagCol {
UINT  dwWidth;
LPTSTR lpsz;
} COL;
typedef COL * LPCOL;

// See ILV_*
static COL _rgColMsg[] = {
{DXP_USER,  TEXT("Name")},
{80,  TEXT("Time")},
{400, TEXT("Message")},
};

//****************************************************************************
//
// BOOL FCreateMsgWindow(void)
//
// Create the main message listview
//
//****************************************************************************

BOOL FCreateMsgWindow(void)
{
ghwndMsg = CreateWindow(TEXT("SysListView32"), NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | LVS_REPORT,
0, 0, 0, 0,
ghwndMain, (HMENU) IDW_USER, ghInst, NULL);
if (ghwndMsg == NULL)
return FALSE;

// Initialize column data
{
int   iCol;
LPCOL lpCol;
LV_COLUMN lvc;

lpCol = _rgColMsg;
ClearStruct(&lvc);
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
for (iCol = 0; iCol < ARRAY_ELEMENTS(_rgColMsg); iCol++)
{
lvc.iSubItem = iCol;
lvc.cx = lpCol->dwWidth;
lvc.pszText = lpCol->lpsz;
if (ListView_InsertColumn(ghwndMsg, iCol, &lvc) == -1)
return FALSE;
lpCol++;
}
}

return TRUE;
}

//****************************************************************************
//
// BOOL FCreateUserList(void)
//
// Create a listbox for the users
//
//****************************************************************************

BOOL FCreateUserList(void)
{
ghwndUser = CreateWindow(TEXT("SysListView32"), NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS,
0, 0, DXP_USER, 0,
ghwndMain, (HMENU) IDW_MSG, ghInst, NULL);

if (NULL == ghwndUser)
return FALSE;

// Initialize column data
{
LV_COLUMN lvc;

ClearStruct(&lvc);
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
lvc.iSubItem = 0;
lvc.cx = DXP_USER;
lvc.pszText = TEXT("Send To");
if (ListView_InsertColumn(ghwndUser, 0, &lvc) == -1)
return FALSE;
}

return TRUE;
}

WNDPROC lpProcEdit = NULL;
LRESULT FAR PASCAL SubClsEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

//****************************************************************************
//
// LRESULT FAR PASCAL SubClsEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT FAR PASCAL SubClsEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if ((uMsg == WM_CHAR) && (VK_RETURN == wParam))
{
SendText();
return 0L;
}

return CallWindowProc(lpProcEdit, hwnd, uMsg, wParam, lParam);
}

//****************************************************************************
//
// BOOL FCreateEditWindow(void)
//
//****************************************************************************

BOOL FCreateEditWindow(void)
{
ghwndEdit = CreateWindow(TEXT("EDIT"), NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
0, 0, 0, DYP_EDIT,
ghwndMain, (HMENU) IDW_EDIT, ghInst, NULL);
if (ghwndEdit == NULL)
return FALSE;

// set the font for the window

lpProcEdit = (WNDPROC) GetWindowLong(ghwndEdit, GWL_WNDPROC);
SetWindowLong(ghwndEdit, GWL_WNDPROC, (LONG)(WNDPROC) SubClsEdit);

return TRUE;
}

//****************************************************************************
//
// BOOL FInitMain(void)
//
// Initialize the window data and register the window class
//
//****************************************************************************

BOOL FInitMain(void)
{
TCHAR szTitle[MAX_PATH];
WNDCLASS  wc;

ASSERT(NULL != ghInst);

ghAccelTable = LoadAccelerators(ghInst, MAKEINTRESOURCE(ID_APPACCEL));

LoadString(ghInst, IDS_APP_TITLE, szTitle, CCHMAX(szTitle));

wc.style         = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc   = (WNDPROC)WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = ghInst;
wc.hIcon         = LoadIcon(ghInst, MAKEINTRESOURCE(ICO_MAIN));
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName  = MAKEINTRESOURCE(IDM_APP);
wc.lpszClassName = gszAppClassName;

// Register the window class and return FALSE if unsuccesful.
if (!RegisterClass(&wc))
return FALSE;

// Create a main window for this application instance.
ghwndMain = CreateWindow(gszAppClassName, szTitle, WS_OVERLAPPEDWINDOW,
0, 0, 400, 300,
NULL, NULL, ghInst, NULL);
if (ghwndMain == NULL)
return FALSE;

ghMenu = GetMenu(ghwndMain);
return (NULL != ghMenu);
}

//****************************************************************************
//
// BOOL FInitApp(LPSTR lpszCmd, int nCmdShow)
//
// Initialize the application after checking command line, etc.
//
//****************************************************************************

BOOL FInitApp(LPSTR lpszCmd, int nCmdShow)
{
InitCommonControls();

 if (FAILED(CoInitialize(NULL)))
return FALSE;

if (!FInitMain())
return FALSE;

if (!FCreateMsgWindow())
return FALSE;

if (!FCreateUserList())
return FALSE;

if (!FCreateEditWindow())
return FALSE;

ASSERT(NULL != ghwndMain);
ShowWindow(ghwndMain, nCmdShow);
UpdateWindow(ghwndMain);

SetFocus(ghwndEdit);

return S_OK == InitConfMgr();
}

//****************************************************************************
//
// VOID TerminateApp(void)
//
//****************************************************************************

VOID TerminateApp(void)
{
// Release all objects to which this app has a reference
FreeDataChannel();
FreeAllUsers();
FreeConference();
FreeConfMgr();

    // Uninitialize OLE
    CoUninitialize();
}

//****************************************************************************
//
// int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hInstPrev, LPSTR  lpszCmd, int nCmdShow)
//
// Main Windows entrypoint
//
//****************************************************************************

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hInstPrev, LPSTR  lpszCmd, int nCmdShow)
{
ghInst = hInstance;

if (FInitApp(lpszCmd, nCmdShow))
{
MSG  msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, ghAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

TerminateApp();

return 0;
}


MSG.CPP

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       MSG.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

// from cmd.cpp
VOID CmdExit(void);
LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

//****************************************************************************
//
// LRESULT MsgDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Handle a WM_DESTROY message
//
//****************************************************************************

LRESULT MsgDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PostQuitMessage(0);
return 0;
}

//****************************************************************************
//
// LRESULT MsgClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Handle a WM_CLOSE message
//
//****************************************************************************

LRESULT MsgClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CmdExit();
return 0;
}

//****************************************************************************
//
// LRESULT MsgSize(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Handle a WM_SIZE message
//
//****************************************************************************

LRESULT MsgSize(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int dxClient = LOWORD(lParam);  // width of client area
int dyClient = HIWORD(lParam);  // height of client area

SizeMsgWindow(dxClient, dyClient);
return 0;
}

//****************************************************************************
//
// LRESULT MsgActivate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT MsgActivate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return 0L;
}

//****************************************************************************
//
// LRESULT MsgGetMinMax(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Handle a WM_GETMINMAXINFO message
//
//****************************************************************************

LRESULT MsgGetMinMax(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPMINMAXINFO lpminmax = (LPMINMAXINFO) lParam;

lpminmax->ptMinTrackSize.x = 325;
lpminmax->ptMinTrackSize.y = 250;

return 0;
}

//****************************************************************************
//
// Main window message table definition.
//
//****************************************************************************

static MSD _rgmsdMain[] =
{
{WM_COMMAND,       MsgCmdMain},
{WM_DESTROY,       MsgDestroy},
{WM_CLOSE,         MsgClose},
{WM_SIZE,          MsgSize},
{WM_ACTIVATEAPP,   MsgActivate},
{WM_GETMINMAXINFO, MsgGetMinMax},

{0, NULL}  // MUST be the last entry
};

//****************************************************************************
//
// LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Main Window proc
//
//****************************************************************************

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
for (LPMSD lpmsd = _rgmsdMain; 0 != lpmsd->uMsg; lpmsd++)
{
if (lpmsd->uMsg == uMsg)
{
return lpmsd->pfnmsg(hwnd, uMsg, wParam, lParam);
}
}

return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


PRECOMP.H

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       PRECOMP.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#ifndef _PRECOMP_H
#define _PRECOMP_H

// System Include files
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <tchar.h>
#include <ocidl.h>

#include "imsconf2.h"

#include "global.h"
#include "clUtil.h"
#include "cConf.h"
#include "cData.h"
#include "cMemb.h"

#include "resource.h"

#endif /* _PRECOMP_H */


CCALL.H

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CCALL.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CCallNotify
//
//****************************************************************************

class CCallNotify : public RefCount, public CNotify, public INmCallNotify
{
public:
CCallNotify();
~CCallNotify();

// INmCallNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE StateChanged(NM_CALL_STATE uState);
HRESULT STDMETHODCALLTYPE Failed(ULONG uError);
HRESULT STDMETHODCALLTYPE Accepted(INmConference *pConference);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};
DECLARE_STANDARD_TYPES(CCallNotify);

// Global Variables
extern COBLIST * g_pListCall;
extern COBLIST * g_pListCallNotify;

// Global Routines
HRESULT HookCall(INmCall * pCall);
VOID DoCallCreate(void);
VOID FreeCalls(void);
INmCall * GetCurrentCall(void);


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by res.rc
//
#define IDS_APP_TITLE                   1
#define IDS_VERSION                     2
#define IDS_SYSMENU                     4
#define ICO_MAIN                        101
#define IDM_APP                         104
#define ID_APPACCEL                     105
#define IDD_OPTIONS                     123
#define IDM_SAVE_AS                     40001
#define IDM_EXIT                        40002
#define IDM_VIEW_FONT                   40011
#define IDM_VIEW_CLEAR                  40012
#define IDM_OPTIONS                     40013
#define IDM_EJECT                       40021
#define IDM_HELP                        40090
#define IDM_HELP_CONTENTS               40091
#define IDM_ABOUT                       40092
#define IDM_VIEW_NAME                   40142
#define IDM_VIEW_TIME                   40143
#define IDM_VIEW_USERS                  40144

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        124
#define _APS_NEXT_COMMAND_VALUE         40145
#define _APS_NEXT_CONTROL_VALUE         1028
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


UTIL.CPP

//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       UTIL.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// VOID SendText(void)
//
// Send the line of text in the edit control.
//
//****************************************************************************

VOID SendText(void)
{
UINT  cb;
TCHAR sz[MAX_PATH];

#ifdef NM2_CHAT_PROTOCOL
BYTE   buffer[CB_NM2_HDR + (MAX_PATH*2)];

// Get the text line
cb = SendMessage(ghwndEdit, WM_GETTEXT, (WPARAM) MAX_PATH, (LPARAM) sz);
SendMessage(ghwndEdit, EM_SETSEL, (WPARAM) 0, (LPARAM)-1);
SendMessage(ghwndEdit, WM_SETTEXT, (WPARAM) 0, (LPARAM) "");

if ((0 != cb) && (NULL != g_pChannelData))
{
INmMember * pMember = GetSelectedMember();
if ((pMember != NULL) && (S_OK == pMember->IsSelf()))
{
// Local Member => Everyone => Broadcast
pMember = NULL;
}

cb++; // include final null
#ifdef UNICODE
lstrcpy(&buffer[CB_NM2_HDR], sz);
#else
cb = MultiByteToWideChar(CP_ACP, 0, sz, cb, (LPWSTR) &buffer[CB_NM2_HDR], MAX_PATH);
cb *= 2; // adjust for UNICODE
#endif /* !UNICODE */

// Add special prefix
cb += CB_NM2_HDR;
ZeroMemory(buffer, CB_NM2_HDR);
* ((LPDWORD) buffer) = CB_NM2_HDR;

g_pChannelData->SendData(pMember, cb, buffer, 0);
}

#else
// Get the text line
cb = SendMessage(ghwndEdit, WM_GETTEXT, (WPARAM) CCHMAX(sz), (LPARAM) sz);
SendMessage(ghwndEdit, EM_SETSEL, (WPARAM) 0, (LPARAM)-1);
SendMessage(ghwndEdit, WM_SETTEXT, (WPARAM) 0, (LPARAM) "");

if ((0 != cb) && (NULL != g_pChannelData))
{
INmMember * pMember = GetSelectedMember();
if ((pMember != NULL) && (S_OK == pMember->IsSelf()))
{
// Local Member => Everyone => Broadcast
pMember = NULL;
}

cb++; // include final null
g_pChannelData->SendData(pMember, cb, (LPBYTE) sz, 0);
}
#endif /* NM2_CHAT_PROTOCOL */
}

//****************************************************************************
//
// VOID ClearText(void)
//
//****************************************************************************

VOID ClearText(void)
{
ListView_DeleteAllItems(ghwndMsg);
}

//****************************************************************************
//
// VOID SizeMsgWindow(int dx, int dy)
//
//****************************************************************************

VOID SizeMsgWindow(int dx, int dy)
{
MoveWindow(ghwndEdit, XP_LEFT, dy - DYP_EDIT, dx, DYP_EDIT, TRUE);
dy -= (YP_TOP + DYP_EDIT);

MoveWindow(ghwndUser, XP_LEFT, YP_TOP, DXP_USER, dy, TRUE);
dx -= DXP_USER;

MoveWindow(ghwndMsg, XP_LEFT + DXP_USER, YP_TOP, dx, dy, TRUE);
}

//****************************************************************************
//
// VOID RecalcMsgWindow(void)
//
//****************************************************************************

VOID RecalcMsgWindow(void)
{
RECT rc;

GetClientRect(ghwndMain, &rc);
SizeMsgWindow(rc.right, rc.bottom);
}

//****************************************************************************
//
// LPVOID LpvAlloc(int cb)
//
// Return a pointer to an allocated array of bytes
//
//****************************************************************************

LPVOID LpvAlloc(int cb)
{
return LocalAlloc(LMEM_FIXED, cb);
}

//****************************************************************************
//
// LPTSTR PszAlloc(int cch)
//
//****************************************************************************

LPTSTR PszAlloc(int cch)
{
return (LPTSTR) LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cch+1));
}

//****************************************************************************
//
// VOID FreePlpv(LPVOID plpv)
//
// Free the data pointed to by plpv and set *plpv to NULL
//
//****************************************************************************

VOID FreePlpv(LPVOID plpv)
{
if ((plpv == NULL) || (*(VOID FAR * FAR *)plpv == NULL)) {
return;
}

LocalFree(*(VOID FAR * FAR *)plpv);
*(VOID FAR * FAR *)plpv = NULL;
}

//****************************************************************************
//
// VOID MaybeDeleteObject(HGDIOBJ * phgdi)
//
// Check usage count, delete if we can
//
//****************************************************************************

VOID MaybeDeleteObject(HGDIOBJ * phgdi)
{
if (*phgdi == hgdiNil)
return;

DeleteObject(*phgdi);
*phgdi = hgdiNil;
}


README.TXT

------------------------------------------------------------------
         Microsoft NetMeeting SDK 2.1 NMFT Readme File
                           November 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE NMFT SAMPLE
BUILDING THE NMFT SAMPLE

OVERVIEW OF THE NMFT SAMPLE
===========================

NMFT is a Windows utility application that allows the user to
send files via an existing NetMeeting connection.  NMFT can be added
to the system pop-up menu by placing the executable in the Windows
SendTo directory.

BUILDING THE NMFT SAMPLE
========================

Building the sample application requires VC++ Version 5.0 or later.

To build the sample under VC++, open the project makefile (nmft.mak)
as a Microsoft VC++ project, and build as usual.  The NetMeeting SDK
directories must be added to your VC++ environment.

If your DOS environment is correctly configured, you can build the sample
from the command line by typing "nmake".  The DOS path, include and lib
environment variables must point to the VC installation and the
NetMeeting SDK directories.

Notes:

1) The NMFT sample is designed to be placed in the Windows SendTo
   directory.  This will make it available on the user's right-click
   menu in the Explorer.

2) Launching NMFT will cause NetMeeting to launched if it is not already
   running.  However, no files will be transferred unless you are in
   a conference when NMFT is executed.

3) The transferred file will be sent to all conference members.


CCONF.H

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       CCONF.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CFtNotify
//
//****************************************************************************

class CFtNotify : public RefCount, public CNotify, public INmChannelFtNotify
{
public:
CFtNotify();
~CFtNotify();

// INmChannelNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember * pMember);

// INmChannelFtNotify
HRESULT STDMETHODCALLTYPE FtUpdate(CONFN uNotify, INmFt *pFt);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

extern INmManager    * g_pMgr;
extern INmConference * g_pConference;
extern INmChannelFt  * g_pChannelFt;
extern CFtNotify     * g_pNotifyFt;
extern INmMember     * g_pMember;
extern INmFt         * g_pFt;

//****************************************************************************
//
// Global Functions
//
//****************************************************************************

BOOL FInitNm(void);
VOID FreeNmObjects(void);
HRESULT SendFile(void);


CLUTIL.CPP

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       CLUTIL.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

RefCount::RefCount(void)
{
m_cRef = 1;
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

RefCount::~RefCount(void)
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE RefCount::AddRef(void)
{
   ASSERT(m_cRef >= 0);

   InterlockedIncrement(&m_cRef);

   return (ULONG) m_cRef;
}

ULONG STDMETHODCALLTYPE RefCount::Release(void)
{
if (0 == InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}

ASSERT(m_cRef > 0);
return (ULONG) m_cRef;
}

//****************************************************************************
//
// CLASS CNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CNotify::CNotify() :
m_pcnpcnt(NULL),
    m_pcnp(NULL),
    m_dwCookie(0),
    m_pUnk(NULL)
{
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

CNotify::~CNotify()
{
Disconnect(); // Make sure we're disconnected
}

//****************************************************************************
//
// HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
//
//****************************************************************************

HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
{
HRESULT hr;

ASSERT(0 == m_dwCookie);

// Get the connection container
hr = pUnk->QueryInterface(IID_IConnectionPointContainer, (void **)&m_pcnpcnt);
if (SUCCEEDED(hr))
{
// Find an appropriate connection point
hr = m_pcnpcnt->FindConnectionPoint(riid, &m_pcnp);
if (SUCCEEDED(hr))
{
ASSERT(NULL != m_pcnp);
// Connect the sink object
hr = m_pcnp->Advise((IUnknown *)pUnkN, &m_dwCookie);
}
}

if (FAILED(hr))
{
m_dwCookie = 0;
}
else
{
    m_pUnk = pUnk; // keep around for caller
    }

return hr;
}

//****************************************************************************
//
// HRESULT CNotify::Disconnect (void)
//
//****************************************************************************

HRESULT CNotify::Disconnect (void)
{
    if (0 != m_dwCookie)
    {
        // Disconnect the sink object
        m_pcnp->Unadvise(m_dwCookie);
        m_dwCookie = 0;

        m_pcnp->Release();
        m_pcnp = NULL;

        m_pcnpcnt->Release();
        m_pcnpcnt = NULL;

        m_pUnk = NULL;
    }

    return S_OK;
}

//****************************************************************************
//
// CLASS COBLIST
//
//****************************************************************************

//****************************************************************************
//
// Destructor
//
//****************************************************************************

COBLIST::~COBLIST()
{
    ASSERT(IsEmpty());
}

#ifdef DEBUG
VOID* COBLIST::GetHead()
{
ASSERT(m_pHead);

return m_pHead->pItem;
}

VOID* COBLIST::GetTail()
{
ASSERT(m_pTail);

return m_pTail->pItem;
}
#endif /* DEBUG */

//****************************************************************************
//
// VOID* COBLIST::GetNext(POSITION& rPos)
//
//****************************************************************************

VOID* COBLIST::GetNext(POSITION& rPos)
{
ASSERT(rPos);

VOID* pReturn = rPos->pItem;
rPos = rPos->pNext;

return pReturn;
}

//****************************************************************************
//
// VOID* COBLIST::RemoveAt(POSITION Pos)
//
//****************************************************************************

VOID* COBLIST::RemoveAt(POSITION Pos)
{
VOID* pReturn = NULL;

if (m_pHead)
{
if (m_pHead == Pos)
{
// Removing the first element in the list

m_pHead = Pos->pNext;
pReturn = Pos->pItem;
delete Pos;
m_cItem--;
ASSERT(0 <= m_cItem);

if (NULL == m_pHead)
{
// Removing the only element!
m_pTail = NULL;
}
}
else
{
POSITION pCur = m_pHead;

while (pCur && pCur->pNext)
{
if (pCur->pNext == Pos)
{
// Removing

pCur->pNext = Pos->pNext;
if (m_pTail == Pos)
{
m_pTail = pCur;
}
pReturn = Pos->pItem;
delete Pos;

m_cItem--;
ASSERT(0 <= m_cItem);
}

pCur = pCur->pNext;
}
}
}

return pReturn;
}

//****************************************************************************
//
// POSITION COBLIST::AddTail(VOID* pItem)
//
//****************************************************************************

POSITION COBLIST::AddTail(VOID* pItem)
{
POSITION posRet = NULL;

if (m_pTail)
{
if (m_pTail->pNext = new COBNODE)
{
m_pTail = m_pTail->pNext;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
m_cItem++;
}
}
else
{
ASSERT(!m_pHead);
if (m_pHead = new COBNODE)
{
m_pTail = m_pHead;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
m_cItem++;
}
}

return m_pTail;
}

//****************************************************************************
//
// void COBLIST::EmptyList()
//
//****************************************************************************

void COBLIST::EmptyList()
{
    while (!IsEmpty()) {
        RemoveAt(GetHeadPosition());
    }
}

#ifdef DEBUG
VOID* COBLIST::RemoveTail()
{
ASSERT(m_pHead);
ASSERT(m_pTail);

return RemoveAt(m_pTail);
}

VOID* COBLIST::RemoveHead()
{
ASSERT(m_pHead);
ASSERT(m_pTail);

return RemoveAt(m_pHead);
}

void * COBLIST::GetFromPosition(POSITION Pos)
{
    void * Result = SafeGetFromPosition(Pos);
ASSERT(Result);
return Result;
}
#endif /* DEBUG */

//****************************************************************************
//
// POSITION COBLIST::GetPosition(void* _pItem)
//
//****************************************************************************

POSITION COBLIST::GetPosition(void* _pItem)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Position->pItem == _pItem) {
            break;
        }
GetNext(Position);
    }
    return Position;
}

//****************************************************************************
//
// POSITION COBLIST::Lookup(void* pComparator)
//
//****************************************************************************

POSITION COBLIST::Lookup(void* pComparator)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Compare(Position->pItem, pComparator)) {
            break;
        }
GetNext(Position);
    }
    return Position;
}

//****************************************************************************
//
// void * COBLIST::SafeGetFromPosition(POSITION Pos)
//
//****************************************************************************

void * COBLIST::SafeGetFromPosition(POSITION Pos)
{
// Safe way to validate that an entry is still in the list,
// which ensures bugs that would reference deleted memory,
// reference a NULL pointer instead
// (e.g. an event handler fires late/twice).
// Note that versioning on entries would provide an additional
// safeguard against re-use of a position.
// Walklist to find entry.

POSITION PosWork = m_pHead;

while (PosWork) {
if (PosWork == Pos) {
return Pos->pItem;
}
GetNext(PosWork);
}
return NULL;
}

//****************************************************************************
//
// COBLIST Utility routines
//
//****************************************************************************

//****************************************************************************
//
// POSITION AddNode(PVOID pv, COBLIST ** ppList)
//
// Add a node to a list.
// Initializes the ObList, if necessary.
// Returns the position in the list or NULL if there was a problem.
//
//****************************************************************************

POSITION AddNode(PVOID pv, COBLIST ** ppList)
{
ASSERT(NULL != ppList);
if (NULL == *ppList)
{
*ppList = new COBLIST();
if (NULL == *ppList)
return NULL;
}

return (*ppList)->AddTail(pv);
}

//****************************************************************************
//
// PVOID RemoveNode(POSITION * pPos, COBLIST *pList)
//
// Remove a node from a list.
// Sets pPos to NULL
//
//****************************************************************************

PVOID RemoveNode(POSITION * pPos, COBLIST *pList)
{
if ((NULL == pList) || (NULL == pPos))
return NULL;

PVOID pv = pList->RemoveAt(*pPos);
*pPos = NULL;
return pv;
}

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
// We don't support construction from an ANSI string in the Unicode build.
//
//****************************************************************************

#if !defined(UNICODE)

BSTRING::BSTRING(LPCSTR lpcString)
{
m_bstr = NULL;

// Compute the length of the required BSTR, including the null
int cWC =  MultiByteToWideChar(CP_ACP, 0, lpcString, -1, NULL, 0);
if (cWC <= 0)
return;

// Allocate the BSTR, including the null
m_bstr = SysAllocStringLen(NULL, cWC - 1); // SysAllocStringLen adds another 1

ASSERT(NULL != m_bstr);
if (NULL == m_bstr)
{
return;
}

// Copy the string
MultiByteToWideChar(CP_ACP, 0, lpcString, -1, (LPWSTR) m_bstr, cWC);

// Verify that the string is null terminated
ASSERT(0 == m_bstr[cWC - 1]);
}

#endif // !defined(UNICODE)

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

BTSTR::BTSTR(BSTR bstr)
{
m_psz = PszFromBstr(bstr);
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

BTSTR::~BTSTR()
{
if (NULL != m_psz)
LocalFree(m_psz);
}

//****************************************************************************
//
// LPTSTR PszFromBstr(BSTR bstr)
//
// Converts a BSTR to a LPSTR
//
//****************************************************************************

LPTSTR PszFromBstr(BSTR bstr)
{
if (NULL == bstr)
return NULL;
int cch =  WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, NULL, 0, NULL, NULL);
if (cch <= 0)
return NULL;

LPTSTR psz = PszAlloc(cch);
if (NULL == psz)
return NULL;

WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, psz, cch+1, NULL, NULL);
return psz;
}


CLUTIL.H

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       CLUTIL.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

// Utility Classes

#ifndef _CL_UTIL_H_
#define _CL_UTIL_H_

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

class RefCount
{
private:
   LONG m_cRef;

public:
   RefCount();
   // Virtual destructor defers to destructor of derived class.
   virtual ~RefCount();

   // IUnknown methods

   ULONG STDMETHODCALLTYPE AddRef(void);
   ULONG STDMETHODCALLTYPE Release(void);
};

//****************************************************************************
//
// CLASS CNotify
//
// Notification sink
//
//****************************************************************************

class CNotify
{
private:
    DWORD  m_dwCookie;
IUnknown * m_pUnk;
    IConnectionPoint           * m_pcnp;
    IConnectionPointContainer  * m_pcnpcnt;
public:
    CNotify(void);
    ~CNotify();

    HRESULT Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN);
    HRESULT Disconnect(void);

    IUnknown * GetPunk() {return m_pUnk;}
};

//****************************************************************************
//
// CLASS COBLIST
//
//****************************************************************************

#define POSITION COBNODE*

struct COBNODE
{
POSITIONpNext;
void*pItem;
};

class COBLIST
{
protected:
POSITION m_pHead;
POSITION m_pTail;
int      m_cItem;
    virtual BOOL Compare(void* pItemToCompare, void* pComparator)
                       { return(pItemToCompare == pComparator); }
public:
COBLIST() : m_pHead(NULL), m_pTail(NULL), m_cItem(0) { }
    virtual         ~COBLIST();

virtual void *  RemoveAt(POSITION rPos);
    void            EmptyList();
POSITION    AddTail(void* pItem);
void *    GetNext(POSITION& rPos);
    void *          SafeGetFromPosition(POSITION rPos);
    POSITION        GetPosition(void* pItem);
    POSITION        Lookup(void* pComparator);
POSITION    GetHeadPosition()  { return (m_pHead); }
POSITION    GetTailPosition()  { return (m_pTail); }
BOOL    IsEmpty()          { return (!m_pHead); }
int             GetItemCount()     { return (m_cItem); }
#ifdef DEBUG
void *    GetHead();
void *    GetTail();
void *    RemoveHead();
void *        RemoveTail();
void *    GetFromPosition(POSITION rPos);
#else
void *    GetHead()          { return GetFromPosition(GetHeadPosition());}
void *          GetTail()          { return m_pTail->pItem;}
void *    RemoveHead()       { return RemoveAt(m_pHead); }
void *        RemoveTail()       { return RemoveAt(m_pTail); }
void *    GetFromPosition(POSITION rPos){return(rPos->pItem);}
#endif
};

//****************************************************************************
//
// Utility Functions
//
//****************************************************************************

POSITION AddNode(PVOID pv, COBLIST ** ppList);
PVOID RemoveNode(POSITION * pPos, COBLIST *pList);

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

class BSTRING
{
private:
BSTR   m_bstr;

public:
// Constructors
BSTRING() {m_bstr = NULL;}

inline BSTRING(LPCWSTR lpcwString);

#if !defined(UNICODE)
// We don't support construction from an ANSI string in the Unicode build.
BSTRING(LPCSTR lpcString);
#endif // !defined(UNICODE)

// Destructor
inline ~BSTRING();

// Cast to BSTR
operator BSTR() {return m_bstr;}
inline LPBSTR GetLPBSTR(void);
};

BSTRING::BSTRING(LPCWSTR lpcwString)
{
if (NULL != lpcwString)
{
m_bstr = SysAllocString(lpcwString);
ASSERT(NULL != m_bstr);
}
else
{
m_bstr = NULL;
}
}

BSTRING::~BSTRING()
{
if (NULL != m_bstr)
{
SysFreeString(m_bstr);
}
}

inline LPBSTR BSTRING::GetLPBSTR(void)
{
ASSERT(NULL == m_bstr);

return &m_bstr;
}

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

class BTSTR
{
private:
LPTSTR m_psz;

public:
BTSTR(BSTR bstr);
~BTSTR();

// Cast to BSTR
operator LPTSTR() {return (NULL == m_psz) ? TEXT("<null>") : m_psz;}
};

LPTSTR PszFromBstr(BSTR bst);

#endif  // _CL_UTIL_H_


DEBUG.CPP

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       DEBUG.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

#ifdef DEBUG /* The whole file! */

//****************************************************************************
//
// VOID FAR PASCAL AssertProc(LPSTR lpszMsg, LPSTR lpszAssert, LPSTR lpszFile, UINT line)
//
//****************************************************************************

VOID FAR PASCAL AssertProc(LPSTR lpszMsg, LPSTR lpszAssert, LPSTR lpszFile, UINT line)
{
int  id;
TCHAR szBuffer[1024]; // a big buffer

wsprintf((LPSTR)szBuffer,
"Assert: %s\nFile %s, Line %d",
(lpszMsg && *lpszMsg) ? lpszMsg : lpszAssert,
lpszFile ? lpszFile : "", line);

OutputDebugString(szBuffer);

do {
id = MessageBox(NULL, (LPSTR)szBuffer, TEXT("NmUi Assert"),
MB_ICONHAND | MB_ABORTRETRYIGNORE | MB_SYSTEMMODAL);

switch (id)
{
case IDRETRY:
DebugBreak();
break;
case IDABORT:
FatalAppExit(0, (LPSTR)szBuffer);
break;
case IDIGNORE:
default:
break;
}
} while (id == IDRETRY);
}

#endif /* DEBUG - the whole file! */


GLOBAL.H

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       GLOBAL.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#ifndef GLOBAL_H
#define GLOBAL_H

//-------------------------------------------------------
// Useful macros

#define ClearBytes(lpv, cb)ZeroMemory((LPVOID) (lpv), (cb))
#define ClearStruct(lpv)ZeroMemory((LPVOID) (lpv), sizeof(*(lpv)))
#define InitStruct(lpv)        {ClearStruct(lpv); (* (LPDWORD) lpv) = sizeof(*(lpv));}
#define CopyStruct(pDest, pSrc)  CopyMemory(pDest, pSrc, sizeof(*(pDest)))

#define SetEmptySz(sz)( *(sz) = _T('\0') )
#define FEmptySz(sz)            (((sz) == NULL) || (*(sz) == _T('\0')))

#define ARRAY_ELEMENTS(rg)      (sizeof(rg) / sizeof(rg[0]))
#define CCHMAX(sz)              (sizeof(sz) / sizeof(TCHAR))

#ifdef DEBUG
VOID FAR PASCAL AssertProc(LPTSTR lpszMsg, LPTSTR lpszAssert, LPTSTR lpszFile, UINT line);

#define DEBASSERT(exp,szMsg)  \
if (!(exp))                                                      \
{                                                                \
static char _szAssert[] = #exp ;   \
static char _szMsg[]    = szMsg;   \
AssertProc(_szMsg,_szAssert,__FILE__,__LINE__); \
}

#define ASSERT(f)       DEBASSERT(f, "(" #f ")")
#define AssertSz(f,sz)  DEBASSERT(f, sz)
#define NotReached()    DEBASSERT(FALSE, "NotReached declaration was reached!")
#else
#define ASSERT(f)
#define AssertSz(f, sz)
#define NotReached()
#endif

//****************************************************************************
//
// Global Function Prototypes.
//
//****************************************************************************

// from msg.cpp
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID CmdClose(void);

// from util.cpp
BOOL FGetFileName(LPTSTR szFileName);
BOOL FFileExists(LPCTSTR szFile);
LPVOID LpvAlloc(int cb);
LPTSTR PszAlloc(int cch);
VOID FreePlpv(LPVOID plpv);
LPCTSTR StrChr(LPCTSTR pcsz, TCHAR ch);

//****************************************************************************
//
// Global variables.
//
//****************************************************************************

extern HINSTANCE ghInst;          // The current instance handle
extern HWND   ghwndMain;          // Main Window
extern const GUID g_guidApp;      // Our NmFt Guid
extern TCHAR  g_szFile[MAX_PATH]; //
extern BOOL   g_fQuiet;           // if TRUE, no progress dialog

#endif /* GLOBAL_H */


IMSCONF2.IDL

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       imsconf2.idl
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include <imsconf2.idl>


MAIN.CPP

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       MAIN.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// Global variables.
//
//****************************************************************************

HINSTANCE ghInst        = NULL;   // Current Instance
HWND      ghwndMain     = NULL;   // Main Window
BOOL      g_fQuiet      = FALSE;  //
TCHAR     g_szFile[MAX_PATH];     //

CONST TCHAR g_szAppClassName[] = TEXT("NMFT Dialog");

//****************************************************************************
//
// BOOL ParseCmdLine(void)
//
//****************************************************************************

BOOL ParseCmdLine(void)
{
LPTSTR pch = ::GetCommandLine();
if (NULL == pch)
return FALSE;

// Work on a copy of the buffer
TCHAR sz[MAX_PATH*2];
lstrcpyn(sz, pch, CCHMAX(sz));
pch = sz;

BOOL fInStr = FALSE;
BOOL fInName = FALSE;
BOOL fInApp = TRUE;
TCHAR ch;
LPTSTR lpsz = NULL;

// walk through the command line, parsing filenames and options
while (_T('\0') != (ch = *pch))
{
switch (ch)
{
case _T('\"'):      // Double quoted filename
fInStr = !fInStr;
if (fInStr)
{
 lpsz = CharNext(pch);
 break;
 }

case _T(' '):       // Space usually terminates a filename
if (fInStr)  // except in a long filename
break;

case _T('\0'):
if (fInApp)
{
// Skip the application name (first string on command line)
fInApp = FALSE;
fInName = FALSE;
 lpsz = CharNext(pch);
break;
}

if (!fInName)
{
 lpsz = CharNext(pch);
break; // ignore extra whitespace
}

{
LPTSTR pchT = CharNext(pch);
*pch = _T('\0'); // null terminate string
fInName = FALSE;
pch = pchT;
continue; // continue loop
}
break;

case _T('/'):      // Command line option
{
if (fInName || fInStr)
break;

pch = CharNext(pch);
ch = *pch;
if (_T('\0') == ch)
return FALSE;

if ((_T('Q') == ch) || (_T('q') == ch))
{
g_fQuiet = TRUE;
}
// TODO: other options

 break;
}

default:        // Normal character
fInName = TRUE;
break;
} /* switch (ch) */

pch = CharNext(pch);
}

if ((NULL == lpsz) || (_T('\0') == *lpsz))
return FALSE;

lstrcpyn(g_szFile, lpsz, MAX_PATH);
return TRUE;
}

//****************************************************************************
//
// BOOL FVerifyFile(void)
//
//****************************************************************************

BOOL FVerifyFile(void)
{
// Check if filename was specified on command line
if (ParseCmdLine())
return TRUE;

// Prompt the user to select a file
if (FGetFileName(g_szFile))
return TRUE;

return FALSE;
}

//****************************************************************************
//
// BOOL FInitMain(void)
//
//****************************************************************************

BOOL FInitMain(void)
{
TCHAR szTitle[MAX_PATH];
WNDCLASS  wc;

ASSERT(NULL != ghInst);

LoadString(ghInst, IDS_APP_TITLE, szTitle, CCHMAX(szTitle));

ClearStruct(&wc);

wc.style         = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc   = (WNDPROC)WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = DLGWINDOWEXTRA;
wc.hInstance     = ghInst;
wc.hIcon         = LoadIcon(ghInst, MAKEINTRESOURCE(ICO_MAIN));
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName  = MAKEINTRESOURCE(IDM_APP);
wc.lpszClassName = g_szAppClassName;

// Register the window class and return FALSE if unsuccesful.
if (!RegisterClass(&wc))
return FALSE;

ghwndMain = CreateDialog(ghInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL);

if (NULL == ghwndMain)
return FALSE;

if (!FInitNm())
{
// TODO: Display error message
// Unable to initialize, no conference, etc.
return FALSE;
}

if (!FVerifyFile())
return FALSE;

LPTSTR pszFile;
GetFullPathName(g_szFile, MAX_PATH, g_szFile, &pszFile);

if (!FFileExists(g_szFile))
{
TCHAR szMsg[MAX_PATH*2];
wsprintf(szMsg, TEXT("Unable to send the file\r\n%s"), g_szFile);
MessageBox(NULL, szMsg, TEXT("NmFt Error"), MB_OK | MB_ICONSTOP);
return FALSE;
}

if (FAILED(SendFile()))
return FALSE;

return TRUE;
}

//****************************************************************************
//
// int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
//
// Main Windows entrypoint
//
//****************************************************************************

#ifdef _DEBUG
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
#else  // _DEBUG
int __cdecl main()
#endif // _DEBUG
{
#ifdef _DEBUG
ghInst = hInstance;
#else
ghInst = ::GetModuleHandle(NULL);
#endif // ! _DEBUG

InitCommonControls();

 if (FAILED(CoInitialize(NULL)))
return 0;

if (FInitMain() && !g_fQuiet)
{
MSG  msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

FreeNmObjects();
    CoUninitialize();

#ifndef DEBUG
ExitProcess(0);
#endif

return 0;
}


MSG.CPP

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       MSG.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

typedef LRESULT (*PFNMSG)(HWND, UINT, WPARAM, LPARAM);

// This structure maps messages to message handling functions.
typedef struct _MSD
{
UINT   uMsg;
PFNMSG pfnmsg;
} MSD;                 // MeSsage Dispatch structure
typedef MSD * LPMSD;

VOID CmdClose(void)
{
DestroyWindow(ghwndMain);
}

//****************************************************************************
//
// LRESULT MsgDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Handle a WM_DESTROY message
//
//****************************************************************************

LRESULT MsgDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PostQuitMessage(0);
return 0;
}

//****************************************************************************
//
// LRESULT MsgClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Handle a WM_CLOSE message
//
//****************************************************************************

LRESULT MsgClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CmdClose();
return 0;
}

//****************************************************************************
//
// LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WORD wCmd = GET_WM_COMMAND_ID(wParam, lParam);
switch (wCmd)
{
case IDCANCEL:
{
if (NULL != g_pFt)
{
g_pFt->Cancel();
CmdClose();
}
break;
}

default:
break;
} /* switch wCmd */

return 0L;
}

////////////////////////////////////////////////////////////////////////////
// Main window message table definition.
static MSD _rgmsdMain[] =
{
{WM_COMMAND,       MsgCmdMain},
{WM_DESTROY,       MsgDestroy},
{WM_CLOSE,         MsgClose},

{0, NULL}  // MUST be the last entry
};

//****************************************************************************
//
// LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
for (LPMSD lpmsd = _rgmsdMain; 0 != lpmsd->uMsg; lpmsd++)
{
if (lpmsd->uMsg == uMsg)
{
return lpmsd->pfnmsg(hwnd, uMsg, wParam, lParam);
}
}

return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


PRECOMP.H

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       PRECOMP.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#ifndef _PRECOMP_H
#define _PRECOMP_H

// System Include files
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <tchar.h>
#include <ocidl.h>

#include "imsconf2.h"

#include "global.h"
#include "clUtil.h"
#include "cConf.h"

#include "resource.h"

#endif /* _PRECOMP_H */


CCONF.CPP

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       CCONF.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

// Global Varaiables
INmManager    * g_pMgr  = NULL;         // The Conference Manager
INmConference * g_pConference = NULL;   // The Current Conference
INmChannelFt  * g_pChannelFt = NULL;    // The File Transfer Channel
CFtNotify     * g_pNotifyFt = NULL;     // The File Transfer Notify Hook
INmMember     * g_pMember = NULL;       // The destination
INmFt         * g_pFt = NULL;           // The File Transfer Object

// Local Application Guid : {3582DED0-9CCF-11d0-8B26-00A0C91BC90E}
const GUID g_guidApp =
{0x3582ded0, 0x9ccf, 0x11d0, { 0x8b, 0x26, 0x0, 0xa0, 0xc9, 0x1b, 0xc9, 0xe} };

//****************************************************************************
//
// HRESULT InitConfMgr(void)
//
//****************************************************************************

HRESULT InitConfMgr(void)
{
HRESULT hr;
LPCLASSFACTORY pcf;

// Notify the system we want to use the conferencing services
// by creating a conference manager object
hr = CoGetClassObject(CLSID_NmManager, CLSCTX_INPROC,
              NULL, IID_IClassFactory, (void**)&pcf);
if (SUCCEEDED(hr))
{
// Get the conference manager object
hr = pcf->CreateInstance(NULL, IID_INmManager, (void**)&g_pMgr);
if (SUCCEEDED(hr))
{
ULONG uchCaps = NMCH_ALL;
ULONG uOptions = 0;
hr = g_pMgr->Initialize(&uOptions, &uchCaps);
}

pcf->Release();
}

return hr;
}

//****************************************************************************
//
// HRESULT GetActiveConference(void)
//
//****************************************************************************

HRESULT GetActiveConference(void)
{
HRESULT hr;
IEnumNmConference * pEnum;

if (NULL == g_pMgr)
{
return E_FAIL;
}

hr = g_pMgr->EnumConference(&pEnum);
if (SUCCEEDED(hr))
{
hr = pEnum->Next(1, &g_pConference, NULL);
if (S_OK == hr)
{
NM_CONFERENCE_STATE confState;
hr = g_pConference->GetState(&confState);
if (SUCCEEDED(hr))
{
hr = (NM_CONFERENCE_ACTIVE == confState) ? S_OK : S_FALSE;
}
}

pEnum->Release();
}

return hr;
}

//****************************************************************************
//
// VOID FreeFtChannel(void)
//
//****************************************************************************

VOID FreeFtChannel(void)
{
if (NULL != g_pFt)
{
g_pFt->Release();
g_pFt = NULL;
}

// Release all notify
if (NULL != g_pNotifyFt)
{
g_pNotifyFt->Disconnect();
g_pNotifyFt->Release();
g_pNotifyFt = NULL;
}

if (NULL != g_pChannelFt)
{
g_pChannelFt->Release();
g_pChannelFt = NULL;
}
}

//****************************************************************************
//
// HRESULT GetFtChannel(void)
//
//****************************************************************************

HRESULT GetFtChannel(void)
{
HRESULT hr;
BOOL  fFound;
ULONG nmch;
INmChannel * pChannel;
IEnumNmChannel * pEnum;

if (NULL == g_pConference)
{
return E_FAIL;
}

// TODO: Check if active and in data conference

fFound = FALSE;

hr = g_pConference->EnumChannel(&pEnum);
if (SUCCEEDED(hr))
{
while ((S_OK == hr) && !fFound)
{
hr = pEnum->Next(1, &pChannel, NULL);
if (S_OK != hr)
break;
hr = pChannel->GetNmch(&nmch);
if ((S_OK == hr) && (NMCH_FT == nmch))
{
hr = pChannel->QueryInterface(IID_INmChannelFt, (PVOID *) &g_pChannelFt);
fFound = (S_OK == hr);
}
pChannel->Release();
}

pEnum->Release();
}

return fFound ? S_OK : hr;
}

//****************************************************************************
//
// HRESULT HookFtChannel(void)
//
//****************************************************************************

HRESULT HookFtChannel(void)
{
HRESULT hr;
CFtNotify * pNotify;

ASSERT(NULL != g_pChannelFt);

    // Connect to the channel object
    pNotify = new CFtNotify();
    if (pNotify == NULL)
    {
        hr = E_OUTOFMEMORY;
    }
    else
    {
hr = pNotify->Connect(g_pChannelFt);
if (SUCCEEDED(hr))
{
g_pNotifyFt = pNotify;
}
}

if (FAILED(hr))
{
FreeFtChannel();
}

return hr;
}

//****************************************************************************
//
// HRESULT SendFile(void)
//
//****************************************************************************

HRESULT SendFile(void)
{
HRESULT hr;
if (NULL == g_pChannelFt)
{
return E_FAIL;
}

BSTRING bstrFile(g_szFile);
hr = g_pChannelFt->SendFile(&g_pFt, g_pMember, bstrFile, 0);
return hr;
}

HWND ghwndProgress = NULL;

VOID UpdateProgress(CONFN uNotify)
{
HRESULT hr;

if ((NULL == g_pFt) || (NULL == ghwndMain))
{
return;
}

NM_FT_STATE uState;
hr = g_pFt->GetState(&uState);

switch (uNotify)
{
case CONFN_FT_STARTED:
{
TCHAR szFmt[MAX_PATH];
TCHAR szMsg[MAX_PATH];

// Fill out "Sending <filename>"
GetDlgItemText(ghwndMain, IDE_SEND, szFmt, MAX_PATH);
wsprintf(szMsg, szFmt, g_szFile);
SetDlgItemText(ghwndMain, IDE_SEND, szMsg);

ULONG uBytes;
if (FAILED(g_pFt->GetSize(&uBytes)))
uBytes = 0;

// Fill out "to <user> (x bytes)"
GetDlgItemText(ghwndMain, IDE_DEST, szFmt, MAX_PATH);

SetDlgItemText(ghwndMain, IDE_DEST, szMsg);

ghwndProgress = GetDlgItem(ghwndMain, IDC_PROGRESS);
ASSERT(NULL != ghwndProgress);

SendMessage(ghwndProgress, PBM_SETPOS, 0, 0);
ShowWindow(ghwndMain, SW_SHOW);
SetFocus(GetDlgItem(ghwndMain, IDCANCEL));
break;
}

case CONFN_FT_PROGRESS:
{
ULONG uPercent;
ULONG uBytesCurr;
ULONG uBytesTotal;

if (FAILED(g_pFt->GetBytesTransferred(&uBytesCurr)) ||
FAILED(g_pFt->GetSize(&uBytesTotal)))
{
return;
}

if (0 == uBytesTotal)
uPercent = 100;
else
uPercent = uBytesCurr * 100 / uBytesTotal;

SendMessage(ghwndProgress, PBM_SETPOS, uPercent, 0);
break;
}

case CONFN_FT_COMPLETE:
case CONFN_FT_CANCELED:
{
SetDlgItemText(ghwndMain, IDCANCEL, TEXT("Close"));
break;
}

default:
return;
}
}

//****************************************************************************
//
// CLASS CNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CFtNotify::CFtNotify() : RefCount(), CNotify()
{
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

CFtNotify::~CFtNotify()
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CFtNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CFtNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CFtNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
}
else if (riid == IID_INmChannelFtNotify)
{
*ppvObject = (INmChannelFtNotify *)this;
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CFtNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmChannelFtNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CFtNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from IConfNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CFtNotify::NmUI(CONFN uNotify)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CFtNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CFtNotify::FtUpdate(CONFN uNotify, INmFt * pFt)
{
if (pFt == g_pFt)
{
UpdateProgress(uNotify);
}

return S_OK;
}

//****************************************************************************
//
// VOID FreeNmObjects(void)
//
//****************************************************************************

VOID FreeNmObjects(void)
{
FreeFtChannel();

// Release conference
if (NULL != g_pConference)
{
g_pConference->Release();
g_pConference = NULL;
}

// Release conference manager
if (NULL != g_pMgr)
{
g_pMgr->Release();
g_pMgr = NULL;
}
}

//****************************************************************************
//
// BOOL FInitNm(void)
//
//****************************************************************************

BOOL FInitNm(void)
{
if (S_OK != InitConfMgr())
return FALSE;

if (S_OK != GetActiveConference())
return FALSE;

if (S_OK != GetFtChannel())
return FALSE;

if (S_OK != HookFtChannel())
return FALSE;

return TRUE;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by res.rc
//
#define IDS_APP_TITLE                   1
#define IDS_VERSION                     2
#define ICO_MAIN                        101
#define IDM_APP                         104
#define ID_APPACCEL                     105
#define IDD_PROGRESS                    124
#define IDE_SEND                        1028
#define IDE_DEST                        1029
#define IDC_PROGRESS                    1030

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        125
#define _APS_NEXT_COMMAND_VALUE         40145
#define _APS_NEXT_CONTROL_VALUE         1031
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


UTIL.CPP

//****************************************************************************
//  Module:     NMFT.EXE
//  File:       UTIL.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

static TCHAR _szFilter[] = TEXT("All Files\0*.*\0Text Files (*.txt)\0*.TXT\0");
static TCHAR _szPickMsg[] = TEXT("Select a file to send");

//****************************************************************************
//
// BOOL FGetFileName(LPTSTR szFileName)
//
//****************************************************************************

BOOL FGetFileName(LPTSTR szFileName)
{
OPENFILENAME ofn;

SetEmptySz(szFileName);
ClearStruct(&ofn);

ofn.lStructSize   = sizeof(OPENFILENAME);
ofn.hwndOwner     = ghwndMain;
ofn.hInstance     = (HINSTANCE) ghInst;
ofn.lpstrFilter   = _szFilter;
ofn.nFilterIndex  = 1L;
ofn.lpstrFile     = (LPSTR) szFileName;
ofn.nMaxFile      = MAX_PATH; // really CCHMAX(szFileName)
ofn.lpstrTitle    = _szPickMsg;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER;

return GetOpenFileName(&ofn);
}

//****************************************************************************
//
// BOOL FFileExists(LPCTSTR szFile)
//
//****************************************************************************

BOOL FFileExists(LPCTSTR szFile)
{
HANDLE hFile;

if ((NULL == szFile) || (_T('\0') == *szFile))
return FALSE;

UINT uErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
SetErrorMode(uErrorMode); // Restore error mode

if (hFile == INVALID_HANDLE_VALUE)
return FALSE;

CloseHandle(hFile);
return TRUE;
}

//****************************************************************************
//
// LPVOID LpvAlloc(int cb)
//
// Return a pointer to an allocated array of bytes
//
//****************************************************************************

LPVOID LpvAlloc(int cb)
{
return LocalAlloc(LMEM_FIXED, cb);
}

//****************************************************************************
//
// LPTSTR PszAlloc(int cch)
//
//****************************************************************************

LPTSTR PszAlloc(int cch)
{
return (LPTSTR) LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cch+1));
}

//****************************************************************************
//
// VOID FreePlpv(LPVOID plpv)
//
// Free the data pointed to by plpv and set *plpv to NULL
//
//****************************************************************************

VOID FreePlpv(LPVOID plpv)
{
if ((plpv == NULL) || (*(VOID FAR * FAR *)plpv == NULL)) {
return;
}

LocalFree(*(VOID FAR * FAR *)plpv);
*(VOID FAR * FAR *)plpv = NULL;
}

//****************************************************************************
//
// LPCTSTR StrChr(LPCTSTR pcsz, TCHAR ch)
//
//****************************************************************************

LPCTSTR StrChr(LPCTSTR pcsz, TCHAR ch)
{
    LPCTSTR pcszFound = NULL;

    if (pcsz)
    {
        while (*pcsz)
        {
            if (*pcsz == ch)
            {
                pcszFound = pcsz;
                break;
            }

            pcsz = CharNext(pcsz);
        }
    }

    return pcszFound;
}


README.TXT

------------------------------------------------------------------
         Microsoft NetMeeting SDK 2.1 NMSHARE Readme File
                           November 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE NMSHARE SAMPLE
BUILDING THE NMSHARE SAMPLE

OVERVIEW OF THE NMSHARE SAMPLE
==============================

NMSHARE is a Windows utility application that allows the user to
share application windows via an existing NetMeeting connection.

BUILDING THE NMSHARE SAMPLE
========================

Building the sample application requires VC++ Version 5.0 or later.

To build the sample from Visual C++, open the project makefile (nmshare.mak)
as a Microsoft VC++ project, and build as usual.  The NetMeeting SDK
directories must be added to your VC++ environment.

If your DOS environment is correctly configured, you can build the sample
from the command line by typing "nmake".  The DOS path, include and lib
environment variables must point to the VC installation and the
NetMeeting SDK directories.

Notes:


CCONF.H

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CCONF.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CMgrNotify
//
//****************************************************************************

class CMgrNotify : public RefCount, public CNotify, public INmManagerNotify
{
public:
CMgrNotify();
~CMgrNotify();

// INmManagerNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN confn);
HRESULT STDMETHODCALLTYPE ConferenceCreated(INmConference *pConference);
HRESULT STDMETHODCALLTYPE CallCreated(INmCall *pCall);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

//****************************************************************************
//
// CLASS CConfNotify
//
//****************************************************************************

class CConfNotify : public RefCount, public CNotify, public INmConferenceNotify
{
public:
CConfNotify();
~CConfNotify();

// INmConferenceNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE StateChanged(NM_CONFERENCE_STATE uState);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pfMember);
HRESULT STDMETHODCALLTYPE ChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel *pChannel);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

// Global Variables
extern INmManager * g_pMgr;
extern INmConference * g_pConference;

// Global Functions
HRESULT InitConfMgr(void);
VOID FreeConfMgr(void);
VOID FreeConference(void);
HRESULT HookConference(INmConference * pConference);


CHWND.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CHWND.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

COBLIST * g_pListApp = (COBLIST *) NULL;

//****************************************************************************
//
// int AddApp(INmSharableApp * pApp)
//
//****************************************************************************

int AddApp(INmSharableApp * pApp)
{
TCHAR sz[MAX_PATH];
wsprintf(sz, TEXT("%08X"), pApp);

pApp->AddRef();
AddNode(pApp, &g_pListApp);

LV_ITEM  lvItem;
ClearStruct(&lvItem);
lvItem.iItem = g_pListApp->GetItemCount();
lvItem.mask = LVIF_PARAM | LVIF_TEXT;
lvItem.pszText = sz;
lvItem.cchTextMax = lstrlen(sz);
lvItem.lParam = (LPARAM) pApp;
return ListView_InsertItem(ghwndApp, &lvItem);
}

//****************************************************************************
//
// int ItemFromPApp(INmSharableApp * pApp)
//
//****************************************************************************

int ItemFromPApp(INmSharableApp * pApp)
{
if (NULL == ghwndApp)
return -1;

LV_FINDINFO findInfo;
ClearStruct(&findInfo);
findInfo.flags = LVFI_PARAM;
    findInfo.lParam = (LPARAM) pApp;
    return ListView_FindItem(ghwndApp, -1, &findInfo);
}

//****************************************************************************
//
// VOID FreeAllApps(void)
//
//****************************************************************************

VOID FreeAllApps(void)
{
if (NULL != g_pListApp)
{
while (!g_pListApp->IsEmpty())
{
INmSharableApp * pApp = (INmSharableApp *) g_pListApp->RemoveHead();
ASSERT(NULL != pApp);
pApp->Release();
}
}
}

//****************************************************************************
//
// VOID UpdateApp(INmSharableApp * pApp)
//
//****************************************************************************

VOID UpdateApp(INmSharableApp * pApp)
{
HRESULT hr;
LPTSTR  psz;
BSTR    bstr;
HWND    hwnd;
int     iItem;
NM_SHAPP_STATE uState;

iItem = ItemFromPApp(pApp);
if (-1 == iItem)
{
iItem = AddApp(pApp);
if (-1 == iItem)
return;
}

hr = pApp->GetHwnd(&hwnd);
if (SUCCEEDED(hr))
{
TCHAR sz[MAX_PATH];
wsprintf(sz, TEXT("%08X"), hwnd);
ListView_SetItemText(ghwndApp, iItem, ILV_HWND, sz);
}

hr = pApp->GetState(&uState);
if (SUCCEEDED(hr))
{
ListView_SetItemText(ghwndApp, iItem, ILV_STATUS,
(NM_SHAPP_SHARED == uState) ? TEXT("Shared") : TEXT("Not Shared"));
}

hr = pApp->GetName(&bstr);
if (SUCCEEDED(hr))
{
psz = PszFromBstr(bstr);
SysFreeString(bstr);

ListView_SetItemText(ghwndApp, iItem, ILV_NAME, psz);
FreePlpv(&psz);
}
}

//****************************************************************************
//
// VOID UpdateAppList(void)
//
//****************************************************************************

VOID UpdateAppList(void)
{
// Update all apps
// - enumerate all
// - ensure all are in list
// - check data for each
}

//****************************************************************************
//
// LRESULT MsgNotifyAppList(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT MsgNotifyAppList(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LV_DISPINFO *pLvdi = (LV_DISPINFO *)lParam;

switch(pLvdi->hdr.code)
{
case NM_DBLCLK:
case NM_RETURN:
{
HRESULT hr;
NM_SHAPP_STATE uState;

int iItem = ListView_GetNextItem(ghwndApp, -1, LVNI_ALL | LVNI_SELECTED);
if (-1 == iItem)
break;

LV_ITEM lvi;
ClearStruct(&lvi);
lvi.iItem = iItem;
lvi.mask = LVIF_PARAM;
if (!ListView_GetItem(ghwndApp, &lvi))
break;

INmSharableApp * pApp = (INmSharableApp *) lvi.lParam;
ASSERT(NULL != pApp);
hr = pApp->GetState(&uState);
if (FAILED(hr))
break;

if (NM_SHAPP_NOT_SHARED == uState)
uState = NM_SHAPP_SHARED;
else
uState = NM_SHAPP_NOT_SHARED;

hr = pApp->SetState(uState);
break;
}
default:
break;
}

return 0;
}


CLUTIL.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CLUTIL.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

RefCount::RefCount(void)
{
m_cRef = 1;
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

RefCount::~RefCount(void)
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE RefCount::AddRef(void)
{
   ASSERT(m_cRef >= 0);

   InterlockedIncrement(&m_cRef);

   return (ULONG) m_cRef;
}

ULONG STDMETHODCALLTYPE RefCount::Release(void)
{
if (0 == InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}

ASSERT(m_cRef > 0);
return (ULONG) m_cRef;
}

//****************************************************************************
//
// CLASS CNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CNotify::CNotify() :
m_pcnpcnt(NULL),
    m_pcnp(NULL),
    m_dwCookie(0),
    m_pUnk(NULL)
{
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

CNotify::~CNotify()
{
Disconnect(); // Make sure we're disconnected
}

//****************************************************************************
//
// HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
//
//****************************************************************************

HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
{
HRESULT hr;

ASSERT(0 == m_dwCookie);

// Get the connection container
hr = pUnk->QueryInterface(IID_IConnectionPointContainer, (void **)&m_pcnpcnt);
if (SUCCEEDED(hr))
{
// Find an appropriate connection point
hr = m_pcnpcnt->FindConnectionPoint(riid, &m_pcnp);
if (SUCCEEDED(hr))
{
ASSERT(NULL != m_pcnp);
// Connect the sink object
hr = m_pcnp->Advise((IUnknown *)pUnkN, &m_dwCookie);
}
}

if (FAILED(hr))
{
m_dwCookie = 0;
}
else
{
    m_pUnk = pUnk; // keep around for caller
    }

return hr;
}

//****************************************************************************
//
// HRESULT CNotify::Disconnect (void)
//
//****************************************************************************

HRESULT CNotify::Disconnect (void)
{
    if (0 != m_dwCookie)
    {
        // Disconnect the sink object
        m_pcnp->Unadvise(m_dwCookie);
        m_dwCookie = 0;

        m_pcnp->Release();
        m_pcnp = NULL;

        m_pcnpcnt->Release();
        m_pcnpcnt = NULL;

        m_pUnk = NULL;
    }

    return S_OK;
}

//****************************************************************************
//
// CLASS COBLIST
//
//****************************************************************************

//****************************************************************************
//
// Destructor
//
//****************************************************************************

COBLIST::~COBLIST()
{
    ASSERT(IsEmpty());
}

#ifdef DEBUG
VOID* COBLIST::GetHead()
{
ASSERT(m_pHead);

return m_pHead->pItem;
}

VOID* COBLIST::GetTail()
{
ASSERT(m_pTail);

return m_pTail->pItem;
}
#endif /* DEBUG */

//****************************************************************************
//
// VOID* COBLIST::GetNext(POSITION& rPos)
//
//****************************************************************************

VOID* COBLIST::GetNext(POSITION& rPos)
{
ASSERT(rPos);

VOID* pReturn = rPos->pItem;
rPos = rPos->pNext;

return pReturn;
}

//****************************************************************************
//
// VOID* COBLIST::RemoveAt(POSITION Pos)
//
//****************************************************************************

VOID* COBLIST::RemoveAt(POSITION Pos)
{
VOID* pReturn = NULL;

if (m_pHead)
{
if (m_pHead == Pos)
{
// Removing the first element in the list

m_pHead = Pos->pNext;
pReturn = Pos->pItem;
delete Pos;
m_cItem--;
ASSERT(0 <= m_cItem);

if (NULL == m_pHead)
{
// Removing the only element!
m_pTail = NULL;
}
}
else
{
POSITION pCur = m_pHead;

while (pCur && pCur->pNext)
{
if (pCur->pNext == Pos)
{
// Removing

pCur->pNext = Pos->pNext;
if (m_pTail == Pos)
{
m_pTail = pCur;
}
pReturn = Pos->pItem;
delete Pos;

m_cItem--;
ASSERT(0 <= m_cItem);
}

pCur = pCur->pNext;
}
}
}

return pReturn;
}

//****************************************************************************
//
// POSITION COBLIST::AddTail(VOID* pItem)
//
//****************************************************************************

POSITION COBLIST::AddTail(VOID* pItem)
{
POSITION posRet = NULL;

if (m_pTail)
{
if (m_pTail->pNext = new COBNODE)
{
m_pTail = m_pTail->pNext;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
m_cItem++;
}
}
else
{
ASSERT(!m_pHead);
if (m_pHead = new COBNODE)
{
m_pTail = m_pHead;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
m_cItem++;
}
}

return m_pTail;
}

//****************************************************************************
//
// void COBLIST::EmptyList()
//
//****************************************************************************

void COBLIST::EmptyList()
{
    while (!IsEmpty()) {
        RemoveAt(GetHeadPosition());
    }
}

#ifdef DEBUG
VOID* COBLIST::RemoveTail()
{
ASSERT(m_pHead);
ASSERT(m_pTail);

return RemoveAt(m_pTail);
}

VOID* COBLIST::RemoveHead()
{
ASSERT(m_pHead);
ASSERT(m_pTail);

return RemoveAt(m_pHead);
}

void * COBLIST::GetFromPosition(POSITION Pos)
{
    void * Result = SafeGetFromPosition(Pos);
ASSERT(Result);
return Result;
}
#endif /* DEBUG */

//****************************************************************************
//
// POSITION COBLIST::GetPosition(void* _pItem)
//
//****************************************************************************

POSITION COBLIST::GetPosition(void* _pItem)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Position->pItem == _pItem) {
            break;
        }
GetNext(Position);
    }
    return Position;
}

//****************************************************************************
//
// POSITION COBLIST::Lookup(void* pComparator)
//
//****************************************************************************

POSITION COBLIST::Lookup(void* pComparator)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Compare(Position->pItem, pComparator)) {
            break;
        }
GetNext(Position);
    }
    return Position;
}

//****************************************************************************
//
// void * COBLIST::SafeGetFromPosition(POSITION Pos)
//
//****************************************************************************

void * COBLIST::SafeGetFromPosition(POSITION Pos)
{
// Safe way to validate that an entry is still in the list,
// which ensures bugs that would reference deleted memory,
// reference a NULL pointer instead
// (e.g. an event handler fires late/twice).
// Note that versioning on entries would provide an additional
// safeguard against re-use of a position.
// Walklist to find entry.

POSITION PosWork = m_pHead;

while (PosWork) {
if (PosWork == Pos) {
return Pos->pItem;
}
GetNext(PosWork);
}
return NULL;
}

//****************************************************************************
//
// COBLIST Utility routines
//
//****************************************************************************

//****************************************************************************
//
// POSITION AddNode(PVOID pv, COBLIST ** ppList)
//
// Add a node to a list.
// Initializes the ObList, if necessary.
// Returns the position in the list or NULL if there was a problem.
//
//****************************************************************************

POSITION AddNode(PVOID pv, COBLIST ** ppList)
{
ASSERT(NULL != ppList);
if (NULL == *ppList)
{
*ppList = new COBLIST();
if (NULL == *ppList)
return NULL;
}

return (*ppList)->AddTail(pv);
}

//****************************************************************************
//
// PVOID RemoveNode(POSITION * pPos, COBLIST *pList)
//
// Remove a node from a list.
// Sets pPos to NULL
//
//****************************************************************************

PVOID RemoveNode(POSITION * pPos, COBLIST *pList)
{
if ((NULL == pList) || (NULL == pPos))
return NULL;

PVOID pv = pList->RemoveAt(*pPos);
*pPos = NULL;
return pv;
}

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
// We don't support construction from an ANSI string in the Unicode build.
//
//****************************************************************************

#if !defined(UNICODE)

BSTRING::BSTRING(LPCSTR lpcString)
{
m_bstr = NULL;

// Compute the length of the required BSTR, including the null
int cWC =  MultiByteToWideChar(CP_ACP, 0, lpcString, -1, NULL, 0);
if (cWC <= 0)
return;

// Allocate the BSTR, including the null
m_bstr = SysAllocStringLen(NULL, cWC - 1); // SysAllocStringLen adds another 1

ASSERT(NULL != m_bstr);
if (NULL == m_bstr)
{
return;
}

// Copy the string
MultiByteToWideChar(CP_ACP, 0, lpcString, -1, (LPWSTR) m_bstr, cWC);

// Verify that the string is null terminated
ASSERT(0 == m_bstr[cWC - 1]);
}

#endif // !defined(UNICODE)

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

BTSTR::BTSTR(BSTR bstr)
{
m_psz = PszFromBstr(bstr);
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

BTSTR::~BTSTR()
{
if (NULL != m_psz)
LocalFree(m_psz);
}

//****************************************************************************
//
// LPTSTR PszFromBstr(BSTR bstr)
//
// Converts a BSTR to a LPSTR
//
//****************************************************************************

LPTSTR PszFromBstr(BSTR bstr)
{
if (NULL == bstr)
return NULL;
int cch =  WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, NULL, 0, NULL, NULL);
if (cch <= 0)
return NULL;

LPTSTR psz = PszAlloc(cch);
if (NULL == psz)
return NULL;

WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, psz, cch+1, NULL, NULL);
return psz;
}


CLUTIL.H

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CLUTIL.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

// Utility Classes

#ifndef _CL_UTIL_H_
#define _CL_UTIL_H_

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

class RefCount
{
private:
   LONG m_cRef;

public:
   RefCount();
   // Virtual destructor defers to destructor of derived class.
   virtual ~RefCount();

   // IUnknown methods

   ULONG STDMETHODCALLTYPE AddRef(void);
   ULONG STDMETHODCALLTYPE Release(void);
};

//****************************************************************************
//
// CLASS CNotify
//
// Notification sink
//
//****************************************************************************

class CNotify
{
private:
    DWORD  m_dwCookie;
IUnknown * m_pUnk;
    IConnectionPoint           * m_pcnp;
    IConnectionPointContainer  * m_pcnpcnt;
public:
    CNotify(void);
    ~CNotify();

    HRESULT Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN);
    HRESULT Disconnect(void);

    IUnknown * GetPunk() {return m_pUnk;}
};

//****************************************************************************
//
// CLASS COBLIST
//
//****************************************************************************

#define POSITION COBNODE*

struct COBNODE
{
POSITIONpNext;
void*pItem;
};

class COBLIST
{
protected:
POSITION m_pHead;
POSITION m_pTail;
int      m_cItem;
    virtual BOOL Compare(void* pItemToCompare, void* pComparator)
                       { return(pItemToCompare == pComparator); }
public:
COBLIST() : m_pHead(NULL), m_pTail(NULL), m_cItem(0) { }
    virtual         ~COBLIST();

virtual void *  RemoveAt(POSITION rPos);
    void            EmptyList();
POSITION    AddTail(void* pItem);
void *    GetNext(POSITION& rPos);
    void *          SafeGetFromPosition(POSITION rPos);
    POSITION        GetPosition(void* pItem);
    POSITION        Lookup(void* pComparator);
POSITION    GetHeadPosition()  { return (m_pHead); }
POSITION    GetTailPosition()  { return (m_pTail); }
BOOL    IsEmpty()          { return (!m_pHead); }
int             GetItemCount()     { return (m_cItem); }
#ifdef DEBUG
void *    GetHead();
void *    GetTail();
void *    RemoveHead();
void *        RemoveTail();
void *    GetFromPosition(POSITION rPos);
#else
void *    GetHead()          { return GetFromPosition(GetHeadPosition());}
void *          GetTail()          { return m_pTail->pItem;}
void *    RemoveHead()       { return RemoveAt(m_pHead); }
void *        RemoveTail()       { return RemoveAt(m_pTail); }
void *    GetFromPosition(POSITION rPos){return(rPos->pItem);}
#endif
};

//****************************************************************************
//
// Utility Functions
//
//****************************************************************************

POSITION AddNode(PVOID pv, COBLIST ** ppList);
PVOID RemoveNode(POSITION * pPos, COBLIST *pList);

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

class BSTRING
{
private:
BSTR   m_bstr;

public:
// Constructors
BSTRING() {m_bstr = NULL;}

inline BSTRING(LPCWSTR lpcwString);

#if !defined(UNICODE)
// We don't support construction from an ANSI string in the Unicode build.
BSTRING(LPCSTR lpcString);
#endif // !defined(UNICODE)

// Destructor
inline ~BSTRING();

// Cast to BSTR
operator BSTR() {return m_bstr;}
inline LPBSTR GetLPBSTR(void);
};

BSTRING::BSTRING(LPCWSTR lpcwString)
{
if (NULL != lpcwString)
{
m_bstr = SysAllocString(lpcwString);
ASSERT(NULL != m_bstr);
}
else
{
m_bstr = NULL;
}
}

BSTRING::~BSTRING()
{
if (NULL != m_bstr)
{
SysFreeString(m_bstr);
}
}

inline LPBSTR BSTRING::GetLPBSTR(void)
{
ASSERT(NULL == m_bstr);

return &m_bstr;
}

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

class BTSTR
{
private:
LPTSTR m_psz;

public:
BTSTR(BSTR bstr);
~BTSTR();

// Cast to BSTR
operator LPTSTR() {return (NULL == m_psz) ? TEXT("<null>") : m_psz;}
};

LPTSTR PszFromBstr(BSTR bst);

#endif  // _CL_UTIL_H_


CMD.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CMD.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

VOID CmdOptions(void)
{
}

//****************************************************************************
//
// VOID CmdClear(void)
//
// Clear the message window
//
//****************************************************************************

VOID CmdClear(void)
{
ClearText();
}

//****************************************************************************
//
// VOID CmdExit(void)
//
// Shutdown the application.
//
//****************************************************************************

VOID CmdExit(void)
{
DestroyWindow(ghwndMain);
}

//****************************************************************************
//
// VOID CmdHelp(void)
//
// Bring up help for the application
//
//****************************************************************************

VOID CmdHelp(void)
{
}

//****************************************************************************
//
// VOID CmdFont(void)
//
// Change the font for the message window
//
//****************************************************************************

VOID CmdFont(void)
{
#ifdef NOTUSED
CHOOSEFONT cf;
LOGFONT    lf;
HFONT      hfont;

lf = gPref.lf;

ClearStruct(&cf);
cf.lStructSize = sizeof(CHOOSEFONT);
cf.hwndOwner = ghwndMain;
cf.lpLogFont = &lf;
cf.iPointSize = 10;
cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT;

if (ChooseFont(&cf) != TRUE)
return;

hfont= CreateFontIndirect(cf.lpLogFont);
if (hfont == hfontNil)
return;

MaybeDeleteObject((HGDIOBJ *) &ghfontEntry);
ghfontEntry = hfont;
SendMessage(ghwndMsg, WM_SETFONT,
(WPARAM) ghfontEntry, MAKELPARAM(TRUE, 0));

gPref.lf = lf;
#endif
}

//****************************************************************************
//
// VOID CmdAbout(void)
//
// Display the about box
//
//****************************************************************************

VOID CmdAbout(void)
{
TCHAR szVersion[MAX_PATH];

LoadString(ghInst, IDS_VERSION, szVersion, CCHMAX(szVersion));

ShellAbout(ghwndMain, szVersion, NULL, LoadIcon(ghInst, MAKEINTRESOURCE(ICO_MAIN)));
}

//****************************************************************************
//
// VOID CmdViewSbar(void)
//
// Show/Hide the status bar
//
//****************************************************************************

VOID CmdViewSbar(void)
{
gPref.fSbar = !gPref.fSbar;
ShowHwnd(ghwndSbar, gPref.fSbar, IDM_VIEW_SBAR);
}

VOID CmdViewMsg(void)
{
gPref.fMsgWindow = !gPref.fMsgWindow;
ShowHwnd(ghwndMsg, gPref.fMsgWindow, IDM_VIEW_MSG);
}

VOID CmdViewUsers(void)
{
gPref.fUserList = !gPref.fUserList;
ShowHwnd(ghwndUser, gPref.fUserList, IDM_VIEW_USERLIST);
}

VOID CmdRefresh(void)
{
DoEnumApps();
}

VOID CmdWorkAlone(void)
{
DoWorkAlone();
}

VOID CmdCollaborate(void)
{
DoCollaborate();
}

VOID CmdInControl(void)
{
DoTakeControl();
}

//****************************************************************************
//
// Main window command table definition.
//
//****************************************************************************

static CMD _rgcmdMain[] =
{
{IDM_EXIT,                CmdExit},

{IDM_WORK_ALONE,          CmdWorkAlone},
{IDM_COLLABORATE,         CmdCollaborate},
{IDM_IN_CONTROL,          CmdInControl},

{IDM_VIEW_CLEAR,          CmdClear},

{IDM_VIEW_SBAR,           CmdViewSbar},
{IDM_VIEW_MSG,            CmdViewMsg},
{IDM_VIEW_USERLIST,       CmdViewUsers},
{IDM_REFRESH,             CmdRefresh},

{IDM_HELP,                CmdHelp},
{IDM_ABOUT,               CmdAbout},

{0, NULL}  // MUST be last entry in the table
};

//****************************************************************************
//
// LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
//
// Handle a WM_COMMAND message
//
//****************************************************************************

LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
{
UINT wCmd = (UINT) GET_WM_COMMAND_ID(wparam, lparam);

for (LPCMD lpcmd = _rgcmdMain; 0 != lpcmd->wCmd; lpcmd++)
{
if (wCmd == lpcmd->wCmd)
{
lpcmd->pfncmd();
break;
}
}

return 0L;
}


CMEMB.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CMEMB.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

COBLIST * g_pListUser = (COBLIST *) NULL;

//****************************************************************************
//
// PUSER PUserFromINmMember(INmMember * pMember)
//
//****************************************************************************

PUSER PUserFromINmMember(INmMember * pMember)
{
if (NULL != g_pListUser)
{
POSITION pos = g_pListUser->GetHeadPosition();
while (NULL != pos)
{
PUSER pUser = (PUSER) g_pListUser->GetNext(pos);
if (pMember == pUser->pMember)
{
ASSERT(pMember == pUser->pMember);
return pUser;
}
}
}

return NULL;
}

//****************************************************************************
//
// int ItemFromPUser(PUSER pUser)
//
//****************************************************************************

int ItemFromPUser(PUSER pUser)
{
if (NULL == ghwndUser)
return -1;

LV_FINDINFO findInfo;
ClearStruct(&findInfo);
findInfo.flags = LVFI_PARAM;
    findInfo.lParam = (LPARAM) pUser;
    return ListView_FindItem(ghwndUser, -1, &findInfo);
}

//****************************************************************************
//
// PUSER AllocUser(INmMember * pMember)
//
//****************************************************************************

PUSER AllocUser(INmMember * pMember)
{
HRESULT hr;
BSTR bstr;

PUSER pUser = new USER;
if (NULL == pUser)
{
return NULL;
}
ClearStruct(pUser);

pUser->pMember = pMember;
pMember->AddRef();

hr = pMember->GetName(&bstr);
if (SUCCEEDED(hr))
{
pUser->pszName = PszFromBstr(bstr);
SysFreeString(bstr);
}

return pUser;
}

//****************************************************************************
//
// VOID FreeUser(PUSER pUser)
//
//****************************************************************************

VOID FreeUser(PUSER pUser)
{
ASSERT(NULL != pUser);

FreePlpv(&pUser->pszName);

if (NULL != pUser->pMember)
{
pUser->pMember->Release();
}
}

VOID FreeAllUsers(void)
{
if (NULL != g_pListUser)
{
while (!g_pListUser->IsEmpty())
{
RemoveMember(((PUSER) g_pListUser->GetHead())->pMember);
}
}
}

//****************************************************************************
//
// VOID AddMember(INmMember * pMember)
//
//****************************************************************************

VOID AddMember(INmMember * pMember)
{
PUSER pUser = AllocUser(pMember);
if (NULL == pUser)
{
return;
}

AddNode(pUser, &g_pListUser);

LV_ITEM  lvItem;
ClearStruct(&lvItem);
lvItem.iItem = g_pListUser->GetItemCount();
lvItem.mask = LVIF_PARAM | LVIF_TEXT;
lvItem.pszText = pUser->pszName;
lvItem.cchTextMax = lstrlen(pUser->pszName);
lvItem.lParam = (LPARAM) pUser;
ListView_InsertItem(ghwndUser, &lvItem);

{
TCHAR szMsg[MAX_PATH];
wsprintf(szMsg, TEXT("%s joined"), pUser->pszName);
DisplayMsg(szMsg);
}
}

//****************************************************************************
//
// VOID RemoveMember(INmMember * pMember)
//
//****************************************************************************

VOID RemoveMember(INmMember * pMember)
{
if (NULL == g_pListUser)
return;

POSITION pos = g_pListUser->GetHeadPosition();
while (NULL != pos)
{
POSITION posSav = pos;
PUSER pUser = (PUSER) g_pListUser->GetNext(pos);
if (pMember == pUser->pMember)
{
TCHAR szMsg[MAX_PATH];
wsprintf(szMsg, TEXT("%s left"), pUser->pszName);
DisplayMsg(szMsg);

ListView_DeleteItem(ghwndUser, ItemFromPUser(pUser));
FreeUser(pUser);
g_pListUser->RemoveAt(posSav);
return;
}
}
}


CMEMB.H

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CMEMB.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

typedef struct tagUser {
INmMember * pMember;
LPTSTR pszName;
} USER;
typedef USER * PUSER;

extern COBLIST * g_pListUser;

// from cMemb.cpp
VOID AddMember(INmMember * pMember);
VOID RemoveMember(INmMember * pMember);
VOID FreeAllUsers(void);
VOID FreeUser(PUSER pUser);
PUSER PUserFromINmMember(INmMember * pMember);


CSHARE.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CSHARE.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

INmChannelAppShare * g_pChannelAppShare = NULL;
CAppShareNotify * g_pNotifyAppShare = NULL;

//****************************************************************************
//
// HRESULT HookChannel(INmChannel * pChannel)
//
//****************************************************************************

HRESULT HookChannel(INmChannel * pChannel)
{
HRESULT hr;
CAppShareNotify * pNotify;
INmChannelAppShare * pChannelAs;

ASSERT(NULL != pChannel);
hr = pChannel->QueryInterface(IID_INmChannelAppShare, (void **)&pChannelAs);
if (FAILED(hr))
{
// Not an app sharing channel
return hr;
}
if (NULL != g_pChannelAppShare)
{
g_pChannelAppShare = NULL;
}
g_pChannelAppShare = pChannelAs;
// keep QI ref around

    // Connect to the channel object
    pNotify = new CAppShareNotify();
    if (pNotify == NULL)
    {
        hr = E_OUTOFMEMORY;
    }
    else
    {
hr = pNotify->Connect(g_pChannelAppShare);
if (FAILED(hr))
{
pNotify->Release();
}
else
{
g_pNotifyAppShare = pNotify;
}
}

if (FAILED(hr))
{
g_pChannelAppShare->Release();
g_pChannelAppShare = NULL;
}
else
{
DoEnumApps();
}

return hr;
}

//****************************************************************************
//
// HRESULT HookChannel(INmChannel * pChannel)
//
//****************************************************************************

VOID UnHookChannel(INmChannel * pChannel)
{
if (NULL == g_pChannelAppShare)
return;

if (S_OK != g_pChannelAppShare->IsSameAs(pChannel))
return;

FreeAppShareChannel();
}

//****************************************************************************
//
// VOID FreeAppShareChannel(void)
//
//****************************************************************************

VOID FreeAppShareChannel(void)
{
// Release all notify
if (NULL != g_pNotifyAppShare)
{
g_pNotifyAppShare->Disconnect();
g_pNotifyAppShare->Release();
g_pNotifyAppShare = NULL;
}

if (NULL != g_pChannelAppShare)
{
g_pChannelAppShare->Release();
g_pChannelAppShare = NULL;
}
}

//****************************************************************************
//
// CLASS CAppShareNotify
//
//****************************************************************************

CAppShareNotify::CAppShareNotify() : RefCount(), CNotify()
{
}

CAppShareNotify::~CAppShareNotify()
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CAppShareNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CAppShareNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CAppShareNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
}
else if (riid == IID_INmChannelAppShareNotify)
{
*ppvObject = (INmChannelAppShareNotify *)this;
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CAppShareNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmChannelAppShareNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CAppShareNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from INmChannelNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CAppShareNotify::NmUI(CONFN uNotify)
{
LPTSTR psz;

switch (uNotify)
{
case CONFN_CLICK_CONTROL:
psz = TEXT("Click: requesting control");
break;
case CONFN_CLICK_OBSCURE:
psz = TEXT("Click: obscured area");
break;
case CONFN_CLICK_REMOTE_NOT_COLLABORATING:
psz = TEXT("Click: Remote not collaborating");
break;
case CONFN_CLICK_LOCAL_NOT_COLLABORATING:
psz = TEXT("Click: Local not collaborating");
break;
default:
psz = TEXT("Unknown confn");
break;
}

DisplayMsg(psz);

return S_OK;
}

HRESULT STDMETHODCALLTYPE CAppShareNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
PUSER pUser = PUserFromINmMember(pMember);
if (NULL != pUser)
{
//UpdateUserChannel(pUser, (INmChannel *) (CNotify::GetPunk()), uNotify);
#ifdef DEBUG
TCHAR szMsg[MAX_PATH];
wsprintf(szMsg, TEXT("%s changed sharing status"), pUser->pszName);
DisplayMsg(szMsg);
#endif

if (S_OK == pMember->IsSelf())
{
NM_SHARE_STATE uState;
if (SUCCEEDED(pMember->GetShareState(&uState)))
{
SetMenuCheck(IDM_WORK_ALONE,  (NM_SHARE_WORKING_ALONE == uState));
SetMenuCheck(IDM_COLLABORATE, (NM_SHARE_COLLABORATING == uState)
|| (NM_SHARE_IN_CONTROL == uState));
SetMenuCheck(IDM_IN_CONTROL,  (NM_SHARE_IN_CONTROL == uState));

#ifdef DEBUG
switch (uState)
{
case NM_SHARE_WORKING_ALONE: DisplayMsg(TEXT("Working Alone"));       break;
case NM_SHARE_COLLABORATING: DisplayMsg(TEXT("Collaborating"));       break;
case NM_SHARE_IN_CONTROL:    DisplayMsg(TEXT("In Control"));          break;
default:                     DisplayMsg(TEXT("Uknown Share State?")); break;
}
#endif
}
}
else
{
#ifdef DEBUG
NM_SHARE_STATE uState;
if (SUCCEEDED(pMember->GetShareState(&uState)))
{
LPTSTR pszState = TEXT("?");
switch (uState)
{
case NM_SHARE_WORKING_ALONE: pszState = TEXT("Working Alone");       break;
case NM_SHARE_COLLABORATING: pszState = TEXT("Collaborating");       break;
case NM_SHARE_IN_CONTROL:    pszState = TEXT("In Control");          break;
default:                     pszState = TEXT("Uknown Share State?"); break;
}

BSTR  bstrName;
if (SUCCEEDED(pMember->GetName(&bstrName)))
{
TCHAR sz[MAX_PATH];
wsprintf(sz, "%s is %s", (LPTSTR) BTSTR(bstrName), pszState);
DisplayMsg(sz);
SysFreeString(bstrName);
}
}
#endif
}
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE CAppShareNotify::StateChanged(NM_SHAPP_STATE uState, INmSharableApp *pApp)
{
TCHAR szMsg[MAX_PATH];
wsprintf(szMsg, TEXT("%08X %s"), pApp,
(NM_SHAPP_SHARED == uState) ? TEXT("Shared") : TEXT("not shared"));
DisplayMsg(szMsg);
UpdateApp(pApp);
return S_OK;
}

VOID DoEnumApps(void)
{
HRESULT hr;
ULONG cFetched;
IEnumNmSharableApp * pEnum;
INmSharableApp * pApp;

if (NULL == g_pChannelAppShare)
return;

hr = g_pChannelAppShare->EnumSharableApp(&pEnum);
if (FAILED(hr))
{
    return;
}

for ( ; ; )
{
hr = pEnum->Next(1, &pApp, &cFetched);
if (S_OK != hr)
break;

UpdateApp(pApp);
pApp->Release();
}

pEnum->Release();
}

VOID DoWorkAlone(void)
{
if (NULL == g_pChannelAppShare)
return;

HRESULT hr = g_pChannelAppShare->SetState(NM_SHARE_WORKING_ALONE);
}

VOID DoCollaborate(void)
{
if (NULL == g_pChannelAppShare)
return;

HRESULT hr = g_pChannelAppShare->SetState(NM_SHARE_COLLABORATING);
}

VOID DoTakeControl(void)
{
if (NULL == g_pChannelAppShare)
return;

HRESULT hr = g_pChannelAppShare->SetState(NM_SHARE_IN_CONTROL);
}


CSHARE.H

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CSHARE.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CAppShareNotify
//
//****************************************************************************

class CAppShareNotify : public RefCount, public CNotify, public INmChannelAppShareNotify
{
public:
CAppShareNotify();
~CAppShareNotify();

// INmChannelAppShareNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember * pMember);
HRESULT STDMETHODCALLTYPE StateChanged(NM_SHAPP_STATE uNotify, INmSharableApp *pApp);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

extern INmChannelAppShare * g_pChannelAppShare;
extern CAppShareNotify    * g_pNotifyAppShare;

//****************************************************************************
//
// Global Routines
//
//****************************************************************************

HRESULT HookChannel(INmChannel * pChannel);
VOID UnHookChannel(INmChannel * pChannel);
VOID FreeAppShareChannel(void);

// from cHwnd.cpp
VOID UpdateApp(INmSharableApp * pApp);
VOID UpdateAppList(void);
VOID DoEnumApps(void);
VOID FreeAllApps(void);

VOID DoWorkAlone(void);
VOID DoCollaborate(void);
VOID DoTakeControl(void);


DEBUG.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       DEBUG.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

#ifdef DEBUG /* The whole file! */

//****************************************************************************
//
// VOID FAR PASCAL AssertProc(LPSTR lpszMsg, LPSTR lpszAssert, LPSTR lpszFile, UINT line)
//
//****************************************************************************

VOID FAR PASCAL AssertProc(LPSTR lpszMsg, LPSTR lpszAssert, LPSTR lpszFile, UINT line)
{
int  id;
TCHAR szBuffer[1024]; // a big buffer

wsprintf((LPSTR)szBuffer,
"Assert: %s\nFile %s, Line %d",
(lpszMsg && *lpszMsg) ? lpszMsg  : lpszAssert,
lpszFile ? lpszFile : "", line);

OutputDebugString(szBuffer);

do {
id = MessageBox(NULL, (LPSTR)szBuffer, TEXT("NmChat Assert"),
MB_ICONHAND | MB_ABORTRETRYIGNORE | MB_SYSTEMMODAL);

switch (id)
{
case IDRETRY:
DebugBreak();
break;
case IDABORT:
FatalAppExit(0, (LPSTR)szBuffer);
break;
case IDIGNORE:
default:
break;
}
} while (id == IDRETRY);
}

#endif /* DEBUG - the whole file! */


GLOBAL.H

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       GLOBAL.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#ifndef GLOBAL_H
#define GLOBAL_H

//-------------------------------------------------------
// Useful constants

#define chNull   ('\0')
#define lpNil    (NULL)

#define hwndNil      ((HWND) NULL)
#define hfontNil     ((HFONT) NULL)
#define hgdiNil      ((HGDIOBJ) NULL)

//-------------------------------------------------------
// Useful macros

#define ClearBytes(lpv, cb)ZeroMemory((LPVOID) (lpv), (cb))
#define ClearStruct(lpv)ZeroMemory((LPVOID) (lpv), sizeof(*(lpv)))
#define InitStruct(lpv)        {ClearStruct(lpv); (* (LPDWORD) lpv) = sizeof(*(lpv));}
#define CopyStruct(pDest, pSrc)  CopyMemory(pDest, pSrc, sizeof(*(pDest)))

#define SetEmptySz(sz)( *(sz) = _T('\0') )
#define FEmptySz(sz)            (((sz) == NULL) || (*(sz) == _T('\0')))

#define ARRAY_ELEMENTS(rg)      (sizeof(rg) / sizeof(rg[0]))
#define CCHMAX(sz)              (sizeof(sz) / sizeof(TCHAR))

#ifdef DEBUG
VOID FAR PASCAL AssertProc(LPTSTR lpszMsg, LPTSTR lpszAssert, LPTSTR lpszFile, UINT line);

#define DEBASSERT(exp,szMsg)  \
if (!(exp))                                                      \
{                                                                \
static char _szAssert[] = #exp ;   \
static char _szMsg[]    = szMsg;   \
AssertProc(_szMsg,_szAssert,__FILE__,__LINE__); \
}

#define ASSERT(f)       DEBASSERT(f, "(" #f ")")
#define AssertSz(f,sz)  DEBASSERT(f, sz)
#define NotReached()    DEBASSERT(FALSE, "NotReached declaration was reached!")
#else
#define ASSERT(f)
#define AssertSz(f, sz)
#define NotReached()
#endif

//-------------------------------------------------------------------------
// Functions for handling main window messages.  The message-dispatching
// mechanism expects all message-handling functions to have the following
// prototype:
//
// Function pointer prototype for message handling functions.
//     LRESULT FunctionName(HWND, UINT, WPARAM, LPARAM);

typedef LRESULT (*PFNMSG)(HWND, UINT, WPARAM, LPARAM);

// This structure maps messages to message handling functions.
typedef struct _MSD
{
UINT   uMsg;
PFNMSG pfnmsg;
} MSD;                 // MeSsage Dispatch structure
typedef MSD * LPMSD;

//-------------------------------------------------------------------------
// Functions for handling main window commands--ie. functions for
// processing WM_COMMAND messages based on the wParam value.
// The message-dispatching mechanism expects all command-handling
// functions to have the following prototype:

typedef VOID (*PFNCMD)(VOID);

// This structure maps command IDs to command handling functions.
typedef struct _CMD
{
UINT     wCmd;
PFNCMD pfncmd;
} CMD;                 // CoMmand Dispatch structure
typedef CMD * LPCMD;

typedef struct _PREF
{
BOOL fSbar;       // Display status bar
BOOL fMsgWindow;  // Display message window
BOOL fUserList;   // Display user list
WINDOWPLACEMENT wpMain;
LOGFONT lf;
} PREF;

//-------------------------------------------------------------------------
// Special resource constants
#define IDW_MSG   100
#define IDW_USER  101
#define IDW_APP  102

// See _rgColMember
#define ILV_ADDR      0
#define ILV_HWND      1
#define ILV_STATUS    2
#define ILV_NAME      3

const int DYP_MSG    = 30;  // Fixed Height of Message window
const int DXP_USER   = 80;  // Fixed Width of User List
const int YP_TOP     = 1;
const int XP_LEFT    = 0;

//-------------------------------------------------------------------------
// Global Function Prototypes.

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// from util.cpp
LPVOID LpvAlloc(int cb);
LPTSTR PszAlloc(int cch);
VOID FreePlpv(LPVOID plpv);
VOID MaybeDeleteObject(HGDIOBJ * phgdi);
VOID SizeMsgWindow(int dx, int dy);
VOID RecalcMsgWindow(void);
VOID SetMenuCheck(UINT idm, BOOL fCheck);
VOID ShowHwnd(HWND hwnd, BOOL fShow, int idm);

VOID ClearText(void);
VOID DisplayMsg(LPTSTR pszMsg);

LRESULT MsgNotifyAppList(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

//-------------------------------------------------------------------------
// Global Variables

extern HINSTANCE ghInst;          // The current instance handle
extern HACCEL    ghAccelTable;    // Menu accelerators
extern HWND      ghwndMain;       // Main Window
extern HWND      ghwndSbar;       // Status Bar
extern HMENU     ghMenu;          // Main Menu
extern HWND      ghwndMsg;        // Message Window
extern HWND      ghwndUser;       // User List
extern HWND      ghwndApp;        // Application List
extern HFONT     ghfontEntry;     // Font for message Window

extern PREF      gPref;           // Preferences

#endif /* GLOBAL_H */


IMSCONF2.IDL

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       imsconf2.idl
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include <imsconf2.idl>


MAIN.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       MAIN.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

HINSTANCE ghInst       = NULL;          // Current Instance
HACCEL    ghAccelTable = NULL;          // Menu accelerators
HMENU     ghMenu       = NULL;          // Main Menu
HWND      ghwndMain    = NULL;          // Main Window
HWND      ghwndSbar    = NULL;          // Status Bar
HWND      ghwndMsg     = hwndNil;       // Message window
HWND      ghwndApp     = hwndNil;       // Application List
HWND      ghwndUser    = hwndNil;       // User List
HFONT     ghfontMsg    = hfontNil;      // Font for messages
PREF      gPref;

TCHAR gszAppTitle[]     = TEXT("NetMeeting Sharing Test Application");

// string constants (no change for international)
TCHAR gszAppClassName[] = TEXT("NMSHARE");
TCHAR gszAppName[]      = TEXT("NmShare");

// List column information

typedef struct _tagCol {
UINT  dwWidth;
LPTSTR lpsz;
} COL;
typedef COL * LPCOL;

// See ILV_*
static COL _rgColMsg[] = {
{70,  TEXT("INmApp")},
{70,  TEXT("Hwnd")},
{70,  TEXT("Status")},
{400, TEXT("Name")},
};

//****************************************************************************
//
// BOOL FInitPref(void)
//
// Initialize user preferences
//
//****************************************************************************

BOOL FInitPref(void)
{
ClearStruct(&gPref);
gPref.fMsgWindow = TRUE;
gPref.fUserList = TRUE;
return TRUE;
}

//****************************************************************************
//
// BOOL FCreateAppWindow(void)
//
// Create the application list window
//
//****************************************************************************

BOOL FCreateAppWindow(void)
{
ghwndApp = CreateWindow(TEXT("SysListView32"), NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | LVS_REPORT,
0, 0, 0, 0,
ghwndMain, (HMENU) IDW_APP, ghInst, NULL);
if (ghwndApp == NULL)
return FALSE;

// Initialize column data
{
int   iCol;
LPCOL lpCol;
LV_COLUMN lvc;

lpCol = _rgColMsg;
ClearStruct(&lvc);
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
for (iCol = 0; iCol < ARRAY_ELEMENTS(_rgColMsg); iCol++)
{
lvc.iSubItem = iCol;
lvc.cx = lpCol->dwWidth;
lvc.pszText = lpCol->lpsz;
if (ListView_InsertColumn(ghwndApp, iCol, &lvc) == -1)
return FALSE;
lpCol++;
}
}

return TRUE;
}

//****************************************************************************
//
// BOOL FCreateUserList(void)
//
// Create a listbox for the users
//
//****************************************************************************

BOOL FCreateUserList(void)
{
ghwndUser = CreateWindow(TEXT("SysListView32"), NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS,
0, 0, DXP_USER, 0,
ghwndMain, (HMENU) IDW_USER, ghInst, NULL);

if (NULL == ghwndUser)
return FALSE;

// Initialize column data
{
LV_COLUMN lvc;

ClearStruct(&lvc);
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
lvc.iSubItem = 0;
lvc.cx = DXP_USER;
lvc.pszText = TEXT("Users");
if (ListView_InsertColumn(ghwndUser, 0, &lvc) == -1)
return FALSE;
}

return TRUE;
}

//****************************************************************************
//
// BOOL FCreateMsgWindow(void)
//
// Create the main message window
//
//****************************************************************************

BOOL FCreateMsgWindow(void)
{
ghwndMsg = CreateWindow(TEXT("LISTBOX"), NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | WS_CLIPSIBLINGS |
LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | LBS_HASSTRINGS,
0, 0, 0, 0,
ghwndMain, (HMENU) IDW_MSG, ghInst, NULL);
if (NULL == ghwndMsg)
return FALSE;

// set the font for the window
ghfontMsg = CreateFontIndirect(&gPref.lf);
if (ghfontMsg == hfontNil)
return TRUE;

SendMessage(ghwndMsg, WM_SETFONT, (WPARAM) ghfontMsg, MAKELPARAM(TRUE, 0));

return TRUE;
}

//****************************************************************************
//
// BOOL FInitMain(void)
//
// Initialize the window data and register the window class
//
//****************************************************************************

BOOL FInitMain(void)
{
TCHAR szTitle[MAX_PATH];
WNDCLASS  wc;

ASSERT(NULL != ghInst);

ghAccelTable = LoadAccelerators(ghInst, MAKEINTRESOURCE(ID_APPACCEL));

LoadString(ghInst, IDS_APP_TITLE, szTitle, CCHMAX(szTitle));

wc.style         = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc   = (WNDPROC)WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = ghInst;
wc.hIcon         = LoadIcon(ghInst, MAKEINTRESOURCE(ICO_MAIN));
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName  = MAKEINTRESOURCE(IDM_APP);
wc.lpszClassName = gszAppClassName;

// Register the window class and return FALSE if unsuccesful.
if (!RegisterClass(&wc))
return FALSE;

// Create a main window for this application instance.
ghwndMain = CreateWindow(gszAppClassName, szTitle, WS_OVERLAPPEDWINDOW,
0, 0, 400, 300,
NULL, NULL, ghInst, NULL);
if (ghwndMain == NULL)
return FALSE;

ghMenu = GetMenu(ghwndMain);
return (NULL != ghMenu);
}

//****************************************************************************
//
// BOOL FInitApp(LPSTR lpszCmd, int nCmdShow)
//
// Initialize the application after checking command line, etc.
//
//****************************************************************************

BOOL FInitApp(LPSTR lpszCmd, int nCmdShow)
{
InitCommonControls();

 if (FAILED(CoInitialize(NULL)))
return FALSE;

if (!FInitPref())
return FALSE;

if (!FInitMain())
return FALSE;

if (!FCreateMsgWindow())
return FALSE;

if (!FCreateUserList())
return FALSE;

if (!FCreateAppWindow())
return FALSE;

ASSERT(NULL != ghwndMain);
ShowWindow(ghwndMain, nCmdShow);
UpdateWindow(ghwndMain);

return S_OK == InitConfMgr();
}

//****************************************************************************
//
// VOID TerminateApp(void)
//
//****************************************************************************

VOID TerminateApp(void)
{
// Release all objects to which this app has a reference
FreeAllApps();
FreeAppShareChannel();
FreeAllUsers();
FreeConference();
FreeConfMgr();

    // Uninitialize OLE
    CoUninitialize();
}

//****************************************************************************
//
// int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hInstPrev, LPSTR  lpszCmd, int nCmdShow)
//
//****************************************************************************

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hInstPrev, LPSTR  lpszCmd, int nCmdShow)
{
ghInst = hInstance;

if (FInitApp(lpszCmd, nCmdShow))
{
MSG  msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, ghAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

TerminateApp();

return 0;
}


MSG.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       MSG.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

// from cmd.cpp
VOID CmdExit(void);
LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

//****************************************************************************
//
// LRESULT MsgDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Handle a WM_DESTROY message
//
//****************************************************************************

LRESULT MsgDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PostQuitMessage(0);
return 0;
}

//****************************************************************************
//
// LRESULT MsgClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Handle a WM_CLOSE message
//
//****************************************************************************

LRESULT MsgClose(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
FreeAllUsers();
CmdExit();
return 0;
}

//****************************************************************************
//
// LRESULT MsgSize(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Handle a WM_SIZE message
//
//****************************************************************************

LRESULT MsgSize(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int dxClient = LOWORD(lParam);  // width of client area
int dyClient = HIWORD(lParam);  // height of client area

SizeMsgWindow(dxClient, dyClient);
return 0;
}

//****************************************************************************
//
// LRESULT MsgGetMinMax(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT MsgGetMinMax(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPMINMAXINFO lpminmax = (LPMINMAXINFO) lParam;

lpminmax->ptMinTrackSize.x = 325;
lpminmax->ptMinTrackSize.y = 250;

return 0;
}

//****************************************************************************
//
// LRESULT MsgNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT MsgNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (((LPNMHDR) lParam)->idFrom)
        {
    case IDW_APP:
        return MsgNotifyAppList(hwnd, uMsg, wParam, lParam);
    default:
    break;
        }

    return 0;
}

////////////////////////////////////////////////////////////////////////////
// Main window message table definition.
static MSD _rgmsdMain[] =
{
{WM_COMMAND,       MsgCmdMain},
{WM_DESTROY,       MsgDestroy},
{WM_CLOSE,         MsgClose},
{WM_SIZE,          MsgSize},
{WM_GETMINMAXINFO, MsgGetMinMax},
{WM_NOTIFY,        MsgNotify},

{0, NULL}  // MUST be the last entry
};

//****************************************************************************
//
// LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
// Main Window proc
//
//****************************************************************************

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
for (LPMSD lpmsd = _rgmsdMain; 0 != lpmsd->uMsg; lpmsd++)
{
if (lpmsd->uMsg == uMsg)
{
return lpmsd->pfnmsg(hwnd, uMsg, wParam, lParam);
}
}

return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


PRECOMP.H

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       PRECOMP.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#ifndef _PRECOMP_H
#define _PRECOMP_H

// System Include files
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <tchar.h>
#include <ocidl.h>

#include "imsconf2.h"

#include "global.h"
#include "clUtil.h"
#include "cConf.h"
#include "cShare.h"
#include "cMemb.h"

#include "resource.h"

#endif /* _PRECOMP_H */


CCONF.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       CCONF.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

// Global Varaiables
INmManager * g_pMgr  = NULL;             // The Conference Manager
CMgrNotify * g_pMgrNotify = NULL;        // Notifications for the Manager
INmConference * g_pConference = NULL;    // The Current Conference
CConfNotify * g_pConferenceNotify =NULL; // Notifications for the Conference

// Local Application Guid : {D29A8C52-774F-11d0-8B1D-00A0C91BC90E}
const GUID g_guidApp =
{ 0xd29a8c52, 0x774f, 0x11d0, { 0x8b, 0x1d, 0x0, 0xa0, 0xc9, 0x1b, 0xc9, 0xe} };

//****************************************************************************
//
// HRESULT InitConfMgr(void)
//
//****************************************************************************
HRESULT InitConfMgr(void)
{
HRESULT hr;
LPCLASSFACTORY pcf;

    // Notify the system we want to use the conferencing services
    // by creating a conference manager object
    hr = CoGetClassObject(CLSID_NmManager,
                          CLSCTX_INPROC,
                          NULL,
                          IID_IClassFactory,
                          (void**)&pcf);
    if (SUCCEEDED(hr))
    {
        // Get the conference manager object
        hr = pcf->CreateInstance(NULL, IID_INmManager, (void**)&g_pMgr);
    if (SUCCEEDED(hr))
        {
            // Connect to the conference manager object
            g_pMgrNotify = new CMgrNotify();
            if (NULL != g_pMgrNotify)
            {
                hr = g_pMgrNotify->Connect(g_pMgr);
    if (SUCCEEDED(hr))
                {
                ULONG uchCaps = NMCH_ALL;
                ULONG uOptions = 0;
hr = g_pMgr->Initialize(&uOptions, &uchCaps);
}
            }
        }

        pcf->Release();
    }

return hr;
}

//****************************************************************************
//
// VOID FreeConfMgr(void)
//
//****************************************************************************

VOID FreeConfMgr(void)
{
// Release conference manager notify
if (NULL != g_pMgrNotify)
{
g_pMgrNotify->Disconnect();
g_pMgrNotify->Release();
g_pMgrNotify = NULL;
}

// Release conference manager
if (NULL != g_pMgr)
{
g_pMgr->Release();
g_pMgr = NULL;
}
}

//****************************************************************************
//
// VOID FreeConference(void)
//
//****************************************************************************

VOID FreeConference(void)
{
if (NULL != g_pConferenceNotify)
{
g_pConferenceNotify->Disconnect();
g_pConferenceNotify->Release();
g_pConferenceNotify = NULL;
}

if (NULL != g_pConference)
{
g_pConference->Release();
g_pConference = NULL;
}
}

//****************************************************************************
//
// CLASS CMgrNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CMgrNotify::CMgrNotify() : RefCount(), CNotify()
{
}

//****************************************************************************
//
// Destructor
//
//****************************************************************************

CMgrNotify::~CMgrNotify()
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CMgrNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CMgrNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CMgrNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
}
else if (riid == IID_INmManagerNotify)
{
*ppvObject = (INmManagerNotify *)this;
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CMgrNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmManagerNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CMgrNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from INmManagerNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CMgrNotify::NmUI(CONFN confn)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CMgrNotify::CallCreated(INmCall *pCall)
{
return S_OK;
}

// As soon as a conference is available, create the data channel
HRESULT STDMETHODCALLTYPE CMgrNotify::ConferenceCreated(INmConference *pConference)
{
ASSERT(NULL == g_pConference);
ASSERT(NULL == g_pChannelAppShare);

HookConference(pConference);

return S_OK;
}

//****************************************************************************
//
// HRESULT HookConference(INmConference * pConference)
//
//****************************************************************************

HRESULT HookConference(INmConference * pConference)
{
HRESULT hr;

ASSERT(NULL != pConference);
ASSERT(NULL == g_pConference);
g_pConference = pConference;

pConference->AddRef();

    // Connect to the conference object
    ASSERT(NULL == g_pConferenceNotify)
    g_pConferenceNotify = new CConfNotify();
    if (NULL == g_pConferenceNotify)
    {
        hr = E_OUTOFMEMORY;
    }
    else
    {
hr = g_pConferenceNotify->Connect(pConference);
if (FAILED(hr))
{
g_pConferenceNotify->Release();
g_pConferenceNotify = NULL;
}
}

return hr;
}

//****************************************************************************
//
// CLASS CConfNotify
//
//****************************************************************************

CConfNotify::CConfNotify() : RefCount(), CNotify()
{
}

CConfNotify::~CConfNotify()
{
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CConfNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CConfNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CConfNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
}
else if (riid == IID_INmConferenceNotify)
{
*ppvObject = (INmConferenceNotify *)this;
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmConferenceNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CConfNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from IConfNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::NmUI(CONFN uNotify)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE CConfNotify::StateChanged(NM_CONFERENCE_STATE uState)
{
if (NULL == g_pConference)
return S_OK; // weird

switch (uState)
{
case NM_CONFERENCE_ACTIVE:
DisplayMsg(TEXT("Conference is active"));
break;

case NM_CONFERENCE_INITIALIZING:
break; // can't do anything just yet

case NM_CONFERENCE_WAITING:
DisplayMsg(TEXT("Waiting in Host Mode"));
break;

case NM_CONFERENCE_IDLE:
FreeAppShareChannel();
FreeAllUsers();
FreeConference();
DisplayMsg(TEXT("No conference"));
break;
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE CConfNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
switch (uNotify)
{
case NM_MEMBER_ADDED:
AddMember(pMember);
break;
case NM_MEMBER_REMOVED:
RemoveMember(pMember);
break;
default:
break;
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE CConfNotify::ChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel *pChannel)
{
switch (uNotify)
{
case NM_CHANNEL_ADDED:
HookChannel(pChannel);
break;
case NM_CHANNEL_REMOVED:
UnHookChannel(pChannel);
break;
default:
break;
}

return S_OK;
}


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by res.rc
//
#define IDS_APP_TITLE                   1
#define IDS_VERSION                     2
#define IDS_SYSMENU                     4
#define ICO_MAIN                        101
#define IDM_APP                         104
#define ID_APPACCEL                     105
#define IDD_OPTIONS                     123
#define IDM_SAVE_AS                     40001
#define IDM_EXIT                        40002
#define IDM_VIEW_FONT                   40011
#define IDM_VIEW_CLEAR                  40012
#define IDM_OPTIONS                     40013
#define IDM_EJECT                       40021
#define IDM_HELP                        40090
#define IDM_HELP_CONTENTS               40091
#define IDM_ABOUT                       40092
#define IDM_VIEW_NAME                   40142
#define IDM_VIEW_TIME                   40143
#define IDM_VIEW_USERS                  40144
#define IDM_CALL                        40145
#define IDM_REFRESH                     40146
#define IDM_WORK_ALONE                  40147
#define IDM_COLLABORATE                 40148
#define IDM_IN_CONTROL                  40149
#define IDM_SHARE                       40150
#define IDM_VIEW_SBAR                   40151
#define IDM_VIEW_USERLIST               40152
#define IDM_VIEW_MSG                    40153

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        125
#define _APS_NEXT_COMMAND_VALUE         40154
#define _APS_NEXT_CONTROL_VALUE         1028
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


UTIL.CPP

//****************************************************************************
//  Module:     NMSHARE.EXE
//  File:       UTIL.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// VOID ClearText(void)
//
//****************************************************************************

VOID ClearText(void)
{
SendMessage(ghwndMsg, LB_RESETCONTENT, 0, 0);
}

//****************************************************************************
//
// VOID DisplayMsg(LPTSTR pszMsg)
//
//****************************************************************************

VOID DisplayMsg(LPTSTR pszMsg)
{
SendMessage(ghwndMsg, LB_ADDSTRING, 0, (LPARAM) pszMsg);
PostMessage(ghwndMsg, WM_VSCROLL, SB_BOTTOM, 0);
}

#ifdef NOTUSED

//****************************************************************************
//
// VOID SizeStatus(int dxClient, int dy)
//
//****************************************************************************

VOID SizeStatus(int dxClient, int dy)
{
int rgdxp[3];

rgdxp[2] = dxClient;
dxClient -= 50;
rgdxp[1] = dxClient;
dxClient -= 75;
rgdxp[0] = dxClient;
SendMessage(ghwndSbar, SB_SETPARTS, (WPARAM) 3, (LPARAM) rgdxp);
}
#endif

//****************************************************************************
//
// VOID SizeMsgWindow(int dx, int dy)
//
//****************************************************************************

VOID SizeMsgWindow(int dx, int dy)
{
int dyT = dy;
int dxLeft = XP_LEFT;

if (gPref.fMsgWindow)
{
dyT = dy / 2;
MoveWindow(ghwndMsg, XP_LEFT, dy - dyT, dx, dyT, TRUE);
dy -= (YP_TOP + dyT);
}

if (gPref.fUserList)
{
MoveWindow(ghwndUser, XP_LEFT, YP_TOP, DXP_USER, dyT, TRUE);
dxLeft += DXP_USER;
dx -= DXP_USER;
}

MoveWindow(ghwndApp, dxLeft, YP_TOP, dx, dy, TRUE);
}

//****************************************************************************
//
// VOID RecalcMsgWindow(void)
//
//****************************************************************************

VOID RecalcMsgWindow(void)
{
RECT rc;

GetClientRect(ghwndMain, &rc);
SizeMsgWindow(rc.right, rc.bottom);
}

//****************************************************************************
//
// VOID ShowHwnd(HWND hwnd, BOOL fShow, int idm)
//
// Show/Hide a window
//
//****************************************************************************

VOID ShowHwnd(HWND hwnd, BOOL fShow, int idm)
{
if (IsWindow(hwnd))
{
ShowWindow(hwnd, fShow ? SW_SHOW : SW_HIDE);
SetMenuCheck(idm, fShow);
RecalcMsgWindow();
}
}

//****************************************************************************
//
// LPVOID LpvAlloc(int cb)
//
// Return a pointer to an allocated array of bytes
//
//****************************************************************************

LPVOID LpvAlloc(int cb)
{
return LocalAlloc(LMEM_FIXED, cb);
}

LPTSTR PszAlloc(int cch)
{
return (LPTSTR) LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cch+1));
}

//****************************************************************************
//
// VOID FreePlpv(LPVOID plpv)
//
// Free the data pointed to by plpv and set *plpv to NULL
//
//****************************************************************************

VOID FreePlpv(LPVOID plpv)
{
if ((plpv == NULL) || (*(VOID FAR * FAR *)plpv == NULL)) {
return;
}

LocalFree(*(VOID FAR * FAR *)plpv);
*(VOID FAR * FAR *)plpv = NULL;
}

//****************************************************************************
//
// VOID MaybeDeleteObject(HGDIOBJ * phgdi)
//
// Check usage count, delete if we can
//
//****************************************************************************

VOID MaybeDeleteObject(HGDIOBJ * phgdi)
{
if (*phgdi == hgdiNil)
return;

DeleteObject(*phgdi);
*phgdi = hgdiNil;
}

//****************************************************************************
//
// VOID SetMenuCheck(UINT idm, BOOL fCheck)
//
// Set the menu item's check mark.
//
//****************************************************************************

VOID SetMenuCheck(UINT idm, BOOL fCheck)
{
CheckMenuItem(ghMenu, idm,
fCheck ? (MF_CHECKED  | MF_BYCOMMAND) : (MF_UNCHECKED | MF_BYCOMMAND));
}


README.TXT

------------------------------------------------------------------
         Microsoft NetMeeting SDK 2.1 NMUI Readme File
                           November 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE NMUI SAMPLE
BUILDING THE NMUI SAMPLE

OVERVIEW OF THE NMUI SAMPLE
===========================

NMUI is a Windows sample that demonstrates how easy it is to replace
the standard NetMeeting user interface with another.  This sample uses
the NetMeeting OCX to provide conference management, file transfer,
application sharing capabilites.

BUILDING THE NMUI SAMPLE
========================

Building the sample application requires VC++ Version 5.0 or later.

To build the sample from Visual C++, open the project makefile (nmui.mak)
as a Microsoft VC++ project, and build as usual.  The NetMeeting SDK
directories must be added to your VC++ environment.

If your DOS environment is correctly configured, you can build the sample
from the command line by typing "nmake".  The DOS path, include and lib
environment variables must point to the VC installation and the
NetMeeting SDK directories.

Notes:


CCALL.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CCALL.CPP
//  Content:    INmCall Routines
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

COBLIST * g_pListCall = (COBLIST *) NULL;
COBLIST * g_pListCallNotify = (COBLIST *) NULL;

//****************************************************************************
//
// INmCall * GetCurrentCall(void)
//
//****************************************************************************

INmCall * GetCurrentCall(void)
{
if (NULL == g_pListCall)
return NULL;

return (INmCall *) g_pListCall->GetTail();
}

//****************************************************************************
//
// HRESULT HookCall(INmCall * pCall)
//
// Hook into notification callback for the call
//
//****************************************************************************

HRESULT HookCall(INmCall * pCall)
{
HRESULT hr;
CCallNotify * pNotify;

ASSERT(NULL != pCall);
pCall->AddRef();
AddNode(pCall, &g_pListCall);

    // Connect to the conference object
    pNotify = new CCallNotify();
    if (pNotify == NULL)
    {
        hr = E_OUTOFMEMORY;
    }
    else
    {
hr = pNotify->Connect(pCall);
if (FAILED(hr))
{
ERROR_MSG("CConfNotify Failed (%s)", GetHrSz(hr));
pNotify->Release();
}
else
{
OBJECT_MSG("Call Connection point established successfully");
AddNode(pNotify, &g_pListCallNotify);
}
}

return hr;
}

//****************************************************************************
//
// VOID FreeCalls(void)
//
//****************************************************************************

VOID FreeCalls(void)
{
// Release all notify
if (NULL != g_pListCallNotify)
{
while (!g_pListCallNotify->IsEmpty())
{
CCallNotify * pNotify = (CCallNotify *) g_pListCallNotify->RemoveHead();
ASSERT(NULL != pNotify);
OBJECT_MSG("[%08X] call notify released", pNotify);
pNotify->Disconnect();
pNotify->Release();
}
}

if (NULL != g_pListCall)
{
while (!g_pListCall->IsEmpty())
{
INmCall * pCall = (INmCall *) g_pListCall->RemoveHead();
ASSERT(NULL != pCall);
OBJECT_MSG("[%08X] call released", pCall);
pCall->Release();
}
}
}

//****************************************************************************
//
// CLASS CCallNotify
//
//****************************************************************************

CCallNotify::CCallNotify() : RefCount(), CNotify()
{
OBJECT_MSG("CCallNotify created");
}

CCallNotify::~CCallNotify()
{
OBJECT_MSG("CCallNotify destroyed");
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CCallNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CCallNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CCallNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
OBJECT_MSG("CCallNotify::QueryInterface(): Returning IUnknown.");
}
else if (riid == IID_INmCallNotify)
{
*ppvObject = (INmCallNotify *)this;
OBJECT_MSG("CCallNotify::QueryInterface(): Returning INmCallNotify.");
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
OBJECT_MSG("CCallNotify::QueryInterface(): Called on unknown interface.");
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CCallNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmCallNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CCallNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from IConfNotify
//
//****************************************************************************

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CCallNotify::StateChanged(NM_CALL_STATE uState)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CCallNotify::StateChanged(NM_CALL_STATE uState)
{
NOTIFY_MSG("CCallNotify::StateChanged uState=%s", GetCallStateSz(uState));
DumpCallInfo((INmCall *) CNotify::GetPunk());
return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CCallNotify::Failed(ULONG uError)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CCallNotify::Failed(ULONG uError)
{
LOG_MSG(RGB(255, 0, 0), "CCallNotify::Failed uError=%08X", uError);
return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CCallNotify::Accepted(INmConference * pConference)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CCallNotify::Accepted(INmConference * pConference)
{
NOTIFY_MSG("CCallNotify::Accepted");
DumpConferenceInfo(pConference);
return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CCallNotify::NmUI(CONFN uNotify)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CCallNotify::NmUI(CONFN uNotify)
{
LOG_MSG(RGB(255, 0, 255), "CCallNotify::NmUI uNotify=%s", GetConfnSz(uNotify));
return S_OK;
}

//****************************************************************************
//
// VOID DoCallConference(void)
//
//****************************************************************************

VOID DoCallConference(void)
{
HRESULT hr;
BSTRING bstrAddr(gPref.pszAddr);
BSTRING bstrConference(gPref.pszConferenceName);
BSTRING bstrPassword(gPref.pszPassword);
INmCall * pCall;
INmConference * pConference;

// Validate system
if (NULL == g_pMgr)
{
ERROR_MSG("No conference manager object");
return;
}

pConference = GetCurrentConference();
if (NULL != pConference)
{
WARN_MSG("can't join conferences!");
}

hr = g_pMgr->CallConference(&pCall, gPref.nmCallType, gPref.nmAddrType,
bstrAddr, bstrConference, bstrPassword);

if (FAILED(hr))
{
ERROR_MSG("CallConference failed HResult=%s", GetHrSz(hr));
}
else
{
ASSERT(pCall == GetCurrentCall());
pCall->Release();
TRACE_MSG("CallConference succeeded");
}
}

//****************************************************************************
//
// VOID DoCallCreate(void)
//
//****************************************************************************

VOID DoCallCreate(void)
{
HRESULT hr;
BSTRING bstrAddr(gPref.pszAddr);
INmCall * pCall;
INmConference * pConference;

// Validate system
if (NULL == g_pMgr)
{
ERROR_MSG("No conference manager object");
return;
}

pConference = GetCurrentConference();
if (NULL == pConference)
{
TRACE_MSG("using no conference");
}

hr = g_pMgr->CreateCall(&pCall, gPref.nmCallType, gPref.nmAddrType, bstrAddr, pConference);
if (FAILED(hr))
{
ERROR_MSG("CreateCall failed HResult=%s", GetHrSz(hr));
}
else
{
ASSERT(pCall == GetCurrentCall());
pCall->Release();
TRACE_MSG("CreateCall succeeded");
}
}

//****************************************************************************
//
// VOID DoHandleCall(BOOL fAccept)
//
//****************************************************************************

VOID DoHandleCall(BOOL fAccept)
{
HRESULT hr;
INmCall * pCall = GetCurrentCall();

if (NULL == pCall)
{
TRACE_MSG("No call to handle?");
return;
}

if (fAccept)
{
hr = pCall->Accept();
}
else
{
hr = pCall->Reject();
}

TRACE_MSG("Call result = %s", GetHrSz(hr));
}

//****************************************************************************
//
// VOID DoCancelCall(void)
//
//****************************************************************************

VOID DoCancelCall(void)
{
HRESULT hr;
INmCall * pCall = GetCurrentCall();

if (NULL == pCall)
{
TRACE_MSG("No call to destroy?");
return;
}

hr = pCall->Cancel();
TRACE_MSG("Call Cancel result = %s", GetHrSz(hr));

// Free just the single call - watch the notify proc
FreeCalls();
}


CCALL.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CCALL.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CCallNotify
//
//****************************************************************************

class CCallNotify : public RefCount, public CNotify, public INmCallNotify
{
public:
CCallNotify();
~CCallNotify();

// INmCallNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE StateChanged(NM_CALL_STATE uState);
HRESULT STDMETHODCALLTYPE Failed(ULONG uError);
HRESULT STDMETHODCALLTYPE Accepted(INmConference *pConference);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

extern COBLIST * g_pListCall;
extern COBLIST * g_pListCallNotify;

//****************************************************************************
//
// Global Routines
//
//****************************************************************************

HRESULT HookCall(INmCall * pCall);
VOID DoCallCreate(void);
VOID DoCallConference(void);
VOID DoCancelCall(void);
VOID DoHandleCall(BOOL fAccept);
VOID FreeCalls(void);
INmCall * GetCurrentCall(void);


CCHAN.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CCHAT.CPP
//  Content:    INmChannel Routines
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

COBLIST * g_pListChannel = (COBLIST *) NULL;
COBLIST * g_pListChannelNotify = (COBLIST *) NULL;

INmFt * g_pFt = NULL;
INmChannelAppShare * g_pChannelShare = NULL;

//****************************************************************************
//
// HRESULT HookChannel(INmChannel * pChannel)
//
// Hook into notification callback for the channel
//
//****************************************************************************

HRESULT HookChannel(INmChannel * pChannel)
{
HRESULT hr;
CChannelNotify * pNotify;

ULONG uch;
pChannel->GetNmch(&uch);
TRACE_MSG("[%08X] - Channel=[%s] Added", pChannel, GetNmchSz(uch));

ASSERT(NULL != pChannel);
pChannel->AddRef();
AddNode(pChannel, &g_pListChannel);

    pNotify = new CChannelNotify();
    if (pNotify == NULL)
    {
        hr = E_OUTOFMEMORY;
    }
    else
    {
hr = pNotify->Connect(pChannel);
if (FAILED(hr))
{
WARN_MSG("CConfNotify Failed (%s)", GetHrSz(hr));
pNotify->Release();
}
else
{
OBJECT_MSG("Conference Connection point established successfully");
AddNode(pNotify, &g_pListChannelNotify);
}
}

if (SUCCEEDED(hr))
{
ULONG uch;
if (SUCCEEDED(pChannel->GetNmch(&uch)))
{
switch (uch)
{
case NMCH_SHARE:
{
if (NULL != g_pChannelShare)
{
WARN_MSG("Replacing App Sharing Channel");
g_pChannelShare->Release();
g_pChannelShare = NULL;
}
hr = pChannel->QueryInterface(IID_INmChannelAppShare, (void **)&g_pChannelShare);
if (FAILED(hr))
{
ERROR_MSG("HookData:Unable to get Data Channel");
}
break;
}
case NMCH_DATA:
HookData(pChannel);
break;
default:
break;
}
}
}

return hr;
}

//****************************************************************************
//
// VOID UnHookChannel(INmChannel * pChannel)
//
//****************************************************************************

VOID UnHookChannel(INmChannel * pChannel)
{
POSITION pos;
POSITION posSav;
IUnknown * pUnk = pChannel;

ULONG uch;
pChannel->GetNmch(&uch);
TRACE_MSG("[%08X] - Channel=[%s] Removed", pChannel, GetNmchSz(uch));

// Search for the notify object responsible for this channel
ASSERT(NULL != g_pListChannelNotify);
for (pos = g_pListChannelNotify->GetHeadPosition(); NULL != pos; )
{
posSav = pos;
CChannelNotify * pNotify = (CChannelNotify *) g_pListChannelNotify->GetNext(pos);
ASSERT(NULL != pNotify);

if (S_OK == pChannel->IsSameAs((INmChannel *) pNotify->GetPunk()))
{
g_pListChannelNotify->RemoveAt(posSav);
pNotify->Disconnect();
pNotify->Release();
break;
}
}

// Search for the channel in the list
ASSERT(NULL != g_pListChannel);
for (pos = g_pListChannel->GetHeadPosition(); NULL != pos; )
{
posSav = pos;
if (S_OK == pChannel->IsSameAs((INmChannel *) g_pListChannel->GetNext(pos)))
{
g_pListChannel->RemoveAt(posSav);
pChannel->Release();
break;
}
}

OBJECT_MSG("Channel connection point removed");
}

//****************************************************************************
//
// VOID UpdateChannelState(INmChannel * pChannel)
//
//****************************************************************************

VOID UpdateChannelState(INmChannel * pChannel)
{
HRESULT hr;
ULONG nmch;
BOOL  fActive;
ULONG cMember;

hr = pChannel->GetNmch(&nmch);
if (FAILED(hr))
{
return;
}
fActive = S_OK == pChannel->IsActive();
pChannel->GetMemberCount(&cMember);
TRACE_MSG("%s channel %08X is %s with %d members", GetNmchSz(nmch), pChannel,
fActive ? TEXT("Active") : TEXT("Inactive"), cMember);

switch (nmch)
{
case NMCH_DATA:
{
INmChannelData * pChannelData;
hr = pChannel->QueryInterface(IID_INmChannelData, (void **)&pChannelData);
if (FAILED(hr))
return;
if (pChannelData != g_pChannelData)
{
WARN_MSG("second data channel?");
}
SetMenuCheck(IDM_CHANNEL_DATA, fActive);
pChannelData->Release();
break;
}
case NMCH_AUDIO:
case NMCH_VIDEO:
break;
case NMCH_SHARE:
{
if (fActive)
break;

if (NULL == g_pChannelShare)
break;
g_pChannelShare->Release();
g_pChannelShare = NULL;
break;
}
break;
default:
break;
}
}

//****************************************************************************
//
// VOID FreeChannels(void)
//
//****************************************************************************

VOID FreeChannels(void)
{
// Release all notify
if (NULL != g_pListChannelNotify)
{
while (!g_pListChannelNotify->IsEmpty())
{
CChannelNotify * pNotify = (CChannelNotify *) g_pListChannelNotify->RemoveHead();
ASSERT(NULL != pNotify);
OBJECT_MSG("[%08X] channel notify released", pNotify);
pNotify->Disconnect();
pNotify->Release();
}
}

if (NULL != g_pListChannel)
{
while (!g_pListChannel->IsEmpty())
{
INmChannel * pChannel = (INmChannel *) g_pListChannel->RemoveHead();
ASSERT(NULL != pChannel);
OBJECT_MSG("[%08X] channel released", pChannel);
pChannel->Release();
}
}
}

//****************************************************************************
//
// CLASS CChannelNotify
//
//****************************************************************************

CChannelNotify::CChannelNotify() : RefCount(), CNotify()
{
OBJECT_MSG("CChannelNotify created");
}

CChannelNotify::~CChannelNotify()
{
OBJECT_MSG("CChannelNotify destroyed");
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CChannelNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CChannelNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CChannelNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
OBJECT_MSG("CChannelNotify::QueryInterface(): Returning IUnknown.");
}
else if (riid == IID_INmChannelNotify)
{
*ppvObject = (INmChannelNotify *)this;
OBJECT_MSG("CChannelNotify::QueryInterface(): Returning INmChannelNotify.");
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
OBJECT_MSG("CChannelNotify::QueryInterface(): Called on unknown interface.");
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CChannelNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmChannelNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CChannelNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from IConfNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CChannelNotify::NmUI(CONFN uNotify)
{
NOTIFY_MSG("CChannelNotify::NmUI uNotify=%08X, pChannel=%08X", uNotify, CNotify::GetPunk());
return S_OK;
}

HRESULT STDMETHODCALLTYPE CChannelNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
NOTIFY_MSG("CChannelNotify::MemberChanged uNotify=%d pMember=%08X, pChannel=%08X", uNotify, pMember, CNotify::GetPunk());

PUSER pUser = PUserFromINmMember(pMember);
if (NULL != pUser)
{
UpdateUserChannel(pUser, (INmChannel *) (CNotify::GetPunk()), uNotify);
}

return S_OK;
}

//****************************************************************************
//
// VOID SendFile(INmMember * pMember)
//
//****************************************************************************

VOID SendFile(INmMember * pMember)
{
PUSER pUser = PUserLocal();
if (NULL == pUser)
{
return;
}

if (NULL == pUser->m_pChannelFt)
{
return;
}

INmFt * pFt;
BSTRING bstrFileName(gPref.pszFileName);
HRESULT hr = pUser->m_pChannelFt->SendFile(&pFt, pMember, bstrFileName, 0);
if (SUCCEEDED(hr))
{
FreeFt();
g_pFt = pFt;
}
}

//****************************************************************************
//
// VOID DoFtCancel(void)
//
//****************************************************************************

VOID DoFtCancel(void)
{
if (NULL == g_pFt)
return;

HRESULT hr = g_pFt->Cancel();
}

//****************************************************************************
//
// VOID FreeFt(void)
//
//****************************************************************************

VOID FreeFt(void)
{
if (NULL != g_pFt)
{
g_pFt->Release();
g_pFt = NULL;
}
}

//****************************************************************************
//
// VOID FreeAs(void)
//
//****************************************************************************

VOID FreeAs(void)
{
if (NULL != g_pChannelShare)
{
g_pChannelShare->Release();
g_pChannelShare = NULL;
}
}


CCHAN.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CCHAN.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CChannelNotify
//
//****************************************************************************

class CChannelNotify : public RefCount, public CNotify, public INmChannelNotify
{
public:
CChannelNotify();
~CChannelNotify();

// INmChannelNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

extern COBLIST * g_pListChannel;
extern COBLIST * g_pListChannelNotify;
extern INmChannelAppShare * g_pChannelShare;

//****************************************************************************
//
// Global Routines
//
//****************************************************************************

HRESULT HookChannel(INmChannel * pChannel);
VOID UnHookChannel(INmChannel * pChannel);
VOID UpdateChannelState(INmChannel * pChannel);
VOID FreeChannels(void);
VOID FreeFt(void);
VOID FreeAs(void);

VOID SendFile(INmMember * pMember);
VOID DoFtCancel(void);


CCONF.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CCONF.CPP
//  Content:    INmConference Routines
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

COBLIST * g_pListConference = (COBLIST *) NULL;
COBLIST * g_pListConferenceNotify = (COBLIST *) NULL;

//****************************************************************************
//
// INmConference * GetCurrentConference(void)
//
//****************************************************************************

INmConference * GetCurrentConference(void)
{
if (NULL == g_pListConference)
return NULL;

return (INmConference *) g_pListConference->GetTail();
}

//****************************************************************************
//
// HRESULT HookConference(INmConference * pConference)
//
//****************************************************************************

HRESULT HookConference(INmConference * pConference)
{
HRESULT hr;
CConfNotify * pNotify;

ASSERT(NULL != pConference);
pConference->AddRef();
AddNode(pConference, &g_pListConference);

    // Connect to the conference object
    pNotify = new CConfNotify();
    if (NULL == pNotify)
    {
        hr = E_OUTOFMEMORY;
        ERROR_MSG("Unable to allocate CConfNotify");
    }
    else
    {
hr = pNotify->Connect(pConference);
if (FAILED(hr))
{
TRACE_MSG("CConfNotify Failed (%s)", GetHrSz(hr));
pNotify->Release();
pNotify = NULL;
}
else
{
TRACE_MSG("Conference Connection point established successfully");
AddNode(pNotify, &g_pListConferenceNotify);
}
}

if (SUCCEEDED(hr))
{
UpdateStatusIcon(ICO_PHONE);
}

return hr;
}

//****************************************************************************
//
// VOID FreeConferences(void)
//
//****************************************************************************

VOID FreeConferences(void)
{
// Release all notify
if (NULL != g_pListConferenceNotify)
{
while (!g_pListConferenceNotify->IsEmpty())
{
CConfNotify * pNotify = (CConfNotify *) g_pListConferenceNotify->RemoveHead();
ASSERT(NULL != pNotify);
OBJECT_MSG("[%08X] conference notify released", pNotify);
pNotify->Disconnect();
pNotify->Release();
}
}

if (NULL != g_pListConference)
{
while (!g_pListConference->IsEmpty())
{
INmConference * pConference = (INmConference *) g_pListConference->RemoveHead();
ASSERT(NULL != pConference);
OBJECT_MSG("[%08X] conference released", pConference);
pConference->Release();
}
}

UpdateStatusIcon(NULL);
}

//****************************************************************************
//
// VOID DoConferenceCreate(void)
//
// Create a local conference object
//
//****************************************************************************

VOID DoConferenceCreate(void)
{
HRESULT hr;
INmConference * pConference;

// Validate system
if (NULL == g_pMgr)
{
ERROR_MSG("No conference manager object");
return;
}
if (NULL != GetCurrentConference())
{
WARN_MSG("Attempting to create second conference");
}

// Create Local Conference
BSTRING bstrName(gPref.pszConferenceName);

hr = g_pMgr->CreateConference(&pConference, bstrName, NULL, NMCH_ALL);
if (SUCCEEDED(hr))
{
if (NULL != pConference)
{
hr = pConference->Host();
if (FAILED(hr))
{
WARN_MSG("Failed to host conference");
}
// Release here, because we always AddRef in INmManager.ConferenceCreated
ASSERT(pConference == GetCurrentConference());
pConference->Release();
}
TRACE_MSG("CreateConference succeeded");
}
else
{
ERROR_MSG("CreateConference failed HResult=%s", GetHrSz(hr));
}
}

//****************************************************************************
//
// VOID DoConferenceLeave(void)
//
//****************************************************************************

VOID DoConferenceLeave(void)
{
HRESULT hr;
INmConference * pConference;

pConference = GetCurrentConference();
if (NULL == pConference)
{
ERROR_MSG("No conference object");
return;
}

hr = pConference->Leave();
if (FAILED(hr))
{
ERROR_MSG("Conference Leave hresult=%s", GetHrSz(hr));
}
else
{
TRACE_MSG("Conference Leave successful...");
}
}

//****************************************************************************
//
// CLASS CConfNotify
//
//****************************************************************************

CConfNotify::CConfNotify() : RefCount(), CNotify()
{
OBJECT_MSG("CConfNotify created");
}

CConfNotify::~CConfNotify()
{
OBJECT_MSG("CConfNotify destroyed");
}

//****************************************************************************
//
// Methods from IUknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CConfNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CConfNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CConfNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
OBJECT_MSG("CConfNotify::QueryInterface(): Returning IUnknown.");
}
else if (riid == IID_INmConferenceNotify)
{
*ppvObject = (INmConferenceNotify *)this;
OBJECT_MSG("CConfNotify::QueryInterface(): Returning INmConferenceNotify.");
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
OBJECT_MSG("CConfNotify::QueryInterface(): Called on unknown interface.");
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmConferenceNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CConfNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from IConfNotify
//
//****************************************************************************

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CConfNotify::NmUI(CONFN uNotify)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::NmUI(CONFN uNotify)
{
LOG_MSG(RGB(255, 0, 255), "INmConference.NmUI CONFN=%s", GetConfnSz(uNotify));
return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CConfNotify::StateChanged(NM_CONFERENCE_STATE uState)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::StateChanged(NM_CONFERENCE_STATE uState)
{
NOTIFY_MSG("INmConference.StateChanged uState=%s", GetConferenceStateSz(uState));

switch (uState)
{
case NM_CONFERENCE_ACTIVE:
{
if (gPref.fAutoChat)
{
DoCreateDataChannel(GetCurrentConference());
}
break;
}
case NM_CONFERENCE_IDLE:
{
FreeDataChannel();
FreeAllUsers();
FreeChannels();
break;
}
default:
break;
} /* switch (uState) */

return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CConfNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
NOTIFY_MSG("INmConference.MemberChanged uNotify=%d", uNotify);
DumpMemberInfo(pMember);

switch (uNotify)
{
case NM_MEMBER_ADDED:
AddMember(pMember);
break;
case NM_MEMBER_REMOVED:
RemoveMember(pMember);
break;
default:
break;
}

return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CConfNotify::ChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel *pChannel)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CConfNotify::ChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel *pChannel)
{
NOTIFY_MSG("INmConference.ChannelChanged uNotify=%d", uNotify);
DumpChannelInfo(pChannel);

switch (uNotify)
{
case NM_CHANNEL_ADDED:
HookChannel(pChannel);
break;
case NM_CHANNEL_REMOVED:
UnHookChannel(pChannel);
break;
case NM_CHANNEL_UPDATED:
UpdateChannelState(pChannel);
break;
default:
break;
}

return S_OK;
}


CCONF.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CCONF.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CConfNotify
//
//****************************************************************************

class CConfNotify : public RefCount, public CNotify, public INmConferenceNotify
{
public:
CConfNotify();
~CConfNotify();

// INmConferenceNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE StateChanged(NM_CONFERENCE_STATE uState);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pfMember);
HRESULT STDMETHODCALLTYPE ChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel *pChannel);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

extern COBLIST * g_pListConference;
extern COBLIST * g_pListConferenceNotify;

//****************************************************************************
//
// Global Functions
//
//****************************************************************************

INmConference * GetCurrentConference(void);
VOID DoConferenceCreate(void);
VOID DoConferenceLeave(void);
VOID FreeConferences(void);
HRESULT HookConference(INmConference * pConference);


CDATA.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CDATA.CPP
//  Content:    INmChannelData Routines
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

INmChannelData * g_pChannelData;
CDataNotify * g_pNotifyData;

//****************************************************************************
//
// HRESULT HookData(INmChannel * pChannel)
//
//****************************************************************************

HRESULT HookData(INmChannel * pChannel)
{
HRESULT hr;
CDataNotify * pNotify;

ASSERT(NULL != pChannel);
if (NULL != g_pChannelData)
{
ERROR_MSG("HookData: replacing existing g_pChannelData");
}

hr = pChannel->QueryInterface(IID_INmChannelData, (void **)&g_pChannelData);
if (FAILED(hr))
{
ERROR_MSG("HookData:Unable to get Data Channel");
return hr;
}
// keep QI ref around

    // Connect to the channel object
    pNotify = new CDataNotify();
    if (pNotify == NULL)
    {
        hr = E_OUTOFMEMORY;
    }
    else
    {
hr = pNotify->Connect(g_pChannelData);
if (FAILED(hr))
{
ERROR_MSG("CConfNotify Failed (%s)", GetHrSz(hr));
pNotify->Release();
}
else
{
OBJECT_MSG("Data Connection point established successfully");
g_pNotifyData = pNotify;
}
}

if (FAILED(hr))
{
g_pChannelData->Release();
g_pChannelData = NULL;
}

return hr;
}

//****************************************************************************
//
// VOID FreeDataChannel(void)
//
//****************************************************************************

VOID FreeDataChannel(void)
{
// Release all notify
if (NULL != g_pNotifyData)
{
OBJECT_MSG("[%08X] data notify released", g_pNotifyData);
g_pNotifyData->Disconnect();
g_pNotifyData->Release();
g_pNotifyData = NULL;
}

if (NULL != g_pChannelData)
{
OBJECT_MSG("[%08X] data channel released", g_pChannelData);
g_pChannelData->Release();
g_pChannelData = NULL;
}
}

//****************************************************************************
//
// CLASS CDataNotify
//
//****************************************************************************

CDataNotify::CDataNotify() : RefCount(), CNotify()
{
OBJECT_MSG("CDataNotify created");
}

CDataNotify::~CDataNotify()
{
OBJECT_MSG("CDataNotify destroyed");
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CDataNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CDataNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CDataNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
OBJECT_MSG("CDataNotify::QueryInterface(): Returning IUnknown.");
}
else if (riid == IID_INmChannelDataNotify)
{
*ppvObject = (INmChannelDataNotify *)this;
OBJECT_MSG("CDataNotify::QueryInterface(): Returning INmChannelDataNotify.");
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
OBJECT_MSG("CDataNotify::QueryInterface(): Called on unknown interface.");
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CDataNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmChannelDataNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CDataNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CDataNotify::NmUI(CONFN uNotify)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CDataNotify::NmUI(CONFN uNotify)
{
NOTIFY_MSG("CDataNotify::NmUI uNotify=%08X, pChannel=%08X", uNotify, CNotify::GetPunk());
return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CDataNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CDataNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
NOTIFY_MSG("CDataNotify::MemberChanged uNotify=%d pMember=%08X, pChannel=%08X", uNotify, pMember, CNotify::GetPunk());

PUSER pUser = PUserFromINmMember(pMember);
if (NULL != pUser)
{
UpdateUserChannel(pUser, (INmChannel *) (CNotify::GetPunk()), uNotify);
}

return S_OK;
}

//****************************************************************************
//
// Methods from IConfNotify
//
//****************************************************************************

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CDataNotify::DataSent(INmMember *pMember, ULONG uSize, LPBYTE pb)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CDataNotify::DataSent(INmMember *pMember, ULONG uSize, LPBYTE pb)
{
LPTSTR psz;
NOTIFY_MSG("CDataNotify::DataSent uSize=%08X", uSize);

#ifdef NM2_CHAT_PROTOCOL
ASSERT(uSize > CB_NM2_HDR);
psz = (LPTSTR) (pb + *(LPDWORD) pb);  // skip past header
#else
psz = (LPTSTR) pb;
#endif /* NM2_CHAT_PROTOCOL */

DisplayChatMsg(pMember, psz, MSG_SENT);
return S_OK;
}

//****************************************************************************
//
// HRESULT STDMETHODCALLTYPE CDataNotify::DataReceived(INmMember *pMember, ULONG uSize, LPBYTE pb, ULONG dwFlags)
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CDataNotify::DataReceived(INmMember *pMember, ULONG uSize, LPBYTE pb, ULONG dwFlags)
{
LPTSTR psz;
NOTIFY_MSG("CDataNotify::DataReceived uSize=%08X", uSize);

#ifdef NM2_CHAT_PROTOCOL
ASSERT(uSize > CB_NM2_HDR);
psz = (LPTSTR) (pb + *(LPDWORD) pb);  // skip past header
#else
psz = (LPTSTR) pb;
#endif /* NM2_CHAT_PROTOCOL */

if (dwFlags & NM_DF_SEGMENT_BEGIN)
{
DisplayChatMsg(pMember, psz, MSG_RECEIVED, dwFlags);
}
else
{
WARN_MSG("Partial data received");
}

return S_OK;
}

//****************************************************************************
//
// VOID DisplayChatMsg(INmMember * pMember, PTCHAR pszMsg, int msgType, DWORD dwFlags)
//
//****************************************************************************

VOID DisplayChatMsg(INmMember * pMember, PTCHAR pszMsg, int msgType, DWORD dwFlags)
{
COLORREF cv;
TCHAR sz[MAX_PATH];

PUSER pUser = PUserFromINmMember(pMember);

switch (msgType)
{
case MSG_SENT:
{
cv = RGB(127, 0, 127);
if (NULL == pUser)
{
lstrcpy(sz, "Msg to Everyone");
}
else
{
wsprintf(sz, "Whisper to %s", pUser->pszName);
}
break;
}
case MSG_RECEIVED:
{
cv = RGB(255, 0, 255);
if (NULL == pUser)
{
lstrcpy(sz, "Message from ????");
}
else if (dwFlags & NM_DF_PRIVATE)
{
wsprintf(sz, "Whisper from %s", pUser->pszName);
}
else if (dwFlags & NM_DF_BROADCAST)
{
wsprintf(sz, "Message from %s", pUser->pszName);
}
break;
}
default:
cv = RGB(0, 0, 0);
break;
}

Log(RGB(0, 0, 0), sz);

#ifdef NM2_CHAT_PROTOCOL
#ifndef UNICODE
// Convert UNICODE to ANSI
WideCharToMultiByte(CP_ACP, 0, (LPWSTR) pszMsg, -1, sz, MAX_PATH, NULL, NULL);
pszMsg = sz;
#endif /* UNICODE */
#endif /* NM2_CHAT_PROTOCOL */

Log(cv, pszMsg);
}

//****************************************************************************
//
// VOID DoSendChatMsg(LPTSTR psz, INmMember * pMember)
//
//****************************************************************************

VOID DoSendChatMsg(LPTSTR psz, INmMember * pMember)
{
HRESULT hr;
UINT cb = lstrlen(psz) + 1; // include final null
if ((1 == cb) || (NULL == g_pChannelData))
{
return;
}

#ifdef NM2_CHAT_PROTOCOL
BYTE buffer[CB_NM2_HDR + (MAX_PATH*2)];

#ifdef UNICODE
lstrcpy(&buffer[CB_NM2_HDR], psz);
#else
cb = MultiByteToWideChar(CP_ACP, 0, psz, cb, (LPWSTR) &buffer[CB_NM2_HDR], MAX_PATH);
cb *= 2; // adjust for UNICODE
#endif /* !UNICODE */

// Add special prefix
cb += CB_NM2_HDR;
ZeroMemory(buffer, CB_NM2_HDR);
* ((LPDWORD) buffer) = CB_NM2_HDR;

psz = (LPTSTR) buffer;

#else
cb++; // include final null
#endif /* NM2_CHAT_PROTOCOL */

if ((NULL != pMember) && (S_OK == pMember->IsSelf()))
pMember = NULL; // local machine = everyone

hr = g_pChannelData->SendData(pMember, cb, (LPBYTE) psz, 0);
if (FAILED(hr))
{
ERROR_MSG("DoSendChatMsg: SendData failed: %s", GetHrSz(hr));
}
}

//****************************************************************************
//
// VOID DoCreateDataChannel(INmConference * pConference)
//
//****************************************************************************

VOID DoCreateDataChannel(INmConference * pConference)
{
INmChannelData * pChannelData;
HRESULT hr;

if (NULL == pConference)
{
WARN_MSG("No conference.");
return;
}

if (NULL != g_pChannelData)
{
WARN_MSG("Data channel already exists");
}

hr = pConference->CreateDataChannel(&pChannelData, g_guidNM2Chat);
if (FAILED(hr))
{
ERROR_MSG("CreateDataChannel: Failed hr=%s", GetHrSz(hr));
return;
}

if (NULL == g_pChannelData)
{
ERROR_MSG("DoCreateDataChannel: Failed to hook");
}
else
{
// The AddRef was done in the hook code.
pChannelData->Release();
}

TRACE_MSG("Created Data Channel");
}

//****************************************************************************
//
// VOID DoActivateDataChannel(BOOL fActive)
//
//****************************************************************************

VOID DoActivateDataChannel(BOOL fActive)
{
HRESULT hr;

if (NULL == g_pChannelData)
return;

hr = g_pChannelData->IsActive();
TRACE_MSG("Data channel is %s", (S_OK == hr) ? "Active" : "Inactive");

hr = g_pChannelData->SetActive(fActive);
TRACE_MSG("Data channel SetActive Result=%s", GetHrSz(hr));
}


CDATA.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CDATA.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CDataNotify
//
//****************************************************************************

class CDataNotify : public RefCount, public CNotify, public INmChannelDataNotify
{
public:
CDataNotify();
~CDataNotify();

// INmChannelDataNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember * pMember);
HRESULT STDMETHODCALLTYPE DataSent(INmMember *pMember, ULONG uSize, LPBYTE pb);
HRESULT STDMETHODCALLTYPE DataReceived(INmMember *pMember, ULONG uSize, LPBYTE pb, ULONG dwFlags);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

extern INmChannelData * g_pChannelData;
extern CDataNotify    * g_pNotifyData;

//****************************************************************************
//
// Global Routines
//
//****************************************************************************

HRESULT HookData(INmChannel * pData);
VOID DoCreateDataChannel(INmConference *pConference);
VOID FreeDataChannel(void);

//****************************************************************************
//
// msgType values
//
//****************************************************************************

#define MSG_SENT 1
#define MSG_RECEIVED 2
VOID DisplayChatMsg(INmMember * pMember, PTCHAR pszMsg, int msgType, DWORD dwFlags = 0);
VOID DoSendChatMsg(LPTSTR psz, INmMember * pMember);
VOID DoActivateDataChannel(BOOL fActive);

#ifdef NM2_CHAT_PROTOCOL
#define CB_NM2_HDR   8     // NetMeeting 2.0 chat protocol has an 8 byte header
#endif


CLUTIL.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CLUTIL.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

//****************************************************************************
//
// Constructor/Destructor
//
//****************************************************************************

RefCount::RefCount(void)
{
OBJECT_MSG("RefCount: [%08X] Created object", this);
m_cRef = 1;
}

RefCount::~RefCount(void)
{
OBJECT_MSG("RefCount: [%08X] Destroying object with m_cRef=%d", this, m_cRef);
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE RefCount::AddRef(void)
{
   ASSERT(m_cRef >= 0);

   InterlockedIncrement(&m_cRef);

   return (ULONG) m_cRef;
}

ULONG STDMETHODCALLTYPE RefCount::Release(void)
{
if (0 == InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}

ASSERT(m_cRef > 0);
return (ULONG) m_cRef;
}

//****************************************************************************
//
// CLASS CNotify
//
//****************************************************************************

//****************************************************************************
//
// Constructor/Destructor
//
//****************************************************************************

CNotify::CNotify() :
m_pcnpcnt(NULL),
    m_pcnp(NULL),
    m_dwCookie(0),
    m_pUnk(NULL)
{
}

CNotify::~CNotify()
{
Disconnect(); // Make sure we're disconnected
OBJECT_MSG("CNotify: Destroying obj=%08X", this);
}

//****************************************************************************
//
// HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
//
//****************************************************************************

HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
{
HRESULT hr;

ASSERT(0 == m_dwCookie);

// Get the connection container
hr = pUnk->QueryInterface(IID_IConnectionPointContainer, (void **)&m_pcnpcnt);
if (SUCCEEDED(hr))
{
// Find an appropriate connection point
hr = m_pcnpcnt->FindConnectionPoint(riid, &m_pcnp);
if (SUCCEEDED(hr))
{
ASSERT(NULL != m_pcnp);
// Connect the sink object
hr = m_pcnp->Advise((IUnknown *)pUnkN, &m_dwCookie);
}
}

if (FAILED(hr))
{
m_dwCookie = 0;
}
else
{
    OBJECT_MSG("Connected for obj=%08X", this);
    m_pUnk = pUnk; // keep around for caller
    }

return hr;
}

//****************************************************************************
//
// HRESULT CNotify::Disconnect (void)
//
//****************************************************************************

HRESULT CNotify::Disconnect (void)
{
    if (0 != m_dwCookie)
    {
    OBJECT_MSG("Disconnecting for obj=%08X", this);

        // Disconnect the sink object
        m_pcnp->Unadvise(m_dwCookie);
        m_dwCookie = 0;

        m_pcnp->Release();
        m_pcnp = NULL;

        m_pcnpcnt->Release();
        m_pcnpcnt = NULL;

        m_pUnk = NULL;
    }

    return S_OK;
}

//****************************************************************************
//
// CLASS COBLIST
//
//****************************************************************************

//****************************************************************************
//
// Destructor
//
//****************************************************************************

COBLIST::~COBLIST()
{
    ASSERT(IsEmpty());
}

#ifdef DEBUG
VOID* COBLIST::GetHead()
{
ASSERT(m_pHead);

return m_pHead->pItem;
}
#endif

VOID* COBLIST::GetTail()
{
ASSERT(m_pTail);

return m_pTail->pItem;
}

//****************************************************************************
//
// VOID* COBLIST::GetNext(POSITION& rPos)
//
//****************************************************************************

VOID* COBLIST::GetNext(POSITION& rPos)
{
ASSERT(rPos);

VOID* pReturn = rPos->pItem;
rPos = rPos->pNext;

return pReturn;
}

//****************************************************************************
//
// VOID* COBLIST::RemoveAt(POSITION Pos)
//
//****************************************************************************

VOID* COBLIST::RemoveAt(POSITION Pos)
{
VOID* pReturn = NULL;

if (m_pHead)
{
if (m_pHead == Pos)
{
// Removing the first element in the list

m_pHead = Pos->pNext;
pReturn = Pos->pItem;
delete Pos;

if (NULL == m_pHead)
{
// Removing the only element!
m_pTail = NULL;
}
}
else
{
POSITION pCur = m_pHead;

while (pCur && pCur->pNext)
{
if (pCur->pNext == Pos)
{
// Removing

pCur->pNext = Pos->pNext;
if (m_pTail == Pos)
{
m_pTail = pCur;
}
pReturn = Pos->pItem;
delete Pos;
}

pCur = pCur->pNext;
}
}
}

return pReturn;
}

//****************************************************************************
//
// POSITION COBLIST::AddTail(VOID* pItem)
//
//****************************************************************************

POSITION COBLIST::AddTail(VOID* pItem)
{
POSITION posRet = NULL;

if (m_pTail)
{
if (m_pTail->pNext = new COBNODE)
{
m_pTail = m_pTail->pNext;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
}
}
else
{
ASSERT(!m_pHead);
if (m_pHead = new COBNODE)
{
m_pTail = m_pHead;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
}
}

return m_pTail;
}

//****************************************************************************
//
// void COBLIST::EmptyList()
//
//****************************************************************************

void COBLIST::EmptyList()
{
    while (!IsEmpty()) {
        RemoveAt(GetHeadPosition());
    }
}

#ifdef DEBUG
VOID* COBLIST::RemoveTail()
{
ASSERT(m_pHead);
ASSERT(m_pTail);

return RemoveAt(m_pTail);
}

VOID* COBLIST::RemoveHead()
{
ASSERT(m_pHead);
ASSERT(m_pTail);

return RemoveAt(m_pHead);
}

void * COBLIST::GetFromPosition(POSITION Pos)
{
    void * Result = SafeGetFromPosition(Pos);
ASSERT(Result);
return Result;
}
#endif /* DEBUG */

//****************************************************************************
//
// POSITION COBLIST::GetPosition(void* _pItem)
//
//****************************************************************************

POSITION COBLIST::GetPosition(void* _pItem)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Position->pItem == _pItem) {
            break;
        }
GetNext(Position);
    }
    return Position;
}

//****************************************************************************
//
// POSITION COBLIST::Lookup(void* pComparator)
//
//****************************************************************************

POSITION COBLIST::Lookup(void* pComparator)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Compare(Position->pItem, pComparator)) {
            break;
        }
GetNext(Position);
    }
    return Position;
}

//****************************************************************************
//
// void * COBLIST::SafeGetFromPosition(POSITION Pos)
//
//****************************************************************************

void * COBLIST::SafeGetFromPosition(POSITION Pos)
{
// Safe way to validate that an entry is still in the list,
// which ensures bugs that would reference deleted memory,
// reference a NULL pointer instead
// (e.g. an event handler fires late/twice).
// Note that versioning on entries would provide an additional
// safeguard against re-use of a position.
// Walklist to find entry.

POSITION PosWork = m_pHead;

while (PosWork) {
if (PosWork == Pos) {
return Pos->pItem;
}
GetNext(PosWork);
}
return NULL;
}

//****************************************************************************
//
// COBLIST Utility routines
//
//****************************************************************************

//****************************************************************************
//
// POSITION AddNode(PVOID pv, COBLIST ** ppList)
//
// Add a node to a list.
// Initializes the ObList, if necessary.
// Returns the position in the list or NULL if there was a problem.
//
//****************************************************************************

POSITION AddNode(PVOID pv, COBLIST ** ppList)
{
ASSERT(NULL != ppList);
if (NULL == *ppList)
{
*ppList = new COBLIST();
if (NULL == *ppList)
return NULL;
}

return (*ppList)->AddTail(pv);
}

//****************************************************************************
//
// PVOID RemoveNode(POSITION * pPos, COBLIST *pList)
//
// Remove a node from a list.
// Sets pPos to NULL
//
//****************************************************************************

PVOID RemoveNode(POSITION * pPos, COBLIST *pList)
{
if ((NULL == pList) || (NULL == pPos))
return NULL;

PVOID pv = pList->RemoveAt(*pPos);
*pPos = NULL;
return pv;
}

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

//****************************************************************************
//
// Constructor
//
// We don't support construction from an ANSI string in the Unicode build.
//
//****************************************************************************

#if !defined(UNICODE)

BSTRING::BSTRING(LPCSTR lpcString)
{
m_bstr = NULL;

// Compute the length of the required BSTR, including the null
int cWC =  MultiByteToWideChar(CP_ACP, 0, lpcString, -1, NULL, 0);
if (cWC <= 0)
return;

// Allocate the BSTR, including the null
m_bstr = SysAllocStringLen(NULL, cWC - 1); // SysAllocStringLen adds another 1

ASSERT(NULL != m_bstr);
if (NULL == m_bstr)
{
return;
}

// Copy the string
MultiByteToWideChar(CP_ACP, 0, lpcString, -1, (LPWSTR) m_bstr, cWC);

// Verify that the string is null terminated
ASSERT(0 == m_bstr[cWC - 1]);
}

#endif // !defined(UNICODE)

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

BTSTR::BTSTR(BSTR bstr)
{
m_psz = PszFromBstr(bstr);
}

BTSTR::~BTSTR()
{
if (NULL != m_psz)
LocalFree(m_psz);
}

LPTSTR PszFromBstr(BSTR bstr)
{
if (NULL == bstr)
return NULL;
int cch =  WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, NULL, 0, NULL, NULL);
if (cch <= 0)
return NULL;

LPTSTR psz = PszAlloc(cch);
if (NULL == psz)
return NULL;

WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, psz, cch+1, NULL, NULL);
return psz;
}


CLUTIL.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CLUTIL.H
//  Content:    Utility Classes
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#ifndef _CL_UTIL_H_
#define _CL_UTIL_H_

//****************************************************************************
//
// CLASS RefCount
//
//****************************************************************************

class RefCount
{
private:
   LONG m_cRef;

public:
   RefCount();
   // Virtual destructor defers to destructor of derived class.
   virtual ~RefCount();

   // IUnknown methods

   ULONG STDMETHODCALLTYPE AddRef(void);
   ULONG STDMETHODCALLTYPE Release(void);
};

//****************************************************************************
//
// CLASS CNotify
//
// Notification sink
//
//****************************************************************************

class CNotify
{
private:
    DWORD  m_dwCookie;
IUnknown * m_pUnk;
    IConnectionPoint           * m_pcnp;
    IConnectionPointContainer  * m_pcnpcnt;
public:
    CNotify(void);
    ~CNotify();

    HRESULT Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN);
    HRESULT Disconnect(void);

    IUnknown * GetPunk() {return m_pUnk;}
};

//****************************************************************************
//
// CLASS COBLIST
//
//****************************************************************************

#define POSITION COBNODE*

struct COBNODE
{
POSITIONpNext;
void*pItem;
};

class COBLIST
{
protected:
POSITION m_pHead;
POSITION m_pTail;
    virtual BOOL Compare(void* pItemToCompare, void* pComparator)
                       { return(pItemToCompare == pComparator); };
public:
COBLIST() : m_pHead(NULL), m_pTail(NULL) { };
    virtual         ~COBLIST();

virtual void *  RemoveAt(POSITION rPos);
    void            EmptyList();
POSITION    AddTail(void* pItem);
void *    GetTail();
void *    GetNext(POSITION& rPos);
    void *          SafeGetFromPosition(POSITION rPos);
    POSITION        GetPosition(void* pItem);
    POSITION        Lookup(void* pComparator);
POSITION    GetHeadPosition()  { return (m_pHead); };
POSITION    GetTailPosition()  { return (m_pTail); };
BOOL    IsEmpty()          { return (!m_pHead); };
#ifdef DEBUG
void *    GetHead();
void *    RemoveHead();
void *        RemoveTail();
void *    GetFromPosition(POSITION rPos);
#else
void *    GetHead()          { return GetFromPosition(GetHeadPosition());};
void *    RemoveHead()       { return RemoveAt(m_pHead); };
void *        RemoveTail()       { return RemoveAt(m_pTail); };
void *    GetFromPosition(POSITION rPos){return(rPos->pItem);};
#endif
};

//****************************************************************************
//
// Utility Functions
//
//****************************************************************************

POSITION AddNode(PVOID pv, COBLIST ** ppList);
PVOID RemoveNode(POSITION * pPos, COBLIST *pList);

//****************************************************************************
//
// CLASS BSTRING
//
//****************************************************************************

class BSTRING
{
private:
BSTR   m_bstr;

public:
// Constructors
BSTRING() {m_bstr = NULL;}

inline BSTRING(LPCWSTR lpcwString);

#if !defined(UNICODE)
// We don't support construction from an ANSI string in the Unicode build.
BSTRING(LPCSTR lpcString);
#endif // !defined(UNICODE)

// Destructor
inline ~BSTRING();

// Cast to BSTR
operator BSTR() {return m_bstr;}
inline LPBSTR GetLPBSTR(void);
};

BSTRING::BSTRING(LPCWSTR lpcwString)
{
if (NULL != lpcwString)
{
m_bstr = SysAllocString(lpcwString);
ASSERT(NULL != m_bstr);
}
else
{
m_bstr = NULL;
}
}

BSTRING::~BSTRING()
{
if (NULL != m_bstr)
{
SysFreeString(m_bstr);
}
}

inline LPBSTR BSTRING::GetLPBSTR(void)
{
ASSERT(NULL == m_bstr);

return &m_bstr;
}

//****************************************************************************
//
// CLASS BTSTR
//
//****************************************************************************

class BTSTR
{
private:
LPTSTR m_psz;

public:
BTSTR(BSTR bstr);
~BTSTR();

// Cast to BSTR
operator LPTSTR() {return (NULL == m_psz) ? TEXT("<null>") : m_psz;}
};

LPTSTR PszFromBstr(BSTR bst);

#endif  // _CL_UTIL_H_


CMD.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CMD.CPP
//  Content:    Main Command Routines
//        All functions in this file have the format:
//                VOID CmdXxx(void);
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// VOID CmdMonitor(void)
//
//****************************************************************************

VOID CmdMonitor(void)
{
if (NULL == g_pMgr)
{
if (FAILED(InitConfMgr()))
{
ERROR_MSG("Failed to monitor incoming calls");
}
else
{
TRACE_MSG("Monitoring incoming calls");
}
}
else
{
FreeConfMgr();
ASSERT(NULL == g_pMgr);
}

BOOL fChecked = NULL != g_pMgr;
SetToolbarCheck(IDM_MONITOR, fChecked);
SetMenuCheck(IDM_MONITOR, fChecked);
}

//****************************************************************************
//
// VOID CmdConfStart(void)
//
//****************************************************************************

VOID CmdConfStart(void)
{
if (!DlgNewConference())
return;

TRACE_MSG("Starting conference [%s] nmch=%08X", gPref.pszConferenceName, gPref.dwNmch);
DoConferenceCreate();
}

//****************************************************************************
//
// VOID CmdConfStop(void)
//
//****************************************************************************

VOID CmdConfStop(void)
{
TRACE_MSG("Stopping conference...");
DoConferenceLeave();
}

//****************************************************************************
//
// VOID CmdCall(void)
//
//****************************************************************************

VOID CmdCall(void)
{
if (!DlgNewCall())
return;
DoCallCreate();
}

//****************************************************************************
//
// VOID CmdCallConf(void)
//
//****************************************************************************

VOID CmdCallConf(void)
{
if (!DlgCallConf())
return;
DoCallConference();
}

//****************************************************************************
//
// VOID CmdCallAccept(void)
//
//****************************************************************************

VOID CmdCallAccept(void)
{
DoHandleCall(TRUE);
}

//****************************************************************************
//
// VOID CmdCallReject(void)
//
//****************************************************************************

VOID CmdCallReject(void)
{
DoHandleCall(FALSE);
}

//****************************************************************************
//
// VOID CmdCallCancel(void)
//
//****************************************************************************

VOID CmdCallCancel(void)
{
DoCancelCall();
}

//****************************************************************************
//
// VOID CmdShare(void)
//
//****************************************************************************

VOID CmdShare(void)
{
DlgShare();
}

//****************************************************************************
//
// VOID CmdLaunch(void)
//
//****************************************************************************

VOID CmdLaunch(void)
{
DlgLaunch();
}

VOID CmdEject(void)
{
DlgEject();
}

VOID CmdFtCancel(void)
{
DoFtCancel();
}

VOID CmdDataEnable(void)
{
if (NULL == g_pChannelData)
return;

DoActivateDataChannel(S_OK != g_pChannelData->IsActive());
}

VOID CmdChannelData(void)
{
DoCreateDataChannel(GetCurrentConference());
}

VOID CmdChat(void)
{
TCHAR sz[MAX_PATH];

// Get the text line
SendMessage(ghwndChat, WM_GETTEXT, (WPARAM) CCHMAX(sz), (LPARAM) sz);
SendMessage(ghwndChat, EM_SETSEL, (WPARAM) 0, (LPARAM)-1);
SendMessage(ghwndChat, WM_SETTEXT, (WPARAM) 0, (LPARAM) "");

if (NULL != g_pChannelData)
{
INmMember * pMember = NULL;
int iSel = SendMessage(ghwndUList, CB_GETCURSEL, 0, 0);
if (CB_ERR != iSel)
{
PUSER pUser = (PUSER) SendMessage(ghwndUList, CB_GETITEMDATA, (WPARAM) iSel, 0);
if (NULL != pUser)
pMember = pUser->pMember;
}
DoSendChatMsg(sz, pMember);
}

SetFocus(ghwndChat);
}

VOID CmdAudio(void)
{
DlgAudio();
}

VOID CmdVideo(void)
{
DlgVideo();
}

VOID CmdSendFile(void)
{
DlgSendFile();
}

VOID CmdSysInfo(void)
{
DumpSysInfo();
}

VOID CmdConfInfo(void)
{
DoEnumAll();
}

VOID CmdOptions(void)
{
DlgOptions();
}

VOID CmdUpdate(void)
{
UpdateConfView();
}

//****************************************************************************
//
// VOID CmdViewSbar(void)
//
// Show/Hide the status bar
//
//****************************************************************************

VOID CmdViewSbar(void)
{
gPref.fSbar = !gPref.fSbar;
ShowHwnd(ghwndSbar, gPref.fSbar, IDM_VIEW_SBAR);
}

//****************************************************************************
//
// VOID CmdViewTbar(void)
//
//****************************************************************************

VOID CmdViewTbar(void)
{
gPref.fTbar = !gPref.fTbar;
ShowHwnd(ghwndTbar, gPref.fTbar, IDM_VIEW_TBAR);
}

//****************************************************************************
//
// VOID CmdViewChat(void)
//
//****************************************************************************

VOID CmdViewChat(void)
{
gPref.fChat = !gPref.fChat;
ShowWindow(ghwndSend, gPref.fChat ? SW_SHOW : SW_HIDE);
ShowWindow(ghwndUList, gPref.fChat ? SW_SHOW : SW_HIDE);
ShowHwnd(ghwndChat, gPref.fChat, IDM_VIEW_CHAT);
}

//****************************************************************************
//
// VOID CmdViewMsg(void)
//
//****************************************************************************

VOID CmdViewMsg(void)
{
gPref.fMsg = !gPref.fMsg;
ShowHwnd(ghwndMsg, gPref.fMsg, IDM_VIEW_MSG);
SetToolbarCheck(IDM_VIEW_MSG, gPref.fMsg);
}

//****************************************************************************
//
// VOID CmdClear(void)
//
// Clear the message window
//
//****************************************************************************

VOID CmdClear(void)
{
ClearLog();
}

//****************************************************************************
//
// VOID CmdClear(void)
//
// Shutdown the application.
//
//****************************************************************************

VOID CmdExit(void)
{
WritePref();
DestroyWindow(ghwndMain);
}

//****************************************************************************
//
// VOID CmdHelp(void)
//
// Bring up help for the application
//
//****************************************************************************

VOID CmdHelp(void)
{
TCHAR  szFile[MAX_PATH];
GetModuleFileName(ghInst, szFile, CCHMAX(szFile));

// convert the app name to the help filename (foo.exe => foo.hlp)
PTCHAR pch = SzFindLastCh(szFile, _T('.'));
if (NULL != pch)
{
lstrcat(szFile, TEXT(".hlp"));
WinHelp(ghwndMain, szFile, HELP_CONTENTS, 0);
}
}

//****************************************************************************
//
// VOID CmdFont(void)
//
// Change the font for the message window
//
//****************************************************************************

VOID CmdFont(void)
{
CHOOSEFONT cf;
LOGFONT    lf;
HFONT      hfont;

lf = gPref.lf;

ClearStruct(&cf);
cf.lStructSize = sizeof(CHOOSEFONT);
cf.hwndOwner = ghwndMain;
cf.lpLogFont = &lf;
cf.iPointSize = 10;
cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT;

if (ChooseFont(&cf) != TRUE)
return;

hfont= CreateFontIndirect(cf.lpLogFont);
if (hfont == hfontNil)
return;

MaybeDeleteObject((HGDIOBJ *) &ghfontEntry);
ghfontEntry = hfont;
SendMessage(ghwndMsg, WM_SETFONT,
(WPARAM) ghfontEntry, MAKELPARAM(TRUE, 0));

gPref.lf = lf;
}

//****************************************************************************
//
// VOID CmdAbout(void)
//
// Display the about box
//
//****************************************************************************

VOID CmdAbout(void)
{
TCHAR szVersion[MAX_PATH];

LoadString(ghInst, IDS_VERSION, szVersion, CCHMAX(szVersion));

ShellAbout(ghwndMain, szVersion, NULL, LoadIcon(ghInst, MAKEINTRESOURCE(ICO_MAIN)));
}

//****************************************************************************
//
// VOID OnStartup(void)
//
// Perform any automatic startup commands
//
//****************************************************************************

VOID OnStartup(void)
{
if (gPref.fAutoMonitor)
{
CmdMonitor();
}

if (gPref.fAutoHost)
{
DoConferenceCreate();
}

if (gPref.fAutoCall)
{
DoCallCreate();
}
}

//****************************************************************************
//
// Main window command table definition.
//
//****************************************************************************

static CMD _rgcmdMain[] =
{
{IDM_MONITOR,             CmdMonitor},
{IDM_SYSINFO,             CmdSysInfo},
{IDM_EXIT,                CmdExit},

{IDM_CONF_START,          CmdConfStart},
{IDM_CONF_STOP,           CmdConfStop},
{IDM_CONFINFO,            CmdConfInfo},

{IDM_VIEW_SBAR,           CmdViewSbar},
{IDM_VIEW_TBAR,           CmdViewTbar},
{IDM_VIEW_MSG,            CmdViewMsg},
{IDM_VIEW_CHAT,           CmdViewChat},
{IDM_VIEW_CLEAR,          CmdClear},
{IDM_VIEW_FONT,           CmdFont},
{IDM_OPTIONS,             CmdOptions},

{IDM_LAUNCH,              CmdLaunch},
{IDM_EJECT,               CmdEject},
{IDM_UPDATE,              CmdUpdate},

{IDM_CALL,                CmdCall},
{IDM_CALL_CONF,           CmdCallConf},
{IDM_CALL_ACCEPT,         CmdCallAccept},
{IDM_CALL_REJECT,         CmdCallReject},
{IDM_CALL_CANCEL,         CmdCallCancel},

{IDM_CHANNEL_DATA,        CmdChannelData},
{IDM_CHANNEL_AUDIO,       CmdAudio},
{IDM_CHANNEL_VIDEO,       CmdVideo},
{IDM_CHANNEL_FT,          CmdSendFile},
{IDM_CHANNEL_SHARE,       CmdShare},

{IDW_SEND,                CmdChat},
{IDM_FT_CANCEL,           CmdFtCancel},
{IDM_DATA_ENABLE,         CmdDataEnable},

{IDM_HELP,                CmdHelp},
{IDM_ABOUT,               CmdAbout},

{0, NULL}  // MUST be last entry in the table
};

//****************************************************************************
//
// LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
//
// Handle a WM_COMMAND message
//
//****************************************************************************

LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
{
UINT wCmd = (UINT) GET_WM_COMMAND_ID(wparam, lparam);

for (LPCMD lpcmd = _rgcmdMain; 0 != lpcmd->wCmd; lpcmd++)
{
if (wCmd == lpcmd->wCmd)
{
lpcmd->pfncmd();
break;
}
}

return 0L;
}


CMEMB.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CMEMB.CPP
//  Content:    INmMember Routines
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

COBLIST * g_pListUser = (COBLIST *) NULL;

UINT gcUsers = 0;

//****************************************************************************
//
// PUSER PUserFromINmMember(INmMember * pMember)
//
//****************************************************************************

PUSER PUserFromINmMember(INmMember * pMember)
{
if (NULL != g_pListUser)
{
POSITION pos = g_pListUser->GetHeadPosition();
while (NULL != pos)
{
PUSER pUser = (PUSER) g_pListUser->GetNext(pos);
if (pMember == pUser->pMember)
return pUser;
}
}

return NULL;
}

//****************************************************************************
//
// int ItemFromPUser(PUSER pUser)
//
//****************************************************************************

int ItemFromPUser(PUSER pUser)
{
if (NULL == ghwndLView)
return -1;

LV_FINDINFO findInfo;
ClearStruct(&findInfo);
findInfo.flags = LVFI_PARAM;
    findInfo.lParam = (LPARAM) pUser;
    return ListView_FindItem(ghwndLView, -1, &findInfo);
}

//****************************************************************************
//
// PUSER PUserLocal()
//
// Return a pointer to the local user information
//
//****************************************************************************

PUSER PUserLocal()
{
if (NULL != g_pListUser)
{
POSITION pos = g_pListUser->GetHeadPosition();
while (NULL != pos)
{
PUSER pUser = (PUSER) g_pListUser->GetNext(pos);
if (pUser->fLocal)
return pUser;
}
}

return NULL;
}

//****************************************************************************
//
// VOID UpdateUserDisplay(PUSER pUser)
//
//****************************************************************************

VOID UpdateUserDisplay(PUSER pUser)
{
int iItem = ItemFromPUser(pUser);
TCHAR sz[MAX_PATH];

ASSERT(NULL != ghwndLView);

wsprintf(sz, "%d", pUser->uVersion);
ListView_SetItemText(ghwndLView, iItem, ILV_VERSION, sz);

wsprintf(sz, "%d", pUser->fMcu);
ListView_SetItemText(ghwndLView, iItem, ILV_MCU, sz);

wsprintf(sz, "%d", pUser->fLocal);
ListView_SetItemText(ghwndLView, iItem, ILV_LOCAL, sz);

wsprintf(sz, "%X", pUser->m_pChannelAudio);
ListView_SetItemText(ghwndLView, iItem, ILV_AUDIO, sz);

wsprintf(sz, "%X", pUser->m_pChannelVideo);
ListView_SetItemText(ghwndLView, iItem, ILV_VIDEO, sz);

wsprintf(sz, "%X", pUser->m_pChannelAs);
ListView_SetItemText(ghwndLView, iItem, ILV_AS, sz);

wsprintf(sz, "%X", pUser->m_pChannelFt);
ListView_SetItemText(ghwndLView, iItem, ILV_FT, sz);

wsprintf(sz, "%08X", pUser->uchCaps);
ListView_SetItemText(ghwndLView, iItem, ILV_CAPS, sz);

ListView_SetItemText(ghwndLView, iItem, ILV_ADDR,
(NULL == pUser->pszAddr) ? TEXT("?") : pUser->pszAddr);

wsprintf(sz, "%08X", pUser->uId);
ListView_SetItemText(ghwndLView, iItem, ILV_ID, sz);

}

//****************************************************************************
//
// VOID UpdateUserCount(void)
//
//****************************************************************************

VOID UpdateUserCount(void)
{
TCHAR sz[MAX_PATH];

if (1 == gcUsers)
lstrcpy(sz, TEXT("1 user"));
else
wsprintf(sz, TEXT("%d users"), gcUsers);

UpdateStatusBar(sz, IDSBP_USERCOUNT, 0);
}

//****************************************************************************
//
// PUSER AllocUser(INmMember * pMember)
//
//****************************************************************************

PUSER AllocUser(INmMember * pMember)
{
HRESULT hr;
BSTR bstr;
INmConference * pConference;

PUSER pUser = new USER;
if (NULL == pUser)
{
return NULL;
}
ClearStruct(pUser);
pUser->pMember = pMember;
pMember->AddRef();

hr = pMember->GetName(&bstr);
if (FAILED(hr))
{
WARN_MSG("Problem with GetName");
}
else
{
pUser->pszName = PszFromBstr(bstr);
SysFreeString(bstr);
}

hr = pMember->GetAddr(&bstr, &pUser->uType);
if (FAILED(hr))
{
WARN_MSG("Problem with GetAddr");
}
else
{
pUser->pszAddr = PszFromBstr(bstr);
SysFreeString(bstr);
}

hr = pMember->GetNmchCaps(&pUser->uchCaps);
if (FAILED(hr))
{
WARN_MSG("Problem with GetNmchCaps");
pUser->uchCaps = 0;
}

hr = pMember->GetNmVersion(&pUser->uVersion);
if (FAILED(hr))
{
WARN_MSG("Problem with GetNmVersion");
pUser->uVersion = 0;
}

hr = pMember->GetID(&pUser->uId);
if (FAILED(hr))
{
WARN_MSG("Problem with GetID");
pUser->uId = 0;
}

pUser->fLocal = S_OK == pMember->IsSelf();
pUser->fMcu = S_OK == pMember->IsMCU();
pUser->fTop = FALSE;
hr = pMember->GetConference(&pConference);
if (FAILED(hr))
{
WARN_MSG("Problem with GetConference");
}
else
{
INmMember * pMemberTop;
if (SUCCEEDED(pConference->GetTopProvider(&pMemberTop)))
{
pUser->fTop = (pMember == pMemberTop);
pMemberTop->Release();
}
pConference->Release();
}

return pUser;
}

//****************************************************************************
//
// VOID FreeUser(PUSER pUser)
//
//****************************************************************************

VOID FreeUser(PUSER pUser)
{
ASSERT(NULL != pUser);

FreePlpv(&pUser->pszName);
FreePlpv(&pUser->pszAddr);

if (NULL != pUser->pMember)
{
OBJECT_MSG("[%08X] member released", pUser->pMember);
pUser->pMember->Release();
}
}

//****************************************************************************
//
// VOID FreeAllUsers(void)
//
//****************************************************************************

VOID FreeAllUsers(void)
{
if (NULL != g_pListUser)
{
while (!g_pListUser->IsEmpty())
{
RemoveMember(((PUSER) g_pListUser->GetHead())->pMember);
}
}
}

//****************************************************************************
//
// VOID AddMember(INmMember * pMember)
//
//****************************************************************************

VOID AddMember(INmMember * pMember)
{
PUSER pUser = AllocUser(pMember);
if (NULL == pUser)
{
return;
}

AddNode(pUser, &g_pListUser);

LV_ITEM  lvItem;
ClearStruct(&lvItem);
lvItem.iItem = gcUsers;
lvItem.mask = LVIF_PARAM | LVIF_TEXT;
lvItem.pszText = pUser->pszName;
lvItem.cchTextMax = lstrlen(pUser->pszName);
lvItem.lParam = (LPARAM) pUser;
ListView_InsertItem(ghwndLView, &lvItem);

gcUsers++;
UpdateUserCount();
UpdateUserDisplay(pUser);

AddUserToUList(pUser, ghwndUList, FALSE);
}

//****************************************************************************
//
// VOID RemoveMember(INmMember * pMember)
//
//****************************************************************************

VOID RemoveMember(INmMember * pMember)
{
if (NULL == g_pListUser)
return;

POSITION pos = g_pListUser->GetHeadPosition();
while (NULL != pos)
{
POSITION posSav = pos;
PUSER pUser = (PUSER) g_pListUser->GetNext(pos);
if (pMember == pUser->pMember)
{
RemoveUserFromUList(pUser, ghwndUList);
ListView_DeleteItem(ghwndLView, ItemFromPUser(pUser));
FreeUser(pUser);
g_pListUser->RemoveAt(posSav);

gcUsers--;
UpdateUserCount();
return;
}
}
}

//****************************************************************************
//
// VOID UpdateUserChannel(PUSER pUser, INmChannel * pChannel, NM_MEMBER_NOTIFY uNotify)
//
//****************************************************************************

VOID UpdateUserChannel(PUSER pUser, INmChannel * pChannel, NM_MEMBER_NOTIFY uNotify)
{
ULONG uch;
PVOID *ppChannel = NULL;

ASSERT(NULL != pChannel);

if (FAILED(pChannel->GetNmch(&uch)))
{
return;
}

if (NM_MEMBER_ADDED == uNotify)
{
switch (uch)
{
case NMCH_AUDIO:
{
INmChannelAudio * pChannelAudio;
if (SUCCEEDED(pChannel->QueryInterface(IID_INmChannelAudio, (PVOID *) &pChannelAudio)))
{
if ((S_OK == pChannelAudio->IsIncoming()) != pUser->fLocal)
{
ASSERT((NULL == pUser->m_pChannelAudio) || (pChannelAudio == pUser->m_pChannelAudio));
// This is non-standard - we're storing a pointer to the specific channel,
// yet keeping a lock on the generic channel - weird....
pUser->m_pChannelAudio = pChannelAudio;
}
pChannelAudio->Release();
}
break;
}
case NMCH_VIDEO:
{
INmChannelVideo * pChannelVideo;
if (SUCCEEDED(pChannel->QueryInterface(IID_INmChannelVideo, (PVOID *) &pChannelVideo)))
{
if ((S_OK == pChannelVideo->IsIncoming()) != pUser->fLocal)
{
ASSERT((NULL == pUser->m_pChannelVideo) || (pChannelVideo == pUser->m_pChannelVideo));
pUser->m_pChannelVideo = pChannelVideo;
}
pChannelVideo->Release();
}
break;
}
case NMCH_SHARE:
if (NULL == pUser->m_pChannelAs)
{
INmChannelAppShare * pChannelAs;
if (SUCCEEDED(pChannel->QueryInterface(IID_INmChannelAppShare, (PVOID *) &pChannelAs)))
{
pUser->m_pChannelAs = pChannelAs;
pChannelAs->Release();
}
}
break;
case NMCH_FT:
if (NULL == pUser->m_pChannelFt)
{
INmChannelFt * pChannelFt;
if (SUCCEEDED(pChannel->QueryInterface(IID_INmChannelFt, (PVOID *) &pChannelFt)))
{
pUser->m_pChannelFt = pChannelFt;
pChannelFt->Release();
}
}
break;
case NMCH_DATA:
ppChannel = (PVOID *) &(pUser->m_pChannelData);
break;
case NMCH_NONE:
case NMCH_ALL:
default:
break;
}
} /* CONFN_MEMBER_ADDED */

UpdateUserDisplay(pUser);
}

//****************************************************************************
//
// VOID UpdateUserInfo(PUSER pUser)
//
//****************************************************************************

VOID UpdateUserInfo(PUSER pUser)
{
ULONG   ul;
HRESULT hr;
LPTSTR  psz;
BSTR    bstr;
INmMember * pMember = pUser->pMember;

hr = pMember->GetName(&bstr);
if (FAILED(hr))
{
WARN_MSG("Problem with GetName");
}
else
{
psz = PszFromBstr(bstr);
SysFreeString(bstr);

if (0 != lstrcmp(pUser->pszName, psz))
{
WARN_MSG("Replacing Name [%s] => [%s]", pUser->pszName, psz);
}
FreePlpv(&pUser->pszName);
pUser->pszName = psz;
}

hr = pMember->GetAddr(&bstr, &pUser->uType);
if (FAILED(hr))
{
WARN_MSG("Problem with GetAddr");
}
else
{
psz = PszFromBstr(bstr);
SysFreeString(bstr);

if (0 != lstrcmp(pUser->pszAddr, psz))
{
WARN_MSG("Replacing Addr [%s] => [%s]", pUser->pszAddr, psz);
}
FreePlpv(&pUser->pszAddr);
pUser->pszAddr = psz;
}

hr = pMember->GetNmchCaps(&ul);
if (FAILED(hr))
{
WARN_MSG("Problem with GetNmchCaps");
pUser->uchCaps = 0;
}
else if (pUser->uchCaps != ul)
{
WARN_MSG("Updating uchCaps [%08X] => [%08X]", pUser->uchCaps, ul);
pUser->uchCaps = ul;
}

hr = pMember->GetNmVersion(&ul);
if (FAILED(hr))
{
WARN_MSG("Problem with GetNmVersion");
pUser->uVersion = 0;
}
else if (pUser->uVersion != ul)
{
WARN_MSG("Updating version [%08X] => [%08X]", pUser->uVersion, ul);
pUser->uchCaps = ul;
}

if (pUser->fLocal != (S_OK == pMember->IsSelf()) )
{
ERROR_MSG("Local User status changed?");
}

if (pUser->fMcu != (S_OK == pMember->IsMCU()))
{
ERROR_MSG("MCU status changed?");
}
}

//****************************************************************************
//
// VOID UpdateConfView(void)
//
//****************************************************************************

VOID UpdateConfView(void)
{
if (NULL == g_pListUser)
return;

POSITION pos = g_pListUser->GetHeadPosition();
while (NULL != pos)
{
PUSER pUser = (PUSER) g_pListUser->GetNext(pos);
UpdateUserInfo(pUser);
UpdateUserDisplay(pUser);
}
}


CMEMB.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CMEMB.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

typedef struct tagUser {
INmMember * pMember;
int    iItem;
ULONG  uId;
LPTSTR pszName;
LPTSTR pszAddr;
NM_ADDR_TYPE uType;
ULONG  uVersion;
ULONG  uchCaps;
BOOL   fLocal;
BOOL   fMcu;
BOOL   fTop;
INmChannelAudio * m_pChannelAudio;
INmChannelVideo * m_pChannelVideo;
INmChannelAppShare * m_pChannelAs;
INmChannelFt       * m_pChannelFt;
INmChannelData   * m_pChannelData;
} USER;
typedef USER * PUSER;

// from cMemb.cpp
VOID AddMember(INmMember * pMember);
VOID RemoveMember(INmMember * pMember);
VOID FreeAllUsers(void);
VOID FreeUser(PUSER pUser);
PUSER PUserFromINmMember(INmMember * pMember);
VOID UpdateUserChannel(PUSER pUser, INmChannel * pChannel, NM_MEMBER_NOTIFY uNotify);
PUSER PUserLocal();

// from dlg.cpp
VOID AddUserToUList(PUSER pUser, HWND hwndCombo, BOOL fEveryone);
VOID RemoveUserFromUList(PUSER pUser, HWND hwndCombo);
VOID UpdateConfView(void);

extern COBLIST * g_pListUser;


CMGR.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CMGR.CPP
//  Content:    Conference Manager Routines
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

// There should be only one conference manager
INmManager * g_pMgr  = NULL;
CMgrNotify * g_pMgrNotify = NULL;

// {D29A8C50-774F-11d0-8B1D-00A0C91BC90E}
const GUID g_guid =
{ 0xd29a8c51, 0x774f, 0x11d0, { 0x8b, 0x1d, 0x0, 0xa0, 0xc9, 0x1b, 0xc9, 0xe} };

// NetMeeting 2.0 chat guid: {340F3A60-7067-11D0-A041-444553540000}
const GUID g_guidNM2Chat =
{ 0x340f3a60, 0x7067, 0x11d0, { 0xa0, 0x41, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0 } };

HRESULT InitializeNm(void)
{
HRESULT hr;
ULONG uchCaps = 0;
ULONG uOptions = (ULONG) gPref.iInit;

hr = g_pMgr->Initialize(&uOptions, &uchCaps);
TRACE_MSG("CConfMgr result=%s uOptions=%08X", GetHrSz(hr), uOptions);

return hr;
}

//****************************************************************************
//
// HRESULT InitConfMgr(void)
//
//****************************************************************************

HRESULT InitConfMgr(void)
{
HRESULT hr;
LPCLASSFACTORY pcf;

    // Notify the system we want to use the conferencing services
    // by creating a conference manager object
    hr = CoGetClassObject(CLSID_NmManager,
                          CLSCTX_INPROC,
                          NULL,
                          IID_IClassFactory,
                          (void**)&pcf);
    if (FAILED(hr))
    {
    ERROR_MSG("CoGetClassObject Failed (%s)", GetHrSz(hr));
    }
    else
    {
        // Get the conference manager object
        hr = pcf->CreateInstance(NULL, IID_INmManager, (void**)&g_pMgr);
        if (FAILED(hr))
        {
    ERROR_MSG("CreateInstance Failed (%s)", GetHrSz(hr));
        }
        else
        {
            // Connect to the conference manager object
            g_pMgrNotify = new CMgrNotify();
            if (g_pMgrNotify == NULL)
            {
                hr = E_OUTOFMEMORY;
    ERROR_MSG("CConfMgrNotify: out of memory!");
            }
            else
            {
                hr = g_pMgrNotify->Connect(g_pMgr);
                if (FAILED(hr))
                {
    ERROR_MSG("CConfMgrNotify Failed (%s)", GetHrSz(hr));
                    g_pMgrNotify->Release();
                    g_pMgrNotify = NULL;
                }
                else
                {
DumpSysInfo();
                hr = InitializeNm();
                DumpLaunchInfo();
}
            }
        }

        pcf->Release();
    }

return hr;
}

//****************************************************************************
//
// VOID FreeConfMgr(void)
//
//****************************************************************************

VOID FreeConfMgr(void)
{
// Release conference manager notify
if (NULL != g_pMgrNotify)
{
OBJECT_MSG("Freeing g_pMgrNotify");
g_pMgrNotify->Disconnect();
g_pMgrNotify->Release();
g_pMgrNotify = NULL;
}

// Release conference manager
if (NULL != g_pMgr)
{
OBJECT_MSG("Freeing g_pMgr");
g_pMgr->Release();
g_pMgr = NULL;
}
}

//****************************************************************************
//
// CLASS CMgrNotify
//
//****************************************************************************

CMgrNotify::CMgrNotify() : RefCount(), CNotify()
{
OBJECT_MSG("CMgrNotify created");
}

CMgrNotify::~CMgrNotify()
{
OBJECT_MSG("CMgrNotify destroyed");
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CMgrNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CMgrNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CMgrNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
OBJECT_MSG("CMgrNotify::QueryInterface(): Returning IUnknown.");
}
else if (riid == IID_INmManagerNotify)
{
*ppvObject = (INmManagerNotify *)this;
OBJECT_MSG("CMgrNotify::QueryInterface(): Returning INmManagerNotify.");
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
OBJECT_MSG("CMgrNotify::QueryInterface(): Called on unknown interface.");
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CMgrNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmManagerNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CMgrNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from INmManagerNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CMgrNotify::NmUI(CONFN confn)
{
NOTIFY_MSG("CMgrNotify::NmUI");
TRACE_MSG("CONFN=%s", GetConfnSz(confn));
return S_OK;
}

HRESULT STDMETHODCALLTYPE CMgrNotify::CallCreated(INmCall *pCall)
{
NOTIFY_MSG("CMgrNotify::CallCreated");

DumpCallInfo(pCall);
HookCall(pCall);
return S_OK;
}

HRESULT STDMETHODCALLTYPE CMgrNotify::ConferenceCreated(INmConference *pConference)
{
NOTIFY_MSG("CMgrNotify::ConferenceCreated");

DumpConferenceInfo(pConference);
HookConference(pConference);
return S_OK;
}

DWSTR _rgSysInfo[] = {
{NM_SYSPROP_EMAIL_NAME,    TEXT("Email     ")},
{NM_SYSPROP_SERVER_NAME,   TEXT("Server    ")},
{NM_SYSPROP_RESOLVE_NAME,  TEXT("Resolve   ")},
{NM_SYSPROP_FIRST_NAME,    TEXT("First Name")},
{NM_SYSPROP_LAST_NAME,     TEXT("Last Name ")},
{NM_SYSPROP_USER_NAME,     TEXT("Name      ")},
{NM_SYSPROP_USER_CITY,     TEXT("City      ")},
{NM_SYSPROP_USER_COUNTRY,  TEXT("Country   ")},
{NM_SYSPROP_USER_COMMENTS, TEXT("Comments  ")},
{NM_SYSPROP_USER_CATEGORY, TEXT("Category  ")},
{NM_SYSPROP_INSTALL_DIRECTORY, TEXT("Directory ")},
{NM_SYSPROP_APP_NAME,      TEXT("App Name  ")},
{NM_SYSPROP_IS_RUNNING,    TEXT("Is Running")},
{NM_SYSPROP_IN_CONFERENCE, TEXT("In Conference")},
{NM_SYSPROP_H323_GATEWAY,  TEXT("H323 Gateway ")},
{NM_SYSPROP_H323_GATEWAY_ENABLE, TEXT("H323 Enable  ")},
{NM_SYSPROP_ICA_ENABLE,    TEXT("ICA Enable   ")},
};

//****************************************************************************
//
// VOID DumpSysInfo(void)
//
//****************************************************************************

VOID DumpSysInfo(void)
{
HRESULT hr;
INmSysInfo * pSysInfo;

if (NULL == g_pMgr)
return;

if (FAILED(g_pMgr->GetSysInfo(&pSysInfo)))
{
return;
}

hr = pSysInfo->IsInstalled();
TRACE_MSG("NetMeeting IsInstalled (%s)", GetHrSz(hr));

TRACE_MSG("--- NetMeeting System Information ---");

for (int i = 0; i < ARRAY_ELEMENTS(_rgSysInfo); i++)
{
BSTR bstr;
hr = pSysInfo->GetProperty((NM_SYSPROP)_rgSysInfo[i].dw, &bstr);
if (S_OK != hr)
{
TRACE_MSG("%s: HRESULT = %s", _rgSysInfo[i].psz, GetHrSz(hr));
}
else
{
LPTSTR psz = PszFromBstr(bstr);
TRACE_MSG("%s = [%s]", _rgSysInfo[i].psz, psz);
FreePlpv(&psz);
SysFreeString(bstr);
}
}
TRACE_MSG("--- NetMeeting System Information ---");

ULONG nmch;
hr = pSysInfo->GetNmchCaps(&nmch);
TRACE_MSG("Caps=%08X  hr=%s", nmch, GetHrSz(hr));

pSysInfo->Release();

DumpAllCalls();
}

//****************************************************************************
//
// VOID DumpLaunchInfo(void)
//
//****************************************************************************

VOID DumpLaunchInfo(void)
{
HRESULT hr;
INmSysInfo * pSysInfo;
INmConference * pConference;
INmMember * pMember;

if (NULL == g_pMgr)
return;

if (FAILED(g_pMgr->GetSysInfo(&pSysInfo)))
{
return;
}

hr = pSysInfo->GetLaunchInfo(&pConference, &pMember);
TRACE_MSG("GetLaunchInfo: (%s) Conference=%08X Member=%08X", GetHrSz(hr), pConference, pMember);
if (NULL != pConference)
pConference->Release();
if (NULL != pMember)
pMember->Release();

pSysInfo->Release();
}


CMGR.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CMGR.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

//****************************************************************************
//
// CLASS CMgrNotify
//
//****************************************************************************

class CMgrNotify : public RefCount, public CNotify, public INmManagerNotify
{
public:
CMgrNotify();
~CMgrNotify();

// INmManagerNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE ConferenceCreated(INmConference *pConference);
HRESULT STDMETHODCALLTYPE CallCreated(INmCall *pCall);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

extern INmManager * g_pMgr;
extern const GUID g_guid;
extern const GUID g_guidNM2Chat;

//****************************************************************************
//
// Global Functions
//
//****************************************************************************

HRESULT InitConfMgr(void);
VOID FreeConfMgr(void);
INmConference * GetCurrentConference(void);
VOID DumpSysInfo(void);
VOID DumpLaunchInfo(void);


CVIDEO.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CVIDEO.CPP
//  Content:    Video Channel Routines
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// CLASS CVideoNotify
//
//****************************************************************************

CVideoNotify::CVideoNotify(PFNSTATECHANGE pfnStateChange, PFNPROPERTYCHANGE pfnPropertyChange, DWORD dwParam) : RefCount(), CNotify()
{
OBJECT_MSG("CVideoNotify created");

m_pfnStateChange = pfnStateChange;
m_pfnPropertyChange = pfnPropertyChange;
m_dwParam = dwParam;
}

CVideoNotify::~CVideoNotify()
{
OBJECT_MSG("CDataNotify destroyed");
}

//****************************************************************************
//
// Methods from IUnknown
//
//****************************************************************************

ULONG STDMETHODCALLTYPE CVideoNotify::AddRef(void)
{
return RefCount::AddRef();
}

ULONG STDMETHODCALLTYPE CVideoNotify::Release(void)
{
return RefCount::Release();
}

HRESULT STDMETHODCALLTYPE CVideoNotify::QueryInterface(REFIID riid, PVOID *ppvObject)
{
HRESULT hr = S_OK;

if (riid == IID_IUnknown)
{
*ppvObject = (IUnknown *)this;
OBJECT_MSG("CVideoNotify::QueryInterface(): Returning IUnknown.");
}
else if (riid == IID_INmChannelVideoNotify)
{
*ppvObject = (INmChannelVideoNotify *)this;
OBJECT_MSG("CVideoNotify::QueryInterface(): Returning INmChannelVideoNotify.");
}
else
{
hr = E_NOINTERFACE;
*ppvObject = NULL;
OBJECT_MSG("CVideoNotify::QueryInterface(): Called on unknown interface.");
}

if (S_OK == hr)
{
AddRef();
}

return hr;
}

//****************************************************************************
//
// Methods from ICNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CVideoNotify::Connect(IUnknown *pUnk)
{
return CNotify::Connect(pUnk, IID_INmChannelVideoNotify, (IUnknown *)this);
}

HRESULT STDMETHODCALLTYPE CVideoNotify::Disconnect(void)
{
return CNotify::Disconnect();
}

//****************************************************************************
//
// Methods from INmChannelNotify
//
//****************************************************************************
HRESULT STDMETHODCALLTYPE CVideoNotify::NmUI(CONFN uNotify)
{
NOTIFY_MSG("CVideoNotify::NmUI uNotify=%08X, pChannel=%08X", uNotify, CNotify::GetPunk());
return S_OK;
}

HRESULT STDMETHODCALLTYPE CVideoNotify::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
NOTIFY_MSG("CVideoNotify::MemberChanged uNotify=%08X, pMember=%08X, pChannel=%08X", uNotify, pMember, CNotify::GetPunk());

PUSER pUser = PUserFromINmMember(pMember);
if (NULL != pUser)
{
UpdateUserChannel(pUser, (INmChannel *) (CNotify::GetPunk()), uNotify);
}

return S_OK;
}

//****************************************************************************
//
// Methods from IConfNotify
//
//****************************************************************************

HRESULT STDMETHODCALLTYPE CVideoNotify::StateChanged(NM_VIDEO_STATE uState)
{
NOTIFY_MSG("CVideoNotify::StateChanged uState=%08X, pChannel=%08X", uState, CNotify::GetPunk());
if (NULL != m_pfnStateChange)
{
m_pfnStateChange(uState, m_dwParam);
}
return S_OK;
}

HRESULT STDMETHODCALLTYPE CVideoNotify::PropertyChanged(DWORD dwReserved)
{
NOTIFY_MSG("CVideoNotify::PropertyChanged dwReserved=%08X, pChannel=%08X", dwReserved, CNotify::GetPunk());
if (NULL != m_pfnPropertyChange)
{
m_pfnPropertyChange(dwReserved, m_dwParam);
}
return S_OK;
}


CVIDEO.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       CVIDEO.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

typedef VOID (* PFNSTATECHANGE)(NM_VIDEO_STATE, DWORD);
typedef VOID (* PFNPROPERTYCHANGE)(DWORD, DWORD);

class CVideoNotify : public RefCount, public CNotify, public INmChannelVideoNotify
{
private:
    PFNSTATECHANGE m_pfnStateChange;
    PFNPROPERTYCHANGE m_pfnPropertyChange;
    DWORD m_dwParam;

public:
CVideoNotify(PFNSTATECHANGE pfnStateChange, PFNPROPERTYCHANGE pfnPropertyChange, DWORD dwParam);
~CVideoNotify();

// INmChannelVideoNotify
HRESULT STDMETHODCALLTYPE NmUI(CONFN uNotify);
HRESULT STDMETHODCALLTYPE MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember * pMember);
    HRESULT STDMETHODCALLTYPE StateChanged(NM_VIDEO_STATE uState);
    HRESULT STDMETHODCALLTYPE PropertyChanged(DWORD dwReserved);

// ICNotify methods
HRESULT STDMETHODCALLTYPE Connect (IUnknown *pUnk);
HRESULT STDMETHODCALLTYPE Disconnect(void);

// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef(void);
ULONG STDMETHODCALLTYPE Release(void);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObj);
};

// Global Routines
HRESULT HookVideo(INmChannel * pChannel, CVideoNotify * pNotify);
VOID UnhookVideo(CVideoNotify * pNotify);


DEBUG.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       DEBUG.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

#define STRING_CASE(val)               case val: pcsz = #val; break

//****************************************************************************
//
// LPCTSTR GetHrSz(HRESULT hr)
//
//****************************************************************************

LPCTSTR GetHrSz(HRESULT hr)
{
LPCTSTR pcsz;
static TCHAR _szHRESULT[] = "0x12345678";

switch (hr)
{
STRING_CASE(S_OK);
STRING_CASE(S_FALSE);

STRING_CASE(NM_E_NOT_INITIALIZED);
STRING_CASE(NM_E_CHANNEL_ALREADY_EXISTS);
STRING_CASE(NM_E_NO_T120_CONFERENCE);
STRING_CASE(NM_E_NOT_ACTIVE);

STRING_CASE(DRAGDROP_S_CANCEL);
STRING_CASE(DRAGDROP_S_DROP);
STRING_CASE(DRAGDROP_S_USEDEFAULTCURSORS);

STRING_CASE(E_UNEXPECTED);
STRING_CASE(E_NOTIMPL);
STRING_CASE(E_OUTOFMEMORY);
STRING_CASE(E_INVALIDARG);
STRING_CASE(E_NOINTERFACE);
STRING_CASE(E_POINTER);
STRING_CASE(E_HANDLE);
STRING_CASE(E_ABORT);
STRING_CASE(E_FAIL);
STRING_CASE(E_ACCESSDENIED);

STRING_CASE(CLASS_E_NOAGGREGATION);

STRING_CASE(CO_E_NOTINITIALIZED);
STRING_CASE(CO_E_ALREADYINITIALIZED);
STRING_CASE(CO_E_INIT_ONLY_SINGLE_THREADED);

STRING_CASE(REGDB_E_INVALIDVALUE);
STRING_CASE(REGDB_E_CLASSNOTREG);
STRING_CASE(REGDB_E_IIDNOTREG);

STRING_CASE(DV_E_DVASPECT);
STRING_CASE(DV_E_LINDEX);
STRING_CASE(DV_E_TYMED);
STRING_CASE(DV_E_FORMATETC);

default:
wsprintf(_szHRESULT, TEXT("%#lx"), hr);
pcsz = _szHRESULT;
break;
}

return pcsz;
}

//****************************************************************************
//
// LPCTSTR GetConfnSz(CONFN confn)
//
//****************************************************************************

LPCTSTR GetConfnSz(CONFN confn)
{
LPCTSTR pcsz;
static TCHAR _szMsg[256];

switch (confn)
{
STRING_CASE(CONFN_FT_UI);
STRING_CASE(CONFN_FT_OFFERED);
STRING_CASE(CONFN_FT_STARTED);
STRING_CASE(CONFN_FT_PROGRESS);
STRING_CASE(CONFN_FT_COMPLETE);
STRING_CASE(CONFN_FT_CANCELED);

STRING_CASE(CONFN_CALL_INCOMPATIBLE);
STRING_CASE(CONFN_CALL_OLDER);
STRING_CASE(CONFN_CALL_NEWER);
STRING_CASE(CONFN_CALL_IGNORED);
STRING_CASE(CONFN_CALL_FAILED);
STRING_CASE(CONFN_CALL_IN_CONFERENCE);

STRING_CASE(CONFN_NM_STARTED);
STRING_CASE(CONFN_NM_STOPPED);

default:
wsprintf(_szMsg, TEXT("CONFN_%08X"), confn);
pcsz = _szMsg;
break;
}
return pcsz;
}

//****************************************************************************
//
// LPCTSTR GetNmchSz(ULONG uch)
//
//****************************************************************************

LPCTSTR GetNmchSz(ULONG uch)
{
LPCTSTR pcsz;
static TCHAR _szMsg[256];

switch (uch)
{
case NMCH_DATA:  pcsz = TEXT("Data");  break;
case NMCH_AUDIO: pcsz = TEXT("Audio"); break;
case NMCH_VIDEO: pcsz = TEXT("Video"); break;
case NMCH_SHARE: pcsz = TEXT("Share"); break;
case NMCH_FT:    pcsz = TEXT("Ft");    break;

case NMCH_ALL:
case NMCH_NONE:
default:
wsprintf(_szMsg, TEXT("%04X"), uch);
pcsz = _szMsg;
break;
}
return pcsz;
}

//****************************************************************************
//
// LPCTSTR GetCallStateSz(NM_CALL_STATE uState)
//
//****************************************************************************

LPCTSTR GetCallStateSz(NM_CALL_STATE uState)
{
LPCTSTR pcsz;
static TCHAR _szMsg[256];

switch (uState)
{
STRING_CASE(NM_CALL_INVALID);
STRING_CASE(NM_CALL_INIT);
STRING_CASE(NM_CALL_RING);
STRING_CASE(NM_CALL_SEARCH);
STRING_CASE(NM_CALL_WAIT);
STRING_CASE(NM_CALL_ACCEPTED);
STRING_CASE(NM_CALL_REJECTED);
STRING_CASE(NM_CALL_CANCELED);

default:
wsprintf(_szMsg, TEXT("NM_CALL_%04X"), uState);
pcsz = _szMsg;
break;
}
return pcsz;
}

//****************************************************************************
//
// LPCTSTR GetConferenceStateSz(NM_CONFERENCE_STATE uState)
//
//****************************************************************************

LPCTSTR GetConferenceStateSz(NM_CONFERENCE_STATE uState)
{
LPCTSTR pcsz;
static TCHAR _szMsg[256];

switch (uState)
{
STRING_CASE(NM_CONFERENCE_IDLE);
STRING_CASE(NM_CONFERENCE_WAITING);
STRING_CASE(NM_CONFERENCE_INITIALIZING);
STRING_CASE(NM_CONFERENCE_ACTIVE);

default:
wsprintf(_szMsg, TEXT("NM_CONFERENCE_%04X"), uState);
pcsz = _szMsg;
break;
}
return pcsz;
}

//****************************************************************************
//
// VOID FAR PASCAL AssertProc(LPSTR lpszMsg, LPSTR lpszAssert, LPSTR lpszFile, UINT line)
//
//****************************************************************************

VOID FAR PASCAL AssertProc(LPSTR lpszMsg, LPSTR lpszAssert, LPSTR lpszFile, UINT line)
{
int  id;
TCHAR szBuffer[1024]; // a big buffer

wsprintf((LPSTR)szBuffer,
"Assert: %s\nFile %s, Line %d",
(lpszMsg && *lpszMsg) ? lpszMsg  : lpszAssert,
lpszFile ? lpszFile : "", line);

OutputDebugString(szBuffer);

do {
id = MessageBox(NULL, (LPSTR)szBuffer, TEXT("NmUi Assert"),
MB_ICONHAND | MB_ABORTRETRYIGNORE | MB_SYSTEMMODAL);

switch (id)
{
case IDRETRY:
DebugBreak();
break;
case IDABORT:
FatalAppExit(0, (LPSTR)szBuffer);
break;
case IDIGNORE:
default:
break;
}
} while (id == IDRETRY);
}

//****************************************************************************
//
// VOID Log(COLORREF cv, LPTSTR pszMsg)
//
//****************************************************************************

VOID Log(COLORREF cv, LPTSTR pszMsg)
{
int iItem;
iItem = SendMessage(ghwndMsg, LB_ADDSTRING, 0, (LPARAM) pszMsg);

SendMessage(ghwndMsg, LB_SETITEMDATA, (WPARAM)iItem, (LPARAM) cv);

PostMessage(ghwndMsg, WM_VSCROLL, SB_BOTTOM, 0);
}

//****************************************************************************
//
// VOID ClearLog(void)
//
//****************************************************************************

VOID ClearLog(void)
{
SendMessage(ghwndMsg, LB_RESETCONTENT, 0, 0);
}

//****************************************************************************
//
// VOID TraceMsg(PSTR pszFormat,...)
//
//****************************************************************************

VOID TraceMsg(PSTR pszFormat,...)
{
TCHAR szMsg[1024];
va_list v1;

va_start(v1, pszFormat);
wvsprintfA(szMsg, pszFormat, v1);
va_end(v1);

Log(RGB(0, 0, 0), szMsg);

lstrcat(szMsg, "\r\n");
OutputDebugString(szMsg);
}

//****************************************************************************
//
// VOID WarnMsg(PSTR pszFormat,...)
//
//****************************************************************************

VOID WarnMsg(PSTR pszFormat,...)
{
TCHAR szMsg[1024];
va_list v1;

va_start(v1, pszFormat);
wvsprintfA(szMsg, pszFormat, v1);
va_end(v1);

Log(RGB(0, 0, 255), szMsg);

lstrcat(szMsg, "\r\n");
OutputDebugString(szMsg);
}

//****************************************************************************
//
// VOID ErrorMsg(PSTR pszFormat,...)
//
//****************************************************************************

VOID ErrorMsg(PSTR pszFormat,...)
{
TCHAR szMsg[1024];
va_list v1;

va_start(v1, pszFormat);
wvsprintfA(szMsg, pszFormat, v1);
va_end(v1);

Log(RGB(255, 0, 0), szMsg);

lstrcat(szMsg, "\r\n");
OutputDebugString(szMsg);
}

//****************************************************************************
//
// VOID LogMsg(COLORREF cv, PSTR pszFormat,...)
//
//****************************************************************************

VOID LogMsg(COLORREF cv, PSTR pszFormat,...)
{
TCHAR szMsg[1024];
va_list v1;

va_start(v1, pszFormat);
wvsprintfA(szMsg, pszFormat, v1);
va_end(v1);

Log(cv, szMsg);

lstrcat(szMsg, "\r\n");
OutputDebugString(szMsg);
}

//****************************************************************************
//
// VOID DumpCallInfo(INmCall * pCall)
//
//****************************************************************************

VOID DumpCallInfo(INmCall * pCall)
{
HRESULT hr;
BSTR bstrName;
BSTR bstrAddr;
NM_ADDR_TYPE uType;
NM_CALL_STATE uState;
INmConference * pConference = NULL;

OBJECT_MSG("[%08X] - dump call info", pCall);
if (NULL == pCall)
{
ERROR_MSG("DumpCallInfo: Invalid Call");
return;
}

hr = pCall->GetName(&bstrName);
if (FAILED(hr))
{
bstrName = NULL;
    ERROR_MSG("DumpCallInfo: GetName failed");
}

if (FAILED(pCall->GetAddr(&bstrAddr, &uType)))
{
bstrAddr = NULL;
uType = NM_ADDR_UNKNOWN;
    ERROR_MSG("DumpCallInfo: GetAddr failed");
}

    if (FAILED(pCall->GetState(&uState)))
    {
uState = NM_CALL_INVALID;
    ERROR_MSG("DumpCallInfo: GetState failed");
    }

hr = pCall->GetConference(&pConference);
if (FAILED(hr))
{
    WARN_MSG("DumpCallInfo: GetConference failed %s", GetHrSz(hr));
}

TRACE_MSG("Call=[%s] Addr=[%s], Type=%d, State=%s %s conf=%08X",
(LPTSTR) BTSTR(bstrName), (LPTSTR) BTSTR(bstrAddr),
uType, GetCallStateSz(uState),
(S_OK == pCall->IsIncoming()) ? TEXT("(Incoming)") : TEXT(""), pConference);

if (NULL != pConference)
{
pConference->Release();
}

SysFreeString(bstrName);
SysFreeString(bstrAddr);
}

//****************************************************************************
//
// VOID DumpAllCalls(void)
//
//****************************************************************************

VOID DumpAllCalls(void)
{
if (NULL == g_pListCall)
return;

for (POSITION pos = g_pListCall->GetHeadPosition(); NULL != pos; )
{
INmCall * pCall = (INmCall *) g_pListCall->GetNext(pos);
DumpCallInfo(pCall);
}
}

//****************************************************************************
//
// VOID DumpConferenceInfo(INmConference * pConference)
//
//****************************************************************************

VOID DumpConferenceInfo(INmConference * pConference)
{
HRESULT hr;
BSTR bstr;
ULONG cMember;
ULONG cChannel;
NM_CONFERENCE_STATE uState;

if (NULL == pConference)
{
ERROR_MSG("DumpConferenceInfo: Invalid conference");
return;
}

hr = pConference->GetName(&bstr);
if (FAILED(hr))
{
ERROR_MSG("GetName failed");
bstr = NULL;
}

hr = pConference->GetState(&uState);
if (FAILED(hr))
{
ERROR_MSG("GetState failed?");
}

hr = pConference->GetMemberCount(&cMember);
if (FAILED(hr))
{
ERROR_MSG("GetMemberCount failed");
cMember = 0;
}

hr = pConference->GetChannelCount(&cChannel);
if (FAILED(hr))
{
ERROR_MSG("GetChannelCount failed");
cChannel = 0;
}

TRACE_MSG("[%08X] - Conference=[%s] state=%s", pConference,
(LPTSTR) BTSTR(bstr), GetConferenceStateSz(uState));
TRACE_MSG("  MemberCount=%d, ChannelCount=%d", cMember, cChannel);

SysFreeString(bstr);
}

//****************************************************************************
//
// VOID DumpMemberInfo(INmMember * pMember)
//
//****************************************************************************

VOID DumpMemberInfo(INmMember * pMember)
{
HRESULT hr;
BSTR bstr;
BOOL fLocal;
BOOL fMcu;
BOOL fTop;
ULONG uchCaps;
ULONG uVer;
INmConference * pConference;

hr = pMember->GetName(&bstr);
if (FAILED(hr))
{
ERROR_MSG("Problem with GetName");
}

hr = pMember->GetNmchCaps(&uchCaps);
if (FAILED(hr))
{
ERROR_MSG("Problem with GetNmchCaps");
uchCaps = 0;
}

hr = pMember->GetNmVersion(&uVer);
if (FAILED(hr))
{
ERROR_MSG("Problem with GetNmVersion");
uVer = 0;
}

fLocal = S_OK == pMember->IsSelf();
fMcu = S_OK == pMember->IsMCU();

fTop = FALSE;
hr = pMember->GetConference(&pConference);
if (FAILED(hr))
{
TRACE_MSG("Problem with GetConference");
}
else
{
INmMember * pMemberTop;
if (SUCCEEDED(pConference->GetTopProvider(&pMemberTop)))
{
fTop = (pMember == pMemberTop);
pMemberTop->Release();
}
pConference->Release();
}

TRACE_MSG("[%08X] Member=[%s] ", pMember, (LPTSTR) BTSTR(bstr));
TRACE_MSG("  Local=%d, MCU=%d, Top=%d, uch=%08X, version=%d", fLocal, fMcu, fTop, uchCaps, uVer);

SysFreeString(bstr);
}

//****************************************************************************
//
// VOID DumpChannelInfo(INmChannel * pChannel)
//
//****************************************************************************

VOID DumpChannelInfo(INmChannel * pChannel)
{
HRESULT hr;
ULONG uch;
ULONG cMember;

hr = pChannel->GetNmch(&uch);
if (FAILED(hr))
{
ERROR_MSG("GetNmch failed");
uch = 0;
}

hr = pChannel->GetMemberCount(&cMember);
if (FAILED(hr))
{
ERROR_MSG("GetMemberCount failed");
cMember = 0;
}

TRACE_MSG("[%08X] - Channel=[%s] MemberCount=%d", pChannel, GetNmchSz(uch), cMember);
switch (uch)
{
case NMCH_AUDIO:
{
INmChannelAudio * pChannelAudio;
hr = pChannel->QueryInterface(IID_INmChannelAudio, (void **)&pChannelAudio);
if (FAILED(hr))
{
ERROR_MSG("Not an audio channel?");
}
else
{
NM_AUDIO_STATE uState;
pChannelAudio->GetState(&uState);
TRACE_MSG("Audio State: %d", uState);
pChannelAudio->Release();
}
break;
}

case NMCH_VIDEO:
{
INmChannelVideo * pChannelVideo;
hr = pChannel->QueryInterface(IID_INmChannelVideo, (void **)&pChannelVideo);
if (FAILED(hr))
{
ERROR_MSG("Not an video channel?");
}
else
{
NM_VIDEO_STATE uState;
pChannelVideo->GetState(&uState);
TRACE_MSG("Video State: %d (%s)", uState,
(S_OK == pChannelVideo->IsActive()) ? TEXT("Active") : TEXT("Inactive"));
pChannelVideo->Release();
}
break;
}

case NMCH_DATA:
{
INmChannelData * pChannelData;
hr = pChannel->QueryInterface(IID_INmChannelData, (void **)&pChannelData);
if (FAILED(hr))
{
ERROR_MSG("Not a data channel?");
}
else
{
TRACE_MSG("Data channel is %s",
(S_OK == pChannelData->IsActive()) ? TEXT("Active") : TEXT("Inactive"));
pChannelData->Release();
}
break;
}
default:
break;
}
}

//****************************************************************************
//
// VOID DoEnumChannelMember(INmChannel * pChannel)
//
//****************************************************************************

VOID DoEnumChannelMember(INmChannel * pChannel)
{
HRESULT hr;
IEnumNmMember * pEnum;
ULONG cFetched;
ULONG cMember;

ASSERT(NULL != pChannel);

hr = pChannel->GetMemberCount(&cMember);
if (FAILED(hr))
{
    ERROR_MSG("GetMemberCount Failed (%s)", GetHrSz(hr));
    return;
}
TRACE_MSG("%d members in channel (%08X)", cMember, pChannel);

if (0 == cMember)
return;

if (cMember > 10)
cMember = 10;

hr = pChannel->EnumMember(&pEnum);
if (FAILED(hr))
{
    ERROR_MSG("EnumMember Failed (%s)", GetHrSz(hr));
    return;
}

INmMember * rgpMember[10];
hr = pEnum->Next(cMember, rgpMember, &cFetched);
if (FAILED(hr))
{
    ERROR_MSG("EnumMember Failed (%s)", GetHrSz(hr));
}
else
{
for (UINT iMember = 0; iMember < cFetched; iMember++)
{
DumpMemberInfo(rgpMember[iMember]);
rgpMember[iMember]->Release();
}
}

pEnum->Release();

}

//****************************************************************************
//
// VOID DoEnumConferenceMember(INmConference * pConference)
//
//****************************************************************************

VOID DoEnumConferenceMember(INmConference * pConference)
{
HRESULT hr;
IEnumNmMember * pEnum;
ULONG cFetched;
ULONG cMember;

ASSERT(NULL != pConference);
hr = pConference->GetMemberCount(&cMember);
if (FAILED(hr))
{
    ERROR_MSG("GetMemberCount Failed (%s)", GetHrSz(hr));
    return;
}
TRACE_MSG("%d members in conference (%08X)", cMember, pConference);

if (cMember > 10)
cMember = 10;

hr = pConference->EnumMember(&pEnum);
if (FAILED(hr))
{
    ERROR_MSG("EnumMember Failed (%s)", GetHrSz(hr));
    return;
}

INmMember * rgpMember[10];
hr = pEnum->Next(cMember, rgpMember, &cFetched);
if (FAILED(hr))
{
    ERROR_MSG("EnumMember Failed (%s)", GetHrSz(hr));
}
else
{
for (UINT iMember = 0; iMember < cFetched; iMember++)
{
DumpMemberInfo(rgpMember[iMember]);
rgpMember[iMember]->Release();
}
}

pEnum->Release();
}

//****************************************************************************
//
// VOID DoEnumAll(void)
//
//****************************************************************************

VOID DoEnumAll(void)
{
HRESULT hr;
IEnumNmChannel * pEnum;
ULONG cFetched;
ULONG cChannel;
INmConference * pConference = GetCurrentConference();

if (NULL == pConference)
{
TRACE_MSG("No current conference");
return;
}

DumpConferenceInfo(pConference);

DoEnumConferenceMember(pConference);

hr = pConference->GetChannelCount(&cChannel);
if (FAILED(hr))
{
    ERROR_MSG("GetChannelCount Failed (%s)", GetHrSz(hr));
    return;
}
TRACE_MSG("%d channels in conference", cChannel);

if (cChannel > 10)
cChannel = 10;

hr = pConference->EnumChannel(&pEnum);
if (FAILED(hr))
{
    ERROR_MSG("EnumChannel Failed (%s)", GetHrSz(hr));
    return;
}

INmChannel * rgpChannel[10];
hr = pEnum->Next(cChannel, rgpChannel, &cFetched);
if (FAILED(hr))
{
    ERROR_MSG("EnumMember Failed (%s)", GetHrSz(hr));
}
else
{
for (UINT iChannel = 0; iChannel < cFetched; iChannel++)
{
DumpChannelInfo(rgpChannel[iChannel]);
DoEnumChannelMember(rgpChannel[iChannel]);
rgpChannel[iChannel]->Release();
}
}

pEnum->Release();
}


DLG.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       DLG.CPP
//  Content:    Dialog Box Procedures
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

DWSTR _rgCallType[] = {
{NM_CALL_DEFAULT, TEXT("Default")},
{NM_CALL_T120,    TEXT("T120")},
{NM_CALL_H323,    TEXT("H323")},
};

DWSTR _rgAddrType[] = {
{NM_ADDR_UNKNOWN,      TEXT("Unknown")},
{NM_ADDR_IP,           TEXT("IP")},
{NM_ADDR_MACHINENAME,  TEXT("Machine name")},
{NM_ADDR_PSTN,         TEXT("PSTN")},
{NM_ADDR_ULS,          TEXT("ULS")},
{NM_ADDR_H323_GATEWAY, TEXT("H323 Gateway")},
};

VOID InitLBox(HWND hwndCombo, DWSTR *pdwstr, UINT cElt, DWORD dwDefault)
{
DWORD dwIndex;
DWORD iSel = 0;

ASSERT(NULL != hwndCombo);

for ( ; cElt > 0; cElt--, pdwstr++)
{
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) pdwstr->psz);
SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, (LPARAM) pdwstr->dw);
if (pdwstr->dw == dwDefault)
{
iSel = dwIndex;
}
}

SendMessage(hwndCombo, CB_SETCURSEL, dwDefault, 0);
}

//****************************************************************************
//
// DWORD GetLBoxDw(HWND hwndCombo)
//
//****************************************************************************

DWORD GetLBoxDw(HWND hwndCombo)
{
int iSel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
if (CB_ERR == iSel)
return 0;

return SendMessage(hwndCombo, CB_GETITEMDATA, (WPARAM) iSel, 0);
}

//****************************************************************************
//
// VOID InitUserLBox(HWND hdlg, BOOL fEveryone)
//
//****************************************************************************

VOID InitUserLBox(HWND hdlg, BOOL fEveryone)
{
HWND hwndCombo = GetDlgItem(hdlg, IDL_USERS);
ASSERT(NULL != hwndCombo);
InitializeUserList(hwndCombo, fEveryone);
}

//****************************************************************************
//
// VOID InitializeUserList(HWND hwndCombo, BOOL fEveryone)
//
//****************************************************************************

VOID InitializeUserList(HWND hwndCombo, BOOL fEveryone)
{
if (NULL == g_pListUser)
return;

POSITION pos = g_pListUser->GetHeadPosition();
while (NULL != pos)
{
POSITION posSav = pos;
PUSER pUser = (PUSER) g_pListUser->GetNext(pos);
AddUserToUList(pUser, hwndCombo, fEveryone);
}

SendMessage(hwndCombo, CB_SETCURSEL, 0, 0);
}

//****************************************************************************
//
// VOID AddUserToUList(PUSER pUser, HWND hwndCombo, BOOL fEveryone)
//
//****************************************************************************

VOID AddUserToUList(PUSER pUser, HWND hwndCombo, BOOL fEveryone)
{
DWORD dwIndex;
ASSERT(NULL != pUser);
if (fEveryone && (S_OK == pUser->pMember->IsSelf()))
{
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) TEXT("<all>"));
}
else
{
dwIndex = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) pUser->pszName);
}
SendMessage(hwndCombo, CB_SETITEMDATA, dwIndex, (LPARAM) pUser);
}

//****************************************************************************
//
// VOID RemoveUserFromUList(PUSER pUser, HWND hwndCombo)
//
//****************************************************************************

VOID RemoveUserFromUList(PUSER pUser, HWND hwndCombo)
{
ASSERT(NULL != pUser);

if (!IsWindow(hwndCombo))
{
ERROR_MSG("RemoveUserFromUList: invalid hwndcombo?");
return;
}

for (DWORD dwIndex = 0; ; dwIndex++)
{
DWORD dw = SendMessage(hwndCombo, CB_GETITEMDATA, dwIndex, 0);
if (CB_ERR == dw)
{
break;
}
if (pUser == (PUSER) dw)
{
SendMessage(hwndCombo, CB_DELETESTRING, (WPARAM)dwIndex, 0);
return;
}
}

ERROR_MSG("RemoveUserFromUList: Unable to delete [%s]", pUser->pszName);
}

//****************************************************************************
//
// VOID DrawUserItem(LPDRAWITEMSTRUCT lpdis)
//
//****************************************************************************

VOID DrawUserItem(LPDRAWITEMSTRUCT lpdis)
{
if (lpdis->itemID == -1)
return;

PUSER pUser = (PUSER) lpdis->itemData;
COLORREF cvText;
COLORREF cvBack;

cvText = SetTextColor(lpdis->hDC, GetSysColor((lpdis->itemState & ODS_SELECTED)
? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));

cvBack = SetBkColor(lpdis->hDC, GetSysColor((lpdis->itemState & ODS_SELECTED)
? COLOR_HIGHLIGHT : COLOR_WINDOW));

if (NULL == pUser->pszName)
{
static const TCHAR szUnknownUser[] = TEXT("? user");
ExtTextOut(lpdis->hDC, lpdis->rcItem.left+2, lpdis->rcItem.top+2,
ETO_CLIPPED | ETO_OPAQUE, &lpdis->rcItem,
szUnknownUser, CCHMAX(szUnknownUser)-1, NULL);
}
else
{
ExtTextOut(lpdis->hDC, lpdis->rcItem.left+2, lpdis->rcItem.top+2,
ETO_CLIPPED | ETO_OPAQUE, &lpdis->rcItem,
pUser->pszName, lstrlen(pUser->pszName), NULL);
}

SetTextColor(lpdis->hDC, cvText);
SetBkColor(lpdis->hDC, cvBack);

if (lpdis->itemState & ODS_FOCUS)
{
DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
}
}

//****************************************************************************
//
// LRESULT CALLBACK ConferenceDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK ConferenceDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
CenterWindow(hdlg, ghwndMain);

SetDlgItemText(hdlg, IDE_NAME, gPref.pszConferenceName);
SetDlgItemText(hdlg, IDE_PASSWORD, gPref.pszPassword);

{ // check channel types
DWORD dwFlag = 0x01;
for (int id = IDC_NMCH_FIRST; id <= IDC_NMCH_LAST; id++)
{
CheckDlgButton(hdlg, id, 0 != (gPref.dwNmch & dwFlag));
dwFlag <<= 1;
}
}

return TRUE;

case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
GetDlgItemPsz(hdlg, IDE_NAME, &gPref.pszConferenceName);
GetDlgItemPsz(hdlg, IDE_PASSWORD, &gPref.pszPassword);

{ // check media types
DWORD dwFlag = 0x01;
gPref.dwNmch = 0;
for (int id = IDC_NMCH_FIRST; id <= IDC_NMCH_LAST; id++)
{
if (IsDlgButtonChecked(hdlg, id))
gPref.dwNmch |= dwFlag;
dwFlag <<= 1;
}
}

EndDialog(hdlg, 0);
return TRUE;

case IDCANCEL:
EndDialog(hdlg, 1);
return TRUE;

default:
return TRUE;
} /* switch (cmd) */

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// BOOL DlgNewConference(VOID)
//
//****************************************************************************

BOOL DlgNewConference(VOID)
{
return (0 == DialogBox(ghInst, MAKEINTRESOURCE(IDD_CONFERENCE),
ghwndMain, (DLGPROC)ConferenceDlgProc));
}

//****************************************************************************
//
// LRESULT CALLBACK SendFileDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK SendFileDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
HWND hwnd;

CenterWindow(hdlg, ghwndMain);

hwnd = GetDlgItem(hdlg, IDC_USERNAME);

InitUserLBox(hdlg, TRUE);

// Set the default filename
SetDlgItemText(hdlg, IDE_FILENAME, gPref.pszFileName);

return TRUE;
}

case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
{
INmMember * pMember = NULL;
int iSel = SendDlgItemMessage(hdlg, IDL_USERS, CB_GETCURSEL, 0, 0);
if (CB_ERR != iSel)
{
PUSER pUser = (PUSER) SendDlgItemMessage(hdlg, IDL_USERS,
CB_GETITEMDATA, (WPARAM) iSel, 0);
if (NULL != pUser)
{
pMember = pUser->pMember;
if (S_OK == pMember->IsSelf())
pMember = NULL; // everyone
}
}
GetDlgItemPsz(hdlg, IDE_FILENAME, &gPref.pszFileName);

SendFile(pMember);
EndDialog(hdlg, 0);
return TRUE;
}

case IDCANCEL:
EndDialog(hdlg, 1);
return TRUE;

case IDB_BROWSE:
{
TCHAR szFileName[MAX_PATH];

// Get the file name
if (!FGetFileName(szFileName))
return FALSE;

SetDlgItemText(hdlg, IDE_FILENAME, szFileName);
return TRUE;
}

default:
return TRUE;
} /* switch (cmd) */

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// BOOL DlgSendFile(VOID)
//
//****************************************************************************

BOOL DlgSendFile(VOID)
{
return (0 == DialogBox(ghInst, MAKEINTRESOURCE(IDD_SENDFILE),
ghwndMain, (DLGPROC)SendFileDlgProc));
}

//****************************************************************************
//
// LRESULT CALLBACK CallConfDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK CallConfDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
CenterWindow(hdlg, ghwndMain);

SetDlgItemText(hdlg, IDE_ADDR,       gPref.pszAddr);
SetDlgItemText(hdlg, IDE_CONFERENCE, gPref.pszConferenceName);
SetDlgItemText(hdlg, IDE_PASSWORD,   gPref.pszPassword);

InitLBox(GetDlgItem(hdlg, IDL_ADDR_TYPE),
_rgAddrType, ARRAY_ELEMENTS(_rgAddrType), gPref.nmAddrType);
InitLBox(GetDlgItem(hdlg, IDL_CALL_TYPE),
_rgCallType, ARRAY_ELEMENTS(_rgCallType), gPref.nmCallType);
return TRUE;

case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
GetDlgItemPsz(hdlg, IDE_ADDR,       &gPref.pszAddr);
GetDlgItemPsz(hdlg, IDE_CONFERENCE, &gPref.pszConferenceName);
GetDlgItemPsz(hdlg, IDE_PASSWORD,   &gPref.pszPassword);

gPref.nmAddrType = (NM_ADDR_TYPE) GetLBoxDw(GetDlgItem(hdlg, IDL_ADDR_TYPE));
gPref.nmCallType = (NM_CALL_TYPE) GetLBoxDw(GetDlgItem(hdlg, IDL_CALL_TYPE));

EndDialog(hdlg, 0);
return TRUE;

case IDCANCEL:
EndDialog(hdlg, 1);
return TRUE;

default:
return TRUE;
} /* switch (cmd) */

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// BOOL DlgCallConf(VOID)
//
//****************************************************************************

BOOL DlgCallConf(VOID)
{
return (0 == DialogBox(ghInst, MAKEINTRESOURCE(IDD_CALL_CONF),
ghwndMain, (DLGPROC)CallConfDlgProc));
}

//****************************************************************************
//
// LRESULT CALLBACK CallDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK CallDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
CenterWindow(hdlg, ghwndMain);

SetDlgItemText(hdlg, IDE_ADDR, gPref.pszAddr);
InitLBox(GetDlgItem(hdlg, IDL_ADDR_TYPE),
_rgAddrType, ARRAY_ELEMENTS(_rgAddrType), gPref.nmAddrType);
InitLBox(GetDlgItem(hdlg, IDL_CALL_TYPE),
_rgCallType, ARRAY_ELEMENTS(_rgCallType), gPref.nmCallType);
return TRUE;

case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
GetDlgItemPsz(hdlg, IDE_ADDR, &gPref.pszAddr);

gPref.nmAddrType = (NM_ADDR_TYPE) GetLBoxDw(GetDlgItem(hdlg, IDL_ADDR_TYPE));
gPref.nmCallType = (NM_CALL_TYPE) GetLBoxDw(GetDlgItem(hdlg, IDL_CALL_TYPE));

EndDialog(hdlg, 0);
return TRUE;

case IDCANCEL:
EndDialog(hdlg, 1);
return TRUE;

default:
return TRUE;
} /* switch (cmd) */

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// BOOL DlgNewCall(VOID)
//
//****************************************************************************

BOOL DlgNewCall(VOID)
{
return (0 == DialogBox(ghInst, MAKEINTRESOURCE(IDD_CALL),
ghwndMain, (DLGPROC)CallDlgProc));
}

//****************************************************************************
//
// LRESULT CALLBACK OptionsDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK OptionsDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
CenterWindow(hdlg, ghwndMain);

CheckDlgButton(hdlg, IDC_MONITOR, gPref.fAutoMonitor);

CheckDlgButton(hdlg, IDC_HOST, gPref.fAutoHost);
SetDlgItemText(hdlg, IDE_CONFERENCE_NAME, gPref.pszConferenceName);

CheckDlgButton(hdlg, IDC_CALL_USER, gPref.fAutoCall);
SetDlgItemText(hdlg, IDE_USER_NAME, gPref.pszAddr);

CheckDlgButton(hdlg, IDC_AUTO_CHAT, gPref.fAutoChat);

CheckDlgButton(hdlg, IDC_MSG_TRACE,   gPref.fMsgTrace);
CheckDlgButton(hdlg, IDC_MSG_OBJECTS, gPref.fMsgObjects);
CheckDlgButton(hdlg, IDC_MSG_NOTIFY,  gPref.fMsgNotify);

CheckRadioButton(hdlg, IDR_INIT_NORMAL, IDR_INIT_NO_LAUNCH,
gPref.iInit + IDR_INIT_NORMAL);
return TRUE;

case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
gPref.fAutoMonitor= IsDlgButtonChecked(hdlg, IDC_MONITOR);

gPref.fAutoHost   = IsDlgButtonChecked(hdlg, IDC_HOST);
GetDlgItemPsz(hdlg, IDE_CONFERENCE_NAME, &gPref.pszConferenceName);

gPref.fAutoCall   = IsDlgButtonChecked(hdlg, IDC_CALL_USER);
GetDlgItemPsz(hdlg, IDE_USER_NAME, &gPref.pszAddr);

gPref.fAutoChat   = IsDlgButtonChecked(hdlg, IDC_AUTO_CHAT);

gPref.fMsgTrace   = IsDlgButtonChecked(hdlg, IDC_MSG_TRACE);
gPref.fMsgObjects = IsDlgButtonChecked(hdlg, IDC_MSG_OBJECTS);
gPref.fMsgNotify  = IsDlgButtonChecked(hdlg, IDC_MSG_NOTIFY);

if (IsDlgButtonChecked(hdlg, IDR_INIT_NORMAL))
gPref.iInit = NM_INIT_NORMAL;
else if (IsDlgButtonChecked(hdlg, IDR_INIT_CONTROL))
gPref.iInit = NM_INIT_CONTROL;
else if (IsDlgButtonChecked(hdlg, IDR_INIT_NO_LAUNCH))
gPref.iInit = NM_INIT_NO_LAUNCH;

EndDialog(hdlg, 0);
return TRUE;

case IDCANCEL:
EndDialog(hdlg, 1);
return TRUE;

default:
return TRUE;
} /* switch (cmd) */

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// BOOL DlgOptions(VOID)
//
//****************************************************************************

BOOL DlgOptions(VOID)
{
return (0 == DialogBox(ghInst, MAKEINTRESOURCE(IDD_OPTIONS),
ghwndMain, (DLGPROC)OptionsDlgProc));
}

//****************************************************************************
//
// VOID UpdateAudioSettings(HWND hdlg)
//
//****************************************************************************

VOID UpdateAudioSettings(HWND hdlg)
{
int iSel = SendDlgItemMessage(hdlg, IDL_USERS, CB_GETCURSEL, 0, 0);
if (CB_ERR == iSel)
return;

PUSER pUser = (PUSER) SendDlgItemMessage(hdlg, IDL_USERS,
CB_GETITEMDATA, (WPARAM) iSel, 0);
INmChannelAudio * pChannel = pUser->m_pChannelAudio;
if (NULL != pChannel)
{
NM_AUDIO_STATE uState;
if (SUCCEEDED(pChannel->GetState(&uState)))
{
TRACE_MSG("Audio State is %d", uState);
}

DWORD dwLevel;
if (SUCCEEDED(pChannel->GetProperty(NM_AUDPROP_LEVEL, &dwLevel)))
{
SendDlgItemMessage (hdlg, IDC_LEVEL, TBM_SETPOS, TRUE,
dwLevel );

SetDlgItemInt(hdlg, IDE_LEVEL, dwLevel, FALSE);
}

CheckDlgButton(hdlg, IDC_ACTIVE,
(S_OK == pChannel->IsActive()) ? BST_CHECKED : BST_UNCHECKED);

DWORD fPause = FALSE;
pChannel->GetProperty(NM_AUDPROP_PAUSE, &fPause);
CheckDlgButton(hdlg, IDC_PAUSE, fPause ? BST_CHECKED : BST_UNCHECKED);
}
}

//****************************************************************************
//
// LRESULT CALLBACK AudioDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK AudioDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HRESULT hr;

switch (uMsg)
{
case WM_INITDIALOG:
{
CenterWindow(hdlg, ghwndMain);

InitUserLBox(hdlg, FALSE);

SendDlgItemMessage (hdlg, IDC_LEVEL, TBM_SETRANGEMIN, FALSE,
0 );
SendDlgItemMessage (hdlg, IDC_LEVEL, TBM_SETRANGEMAX, FALSE,
NM_MAX_AUDIO_LEVEL );
SendDlgItemMessage (hdlg, IDC_LEVEL, TBM_SETTICFREQ,
8096, 0 );
SendDlgItemMessage (hdlg, IDC_LEVEL, TBM_SETPAGESIZE,
0, 8096 );
SendDlgItemMessage (hdlg, IDC_LEVEL, TBM_SETLINESIZE,
0, 256 );

UpdateAudioSettings(hdlg);
return TRUE;
}

case WM_DRAWITEM:
DrawUserItem((LPDRAWITEMSTRUCT) lParam);
break;

case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
case IDCANCEL:
EndDialog(hdlg, 1);
return TRUE;

case IDB_UPDATE:
{
UpdateAudioSettings(hdlg);
return TRUE;
}

case IDC_ACTIVE:
{
int iSel = SendDlgItemMessage(hdlg, IDL_USERS, CB_GETCURSEL, 0, 0);
if (CB_ERR != iSel)
{
PUSER pUser = (PUSER) SendDlgItemMessage(hdlg, IDL_USERS,
CB_GETITEMDATA, (WPARAM) iSel, 0);
INmChannelAudio * pChannel = pUser->m_pChannelAudio;
if (NULL != pChannel)
{
// Flip the active state
BOOL fActive = (S_OK != pChannel->IsActive());
hr = pChannel->SetActive(fActive);
TRACE_MSG("SetActive result=%s", GetHrSz(hr));
}
}
return TRUE;
}

case IDC_PAUSE:
{
int iSel = SendDlgItemMessage(hdlg, IDL_USERS, CB_GETCURSEL, 0, 0);
if (CB_ERR != iSel)
{
PUSER pUser = (PUSER) SendDlgItemMessage(hdlg, IDL_USERS,
CB_GETITEMDATA, (WPARAM) iSel, 0);
INmChannelAudio * pChannel = pUser->m_pChannelAudio;
if (NULL != pChannel)
{
// Flip the pause state
DWORD fPause = IsDlgButtonChecked(hdlg, IDC_PAUSE);
hr = pChannel->SetProperty(NM_AUDPROP_PAUSE, fPause);
TRACE_MSG("SetProperty result=%s", GetHrSz(hr));
}
}
return TRUE;
}

default:
return TRUE;
} /* switch (cmd) */

case WM_HSCROLL:
{
DWORD dwLevel = SendDlgItemMessage( hdlg, IDC_LEVEL,
TBM_GETPOS, 0, 0 );
SetDlgItemInt(hdlg, IDE_LEVEL, dwLevel, FALSE);

if (TB_ENDTRACK == LOWORD(wParam))
{
int iSel = SendDlgItemMessage(hdlg, IDL_USERS, CB_GETCURSEL, 0, 0);
if (CB_ERR != iSel)
{
PUSER pUser = (PUSER) SendDlgItemMessage(hdlg, IDL_USERS,
CB_GETITEMDATA, (WPARAM) iSel, 0);
INmChannelAudio * pChannel = pUser->m_pChannelAudio;
if (NULL != pChannel)
{
hr = pChannel->SetProperty(NM_AUDPROP_LEVEL, dwLevel);
TRACE_MSG("SetProperty result=%s", GetHrSz(hr));
}
}
}
return TRUE;
}

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// BOOL DlgAudio(VOID)
//
//****************************************************************************

BOOL DlgAudio(VOID)
{
return (0 == DialogBox(ghInst, MAKEINTRESOURCE(IDD_AUDIO),
ghwndMain, (DLGPROC)AudioDlgProc));
}

//****************************************************************************
//
// Application sharing
//
//****************************************************************************

#define ILV_AS_HWND   0
#define ILV_AS_STATUS 1
#define ILV_AS_NAME   2

static COL _rgColShare[] = {
{70,  "Hwnd"},
{70,  "Status"},
{120, "Name"},
};

//****************************************************************************
//
// VOID FreeShareList(HWND hwnd)
//
//****************************************************************************

VOID FreeShareList(HWND hwnd)
{
LV_ITEM lvi;

ClearStruct(&lvi);
lvi.mask = LVIF_PARAM;

for (int iItem = 0; ; iItem++)
{
lvi.iItem = iItem;
if (!ListView_GetItem(hwnd, &lvi))
break;

INmSharableApp * pApp = (INmSharableApp *) lvi.lParam;
ASSERT(NULL != pApp);
pApp->Release();
}

ListView_DeleteAllItems(hwnd);
}

//****************************************************************************
//
// VOID UpdateShareList(HWND hwnd)
//
//****************************************************************************

VOID UpdateShareList(HWND hwnd)
{
LV_ITEM  lvItem;
HRESULT hr;
ULONG cFetched;
IEnumNmSharableApp * pEnum;
INmSharableApp * pApp;
NM_SHAPP_STATE uState;
HWND   hwndApp;
int    iItem;
BSTR   bstr;
LPTSTR psz;
TCHAR  sz[MAX_PATH];

FreeShareList(hwnd);

if (NULL == g_pChannelShare)
return;

ClearStruct(&lvItem);
lvItem.mask = LVIF_PARAM | LVIF_TEXT;

hr = g_pChannelShare->EnumSharableApp(&pEnum);
if (FAILED(hr))
{
    return;
}

for ( ; ; )
{
hr = pEnum->Next(1, &pApp, &cFetched);
if (S_OK != hr)
break;

hr = pApp->GetHwnd(&hwndApp);
wsprintf(sz, "%08X", hwndApp);

lvItem.pszText = sz;
lvItem.cchTextMax = lstrlen(sz);
lvItem.lParam = (LPARAM) pApp;
iItem = ListView_InsertItem(hwnd, &lvItem);

hr = pApp->GetState(&uState);
ListView_SetItemText(hwnd, iItem, ILV_AS_STATUS,
(NM_SHAPP_SHARED == uState) ? TEXT("Shared") : TEXT("Not Shared"));

hr = pApp->GetName(&bstr);
psz = PszFromBstr(bstr);
ListView_SetItemText(hwnd, iItem, ILV_AS_NAME, psz);
SysFreeString(bstr);
FreePlpv(&psz);

// pApp->Release();  // Release this in FreeShareList()
}

pEnum->Release();
}

//****************************************************************************
//
// VOID InitShareList(HWND hdlg)
//
//****************************************************************************

VOID InitShareList(HWND hdlg)
{
HWND hwnd = GetDlgItem(hdlg, IDL_APP);
ASSERT(NULL != hwnd);

// Initialize column data
{
int   iCol;
LPCOL lpCol;
LV_COLUMN lvc;

lpCol = _rgColShare;
ClearStruct(&lvc);
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
for (iCol = 0; iCol < ARRAY_ELEMENTS(_rgColShare); iCol++)
{
lvc.iSubItem = iCol;
lvc.cx = lpCol->dwWidth;
lvc.pszText = lpCol->lpsz;
if (ListView_InsertColumn(hwnd, iCol, &lvc) == -1)
return;
lpCol++;
}
}

UpdateShareList(hwnd);
}

//****************************************************************************
//
// LRESULT CALLBACK ShareDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK ShareDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
CenterWindow(hdlg, ghwndMain);
InitShareList(hdlg);
return TRUE;
}

case WM_COMMAND:
{
UINT wCmd = GET_WM_COMMAND_ID(wParam, lParam);
switch (wCmd)
{
case IDOK:
case IDCANCEL:
FreeShareList(GetDlgItem(hdlg, IDL_APP));
EndDialog(hdlg, 1);
return TRUE;

case IDB_UPDATE:
UpdateShareList(GetDlgItem(hdlg, IDL_APP));
break;

case IDB_SHARE:
case IDB_UNSHARE:
{
HRESULT hr;
NM_SHAPP_STATE uState;
HWND hwndLv = GetDlgItem(hdlg, IDL_APP);
int iItem = ListView_GetNextItem(hwndLv, -1, LVNI_ALL | LVNI_SELECTED);
if (-1 == iItem)
break;

LV_ITEM lvi;
ClearStruct(&lvi);
lvi.iItem = iItem;
lvi.mask = LVIF_PARAM;
if (!ListView_GetItem(hwndLv, &lvi))
break;

INmSharableApp * pApp = (INmSharableApp *) lvi.lParam;
ASSERT(NULL != pApp);
hr = pApp->GetState(&uState);
if (FAILED(hr))
{
ERROR_MSG("Invalid hwnd? hr=%s", GetHrSz(hr));
break;
}
TRACE_MSG("hwnd state=%d", uState);
hr = pApp->SetState((IDB_SHARE == wCmd) ? NM_SHAPP_SHARED : NM_SHAPP_NOT_SHARED);
return TRUE;
}

default:
return TRUE;
} /* switch (cmd) */
}

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// BOOL DlgShare(VOID)
//
//****************************************************************************

BOOL DlgShare(VOID)
{
return (0 == DialogBox(ghInst, MAKEINTRESOURCE(IDD_SHARE),
ghwndMain, (DLGPROC)ShareDlgProc));
}

//****************************************************************************
//
// VOID _InitGuidLBox(HWND hwnd)
//
//****************************************************************************

VOID _InitGuidLBox(HWND hwnd)
{
HRESULT hr;
DWORD dwIndex;
GUID  guid;
TCHAR szGuid[MAX_PATH];
INmSysInfo * pSysInfo;

hr = g_pMgr->GetSysInfo(&pSysInfo);
if (FAILED(hr))
{
ERROR_MSG("GetSysInfo Failed hr=%s", GetHrSz(hr));
return;
}

// TODO: use INmMgr::GetNmApp to enumerate local items

GuidToSz((GUID *)&g_guid, szGuid);
dwIndex = SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM) szGuid);

GuidToSz((GUID *)&g_guidNM2Chat, szGuid);
dwIndex = SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM) szGuid);

memset(&guid, 0x00, sizeof(GUID));
GuidToSz((GUID *)&guid, szGuid);
dwIndex = SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM) szGuid);

memset(&guid, 0x11, sizeof(GUID));
GuidToSz((GUID *)&guid, szGuid);
dwIndex = SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM) szGuid);

memset(&guid, 0x22, sizeof(GUID));
GuidToSz((GUID *)&guid, szGuid);
dwIndex = SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM) szGuid);

SendMessage(hwnd, CB_SETCURSEL, 0, 0);
pSysInfo->Release();
}

//****************************************************************************
//
// LRESULT CALLBACK LaunchDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK LaunchDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
CenterWindow(hdlg, ghwndMain);

InitUserLBox(hdlg, TRUE);
_InitGuidLBox(GetDlgItem(hdlg, IDL_GUID));
return TRUE;
}

case WM_DRAWITEM:
DrawUserItem((LPDRAWITEMSTRUCT) lParam);
break;

case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
{
// Get the Member
INmMember * pMember = NULL;
int iSel = SendDlgItemMessage(hdlg, IDL_USERS, CB_GETCURSEL, 0, 0);
if (CB_ERR != iSel)
{
PUSER pUser = (PUSER) SendDlgItemMessage(hdlg, IDL_USERS,
CB_GETITEMDATA, (WPARAM) iSel, 0);
if (NULL != pUser)
{
pMember = pUser->pMember;
if (S_OK == pMember->IsSelf())
pMember = NULL; // everyone
}
}

// Get the Guid
GUID guid;
TCHAR szGuid[MAX_PATH];
GetDlgItemText(hdlg, IDL_GUID, szGuid, CCHMAX(szGuid));
SzToGuid(szGuid, &guid);

INmConference * pConference = GetCurrentConference();
HRESULT hr;
if (NULL == pConference)
{
hr = E_FAIL;
}
else
{
hr = pConference->LaunchRemote(guid, pMember);
}

if (FAILED(hr))
{
ERROR_MSG("LaunchRemote Failed hr=%s", GetHrSz(hr));
}
else
{
EndDialog(hdlg, 1);
}
return TRUE;
}
case IDCANCEL:
EndDialog(hdlg, 0);
return TRUE;

default:
return TRUE;
} /* switch (cmd) */

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// BOOL DlgLaunch(VOID)
//
//****************************************************************************

BOOL DlgLaunch(VOID)

{
if (NULL == GetCurrentConference())
return FALSE;

return (0 == DialogBox(ghInst, MAKEINTRESOURCE(IDD_LAUNCH),
ghwndMain, (DLGPROC)LaunchDlgProc));
}

//****************************************************************************
//
// LRESULT CALLBACK EjectDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK EjectDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
CenterWindow(hdlg, ghwndMain);

InitUserLBox(hdlg, FALSE);
return TRUE;
}

case WM_DRAWITEM:
DrawUserItem((LPDRAWITEMSTRUCT) lParam);
break;

case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
{
// Get the Member
INmMember * pMember = NULL;
int iSel = SendDlgItemMessage(hdlg, IDL_USERS, CB_GETCURSEL, 0, 0);
if (CB_ERR != iSel)
{
PUSER pUser = (PUSER) SendDlgItemMessage(hdlg, IDL_USERS,
CB_GETITEMDATA, (WPARAM) iSel, 0);
if (NULL != pUser)
pMember = pUser->pMember;
}

if (NULL != pMember)
{
HRESULT hr = pMember->Eject();

if (FAILED(hr))
{
ERROR_MSG("LaunchRemote Failed hr=%s", GetHrSz(hr));
}
}

EndDialog(hdlg, 1);
return TRUE;
}
case IDCANCEL:
EndDialog(hdlg, 0);
return TRUE;

default:
return TRUE;
} /* switch (cmd) */

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// BOOL DlgEject(VOID)
//
//****************************************************************************

BOOL DlgEject(VOID)
{
return (0 == DialogBox(ghInst, MAKEINTRESOURCE(IDD_EJECT),
ghwndMain, (DLGPROC)EjectDlgProc));
}

const int iMinVideoQuality = NM_VIDEO_MIN_QUALITY;
const int iMaxVideoQuality = NM_VIDEO_MAX_QUALITY;

const int iMinAutoSize = 0;
const int iMaxAutoSize = 400;

//****************************************************************************
//
// CLASS CVideoDlg
//
//****************************************************************************

class CVideoDlg
{
private:
HWND m_hDlg;
CVideoNotify *m_pVideoNotify[10];
BOOL m_fPaused;

INmChannelVideo* CVideoDlg::GetChannel(int iChnl);
INmChannelVideo* CVideoDlg::GetCurrentChannel();

VOID FormatDialog();
VOID SourceDialog();
VOID SetFrameSize(DWORD dwFrameSize);
VOID SetQuality(DWORD dwQuality);
VOID Pause(BOOL fPause);
VOID ShowWindow(BOOL fShow);
VOID SetTopMost(BOOL fTopMost);
VOID AdjustSize(int cx, int cy);
VOID AdjustPosition(int cx, int cy);
VOID SetAutoSize(DWORD dwSize);
VOID Activate(BOOL fActive);

BOOL ProcessInitDialog(HWND hDdlg);
BOOL ProcessCommand(WPARAM wParam, LPARAM lParam);
BOOL ProcessQualitySlider(WPARAM wParam);
BOOL ProcessAutoSizeSlider(WPARAM wParam);
BOOL ProcessDestroy();

public:
CVideoDlg();
VOID UpdateControls();
VOID UpdateStatus(DWORD dwProperty);

static LRESULT CALLBACK DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
static VOID StateChanged(NM_VIDEO_STATE uState, DWORD dwParam);
static VOID PropertyChanged(DWORD dwProperty, DWORD dwParam);
};

//****************************************************************************
//
// Constructor
//
//****************************************************************************

CVideoDlg::CVideoDlg() :
m_fPaused(TRUE)
{
}

//****************************************************************************
//
// INmChannelVideo* CVideoDlg::GetChannel(int iChnl)
//
//****************************************************************************

INmChannelVideo* CVideoDlg::GetChannel(int iChnl)
{
PUSER pUser = (PUSER) SendDlgItemMessage(m_hDlg, IDL_USERS,
CB_GETITEMDATA, (WPARAM) iChnl, 0);
return pUser->m_pChannelVideo;
}

//****************************************************************************
//
// INmChannelVideo* CVideoDlg::GetCurrentChannel()
//
//****************************************************************************

INmChannelVideo* CVideoDlg::GetCurrentChannel()
{
int iSel = SendDlgItemMessage(m_hDlg, IDL_USERS, CB_GETCURSEL, 0, 0);
if (CB_ERR != iSel)
{
return GetChannel(iSel);
}
return NULL;
}

//****************************************************************************
//
// VOID CVideoDlg::UpdateControls()
//
//****************************************************************************

VOID CVideoDlg::UpdateControls()
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
BOOL fCapturing = FALSE;
    BOOL fIncoming  = (S_OK == pChannel->IsIncoming());
LPTSTR lpszState = "Unknown";
NM_VIDEO_STATE uState;
pChannel->GetState(&uState);
switch (uState)
{
case NM_VIDEO_IDLE:
lpszState = "Idle";
break;
case NM_VIDEO_LOCAL_PAUSED:
lpszState = "Local Paused";
break;
case NM_VIDEO_PREVIEWING:
lpszState = "Previewing";
fCapturing = !fIncoming;
break;
case NM_VIDEO_TRANSFERRING:
lpszState = "Transferring";
fCapturing = !fIncoming;
break;
case NM_VIDEO_BOTH_PAUSED:
lpszState = "Both Paused";
break;
case NM_VIDEO_REMOTE_PAUSED:
lpszState = "Remote Paused";
fCapturing = !fIncoming;
break;
}
SetDlgItemText(m_hDlg, IDC_VIDEO_STATE, lpszState);

EnableWindow(GetDlgItem(m_hDlg, IDC_VIDEO_SMALL), !fIncoming);
EnableWindow(GetDlgItem(m_hDlg, IDC_VIDEO_MEDIUM), !fIncoming);
EnableWindow(GetDlgItem(m_hDlg, IDC_VIDEO_LARGE), !fIncoming);

        ULONG uValue;
pChannel->GetProperty(NM_VIDPROP_IMAGE_PREFERRED_SIZE, &uValue);
switch (uValue)
{
case 0x01:
uValue = 0;
break;
default:
case 0x02:
uValue = 1;
break;
case 0x04:
uValue = 2;
break;
}
CheckRadioButton(m_hDlg,
IDC_VIDEO_SMALL,
IDC_VIDEO_LARGE,
IDC_VIDEO_SMALL + uValue);

if (fCapturing)
{
pChannel->GetProperty(NM_VIDPROP_CAMERA_DIALOG, &uValue);
}
else
{
uValue = 0;
}
EnableWindow(GetDlgItem(m_hDlg, IDC_VIDEO_SOURCE), uValue & 0x01);
EnableWindow(GetDlgItem(m_hDlg, IDC_VIDEO_FORMAT), uValue & 0x02);

uValue = (S_OK == pChannel->IsActive());
EnableWindow(GetDlgItem(m_hDlg, IDC_ACTIVE), fIncoming || uValue);
CheckDlgButton(m_hDlg, IDC_ACTIVE, uValue ? BST_CHECKED : BST_UNCHECKED);

pChannel->GetProperty(NM_VIDPROP_PAUSE, &uValue);
CheckDlgButton(m_hDlg, IDC_PAUSE, uValue ? BST_CHECKED : BST_UNCHECKED);

EnableWindow(GetDlgItem(m_hDlg, IDC_VIDEO_QUALITY), fIncoming);
if (fIncoming)
{
pChannel->GetProperty(NM_VIDPROP_IMAGE_QUALITY, &uValue);
}
else
{
uValue = 0;
}
SendDlgItemMessage (m_hDlg, IDC_VIDEO_QUALITY, TBM_SETRANGE, FALSE,
MAKELONG (iMinVideoQuality, iMaxVideoQuality ));

SendDlgItemMessage (m_hDlg, IDC_VIDEO_QUALITY, TBM_SETTICFREQ,
( iMaxVideoQuality - iMinVideoQuality ) / 8, 0 );

SendDlgItemMessage (m_hDlg, IDC_VIDEO_QUALITY, TBM_SETPAGESIZE,
0, ( iMaxVideoQuality - iMinVideoQuality ) / 8 );

SendDlgItemMessage (m_hDlg, IDC_VIDEO_QUALITY, TBM_SETLINESIZE,
0, 1 );

SendDlgItemMessage (m_hDlg, IDC_VIDEO_QUALITY, TBM_SETPOS, TRUE,
iMaxVideoQuality - uValue );

pChannel->GetProperty(NM_VIDPROP_WINDOW_AUTO_SIZE, &uValue);
SetDlgItemInt(m_hDlg, IDC_E_AUTOSIZE, uValue, FALSE);

SendDlgItemMessage (m_hDlg, IDC_S_AUTOSIZE, TBM_SETRANGE, FALSE,
MAKELONG (iMinAutoSize, iMaxAutoSize ));

SendDlgItemMessage (m_hDlg, IDC_S_AUTOSIZE, TBM_SETTICFREQ,
100, 0 );

SendDlgItemMessage (m_hDlg, IDC_S_AUTOSIZE, TBM_SETPAGESIZE,
0, 100 );

SendDlgItemMessage (m_hDlg, IDC_S_AUTOSIZE, TBM_SETLINESIZE,
0, 10 );

SendDlgItemMessage (m_hDlg, IDC_S_AUTOSIZE, TBM_SETPOS, TRUE,
uValue );

pChannel->GetProperty(NM_VIDPROP_WINDOW_VISIBLE, &uValue);
CheckDlgButton(m_hDlg, IDC_VISIBLE, uValue ? BST_CHECKED : BST_UNCHECKED);

pChannel->GetProperty(NM_VIDPROP_WINDOW_TOP_MOST, &uValue);
CheckDlgButton(m_hDlg, IDC_TOPMOST, uValue ? BST_CHECKED : BST_UNCHECKED);
}
}

//****************************************************************************
//
// VOID CVideoDlg::UpdateStatus(DWORD dwProperty)
//
//****************************************************************************

VOID CVideoDlg::UpdateStatus(DWORD dwProperty)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
ULONG uValue;
pChannel->GetProperty((NM_VIDPROP)dwProperty, &uValue);

TCHAR szProperty[MAX_PATH];

switch (dwProperty)
{
case NM_VIDPROP_PAUSE:
wsprintf(szProperty, "Pause: %d", uValue);
break;
case NM_VIDPROP_WINDOW_AUTO_SIZE:
wsprintf(szProperty, "Window Auto Size: %d", uValue);
break;
case NM_VIDPROP_WINDOW_SIZE:
wsprintf(szProperty, "Window Size: (%d, %d)",
(short)LOWORD(uValue), (short)HIWORD(uValue));
break;
case NM_VIDPROP_WINDOW_POSITION:
wsprintf(szProperty, "Window Position: (%d, %d)",
(short)LOWORD(uValue), (short)HIWORD(uValue));
break;
case NM_VIDPROP_WINDOW_TOP_MOST:
wsprintf(szProperty, "Window Top Most: %d", uValue);
break;
case NM_VIDPROP_WINDOW_VISIBLE:
wsprintf(szProperty, "Window Visible: %d", uValue);
break;
case NM_VIDPROP_IMAGE_PREFERRED_SIZE:
wsprintf(szProperty, "Image Prefered Size: %d", uValue);
break;
case NM_VIDPROP_IMAGE_QUALITY:
wsprintf(szProperty, "Image Quality: %d", uValue);
break;
case NM_VIDPROP_CAMERA_DIALOG:
wsprintf(szProperty, "Camera Dialog: %d", uValue);
break;
default:
wsprintf(szProperty, "Unknown Property: %d", dwProperty);
break;
}
SetDlgItemText(m_hDlg, IDC_VIDEO_STATUS, szProperty);
}
}

//****************************************************************************
//
// VOID CVideoDlg::SourceDialog()
//
//****************************************************************************

VOID CVideoDlg::SourceDialog()
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
pChannel->SetProperty(NM_VIDPROP_CAMERA_DIALOG, NM_VIDEO_SOURCE_DIALOG);
}
}

//****************************************************************************
//
// VOID CVideoDlg::FormatDialog()
//
//****************************************************************************

VOID CVideoDlg::FormatDialog()
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
pChannel->SetProperty(NM_VIDPROP_CAMERA_DIALOG, NM_VIDEO_FORMAT_DIALOG);
}
}

//****************************************************************************
//
// VOID CVideoDlg::SetFrameSize(DWORD dwFrameSize)
//
//****************************************************************************

VOID CVideoDlg::SetFrameSize(DWORD dwFrameSize)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
pChannel->SetProperty(NM_VIDPROP_IMAGE_PREFERRED_SIZE, dwFrameSize);
}
}

//****************************************************************************
//
// VOID CVideoDlg::SetQuality(DWORD dwQuality)
//
//****************************************************************************

VOID CVideoDlg::SetQuality(DWORD dwQuality)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
pChannel->SetProperty(NM_VIDPROP_IMAGE_QUALITY, iMaxVideoQuality - dwQuality);
}
}

//****************************************************************************
//
// BOOL CVideoDlg::ProcessInitDialog(HWND hDlg)
//
//****************************************************************************

BOOL CVideoDlg::ProcessInitDialog(HWND hDlg)
{
m_hDlg = hDlg;

CenterWindow(hDlg, ghwndMain);

InitUserLBox(hDlg, FALSE);

UpdateControls();

int iCnt = SendDlgItemMessage(hDlg, IDL_USERS, CB_GETCOUNT, 0, 0);

for (int i = 0; i < iCnt; ++i)
{
INmChannelVideo * pChannel = GetChannel(i);
if (NULL != pChannel)
{
m_pVideoNotify[i] = new CVideoNotify(CVideoDlg::StateChanged, CVideoDlg::PropertyChanged, (DWORD)this);
m_pVideoNotify[i]->Connect(pChannel);
}
}

return TRUE;
}

//****************************************************************************
//
// VOID CVideoDlg::Pause(BOOL fPause)
//
//****************************************************************************

VOID CVideoDlg::Pause(BOOL fPause)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
pChannel->SetProperty(NM_VIDPROP_PAUSE, fPause);
}
}

//****************************************************************************
//
// VOID CVideoDlg::ShowWindow(BOOL fShow)
//
//****************************************************************************

VOID CVideoDlg::ShowWindow(BOOL fShow)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
DWORD dwShow = fShow;
pChannel->SetProperty(NM_VIDPROP_WINDOW_VISIBLE, dwShow);
}
}

//****************************************************************************
//
// VOID CVideoDlg::SetTopMost(BOOL fTopMost)
//
//****************************************************************************

VOID CVideoDlg::SetTopMost(BOOL fTopMost)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
DWORD dwTopMost = fTopMost;
pChannel->SetProperty(NM_VIDPROP_WINDOW_TOP_MOST, dwTopMost);
}
}

//****************************************************************************
//
// VOID CVideoDlg::AdjustSize(int cx, int cy)
//
//****************************************************************************

VOID CVideoDlg::AdjustSize(int cx, int cy)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
DWORD dwSize;
WORD x, y;
pChannel->GetProperty(NM_VIDPROP_WINDOW_SIZE, &dwSize);
x = LOWORD(dwSize) + cx;
y = HIWORD(dwSize) + cy;
dwSize = MAKELONG(x, y);
pChannel->SetProperty(NM_VIDPROP_WINDOW_SIZE, dwSize);
}
}

//****************************************************************************
//
// VOID CVideoDlg::AdjustPosition(int cx, int cy)
//
//****************************************************************************

VOID CVideoDlg::AdjustPosition(int cx, int cy)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
DWORD dwPos;
WORD x, y;
pChannel->GetProperty(NM_VIDPROP_WINDOW_POSITION, &dwPos);
x = LOWORD(dwPos) + cx;
y = HIWORD(dwPos) + cy;
dwPos = MAKELONG(x, y);
pChannel->SetProperty(NM_VIDPROP_WINDOW_POSITION, dwPos);
}
}

//****************************************************************************
//
// VOID CVideoDlg::SetAutoSize(DWORD dwSize)
//
//****************************************************************************

VOID CVideoDlg::SetAutoSize(DWORD dwSize)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
pChannel->SetProperty(NM_VIDPROP_WINDOW_AUTO_SIZE, dwSize);
}
}

//****************************************************************************
//
// VOID CVideoDlg::Activate(BOOL fActive)
//
//****************************************************************************

VOID CVideoDlg::Activate(BOOL fActive)
{
INmChannelVideo * pChannel = GetCurrentChannel();
if (NULL != pChannel)
{
pChannel->SetActive(fActive);
}
}

//****************************************************************************
//
// BOOL CVideoDlg::ProcessCommand(WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

BOOL CVideoDlg::ProcessCommand(WPARAM wParam, LPARAM lParam)
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
case IDCANCEL:
EndDialog(m_hDlg, 1);
break;

case IDC_ACTIVE:
Activate(IsDlgButtonChecked(m_hDlg, IDC_ACTIVE));
break;

case IDB_UPDATE:
UpdateControls();
break;

case IDC_VIDEO_SOURCE:
SourceDialog();
break;

case IDC_VIDEO_FORMAT:
FormatDialog();
break;

case IDC_PAUSE:
Pause(IsDlgButtonChecked(m_hDlg, IDC_PAUSE));
break;

case IDL_USERS:
UpdateControls();
break;

case IDC_VIDEO_SMALL:
SetFrameSize(NM_VIDEO_SMALL);
break;
case IDC_VIDEO_MEDIUM:
SetFrameSize(NM_VIDEO_MEDIUM);
break;
case IDC_VIDEO_LARGE:
SetFrameSize(NM_VIDEO_LARGE);
break;

case IDC_VISIBLE:
ShowWindow(IsDlgButtonChecked(m_hDlg, IDC_VISIBLE));
break;

case IDC_TOPMOST:
SetTopMost(IsDlgButtonChecked(m_hDlg, IDC_TOPMOST));
break;

case IDC_TALL:
AdjustSize(0, 10);
break;
case IDC_SHORT:
AdjustSize(0, -10);
break;
case IDC_THIN:
AdjustSize(-10, 0);
break;
case IDC_WIDE:
AdjustSize(10, 0);
break;

case IDC_UP:
AdjustPosition(0, -10);
break;
case IDC_DOWN:
AdjustPosition(0, 10);
break;
case IDC_LEFT:
AdjustPosition(-10, 0);
break;
case IDC_RIGHT:
AdjustPosition(10, 0);
break;

default:
return FALSE;
} /* switch (cmd) */

return TRUE;
}

//****************************************************************************
//
// BOOL CVideoDlg::ProcessQualitySlider(WPARAM wParam)
//
//****************************************************************************

BOOL CVideoDlg::ProcessQualitySlider(WPARAM wParam)
{
if (TB_ENDTRACK == LOWORD(wParam))
{
DWORD dwValue = SendDlgItemMessage( m_hDlg, IDC_VIDEO_QUALITY,
TBM_GETPOS, 0, 0 );

{
SetQuality(dwValue);
return TRUE;
}
}
return FALSE;
}

//****************************************************************************
//
// BOOL CVideoDlg::ProcessAutoSizeSlider(WPARAM wParam)
//
//****************************************************************************

BOOL CVideoDlg::ProcessAutoSizeSlider(WPARAM wParam)
{
if (TB_ENDTRACK == LOWORD(wParam))
{
DWORD dwValue = SendDlgItemMessage( m_hDlg, IDC_S_AUTOSIZE,
TBM_GETPOS, 0, 0 );

{
SetAutoSize(dwValue);
return TRUE;
}
}
return FALSE;
}

//****************************************************************************
//
// BOOL CVideoDlg::ProcessDestroy()
//
//****************************************************************************

BOOL CVideoDlg::ProcessDestroy()
{
int iCnt = SendDlgItemMessage(m_hDlg, IDL_USERS, CB_GETCOUNT, 0, 0);
for (int i = 0; i < iCnt; ++i)
{
m_pVideoNotify[i]->Disconnect();
m_pVideoNotify[i]->Release();
}
return TRUE;
}

//****************************************************************************
//
// LRESULT CALLBACK CVideoDlg::DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT CALLBACK CVideoDlg::DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CVideoDlg* pVideoDlg = (CVideoDlg*)GetWindowLong(hdlg, GWL_USERDATA);

switch (uMsg)
{
case WM_INITDIALOG:
SetWindowLong(hdlg, GWL_USERDATA, lParam);

pVideoDlg = (CVideoDlg*)lParam;
return pVideoDlg->ProcessInitDialog(hdlg);

case WM_DRAWITEM:
DrawUserItem((LPDRAWITEMSTRUCT) lParam);
break;

case WM_COMMAND:
return pVideoDlg->ProcessCommand(wParam, lParam);

case WM_VSCROLL:
return pVideoDlg->ProcessQualitySlider(wParam);

case WM_HSCROLL:
return pVideoDlg->ProcessAutoSizeSlider(wParam);

case WM_DESTROY:
return pVideoDlg->ProcessDestroy();

default:
break;
} /* switch (uMsg) */

return FALSE;
}

//****************************************************************************
//
// VOID CVideoDlg::StateChanged(NM_VIDEO_STATE uState, DWORD dwParam)
//
//****************************************************************************

VOID CVideoDlg::StateChanged(NM_VIDEO_STATE uState, DWORD dwParam)
{
((CVideoDlg*)dwParam)->UpdateControls();
}

//****************************************************************************
//
// VOID CVideoDlg::PropertyChanged(DWORD dwProperty, DWORD dwParam)
//
//****************************************************************************

VOID CVideoDlg::PropertyChanged(DWORD dwProperty, DWORD dwParam)
{
((CVideoDlg*)dwParam)->UpdateControls();
((CVideoDlg*)dwParam)->UpdateStatus(dwProperty);
}

//****************************************************************************
//
// BOOL DlgVideo(VOID)
//
//****************************************************************************

BOOL DlgVideo(VOID)
{
CVideoDlg VideoDlg;

return (0 == DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_VIDEO),
ghwndMain, (DLGPROC)CVideoDlg::DlgProc, (LPARAM)&VideoDlg));
}


GLOBAL.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       GLOBAL.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#ifndef GLOBAL_H
#define GLOBAL_H

//-------------------------------------------------------
// Useful constants

#define chNull   ('\0')
#define lpNil    (NULL)

#define hwndNil      ((HWND) NULL)
#define hfontNil     ((HFONT) NULL)
#define hgdiNil      ((HGDIOBJ) NULL)

//-------------------------------------------------------
// Useful macros

#define CchSz(pcsz)( lstrlen(pcsz) )
#define CbSz(pcsz)( lstrlen(pcsz) + 1*sizeof(TCHAR) )

#define ClearBytes(lpv, cb)ZeroMemory((LPVOID) (lpv), (cb))
#define ClearStruct(lpv)ZeroMemory((LPVOID) (lpv), sizeof(*(lpv)))
#define InitStruct(lpv)        {ClearStruct(lpv); (* (LPDWORD) lpv) = sizeof(*(lpv));}
#define CopyStruct(pDest, pSrc)  CopyMemory(pDest, pSrc, sizeof(*(pDest)))

#define SetEmptySz(sz)( *(sz) = _T('\0') )
#define FEmptySz(sz)            (((sz) == NULL) || (*(sz) == _T('\0')))

#define ARRAY_ELEMENTS(rg)      (sizeof(rg) / sizeof(rg[0]))
#define CCHMAX(sz)              (sizeof(sz) / sizeof(TCHAR))

#ifdef DEBUG
VOID FAR PASCAL AssertProc(LPTSTR lpszMsg, LPTSTR lpszAssert, LPTSTR lpszFile, UINT line);

#define DEBASSERT(exp,szMsg)  \
if (!(exp))                                                      \
{                                                                \
static char _szAssert[] = #exp ;   \
static char _szMsg[]    = szMsg;   \
AssertProc(_szMsg,_szAssert,__FILE__,__LINE__); \
}

#define ASSERT(f)       DEBASSERT(f, "(" #f ")")
#define AssertSz(f,sz)  DEBASSERT(f, sz)
#define NotReached()    DEBASSERT(FALSE, "NotReached declaration was reached!")
#else
#define ASSERT(f)
#define AssertSz(f, sz)
#define NotReached()
#endif

VOID TraceMsg(PSTR pszFormat,...);
VOID ErrorMsg(PSTR pszFormat,...);
VOID WarnMsg(PSTR pszFormat,...);
#define WARN_MSG   ::WarnMsg
#define ERROR_MSG  ::ErrorMsg

#define TRACE_MSG  (!gPref.fMsgTrace)   ? (void) 0 : ::TraceMsg
#define OBJECT_MSG (!gPref.fMsgObjects) ? (void) 0 : ::TraceMsg
#define NOTIFY_MSG (!gPref.fMsgNotify)  ? (void) 0 : ::TraceMsg

VOID LogMsg(COLORREF cv, PSTR pszFormat,...);
#define LOG_MSG ::LogMsg

// no debug messages:
// inline void WINAPI Trace(LPCTSTR, ...) { }
// #define TRACE_MSG     1 ? (void)0 : ::TraceMsg
// #define WARN_MSG      1 ? (void)0 : ::TraceMsg
// #define ERROR_MSG     1 ? (void)0 : ::TraceMsg

#ifndef NEEDDEBUG
LPCTSTR GetHrSz(HRESULT hr);
LPCTSTR GetConfnSz(CONFN confn);
LPCTSTR GetNmchSz(ULONG uch);
LPCTSTR GetCallStateSz(NM_CALL_STATE uState);
LPCTSTR GetConferenceStateSz(NM_CONFERENCE_STATE uState);
VOID DumpCallInfo(INmCall * pCall);
VOID DumpAllCalls(void);
VOID DumpMemberInfo(INmMember * pMember);
VOID DumpConferenceInfo(INmConference * pConference);
VOID DumpChannelInfo(INmChannel * pChannel);
#else
#define GetHrSz(hr)       TEXT("")
#define GetConfnSz(confn) TEXT("")
#define GetNmchSz(uch)    TEXT("")
#define GetCallStateSz(uState)  TEXT("")
#define GetConferenceStateSz(uState) TEXT("")
#define DumpCallInfo(pCall);
#define DumpMemberInfo(pMember);
#define DumpConferenceInfo(pConference);
#define DumpChannelInfo(pChannel);
#endif

//-------------------------------------------------------------------------
// Functions for handling main window messages.  The message-dispatching
// mechanism expects all message-handling functions to have the following
// prototype:
//
// Function pointer prototype for message handling functions.
//     LRESULT FunctionName(HWND, UINT, WPARAM, LPARAM);

typedef LRESULT (*PFNMSG)(HWND, UINT, WPARAM, LPARAM);

// This structure maps messages to message handling functions.
typedef struct _MSD
{
UINT   uMsg;
PFNMSG pfnmsg;
} MSD;                 // MeSsage Dispatch structure
typedef MSD * LPMSD;

//-------------------------------------------------------------------------
// Functions for handling main window commands--ie. functions for
// processing WM_COMMAND messages based on the wParam value.
// The message-dispatching mechanism expects all command-handling
// functions to have the following prototype:

typedef VOID (*PFNCMD)(VOID);

// This structure maps command IDs to command handling functions.
typedef struct _CMD
{
UINT     wCmd;
PFNCMD pfncmd;
} CMD;                 // CoMmand Dispatch structure
typedef CMD * LPCMD;

//-------------------------------------------------------------------------
// Preferences
typedef struct _PREF
{
BOOL    fConfirm;           // Confirm before receiving files
BOOL    fSbar;              // Show status bar
BOOL    fTbar;              // Show tool bar
BOOL    fMsg;               // Show message window
BOOL    fChat;              // Show Chat Window

LOGFONT lf;                 // Font to use for main message window
WINDOWPLACEMENT wpMain;
PTCHAR  pszAddr;            // Default Address information (machine name, ip addr)
NM_ADDR_TYPE nmAddrType;    // Address Type (pszUserAddr)

DWORD   dwNmch;             // Allowable channel types
PTCHAR  pszConferenceName;  // Conference Name
PTCHAR  pszPassword;        // Conference password
PTCHAR  pszFileName;        // File name

int     iInit;              // Initialize option (NM_INIT_NORMAL)

BOOL    fAutoMonitor;
BOOL    fAutoHost;
BOOL    fAutoCall;
BOOL    fAutoChat;

BOOL    fMsgTrace;
BOOL    fMsgObjects;
BOOL    fMsgNotify;

NM_CALL_TYPE nmCallType;
} PREF;

// List column information

typedef struct _tagCol {
UINT  dwWidth;
LPSTR lpsz;
} COL;
typedef COL * LPCOL;

// Generic int, string array
typedef struct {
DWORD  dw;
LPTSTR psz;
} DWSTR;

//-------------------------------------------------------------------------
// Special resource constants
#define IDW_MSG   100
#define IDW_LVIEW 101
#define IDW_SBAR  102
#define IDW_TBAR  103
#define IDW_CHAT  104
#define IDW_SEND  105

// toolbar constants
#define NUMIMAGES       29
#define IMAGEWIDTH      16
#define IMAGEHEIGHT     16
#define BUTTONWIDTH     16
#define BUTTONHEIGHT    16

#define STATUSICONSIZE  16

// status bar parts
#define IDSBP_MSG        0
#define IDSBP_USERCOUNT  1
#define IDSBP_ICON       2

// See _rgColMember
#define ILV_NAME    0
#define ILV_VERSION 1
#define ILV_CAPS    2
#define ILV_AUDIO   3
#define ILV_VIDEO   4
#define ILV_FT      5
#define ILV_AS      6
#define ILV_MCU     7
#define ILV_LOCAL   8
#define ILV_ADDR    9
#define ILV_ID     10

#define DXP_SEND   60   // Width of the "Send" button
#define DYP_SEND   25   // Height of the "Send" button
#define DXP_ULIST  120  // Width of the chat user list
#define DYP_ULIST  90   // Height of the list when dropped down

//-------------------------------------------------------------------------
// Global Function Prototypes.

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

VOID DlgExit(HWND, WORD, WORD, HWND);

// from util.cpp
BOOL  GetIniBool(LPTSTR szEntry, BOOL f);
DWORD GetIniInt(LPTSTR szEntry, DWORD dwDefault);
LPTSTR GetIniStr(LPTSTR szEntry, LPTSTR szDefault);
VOID GetIniHex(LPTSTR szEntry, LPVOID lpv, int cb);
VOID WriteIniBool(LPTSTR szEntry, BOOL f);
VOID WriteIniInt(LPTSTR szEntry, DWORD dw);
VOID WriteIniStr(LPTSTR szEntry, LPTSTR sz);
VOID WriteIniHex(LPTSTR szEntry, LPVOID lpv, int cb);
VOID CenterWindow(HWND hwndChild, HWND hwndParent);
LPVOID LpvAlloc(int cb);
LPTSTR PszAlloc(int cch);
VOID FreePlpv(LPVOID plpv);
VOID UpdateHdlgIdText(HWND hdlg, int id, LPVOID lpv);
VOID UpdateHdlgIdInt(HWND hdlg, int id, LPINT lpint);
char ChFromHex(LPTSTR lpch);
VOID GetDlgItemsRc(HWND hdlg, int id1, int id2, LPRECT prc);
VOID HexToData(LPTSTR lpchSrc, LPVOID lpchDest, int cb);
VOID DataToHex(LPTSTR lpchSrc, LPTSTR lpchDest, int cb);
VOID SetMenuCheck(UINT idm, BOOL fCheck);
VOID MaybeDeleteObject(HGDIOBJ * phgdi);
int GetRadioButton(HWND hdlg, int idrFirst, int idrLast);
VOID GetDlgItemPsz(HWND hdlg, UINT id, LPTSTR *ppsz);

VOID GuidToSz(GUID * pguid, LPTSTR sz);
VOID SzToGuid(LPTSTR sz, GUID * pguid);
LPTSTR SzFindLastCh(LPTSTR lpsz, TCHAR ch);

// from rtns.cpp
VOID SizeStatus(int dxClient, int dy);
VOID SizeMsgWindow(int dx, int dy);
VOID RecalcMsgWindow(void);
VOID ShowHwnd(HWND hwnd, BOOL fShow, int idm);

// from dlg.cpp
BOOL DlgNewConference(VOID);
BOOL DlgNewCall(VOID);
BOOL DlgCallConf(VOID);
BOOL DlgSendFile(VOID);
BOOL DlgOptions(VOID);
BOOL DlgAudio(VOID);
BOOL DlgShare(VOID);
BOOL DlgLaunch(VOID);
BOOL DlgEject(VOID);
VOID InitializeUserList(HWND hwndCombo, BOOL fEveryone);
VOID DrawUserItem(LPDRAWITEMSTRUCT lpdis);
BOOL DlgVideo(VOID);

// from init.cpp
BOOL FInitApp(LPTSTR lpszCmd, int nCmdShow);
VOID FreeAllComObjects(void);
VOID ReadPref(void);
VOID WritePref(void);

// from dlg.cpp
VOID DisplayStatus(BOOL fShow);
VOID DisplayDlg(BOOL fShow, HWND * phdlg, int id, int idm);

// from sbar.cpp
BOOL FCreateSbar(void);
VOID UpdateStatusIcon(DWORD dwId);
VOID UpdateStatusBar(LPTSTR lpsz, WORD wPart, WORD wFlags);
VOID StatusMsg(LPTSTR sz);
VOID WriteIniTbar(void);
VOID SetToolbarCheck(int idm, BOOL fCheck);

// from tbar.cpp
BOOL FCreateTbar(void);

// from cmd.cpp
VOID RecalcMsgWindow(void);
VOID ShowHwnd(HWND hwnd, BOOL fShow, int idm);
VOID OnStartup(void);

// from msg.cpp
INT MsgBoxIMbd(int iMbd);
INT MsgBoxIMbdDw(int iMbd, DWORD dw);
INT MsgBoxIMbdSz(int iMbd, LPTSTR lpsz);

// from util.cpp
VOID Log(COLORREF cv, LPTSTR lpsz);
VOID ClearLog(void);
VOID LogTestStart(LPTSTR sz);
VOID LogTestStop(LPTSTR sz);
VOID LogTestCancel(LPTSTR sz);
VOID LogResult(LPTSTR sz, DWORD dwTest, DWORD dwResult);
VOID InitDbg(void);
LPTSTR ConfErrToSz(DWORD dwError, LPTSTR sz);
BOOL FGetFileName(LPTSTR szFileName);
BOOL FGetDirectory(LPTSTR szDir);

VOID DoEnumChannelMember(INmChannel * pChannel);
VOID DoEnumConferenceMember(INmConference * pConference);
VOID DoEnumAll(void);

#define SetPropResult(hdlg, f)  SetWindowLong(GetParent(hdlg), DWL_MSGRESULT, f)

//-------------------------------------------------------------------------
// Global Variables

extern HINSTANCE ghInst;          // The current instance handle
extern HACCEL    ghAccelTable;    // Menu accelerators
extern PREF      gPref;           // User preferences
extern HWND      ghwndMain;       // Main Window
extern HMENU     ghMenu;          // Main Menu
extern HWND      ghdlgStatus;     // Modeless Status Dialog
extern HWND      ghwndSbar;       // Status bar
extern HWND      ghwndTbar;       // Toolbar
extern HWND      ghwndMsg;        // Message Window
extern HWND      ghwndLView;      // Main List View
extern HWND      ghwndChat;       // Chat Edit control
extern HWND      ghwndSend;       // Send Button
extern HWND      ghwndUList;      // User List
extern HFONT     ghfontEntry;     // Font for message Window

extern int       gdxWndMin;       // maximum width of window
extern int       gdyWndMin;       // maximum width of window

extern TCHAR gszAppName[];
extern TCHAR gszIniName[];

#endif /* GLOBAL_H */


IMSCONF2.IDL

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       imsconf2.idl
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include <imsconf2.idl>


INIT.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       INIT.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

TCHAR gszAppTitle[]     = TEXT("NetMeeting UI Test Application");

// string constants (no change for international)
TCHAR gszAppClassName[] = TEXT("NMUI");
TCHAR gszAppName[]      = TEXT("NmUi");
TCHAR gszIniName[]      = TEXT("NmUi.ini");

// Section Names
TCHAR gszMainWnd[]  = TEXT("MainWnd");
TCHAR gszSbar[]     = TEXT("SBar");
TCHAR gszTbar[]     = TEXT("TBar");
TCHAR gszChat[]     = TEXT("Chat");
TCHAR gszMsg[]      = TEXT("Messages");
TCHAR gszMsgFont[]  = TEXT("Message Font");
TCHAR gszAddr[]     = TEXT("Addr");
TCHAR gszAddrType[] = TEXT("AddrType");
TCHAR gszConferenceName[] = TEXT("Conference Name");
TCHAR gszFileName[] = TEXT("Filename");

TCHAR gszIniCallType[] = TEXT("CallType");

TCHAR gszIniAutoMonitor[] = TEXT("AutoMonitor");
TCHAR gszIniAutoHost[]    = TEXT("AutoHost");
TCHAR gszIniAutoCall[]    = TEXT("AutoCall");
TCHAR gszIniAutoChat[]    = TEXT("AutoChat");

TCHAR gszIniInit[]        = TEXT("Init");

TCHAR gszIniMsgTrace[]    = TEXT("Trace Msgs");
TCHAR gszIniMsgObjects[]  = TEXT("Object Msgs");
TCHAR gszIniMsgNotify[]   = TEXT("Notify Msgs");

// See ILV_*
static COL _rgColMember[] = {
{80, "Name"},
{40, "Ver"},
{60, "Caps"},
{60, "Audio"},
{60, "Video"},
{60, "Ft"},
{90, "App Sharing"},
{40, "MCU"},
{40, "Local"},
{80, "IP Addr"},
{80, "ID"},
};

//****************************************************************************
//
// VOID ReadPref(void)
//
// Reads the current preferences from the NmUi.ini file.
//
//****************************************************************************

VOID ReadPref(void)
{
ClearStruct(&gPref);

gPref.fSbar       = GetIniBool(gszSbar, TRUE);
gPref.fTbar       = GetIniBool(gszTbar, TRUE);
gPref.fMsg        = GetIniBool(gszMsg, TRUE);
gPref.fChat       = GetIniBool(gszChat, TRUE);

gPref.pszConferenceName = GetIniStr(gszConferenceName, TEXT("NmUi Conference"));

gPref.pszAddr = GetIniStr(gszAddr, TEXT("User 1"));
gPref.nmAddrType = (NM_ADDR_TYPE) GetIniInt(gszAddrType, NM_ADDR_UNKNOWN);
gPref.nmCallType = (NM_CALL_TYPE) GetIniInt(gszIniCallType, (int) NM_CALL_DEFAULT);

GetIniHex(gszMainWnd, &gPref.wpMain, sizeof(WINDOWPLACEMENT));
GetIniHex(gszMsgFont, &gPref.lf, sizeof(LOGFONT));

gPref.fAutoMonitor= GetIniBool(gszIniAutoMonitor, FALSE);
gPref.fAutoHost   = GetIniBool(gszIniAutoHost, FALSE);
gPref.fAutoCall   = GetIniBool(gszIniAutoCall, FALSE);
gPref.fAutoChat   = GetIniBool(gszIniAutoChat, FALSE);

gPref.fMsgTrace   = GetIniBool(gszIniMsgTrace, TRUE);
gPref.fMsgObjects = GetIniBool(gszIniMsgObjects, FALSE);
gPref.fMsgNotify  = GetIniBool(gszIniMsgNotify, TRUE);

gPref.iInit       = GetIniInt(gszIniInit, NM_INIT_NORMAL);

gPref.pszFileName = GetIniStr(gszFileName, TEXT("C:\\autoexec.bat"));
}

//****************************************************************************
//
// VOID WritePref(void)
//
// Write the current preferences
//
//****************************************************************************

VOID WritePref(void)
{
WriteIniBool(gszSbar, gPref.fSbar);
WriteIniBool(gszTbar, gPref.fTbar);
WriteIniBool(gszMsg, gPref.fMsg);
WriteIniBool(gszChat, gPref.fChat);

WriteIniStr(gszConferenceName, gPref.pszConferenceName);
FreePlpv(&gPref.pszConferenceName);

WriteIniStr(gszFileName, gPref.pszFileName);
FreePlpv(&gPref.pszFileName);

WriteIniStr(gszAddr, gPref.pszAddr);
FreePlpv(&gPref.pszAddr);
WriteIniInt(gszAddrType, (int) gPref.nmAddrType);
WriteIniInt(gszIniCallType, (int) gPref.nmCallType);

if (NULL != ghwndMain)
{
gPref.wpMain.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(ghwndMain, &gPref.wpMain);
}
WriteIniHex(gszMainWnd, &gPref.wpMain, sizeof(WINDOWPLACEMENT));

WriteIniHex(gszMsgFont, &gPref.lf, sizeof(LOGFONT));

WriteIniTbar();

WriteIniBool(gszIniAutoMonitor, gPref.fAutoMonitor);
WriteIniBool(gszIniAutoHost,    gPref.fAutoHost);
WriteIniBool(gszIniAutoCall,    gPref.fAutoCall);
WriteIniBool(gszIniAutoChat,    gPref.fAutoChat);

WriteIniInt(gszIniInit,        gPref.iInit);

WriteIniBool(gszIniMsgTrace,    gPref.fMsgTrace);
WriteIniBool(gszIniMsgObjects,  gPref.fMsgObjects);
WriteIniBool(gszIniMsgNotify,   gPref.fMsgNotify);
}

//****************************************************************************
//
// BOOL FCreateLView(void)
//
//****************************************************************************

BOOL FCreateLView(void)
{
ghwndLView = CreateWindow(TEXT("SysListView32"), NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | WS_CLIPSIBLINGS | LVS_REPORT,
0, 0, 0, 0,
ghwndMain, (HMENU) IDW_LVIEW, ghInst, NULL);
if (ghwndLView == NULL)
return FALSE;

ListView_SetExtendedListViewStyle(ghwndLView, LVS_EX_FULLROWSELECT);

// Initialize column data
{
int   iCol;
LPCOL lpCol;
LV_COLUMN lvc;

lpCol = _rgColMember;
ClearStruct(&lvc);
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
for (iCol = 0; iCol < ARRAY_ELEMENTS(_rgColMember); iCol++)
{
lvc.iSubItem = iCol;
lvc.cx = lpCol->dwWidth;
lvc.pszText = lpCol->lpsz;
if (ListView_InsertColumn(ghwndLView, iCol, &lvc) == -1)
return FALSE;
lpCol++;
}
}

return TRUE;
}

//****************************************************************************
//
// BOOL FCreateMsgWindow(void)
//
// Create the main message window
//
//****************************************************************************

BOOL FCreateMsgWindow(void)
{
ghwndMsg = CreateWindow(TEXT("LISTBOX"), NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | WS_CLIPSIBLINGS |
LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS ,
0, 0, 0, 0,
ghwndMain, (HMENU) IDW_MSG, ghInst, NULL);
if (ghwndMsg == NULL)
return FALSE;

// set the font for the window
ghfontEntry = CreateFontIndirect(&gPref.lf);
if (ghfontEntry == hfontNil)
return TRUE;

SendMessage(ghwndMsg, WM_SETFONT, (WPARAM) ghfontEntry, MAKELPARAM(TRUE, 0));

return TRUE;
}

WNDPROC lpProcEdit = NULL;
VOID CmdChat(void);
LRESULT FAR PASCAL SubClsEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

LRESULT FAR PASCAL SubClsEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if ((uMsg == WM_CHAR) && (VK_RETURN == wParam))
{
CmdChat();
return 0L;
}

return CallWindowProc(lpProcEdit, hwnd, uMsg, wParam, lParam);
}

//****************************************************************************
//
// BOOL FCreateChatWindow(void)
//
//****************************************************************************

BOOL FCreateChatWindow(void)
{
ghwndChat = CreateWindow(TEXT("EDIT"), NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS |  WS_TABSTOP |
ES_AUTOHSCROLL,
0, 0, DXP_SEND, DYP_SEND,
ghwndMain, (HMENU) IDW_CHAT, ghInst, NULL);
if (ghwndChat == NULL)
return FALSE;

// set the font for the window

ghwndSend = CreateWindow(TEXT("BUTTON"), TEXT("Send To"),
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS | WS_TABSTOP |
BS_DEFPUSHBUTTON,
0, 0, DXP_SEND, DYP_SEND,
ghwndMain, (HMENU) IDW_SEND, ghInst, NULL);
if (ghwndSend == NULL)
return FALSE;

lpProcEdit = (WNDPROC) GetWindowLong(ghwndChat, GWL_WNDPROC);
SetWindowLong(ghwndChat, GWL_WNDPROC, (LONG)(WNDPROC) SubClsEdit);

ghwndUList = CreateWindow(TEXT("COMBOBOX"), TEXT("Everyone"),
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS | WS_VSCROLL | WS_TABSTOP |
CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED,
0, 0, DXP_ULIST, DYP_ULIST,
ghwndMain, (HMENU) IDL_USERS, ghInst, NULL);
if (ghwndUList == NULL)
return FALSE;

return TRUE;
}

//****************************************************************************
//
// BOOL FInitMain(void)
//
// Initialize the window data and register the window class
//
//****************************************************************************

BOOL FInitMain(void)
{
TCHAR szTitle[MAX_PATH];
WNDCLASS  wc;

ASSERT(NULL != ghInst);

ghAccelTable = LoadAccelerators(ghInst, MAKEINTRESOURCE(ID_APPACCEL));

LoadString(ghInst, IDS_APP_TITLE, szTitle, CCHMAX(szTitle));

wc.style         = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc   = (WNDPROC)WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = ghInst;
wc.hIcon         = LoadIcon(ghInst, MAKEINTRESOURCE(ICO_MAIN));
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName  = MAKEINTRESOURCE(IDM_APP);
wc.lpszClassName = gszAppClassName;

// Register the window class and return FALSE if unsuccesful.
if (!RegisterClass(&wc))
return FALSE;

// MAIN WINDOW
// Create a main window for this application instance.
ghwndMain = CreateWindow(gszAppClassName, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
NULL, NULL, ghInst, NULL);
if (ghwndMain == NULL)
return FALSE;

ghMenu = GetMenu(ghwndMain);
if (NULL == ghMenu)
return FALSE;

if (!FCreateSbar() || !FCreateTbar() ||
!FCreateLView() || !FCreateMsgWindow() || !FCreateChatWindow())
return FALSE;

ShowHwnd(ghwndTbar, gPref.fTbar, IDM_VIEW_TBAR);
ShowHwnd(ghwndSbar, gPref.fSbar, IDM_VIEW_SBAR);

if (gPref.wpMain.length != 0)
SetWindowPlacement(ghwndMain, &gPref.wpMain);

return TRUE;
}

//****************************************************************************
//
// BOOL FInitApp(LPSTR lpszCmd, int nCmdShow)
//
// Initialize the application after checking command line, etc.
//
//****************************************************************************

BOOL FInitApp(LPSTR lpszCmd, int nCmdShow)
{
InitCommonControls();

 if (FAILED(CoInitialize(NULL)))
return FALSE;

ReadPref();

if (!FInitMain())
return FALSE;

ASSERT(NULL != ghwndMain);
ShowWindow(ghwndMain, nCmdShow);
UpdateWindow(ghwndMain);

TRACE_MSG("System Initialized");

OnStartup();
return TRUE;
}

//****************************************************************************
//
// VOID FreeAllComObjects(void)
//
//****************************************************************************

VOID FreeAllComObjects(void)
{
// Release all objects to which this app has a reference
FreeAs();
FreeFt();
FreeDataChannel();
FreeAllUsers();
FreeChannels();
FreeCalls();
FreeConferences();
FreeConfMgr();
}


MAIN.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       MAIN.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// Global Variables
//
//****************************************************************************

HINSTANCE ghInst       = NULL;          // Current Instance
HACCEL    ghAccelTable = NULL;          // Menu accelerators
HMENU     ghMenu       = NULL;          // Main Menu
HWND      ghwndMain    = NULL;          // Main Window

HWND      ghwndSbar    = hwndNil;       // Status bar window
HWND      ghwndTbar    = hwndNil;       // Toolbar window
HWND      ghwndMsg     = hwndNil;       // Message window
HWND      ghwndChat    = hwndNil;       // Chat Edit Control
HWND      ghwndSend    = hwndNil;       // Send Button
HWND      ghwndUList   = hwndNil;       // User List
HWND      ghwndLView   = hwndNil;       // Main List View
HWND      ghwndEntry   = hwndNil;       // szEntry Edit control
HFONT     ghfontEntry  = hfontNil;      // Font for edit control

PREF      gPref;                        // User preferences
int       gdxWndMin    = 325;           // maximum size of window
int       gdyWndMin    = 250;           // maximum size of window

//****************************************************************************
//
// Main Windows entrypoint
//
//****************************************************************************

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hInstPrev, LPSTR  lpszCmd, int nCmdShow)
{
ghInst = hInstance;

if (FInitApp(lpszCmd, nCmdShow))
{
MSG  msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, ghAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

    // Uninitialize OLE
    CoUninitialize();

return 0;
}


MSG.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       MSG.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

// from cmd.cpp
VOID CmdExit(void);
LRESULT MsgCmdMain(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam);

// from tbar.cpp
LRESULT MsgNotifyTbar(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam);
LRESULT MsgMenuSelect(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam);

//****************************************************************************
//
// Handle a WM_DESTROY message
//
//****************************************************************************

LRESULT MsgDestroy(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lParam)
{
FreeAllComObjects();
PostQuitMessage(0);
return 0;
}

//****************************************************************************
//
// LRESULT MsgCreate(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lParam)
//
//****************************************************************************

LRESULT MsgCreate(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lParam)
{
return 0;
}

//****************************************************************************
//
// Handle a WM_CLOSE message
//
//****************************************************************************

LRESULT MsgClose(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
{
FreeAllComObjects();
CmdExit();
return 0;
}

//****************************************************************************
//
// Handle a WM_SIZE message
//
//****************************************************************************

LRESULT MsgSize(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
{
int dxClient = LOWORD(lparam);  // width of client area
int dyClient = HIWORD(lparam);  // height of client area

SizeMsgWindow(dxClient, dyClient);
SizeStatus(dxClient, 0);
SendMessage(ghwndSbar, uMsg, wparam, lparam);

return 0;
}

//****************************************************************************
//
// Handle a WM_GETMINMAXINFO message
//
//****************************************************************************

LRESULT MsgGetMinMax(HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
{
LPMINMAXINFO lpminmax = (LPMINMAXINFO) lparam;

if (gdxWndMin == 0 || gdyWndMin == 0)
return 0;

lpminmax->ptMinTrackSize.x = gdxWndMin;
lpminmax->ptMinTrackSize.y = gdyWndMin;

return 0;
}

//****************************************************************************
//
// LRESULT MsgDrawItem(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT MsgDrawItem(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case IDW_MSG:
{
LPDRAWITEMSTRUCT lpdi = (LPDRAWITEMSTRUCT)lParam;
TCHAR sz[MAX_PATH];

COLORREF cv = SendMessage(ghwndMsg, LB_GETITEMDATA, (WPARAM)lpdi->itemID, (LPARAM)0);
SetTextColor(lpdi->hDC, cv);

SendMessage(ghwndMsg, LB_GETTEXT, (WPARAM)lpdi->itemID, (LPARAM)sz);
DrawText(lpdi->hDC, sz, -1, &lpdi->rcItem, DT_LEFT | DT_SINGLELINE);
break;
}
case IDW_SBAR:
{
if (0 != (((LPDRAWITEMSTRUCT) lParam)->itemData))
{
DrawIconEx(((LPDRAWITEMSTRUCT) lParam)->hDC,
((LPDRAWITEMSTRUCT) lParam)->rcItem.left,
((LPDRAWITEMSTRUCT) lParam)->rcItem.top,
(HICON) (((LPDRAWITEMSTRUCT) lParam)->itemData),
STATUSICONSIZE, STATUSICONSIZE, 0, NULL, DI_NORMAL);
}
break;
}

case IDL_USERS:
{
DrawUserItem((LPDRAWITEMSTRUCT) lParam);
break;
}

default:
break;
} /* switch */

return 0;
}

//****************************************************************************
//
// LRESULT MsgNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//
//****************************************************************************

LRESULT MsgNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (((LPNMHDR) lParam)->idFrom)
        {
    case IDW_TBAR:
        return MsgNotifyTbar(hwnd, uMsg, wParam, lParam);
    default:
        // It might still be for the toolbar (tooltips)
        return MsgNotifyTbar(hwnd, uMsg, wParam, lParam);
        }

    return 0;
}

////////////////////////////////////////////////////////////////////////////
// Main window message table definition.
static MSD _rgmsdMain[] =
{
{WM_COMMAND,       MsgCmdMain},
{WM_NOTIFY,        MsgNotify},
{WM_CREATE,        MsgCreate},
{WM_DESTROY,       MsgDestroy},
{WM_CLOSE,         MsgClose},
{WM_SIZE,          MsgSize},
{WM_MENUSELECT,    MsgMenuSelect},
{WM_GETMINMAXINFO, MsgGetMinMax},
{WM_DRAWITEM,      MsgDrawItem},

{0, NULL}  // MUST be the last entry
};

//****************************************************************************
//
// Main Window proc
//
//****************************************************************************

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
for (LPMSD lpmsd = _rgmsdMain; 0 != lpmsd->uMsg; lpmsd++)
{
if (lpmsd->uMsg == uMsg)
{
return lpmsd->pfnmsg(hwnd, uMsg, wParam, lParam);
}
}

return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


PRECOMP.H

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       PRECOMP.H
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#ifndef _PRECOMP_H
#define _PRECOMP_H

// Build Options
#define NM2_CHAT_PROTOCOL  // Use the NetMeeting 2.0 Chat Protocol for sending text

// System Include files
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <tchar.h>
#include <ocidl.h>

#include "imsconf2.h"

#include "global.h"
#include "clUtil.h"
#include "cMgr.h"
#include "cConf.h"
#include "cCall.h"
#include "cChan.h"
#include "cMemb.h"
#include "cData.h"
#include "cVideo.h"

#include "resource.h"

#endif /* _PRECOMP_H */


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by res.rc
//
#define IDS_APP_TITLE                   1
#define IDS_VERSION                     2
#define IDS_SYSMENU                     4
#define ICO_MAIN                        101
#define IDM_APP                         104
#define ID_APPACCEL                     105
#define IDD_CONFERENCE                  106
#define IDD_CALL                        107
#define IDD_SENDFILE                    112
#define IDC_TOOLBAR                     112
#define IDB_TBAR                        114
#define ICO_NOAUDIO                     116
#define ICO_NOVIDEO                     117
#define ICO_PHONE                       118
#define ICO_SPEAKER                     119
#define ICO_AUDIO                       120
#define ICO_VIDEO                       121
#define IDD_OPTIONS                     123
#define IDD_AUDIO                       124
#define IDD_SHARE                       125
#define IDD_CALL_CONF                   126
#define IDD_SWITCH                      127
#define IDD_LAUNCH                      128
#define IDD_VIDEO                       129
#define IDD_EJECT                       130
#define IDE_ADDR                        1001
#define IDE_PASSWORD                    1002
#define IDE_APPLICATION                 1010
#define IDE_DATA                        1010
#define IDE_COMMANDLINE                 1011
#define IDE_USERID                      1011
#define IDE_HCONF                       1011
#define IDE_DIRECTORY                   1012
#define IDC_HCONFNOTIFY                 1014
#define IDC_DWUSERID                    1016
#define IDC_USERNAME                    1016
#define IDE_FILENAME                    1017
#define IDB_BROWSE                      1018
#define IDC_USERID                      1022
#define IDL_ADDR_TYPE                   1024
#define IDC_MONITOR                     1025
#define IDL_CALL_TYPE                   1025
#define IDC_HOST                        1026
#define IDE_CONFERENCE_NAME             1027
#define IDC_CALL_USER                   1028
#define IDE_LEVEL                       1028
#define IDE_USER_NAME                   1029
#define IDC_SPIN                        1029
#define IDC_UI_CALL                     1030
#define IDB_ACTIVATE                    1030
#define IDC_UI_JOIN                     1031
#define IDB_SET_LEVEL                   1031
#define IDC_UI_APP_SHARE                1032
#define IDL_APP                         1032
#define IDC_UI_FT                       1033
#define IDB_SHARE                       1033
#define IDC_MSG_OBJECTS                 1034
#define IDB_UNSHARE                     1034
#define IDE_CONFERENCE                  1034
#define IDC_MSG_NOTIFY                  1035
#define IDB_UPDATE                      1035
#define IDB_SWITCH                      1035
#define IDC_ACTIVE                      1036
#define IDC_AUTO_CHAT                   1036
#define IDC_MSG_TRACE                   1037
#define IDC_PAUSE                       1037
#define IDL_GUID                        1038
#define IDC_VIDEO_SMALL                 1039
#define IDC_VIDEO_MEDIUM                1040
#define IDC_VIDEO_LARGE                 1041
#define IDC_VIDEO_FORMAT                1042
#define IDC_VIDEO_QUALITY               1043
#define IDC_VIDEO_STATE                 1044
#define IDC_VIDEO_WINDOW                1045
#define IDC_VIDEO_SOURCE                1046
#define IDC_VIDEO_SHOW                  1047
#define IDC_VIDEO_PAUSE                 1048
#define IDC_UPDATE                      1049
#define IDC_NOUI                        1050
#define IDC_SHORT                       1051
#define IDC_TALL                        1052
#define IDC_THIN                        1053
#define IDC_WIDE                        1054
#define IDC_VISIBLE                     1055
#define IDC_UP                          1056
#define IDC_DOWN                        1057
#define IDC_LEFT                        1058
#define IDC_RIGHT                       1059
#define IDC_TOPMOST                     1060
#define IDC_E_AUTOSIZE                  1061
#define IDC_PB_AUTOSIZE                 1062
#define IDC_VIDEO_STATUS                1063
#define IDR_INIT_NORMAL                 1064
#define IDR_INIT_CONTROL                1065
#define IDR_INIT_NO_LAUNCH              1066
#define IDC_S_AUTOSIZE                  1067
#define IDC_LEVEL                       1069
#define IDC_NMCH_FIRST                  5001
#define IDC_NMCH_DATA                   5001
#define IDC_NMCH_AUDIO                  5002
#define IDC_NMCH_VIDEO                  5003
#define IDC_NMCH_FT                     5004
#define IDC_NMCH_AS                     5005
#define IDC_NMCH_LAST                   5005
#define IDE_NAME                        6000
#define IDL_CONFERENCE                  6001
#define IDL_USERS                       6002
#define IDM_VIEW_SBAR                   40014
#define IDM_VIEW_FONT                   40016
#define IDM_EXIT                        40017
#define IDM_CMD_ENUMCONF                40044
#define IDM_CMD_ENUMUSER                40045
#define IDM_CMD_SETRECDIR               40048
#define IDM_VIEW_CLEAR                  40055
#define IDM_VIEW_ENVIRON                40058
#define IDM_CMD_SETGUID                 40059
#define IDM_CMD_LAUNCHREMOTE            40060
#define IDT_CMD_LAUNCHREMOTE            40061
#define IDM_VIEW_TBAR                   40062
#define IDM_HELP_CONTENTS               40063
#define IDM_CONF_START                  40064
#define IDM_CONF_STOP                   40065
#define IDM_SYSINFO                     40066
#define IDM_CHANNEL_AUDIO               40067
#define IDM_CHANNEL_VIDEO               40068
#define IDM_CHANNEL_FT                  40069
#define IDM_CHANNEL_SHARE               40070
#define IDM_CALL                        40071
#define IDM_EJECT                       40072
#define IDM_CHANNEL_DATA                40073
#define IDM_CALL_CANCEL                 40074
#define IDM_MONITOR                     40084
#define IDM_VIEW_MSG                    40085
#define ID_BUTTON40100                  40100
#define ID_BUTTON40101                  40101
#define ID_BUTTON40102                  40102
#define ID_BUTTON40103                  40103
#define ID_BUTTON40104                  40104
#define ID_BUTTON40105                  40105
#define ID_BUTTON40106                  40106
#define ID_BUTTON40107                  40107
#define ID_BUTTON40108                  40108
#define ID_BUTTON40109                  40109
#define ID_BUTTON40110                  40110
#define ID_BUTTON40111                  40111
#define ID_BUTTON40112                  40112
#define ID_BUTTON40113                  40113
#define ID_BUTTON40114                  40114
#define ID_BUTTON40115                  40115
#define ID_BUTTON40116                  40116
#define ID_BUTTON40117                  40117
#define ID_BUTTON40118                  40118
#define ID_BUTTON40119                  40119
#define ID_BUTTON40120                  40120
#define ID_BUTTON40121                  40121
#define ID_BUTTON40122                  40122
#define ID_BUTTON40123                  40123
#define ID_BUTTON40124                  40124
#define ID_BUTTON40125                  40125
#define ID_BUTTON40126                  40126
#define ID_BUTTON40127                  40127
#define ID_BUTTON40140                  40140
#define IDM_OPTIONS                     40141
#define IDM_SWITCH                      40142
#define IDM_CALL_CONF                   40143
#define IDM_VIEW_CHAT                   40144
#define IDM_LAUNCH                      40145
#define IDM_UPDATE                      40146
#define IDM_HELP                        40999
#define IDM_ABOUT                       41000
#define IDM_CMD_CONNECT                 41001
#define IDM_CMD_DISCONNECT              41002
#define IDM_CMD_GETINFO                 41003
#define IDM_CMD_SETINFO                 41004
#define IDM_CMD_SENDDATA                41005
#define IDM_CMD_SENDFILE                41006
#define IDM_CMD_SHAREWINDOW             41007
#define IDM_CMD_ISWINDOWSHARED          41008
#define IDM_CMD_UNSHAREWINDOW           41009
#define IDM_CMD_CANCELTRANSFER          41010
#define IDM_CMD_LISTEN                  41011
#define IDM_CMD_SETNOTIFY               41012
#define IDM_CMD_REMOVENOTIFY            41013
#define IDM_FT_CANCEL                   41014
#define IDM_CONFINFO                    41015
#define IDM_CALL_ACCEPT                 41016
#define IDM_CALL_REJECT                 41017
#define IDM_DATA_ENABLE                 41018

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        130
#define _APS_NEXT_COMMAND_VALUE         41019
#define _APS_NEXT_CONTROL_VALUE         1071
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


RTNS.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       RTNS.CPP
//  Content:    General utility routines
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

//****************************************************************************
//
// VOID SizeStatus(int dxClient, int dy)
//
//****************************************************************************

VOID SizeStatus(int dxClient, int dy)
{
int rgdxp[3];

rgdxp[2] = dxClient;
dxClient -= 50;
rgdxp[1] = dxClient;
dxClient -= 75;
rgdxp[0] = dxClient;
SendMessage(ghwndSbar, SB_SETPARTS, (WPARAM) 3, (LPARAM) rgdxp);
}

//****************************************************************************
//
// VOID SizeMsgWindow(int dx, int dy)
//
//****************************************************************************

VOID SizeMsgWindow(int dx, int dy)
{
RECT rc;
int xLeft;
int yTop;
int d;
int dyLView;

xLeft = 0;
yTop = 3;
dy -= 3;

if (gPref.fTbar)
{
// adjust for Toolbar
GetClientRect(ghwndTbar, &rc);
d = (rc.bottom - rc.top);
yTop += d;
dy -= d;
}

if (gPref.fSbar)
{
// adjust for status bar
GetClientRect(ghwndSbar, &rc);
d = (rc.bottom - rc.top);
dy -= d;
}

if (gPref.fChat)
{
// adjust for status bar
GetClientRect(ghwndChat, &rc);
d = (rc.bottom - rc.top);
dy -= d;
MoveWindow(ghwndChat, xLeft, yTop + dy, dx-(DXP_SEND+DXP_ULIST), d, TRUE);
MoveWindow(ghwndSend, xLeft+dx-(DXP_SEND+DXP_ULIST), yTop + dy, DXP_SEND, DYP_SEND, TRUE);
MoveWindow(ghwndUList, xLeft+dx-DXP_ULIST, yTop + dy, DXP_ULIST, DYP_ULIST, TRUE);
}

dyLView = dy;
if (gPref.fMsg)
{
dyLView = dyLView / 2;
MoveWindow(ghwndMsg, xLeft, yTop+dyLView, dx, dyLView, TRUE);
}

MoveWindow(ghwndLView, xLeft, yTop, dx, dyLView, TRUE);
}

//****************************************************************************
//
// VOID RecalcMsgWindow(void)
//
//****************************************************************************

VOID RecalcMsgWindow(void)
{
RECT rc;

GetClientRect(ghwndMain, &rc);
SizeMsgWindow(rc.right, rc.bottom);
}

//****************************************************************************
//
// VOID ShowHwnd(HWND hwnd, BOOL fShow, int idm)
//
//****************************************************************************

VOID ShowHwnd(HWND hwnd, BOOL fShow, int idm)
{
if (IsWindow(hwnd))
{
ShowWindow(hwnd, fShow ? SW_SHOW : SW_HIDE);
SetMenuCheck(idm, fShow);
RecalcMsgWindow();
}
}


SBAR.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       SBAR.CPP
//  Content:    Status Bar Routines
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"

TBBUTTON _rgtbb[] =
{
    {0,   0,                      TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0,  0, 0},
    {28,  IDM_MONITOR,            TBSTATE_ENABLED, TBSTYLE_CHECK,  0, 0,  1, 0},
    {10,  IDM_CONF_START,         TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0,  2, 0},
    {6,   IDM_CONF_STOP,          TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0,  3, 0},
    {4,   IDM_VIEW_CLEAR,         TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0,  4, 0},
    {5,   IDM_CALL,               TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0,  5, 0},
    {1,   IDM_VIEW_MSG,           TBSTATE_ENABLED, TBSTYLE_CHECK,  0, 0,  6, 0},
    {0,   IDM_VIEW_CLEAR,         TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0,  7, 0},
    {6,   IDM_VIEW_FONT,          TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0,  8, 0},
    {10,  IDM_CHANNEL_DATA,       TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0,  9, 0},
    {11,  IDM_CHANNEL_AUDIO,      TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 10, 0},
    {12,  IDM_CHANNEL_VIDEO,      TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 11, 0},
    {26,  IDM_CHANNEL_FT,         TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 12, 0},
    {14,  IDM_CHANNEL_SHARE,      TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 13, 0},
    {3,   IDM_FT_CANCEL,          TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 14, 0},

};

// default ordering of toolbar buttons
static char _szBtnDefault[] = "0100020304000500060007";

static char _szToolbar[] = "Toolbar";

//****************************************************************************
//
// LPTBBUTTON _GetDefaultTbb(DWORD * pcTbb, BOOL fReset)
//
//****************************************************************************

LPTBBUTTON _GetDefaultTbb(DWORD * pcTbb, BOOL fReset)
{
DWORD iBtn, cBtn;
LPSTR lpsz, lpch;
LPTBBUTTON pttb;

if (fReset)
lpsz = _szBtnDefault;
else
lpsz = GetIniStr(_szToolbar, _szBtnDefault);

lpch = lpsz;
cBtn = lstrlen(lpsz) / 2;

pttb = (LPTBBUTTON) LpvAlloc(sizeof(TBBUTTON) * cBtn);
if (NULL == pttb)
return NULL;
for (iBtn = 0; iBtn < cBtn; iBtn++)
{
CopyStruct(pttb + iBtn, &_rgtbb[ChFromHex(lpch)]);
lpch += 2;
}

if (!fReset)
FreePlpv(&lpsz);

*pcTbb = cBtn;
return pttb;
}

//****************************************************************************
//
// VOID ResetToolbar(void)
//
// Reset the toolbar to the initial defaults
//
//****************************************************************************

VOID ResetToolbar(void)
{
DWORD cBtn, iBtn;
LPTBBUTTON ptbb;

ptbb = _GetDefaultTbb(&cBtn, TRUE /* fReset */);
if (NULL == ptbb)
return;

iBtn = SendMessage(ghwndTbar, TB_BUTTONCOUNT, 0, 0);
while (iBtn != 0)
{
SendMessage(ghwndTbar, TB_DELETEBUTTON, (WPARAM) --iBtn, 0);
}

SendMessage(ghwndTbar, TB_ADDBUTTONS, (WPARAM) cBtn, (LPARAM) ptbb);

FreePlpv(&ptbb);
}

//****************************************************************************
//
// BOOL FCreateTbar(void)
//
// Create the toolbar
//
//****************************************************************************

BOOL FCreateTbar(void)
{
DWORD cBtn;
LPTBBUTTON ptbb;

ptbb = _GetDefaultTbb(&cBtn, FALSE /* fReset */);
if (NULL == ptbb)
return FALSE;

ghwndTbar = CreateToolbarEx(ghwndMain,
WS_CHILD | CCS_ADJUSTABLE |
TBSTYLE_TOOLTIPS | TBSTYLE_ALTDRAG, // | TBSTYLE_WRAPABLE
IDW_TBAR,
NUMIMAGES,
ghInst,
IDC_TOOLBAR,
ptbb,
cBtn,
BUTTONWIDTH,
BUTTONHEIGHT,
IMAGEWIDTH,
IMAGEHEIGHT,
sizeof(TBBUTTON));

FreePlpv(&ptbb);

return (ghwndTbar != NULL);
}

//****************************************************************************
//
// LRESULT MsgNotifyTbar(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
//
//****************************************************************************

LRESULT MsgNotifyTbar(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
LPTOOLTIPTEXT lpToolTipText;
static TCHAR szBuffer[MAX_PATH];

lpToolTipText = (LPTOOLTIPTEXT)lparam;

switch (lpToolTipText->hdr.code)
{
case TTN_NEEDTEXT:
{
int cb;

cb = LoadString(ghInst, lpToolTipText->hdr.idFrom,   // string ID == command ID
szBuffer, CCHMAX(szBuffer));
if (cb == 0)
{
szBuffer[0] = '\0';
}

lpToolTipText->lpszText = szBuffer;
return 1;
}
case TBN_QUERYINSERT:
return 1; // allow insert
case TBN_QUERYDELETE:
return 1; // allow delete
case TBN_RESET:
ResetToolbar();
return 1;
case TBN_GETBUTTONINFO:
{
LPTBNOTIFY ptbn = (LPTBNOTIFY) lparam;
int iBtn = ptbn->iItem+1;

if (iBtn >= (ARRAY_ELEMENTS(_rgtbb)))
return 0; // all button data has been passed to the control

LoadString(ghInst, _rgtbb[iBtn].idCommand,
ptbn->pszText, ptbn->cchText);
//?ptbn->tbButton = _rgtbb[iBtn];
return 1;

}
case TBN_CUSTHELP:
break;
case TBN_TOOLBARCHANGE:
break; // changes handled when exiting
default:
break;
}

return 0;
}

//****************************************************************************
//
// VOID SetToolbarCheck(int idm, BOOL fCheck)
//
// Change the status of a toolbar command to be checked
//
//****************************************************************************

VOID SetToolbarCheck(int idm, BOOL fCheck)
{
SendMessage(ghwndTbar, TB_CHECKBUTTON,
(WPARAM) idm, (LPARAM) MAKELONG(fCheck, 0));
}

//****************************************************************************
//
// VOID CmdToolbar(HWND hwnd, WORD wCmd, WORD wNotify, HWND hwndCtrl)
//
//****************************************************************************

VOID CmdToolbar(HWND hwnd, WORD wCmd, WORD wNotify, HWND hwndCtrl)
{
SendMessage(ghwndTbar, TB_CUSTOMIZE, 0, 0);
}

//****************************************************************************
//
// VOID WriteIniTbar(void)
//
//****************************************************************************

VOID WriteIniTbar(void)
{
int   iBtn;
int   cBtn;
LPSTR lpsz;
LPSTR lpch;
TBBUTTON tbb;

if (NULL == ghwndTbar)
return; // no toolbar?

cBtn = SendMessage(ghwndTbar, TB_BUTTONCOUNT, 0, 0);
if (0 == cBtn)
return;

lpsz = (LPSTR) LpvAlloc(cBtn*2 + 1);
if (NULL == lpsz)
return;

lpch = lpsz;
for (iBtn = 0; iBtn < cBtn; iBtn++)
{
SendMessage(ghwndTbar, TB_GETBUTTON, (WPARAM) iBtn, (LPARAM) (LPTBBUTTON) &tbb);
wsprintf(lpch, TEXT("%02X"), LOBYTE(tbb.dwData));
lpch += 2;
}
WriteIniStr(_szToolbar, lpsz);
FreePlpv(&lpsz);
}

//****************************************************************************
//
// BOOL FCreateSbar(void)
//
// Create the status bar.
//
//****************************************************************************

BOOL FCreateSbar(void)
{
ghwndSbar = CreateWindow(STATUSCLASSNAME, NULL,
WS_CHILD | SBARS_SIZEGRIP,
0, 0, 0, 0,
ghwndMain, (HMENU) IDW_SBAR, ghInst, NULL);

return (ghwndSbar != NULL);
}

//****************************************************************************
//
// VOID UpdateStatusBar(LPTSTR lpsz, WORD wPart, WORD wFlags)
//
// Update the text for part of the status bar
//
//****************************************************************************

VOID UpdateStatusBar(LPTSTR lpsz, WORD wPart, WORD wFlags)
{
    SendMessage(ghwndSbar, SB_SETTEXT, wPart | wFlags, (LPARAM) lpsz);
}

//****************************************************************************
//
// VOID StatusMsg(LPTSTR sz)
//
//****************************************************************************

VOID StatusMsg(LPTSTR sz)
{
UpdateStatusBar(sz, IDSBP_MSG, 0);
}

//****************************************************************************
//
// VOID UpdateStatusIcon(DWORD dwId)
//
//****************************************************************************

VOID UpdateStatusIcon(DWORD dwId)
{
static DWORD m_idIconStatus = 0;
static HICON m_hIconStatus = 0;

if (dwId == m_idIconStatus)
return;

HICON hIcon = (HICON) LoadImage(ghInst,
MAKEINTRESOURCE(dwId), IMAGE_ICON,
STATUSICONSIZE, STATUSICONSIZE,
LR_DEFAULTCOLOR | LR_SHARED);
if (NULL == hIcon)
{
return;
}

m_idIconStatus = dwId;
m_hIconStatus = hIcon;
SendMessage(ghwndSbar, SB_SETTEXT,
IDSBP_ICON | SBT_OWNERDRAW, (LPARAM) m_hIconStatus);
}

//****************************************************************************
//
// LRESULT MsgMenuSelect(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
//
// Change the status bar text to display the menu help.
//
//****************************************************************************

LRESULT MsgMenuSelect(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
{
TCHAR  szBuffer[MAX_PATH];
UINT   nStringID = 0;
UINT   fuFlags = GET_WM_MENUSELECT_FLAGS(wparam, lparam) & 0xffff;
UINT   uCmd    = GET_WM_MENUSELECT_CMD(wparam, lparam);
HMENU  hMenu   = GET_WM_MENUSELECT_HMENU(wparam, lparam);

szBuffer[0] = 0;
nStringID = 0;

if (fuFlags == 0xffff && hMenu == NULL)     // Menu has been closed
{
return 0;
//nStringID = IDS_DESCRIPTION;
}
else if (fuFlags & MFT_SEPARATOR)           // Ignore separators
{
nStringID = 0;
}
else if (fuFlags & MF_POPUP)                // Popup menu
{
if (fuFlags & MF_SYSMENU)               // System menu
nStringID = IDS_SYSMENU;
}
else                                        // Must be a command item
{
nStringID = uCmd;               // String ID == Command ID
}

// Load the string if we have an ID
if (nStringID != 0)
{
LoadString(ghInst, nStringID, szBuffer, CCHMAX(szBuffer));
}

// Finally... send the string to the status bar
UpdateStatusBar(szBuffer, IDSBP_MSG, 0);

return 0;
}


UTIL.CPP

//****************************************************************************
//  Module:     NMUI.EXE
//  File:       UTIL.CPP
//  Content:
//
//
//  Copyright (c) Microsoft Corporation 1995-1997
//
// 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.
//****************************************************************************

#include "precomp.h"
#include "shlobj.h"

//****************************************************************************
//
// BOOL GetIniBool(LPTSTR pszEntry, BOOL fDefault)
//
// Get the boolean data for an entry
//
//****************************************************************************

BOOL GetIniBool(LPTSTR pszEntry, BOOL fDefault)
{
return GetPrivateProfileInt(gszAppName, pszEntry, fDefault, gszIniName) != 0;
}

//****************************************************************************
//
// DWORD GetIniInt(LPTSTR pszEntry, DWORD dwDefault)
//
// Get the numeric data for an entry
//
//****************************************************************************

DWORD GetIniInt(LPTSTR pszEntry, DWORD dwDefault)
{
return GetPrivateProfileInt(gszAppName, pszEntry, dwDefault, gszIniName);
}

//****************************************************************************
//
// LPTSTR GetIniStr(LPTSTR pszEntry, LPTSTR pszDefault)
//
// Get the string data for an entry
//
//****************************************************************************

LPTSTR GetIniStr(LPTSTR pszEntry, LPTSTR pszDefault)
{
int     cch;
LPTSTR  pszRet;
TCHAR   sz[MAX_PATH];

cch = GetPrivateProfileString(gszAppName, pszEntry, pszDefault, sz, MAX_PATH, gszIniName);
if (cch == 0)
{
if (NULL == pszDefault)
return NULL;

cch = lstrlen(pszDefault);
lstrcpy(sz, pszDefault);
}
pszRet = PszAlloc(cch);
if (pszRet == NULL)
return NULL;

lstrcpy(pszRet, sz);

return pszRet;
}

//****************************************************************************
//
// VOID GetIniHex(LPTSTR pszEntry, LPVOID lpv, int cb)
//
// Convert the hex data into binary.
// If no entry is found, lpv isn't modified
//
//****************************************************************************

VOID GetIniHex(LPTSTR pszEntry, LPVOID lpv, int cb)
{
TCHAR sz[MAX_PATH];

if (GetPrivateProfileString(gszAppName, pszEntry, "", sz, MAX_PATH, gszIniName) != 0)
{
HexToData(sz, lpv, cb);
}
}

//****************************************************************************
//
// VOID WriteIniStr(LPTSTR pszEntry, LPTSTR pszData)
//
// Write the data to an entry in the ini file
//
//****************************************************************************

VOID WriteIniStr(LPTSTR pszEntry, LPTSTR pszData)
{
WritePrivateProfileString(gszAppName, pszEntry, pszData, gszIniName);
}

//****************************************************************************
//
// VOID WriteIniInt(LPTSTR pszEntry, DWORD dw)
//
// Write the numeric data to an entry in the ini file
//
//****************************************************************************

VOID WriteIniInt(LPTSTR pszEntry, DWORD dw)
{
TCHAR szData[MAX_PATH];

wsprintf(szData, TEXT("%d"), dw);
WritePrivateProfileString(gszAppName, pszEntry, szData, gszIniName);
}

//****************************************************************************
//
// VOID WriteIniBool(LPTSTR pszEntry, BOOL f)
//
// Write the boolean value to an entry in the registery
//
//****************************************************************************

VOID WriteIniBool(LPTSTR pszEntry, BOOL f)
{
WritePrivateProfileString(gszAppName, pszEntry, f ? TEXT("1") : TEXT("0"), gszIniName);
}

//****************************************************************************
//
// VOID WriteIniHex(LPTSTR pszEntry, LPVOID lpv, int cb)
//
//****************************************************************************
VOID WriteIniHex(LPTSTR pszEntry, LPVOID lpv, int cb)
{
TCHAR sz[MAX_PATH];

DataToHex((LPTSTR) lpv, sz, cb);

WriteIniStr(pszEntry, sz);
}

//****************************************************************************
//
// VOID CenterWindow(HWND hwndChild, HWND hwndParent)
//
// Center a window over another window.
//
//****************************************************************************

VOID CenterWindow(HWND hwndChild, HWND hwndParent)
{
int   xNew, yNew;
int   cxChild, cyChild;
int   cxParent, cyParent;
int   cxScreen, cyScreen;
RECT  rcChild, rcParent;
HDC   hdc;

// Get the Height and Width of the child window
GetWindowRect(hwndChild, &rcChild);
cxChild = rcChild.right - rcChild.left;
cyChild = rcChild.bottom - rcChild.top;

// Get the Height and Width of the parent window
GetWindowRect(hwndParent, &rcParent);
cxParent = rcParent.right - rcParent.left;
cyParent = rcParent.bottom - rcParent.top;

// Get the display limits
hdc = GetDC(hwndChild);
if (hdc == NULL) {
// major problems - move window to 0,0
xNew = yNew = 0;
} else {
cxScreen = GetDeviceCaps(hdc, HORZRES);
cyScreen = GetDeviceCaps(hdc, VERTRES);
ReleaseDC(hwndChild, hdc);

if (hwndParent == hwndNil) {
cxParent = cxScreen;
cyParent = cyScreen;
SetRect(&rcParent, 0, 0, cxScreen, cyScreen);
}

// Calculate new X position, then adjust for screen
xNew = rcParent.left + ((cxParent - cxChild) / 2);
if (xNew < 0) {
xNew = 0;
} else if ((xNew + cxChild) > cxScreen) {
xNew = cxScreen - cxChild;
}

// Calculate new Y position, then adjust for screen
yNew = rcParent.top  + ((cyParent - cyChild) / 2);
if (yNew < 0) {
yNew = 0;
} else if ((yNew + cyChild) > cyScreen) {
yNew = cyScreen - cyChild;
}

}

SetWindowPos(hwndChild, NULL, xNew, yNew,0, 0,
SWP_NOSIZE | SWP_NOZORDER);
}

//****************************************************************************
//
// LPVOID LpvAlloc(int cb)
//
// Return a pointer to an allocated array of bytes
//
//****************************************************************************

LPVOID LpvAlloc(int cb)
{
return LocalAlloc(LMEM_FIXED, cb);
}

//****************************************************************************
//
// LPTSTR PszAlloc(int cch)
//
//****************************************************************************

LPTSTR PszAlloc(int cch)
{
return (LPTSTR) LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cch+1));
}

//****************************************************************************
//
// VOID FreePlpv(LPVOID plpv)
//
// Free the data pointed to by plpv and set *plpv to NULL
//
//****************************************************************************

VOID FreePlpv(LPVOID plpv)
{
if ((plpv == NULL) || (*(VOID FAR * FAR *)plpv == NULL)) {
return;
}

LocalFree(*(VOID FAR * FAR *)plpv);
*(VOID FAR * FAR *)plpv = NULL;
}

//****************************************************************************
//
// TCHAR ChFromHex(LPTSTR lpch)
//
// Convert from Hex data to ascii character
//
//****************************************************************************

TCHAR ChFromHex(LPTSTR lpch)
{
int i = 0;
int cch = 2;
char ch;

while (cch-- > 0) {
ch = *lpch++;
if (ch >= 'A') {
i = (i*16) + (ch - ('A'-10));
} else {
i = (i*16) + (ch - '0');
}
}

return (TCHAR) i;
}

//****************************************************************************
//
// VOID HexToData(LPTSTR lpchSrc, LPVOID lpvDest, int cb)
//
// Convert from Hex data to ascii character
//
//****************************************************************************

VOID HexToData(LPTSTR lpchSrc, LPVOID lpvDest, int cb)
{
PTCHAR lpchDest = (PTCHAR) lpvDest;

CharUpperBuff(lpchSrc, cb*2);

while (cb-- > 0)
{
*lpchDest++ = ChFromHex(lpchSrc);
lpchSrc += 2;
}
}

//****************************************************************************
//
// LPSTR HexToBin(LPSTR lpchSrc, LPVOID lpvDest, int cb)
//
// Convert from Hex data to ascii character
//
//****************************************************************************

LPSTR HexToBin(LPSTR lpchSrc, LPVOID lpvDest, int cb)
{
PTCHAR lpchDest = ((CHAR *) lpvDest) + cb;

CharUpperBuff(lpchSrc, cb);

while (cb-- > 0)
{
lpchDest--;
*lpchDest = ChFromHex(lpchSrc);
lpchSrc += 2;
}
return lpchSrc;
}

//****************************************************************************
//
// VOID DataToHex(LPTSTR lpchSrc, LPTSTR lpchDest, int cb)
//
//****************************************************************************

VOID DataToHex(LPTSTR lpchSrc, LPTSTR lpchDest, int cb)
{
TCHAR ch;

if (lpchSrc == lpNil)
{
SetEmptySz(lpchDest);
return;
}

while (cb-- > 0) {
ch = 0x00FF & (unsigned char) (*lpchSrc++);
wsprintf(lpchDest, "%02X", ch);
lpchDest += 2;
}
}

//****************************************************************************
//
// VOID SetMenuCheck(UINT idm, BOOL fCheck)
//
// Set the menu item's check mark.
//
//****************************************************************************

VOID SetMenuCheck(UINT idm, BOOL fCheck)
{
CheckMenuItem(ghMenu, idm,
fCheck ? (MF_CHECKED  | MF_BYCOMMAND) : (MF_UNCHECKED | MF_BYCOMMAND));
}

//****************************************************************************
//
// VOID MaybeDeleteObject(HGDIOBJ * phgdi)
//
// Check usage count, delete if we can
//
//****************************************************************************

VOID MaybeDeleteObject(HGDIOBJ * phgdi)
{
if (*phgdi == hgdiNil)
return;

DeleteObject(*phgdi);
*phgdi = hgdiNil;
}

static TCHAR _szFilter[] = TEXT("All Files\0*.*\0Text Files (*.txt)\0*.TXT\0");
static TCHAR _szPickMsg[] = TEXT("Pick a file to send");
static TCHAR _szPickDir[] = TEXT("Select a Directory");

//****************************************************************************
//
// BOOL FGetFileName(LPTSTR szFileName)
//
//****************************************************************************

BOOL FGetFileName(LPTSTR szFileName)
{
OPENFILENAME ofn;

SetEmptySz(szFileName);
ClearStruct(&ofn);

ofn.lStructSize   = sizeof(OPENFILENAME);
ofn.hwndOwner     = ghwndMain;
ofn.hInstance     = (HINSTANCE) ghInst;
ofn.lpstrFilter   = _szFilter;
ofn.nFilterIndex  = 1L;
ofn.lpstrFile     = (LPSTR) szFileName;
ofn.nMaxFile      = MAX_PATH; // really CCHMAX(szFileName)
ofn.lpstrTitle    = _szPickMsg;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER;

if (!GetOpenFileName(&ofn))
return FALSE;

return TRUE;
}

//****************************************************************************
//
// BOOL FGetDirectory(LPTSTR szDir)
//
//****************************************************************************

BOOL FGetDirectory(LPTSTR szDir)
{
BOOL  fRet;
TCHAR  szPath[MAX_PATH];
LPITEMIDLIST pidl;
LPITEMIDLIST pidlRoot;
LPMALLOC lpMalloc;

BROWSEINFO bi = {
ghwndMain,
NULL,
szPath,
_szPickDir,
BIF_RETURNONLYFSDIRS,
NULL, 0L, 0};

if (0 != SHGetSpecialFolderLocation(HWND_DESKTOP, CSIDL_DRIVES, &pidlRoot))
return FALSE;
if (NULL == pidlRoot)
return FALSE;

bi.pidlRoot = pidlRoot;
pidl = SHBrowseForFolder(&bi);

if (NULL != pidl)
fRet = SHGetPathFromIDList(pidl, szDir);
else
fRet = FALSE;

// Get the shell's allocator to free PIDLs
if (!SHGetMalloc(&lpMalloc) && (NULL != lpMalloc))
{
if (NULL != pidlRoot)
{
lpMalloc->Free(pidlRoot);
}

if (NULL != pidl)
{
lpMalloc->Free(pidl);
}

lpMalloc->Release();
}

return fRet;
}

//****************************************************************************
//
// VOID GetDlgItemPsz(HWND hdlg, UINT id, LPTSTR *ppsz)
//
// Get the text in the dialog item and retun a pointer to it in *ppsz.
// Note: Any previous data in ppsz is released.
//
//****************************************************************************

VOID GetDlgItemPsz(HWND hdlg, UINT id, LPTSTR *ppsz)
{
UINT cch;
TCHAR sz[MAX_PATH];

FreePlpv(ppsz);
cch = GetDlgItemText(hdlg, id, sz, CCHMAX(sz));
if (0 == cch)
return;
*ppsz = (LPTSTR) PszAlloc(cch);
if (NULL == *ppsz)
return;

lstrcpy(*ppsz, sz);
}

//****************************************************************************
//
// int GetRadioButton(HWND hdlg, int idrFirst, int idrLast)
//
//****************************************************************************

int GetRadioButton(HWND hdlg, int idrFirst, int idrLast)
{
int id;

for (id = idrFirst; id <= idrLast; id++)
{
if (IsDlgButtonChecked(hdlg, id))
return id;
}
return idrFirst;
}

//****************************************************************************
//
// VOID GuidToSz(GUID * pguid, LPTSTR lpchDest)
//
// Convert the guid to a special hex string.
// Assumes lpchDest has space for at least sizeof(GUID)*2 +6 chars.
// LENGTH_SZGUID_FORMATTED is 30 and includes space for the null terminator.
//
// Note the difference between this and UuidToString (or StringFromGUID2)
//
// GUID Format: {12345678-1234-1234-1234-567890123456}
//
//****************************************************************************

VOID GuidToSz(GUID * pguid, LPTSTR lpchDest)
{
ASSERT(NULL != pguid);
ASSERT(NULL != lpchDest);

wsprintf(lpchDest, TEXT("{%08X-%04X-%04X-%02X%02X-"),
pguid->Data1, pguid->Data2, pguid->Data3, pguid->Data4[0], pguid->Data4[1]);
lpchDest += 1+8+1+4+1+4+1+2+2+1;

for (int i = 2; i < 8; i++)
{
wsprintf(lpchDest, TEXT("%02X"), pguid->Data4[i]);
lpchDest += 2;
}
lstrcpy(lpchDest, TEXT("}") );
}

//****************************************************************************
//
// VOID SzToGuid(LPTSTR lpchSrc, GUID * pguid)
//
//****************************************************************************

VOID SzToGuid(LPTSTR lpchSrc, GUID * pguid)
{
if (FEmptySz(lpchSrc) || (lstrlen(lpchSrc) < sizeof(GUID)*2))
{
ClearStruct(pguid);
return;
}

ClearStruct(pguid);

if (_T('{') != *lpchSrc++)
return;
lpchSrc = HexToBin(lpchSrc, &pguid->Data1, sizeof(pguid->Data1));
if (_T('-') != *lpchSrc++)
return;
lpchSrc = HexToBin(lpchSrc, &pguid->Data2, sizeof(pguid->Data2));
if (_T('-') != *lpchSrc++)
return;
lpchSrc = HexToBin(lpchSrc, &pguid->Data3, sizeof(pguid->Data3));
if (_T('-') != *lpchSrc++)
return;
lpchSrc = HexToBin(lpchSrc, &pguid->Data4[0], sizeof(BYTE));
lpchSrc = HexToBin(lpchSrc, &pguid->Data4[1], sizeof(BYTE));
if (_T('-') != *lpchSrc++)
return;
HexToData(lpchSrc, &pguid->Data4[2], sizeof(pguid->Data4) - 2*sizeof(BYTE));
}

//****************************************************************************
//
// LPTSTR SzFindLastCh(LPTSTR lpsz, TCHAR ch)
//
// Returns a pointer to the ch within the lpsz or NULL if not found
//
//****************************************************************************

LPTSTR SzFindLastCh(LPTSTR lpsz, TCHAR ch)
{
LPTSTR lpchRet;

for (lpchRet = NULL; *lpsz; lpsz = CharNext(lpsz))
{
if (ch == *lpsz)
lpchRet = lpsz;
}

return lpchRet;
}


README.TXT

------------------------------------------------------------------
         Microsoft NetMeeting SDK 2.1 SKIPPY! Readme File
                           November 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1997

CONTENTS
========

OVERVIEW OF THE SKIPPY! SAMPLE
BUILDING THE SKIPPY! SAMPLE

OVERVIEW OF THE SKIPPY! SAMPLE
=============================

SKIPPY! is a COM based Internet Location Services application that allows
the user to query an ILS server for users and user information.  It
demonstrates filtering the server results, creation of user objects and
registration of users on a server.

BUILDING THE SKIPPY! SAMPLE
=============================

Building the sample application requires VC++ Version 5.0 or later.

To build the sample from Visual C++, open the project makefile (skippy.mak)
as a Microsoft VC++ project, and build as usual.  The NetMeeting SDK
directories must be added to your VC++ environment.

If your DOS environment is correctly configured, you can build the sample
from the command line by typing "nmake".  The DOS path, include and lib
environment variables must point to the VC installation and the
NetMeeting SDK directories.

Notes:

1) To run the SKIPPY! sample you do not need to have Microsoft
   NetMeeting installed but you must install the ILS client support
   DLLs.  To do this, go to the REISTRB directory and run the ilsinst.exe
   utility.

2) User objects created on a server by this application will appear
   in a NetMeeting directory listing for that server but NetMeeting
   users will not be able to connect to them.

3) You do not need to specify IP address in the user information dialog.
   If you do not, the IP address for your machine will be used.  If you
   do, the IP Address you provide will be used.

4) User objects obtained via a call to EnumUsers cannot be used in calls to
   EnumProtocols.  To work around this issue, call EnumUserNames and then
   GetUser on each name.


ATTRIB.CPP

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       attrib.cpp
//  Content:    This file contains attribute functions.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include "ilstest.h"

//****************************************************************************
//
// HRESULT EnumUserExAttributes(HWND hwnd, IIlsUser *pu)
//
// Retrieve an all extended attributes for this user.
//
//****************************************************************************
HRESULT EnumUserExAttributes(HWND hwnd, IIlsUser *pu)
{
IIlsAttributes *pAttributes;
IEnumIlsNames *pEnumAttr;
HRESULT hr = E_FAIL;
BSTR bstrName = NULL, bstrValue = NULL;
LPTSTR pszName = NULL, pszValue = NULL;

// Enumerate the extended attributes for this user
//
hr = pu->GetAllExtendedAttributes(&pAttributes);

if (SUCCEEDED(hr))
{
// success, enum the attributes values.
//
hr = pAttributes->EnumAttributes( &pEnumAttr ) ;
if (SUCCEEDED(hr))
{
// success, walk the enumerator and print to the screen.
//
MyTextOut(TEXT("***** Found the following extended attributes *******\r\n"));

while (pEnumAttr->Next(1, &bstrName, NULL) == NOERROR)
{
pu->GetExtendedAttribute(bstrName, &bstrValue);

BSTR_to_LPTSTR(&pszName, bstrName);
BSTR_to_LPTSTR(&pszValue, bstrValue);

// Hmm... these "names" are pretty hard to read
MyTextOut(TEXT("%ld = %s\r\n"), PROP_ID(atol(pszName)), pszValue);

SysFreeString(bstrName);
SysFreeString(bstrValue);
FreeLPTSTR(pszName);
FreeLPTSTR(pszValue);
};
pEnumAttr->Release();

MyTextOut(TEXT("*****************************************************\r\n"));
}
else
ErrorMessage(hwnd, TEXT("IIlsAttributes::EnumAttributes fails."), hr);
}
else
ErrorMessage(hwnd, TEXT("IIlsUser::GetAllExtendedAttributes fails."), hr);

  return hr;
}


FILTER.CPP

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       filter.cpp
//  Content:    This file contains filter functions.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include "ilstest.h"

void UpdateControls(HWND hwnd, BOOL fEnable);
void InitFilterDlg(HWND hwnd, PFILTERINFO pfi);
void GetFilterDlg(HWND hwnd, PFILTERINFO pfi);
void AddUserStrings(HWND hwnd);
void AddProtStrings(HWND hwnd);

//****************************************************************************
//
// HRESULT CreateFilterDialog(HWND hwnd, FILTER_TYPE fType)
//
//****************************************************************************
HRESULT CreateFilterDialog(HWND hwnd, FILTER_USAGE fUsage)
{
HRESULT hr = E_FAIL;
IIlsFilter *pf, *pf1, *pf2;
BSTR bstrName = NULL;

FILTERINFO fi;
PFILTERINFO pfi;
HMENU hMenu;

pfi = &fi;
pfi->fUsage = fUsage;
pfi->fType = ILS_FILTERTYPE_SIMPLE;

hMenu = GetMenu(hwnd);

if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_FILTER),
hwnd, (DLGPROC)FilterDlgProc, (LPARAM)pfi) == IDOK)
{

if(pfi->fType == ILS_FILTERTYPE_SIMPLE)
{

if(!MyIsGoodString(pfi->szValue1))
{
MyTextOut(TEXT("Filter value is empty - no filter object created\r\n"));
hr = E_FAIL;
goto MyExit;
}

MyTextOut(TEXT("Creating simple filter.\r\n"));

// Filter type is simple, create one
hr = g_pIls->CreateFilter(ILS_FILTERTYPE_SIMPLE, pfi->fOp1, &pf);

if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIls::CreateFilter fails."), hr);
goto MyExit;
}

// Set the attribute name
//
hr = pf->SetStandardAttributeName(pfi->attName1);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsFilter::SetStandardAttributeName fails."), hr);
goto MyExit;
}

// Set the attribute value
LPTSTR_to_BSTR(&bstrName, pfi->szValue1);
hr = pf->SetAttributeValue(bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsFilter::SetAttributeValue fails."), hr);
goto MyExit;
}

MyTextOut(TEXT("Simple filter created.\r\n"));

}
else // ILS_FILTERTYPE_COMPOSITE
{
if(!MyIsGoodString(pfi->szValue1) || !MyIsGoodString(pfi->szValue2))
{
MyTextOut(TEXT("Filter value is empty - no filter object created\r\n"));
hr = E_FAIL;
goto MyExit;
}

MyTextOut(TEXT("Creating composite filter.\r\n"));

// Filter type is composite, create three
//
// create first simple
hr = g_pIls->CreateFilter(ILS_FILTERTYPE_SIMPLE, pfi->fOp1, &pf1);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIls::CreateFilter fails."), hr);
goto MyExit;
}
// Set the first attribute name
//
hr = pf1->SetStandardAttributeName(pfi->attName1);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsFilter::SetStandardAttributeName fails."), hr);
goto MyExit;
}

// Set the first attribute value
LPTSTR_to_BSTR(&bstrName, pfi->szValue1);
hr = pf1->SetAttributeValue(bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsFilter::SetAttributeValue fails."), hr);
goto MyExit;
}

// create second simple
hr = g_pIls->CreateFilter(ILS_FILTERTYPE_SIMPLE, pfi->fOp2, &pf2);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIls::CreateFilter fails."), hr);
goto MyExit;
}

// Set the second attribute name
//
hr = pf2->SetStandardAttributeName(pfi->attName2);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsFilter::SetStandardAttributeName fails."), hr);
goto MyExit;
}

// Set the second attribute value
LPTSTR_to_BSTR(&bstrName, pfi->szValue2);
hr = pf2->SetAttributeValue(bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsFilter::SetAttributeValue fails."), hr);
goto MyExit;
}

// Create the composite filter
hr = g_pIls->CreateFilter(ILS_FILTERTYPE_COMPOSITE, pfi->fOpComp, &pf);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIls::CreateFilter fails."), hr);
goto MyExit;
}

// add filter one
hr = pf->AddSubFilter(pf1);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsFilter::AddSubFilter fails."), hr);
goto MyExit;
}

// add filter two
hr = pf->AddSubFilter(pf2);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsFilter::AddSubFilter fails."), hr);
goto MyExit;
}

MyTextOut(TEXT("Composite filter created.\r\n"));

}

// Set the global filter pointers
// destroy existing ones, only allow one at a time
if(fUsage == USERFILTER)
{
if(g_pUserFilter)
g_pUserFilter->Release();

g_pUserFilter = pf;
EnableMenuItem(hMenu, IDM_DESTROY_USERFILTER, MF_BYCOMMAND | MF_ENABLED);
}
else if(fUsage == PROTFILTER)
{
if(g_pProtFilter)
g_pProtFilter->Release();

g_pProtFilter = pf;
EnableMenuItem(hMenu, IDM_DESTROY_PROTFILTER, MF_BYCOMMAND | MF_ENABLED);
}
else
ErrorMessage(hwnd, TEXT("filter usage is indeterminate.\r\n"), E_FAIL);

}
else
{
//User cancelled
MyTextOut(TEXT("Create Filter cancelled.\r\n"));
}

MyExit:

// Cleanup
//
if(bstrName)
SysFreeString(bstrName);

return hr;
}

//****************************************************************************
//
// int FilterDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog proc that displays the current user objects
// and returns the chosen item.
//
//****************************************************************************
int FilterDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PFILTERINFO pfi;

switch (msg)
{
case WM_INITDIALOG:
{
SetWindowLong (hwnd, DWL_USER, lParam);
InitFilterDlg(hwnd, (PFILTERINFO)lParam);
break;
}

case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
// return the filter info
pfi = (FILTERINFO *)GetWindowLong(hwnd, DWL_USER);
GetFilterDlg(hwnd, pfi);

// FALL THROUGH !!!
//
case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

case IDC_SIMPLE:
{
// Turn on/off dialog controls
//
UpdateControls(hwnd, FALSE);
break;
}
case IDC_COMPOSITE:
{
// Turn on/off dialog controls
//
UpdateControls(hwnd, TRUE);
break;
}

default:
break;
};
break;
}

case WM_ENABLE:
return 1;
break;

default:
break;
};

return 0;
}

//****************************************************************************
//
// int InitFilterDlg (HWND hwnd, PFILTERINFO pfi)
//
//****************************************************************************
void InitFilterDlg(HWND hwnd, PFILTERINFO pfi)
{

// select the correct radio buttons
//
CheckRadioButton( hwnd, IDC_SIMPLE, IDC_COMPOSITE, IDC_SIMPLE);
CheckRadioButton( hwnd, IDC_OP_EQUAL1, IDC_OP_EXIST1, IDC_OP_EQUAL1);
CheckRadioButton( hwnd, IDC_OP_EQUAL2, IDC_OP_EXIST2, IDC_OP_EQUAL2);
CheckRadioButton( hwnd, IDC_AND, IDC_NOT, IDC_AND);

if(pfi->fUsage == USERFILTER)
{
AddUserStrings(GetDlgItem(hwnd, IDC_ATT_LIST1));
AddUserStrings(GetDlgItem(hwnd, IDC_ATT_LIST2));
}
else // PROTFILTER
{
AddProtStrings(GetDlgItem(hwnd, IDC_ATT_LIST1));
AddProtStrings(GetDlgItem(hwnd, IDC_ATT_LIST2));
}

// send a message to disable controls
//
SendMessage(hwnd, WM_COMMAND, IDC_SIMPLE, 0);
}

//****************************************************************************
//
// int UpdateControls (HWND hwnd, BOOL fEnable)
//
//****************************************************************************
void UpdateControls(HWND hwnd, BOOL fEnable)
{
EnableWindow(GetDlgItem(hwnd, IDC_COMP_FRAME), fEnable);

EnableWindow(GetDlgItem(hwnd, IDC_AND), fEnable);
EnableWindow(GetDlgItem(hwnd, IDC_NOT), fEnable);
EnableWindow(GetDlgItem(hwnd, IDC_OR), fEnable);

EnableWindow(GetDlgItem(hwnd, IDC_STD_TXT2), fEnable);
EnableWindow(GetDlgItem(hwnd, IDC_ATT_LIST2), fEnable);

EnableWindow(GetDlgItem(hwnd, IDC_ATT_TXT2), fEnable);
EnableWindow(GetDlgItem(hwnd, IDC_VALUE2), fEnable);

EnableWindow(GetDlgItem(hwnd, IDC_FIL_OP2), fEnable);
EnableWindow(GetDlgItem(hwnd, IDC_OP_EQUAL2), fEnable);
EnableWindow(GetDlgItem(hwnd, IDC_OP_APPROX2), fEnable);
EnableWindow(GetDlgItem(hwnd, IDC_OP_LESSTHAN2), fEnable);
EnableWindow(GetDlgItem(hwnd, IDC_OP_GREATERTHAN2), fEnable);
EnableWindow(GetDlgItem(hwnd, IDC_OP_EXIST2), fEnable);
}

//****************************************************************************
//
// int GetFilterDlg(HWND hwnd, PFILTERINFO pfi)
//
// Fill in the FILTERINFO structure
//
//****************************************************************************
void GetFilterDlg(HWND hwnd, PFILTERINFO pfi)
{
TCHAR szTemp[MAX_NAME];
HWND hwndCombo;

// Get standard attribute name for first simple filter
//
hwndCombo = GetDlgItem(hwnd, IDC_ATT_LIST1);
pfi->attName1 = (ILS_STD_ATTR_NAME)(SendMessage(hwndCombo, CB_GETCURSEL, 0, 0) + 1);

// Get standard attribute value for first simple filter
//
GetDlgItemText(hwnd, IDC_VALUE1, szTemp, MAX_NAME);
pfi->szValue1 = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(pfi->szValue1, szTemp);

// Get standard attribute name for second simple filter
//
hwndCombo = GetDlgItem(hwnd, IDC_ATT_LIST2);
pfi->attName2 = (ILS_STD_ATTR_NAME)(SendMessage(hwnd, CB_GETCURSEL, 0, 0) + 1);

// Get standard attribute value for second simple filter
//
GetDlgItemText(hwnd, IDC_VALUE2, szTemp, MAX_NAME);
pfi->szValue2 = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(pfi->szValue2, szTemp);

// some fancy arithmetic to calculate filter operations
//
pfi->fOp1 = (ILS_FILTER_OP)((int)ILS_FILTEROP_EQUAL + GetRadioButton(hwnd, IDC_OP_EQUAL1, IDC_OP_EXIST1) - IDC_OP_EQUAL1);
pfi->fOp2 = (ILS_FILTER_OP)((int)ILS_FILTEROP_EQUAL + GetRadioButton(hwnd, IDC_OP_EQUAL2, IDC_OP_EXIST2) - IDC_OP_EQUAL2);

pfi->fOpComp = (ILS_FILTER_OP)((int)ILS_FILTEROP_AND + GetRadioButton(hwnd, IDC_AND, IDC_NOT) - IDC_AND);

pfi->fType = ((GetRadioButton(hwnd, IDC_SIMPLE, IDC_COMPOSITE) == IDC_SIMPLE) ? ILS_FILTERTYPE_SIMPLE : ILS_FILTERTYPE_COMPOSITE);

}

//****************************************************************************
//
// void AddUserStrings(HWND hwnd)
//
//****************************************************************************
void AddUserStrings(HWND hwnd)
{
// Add USER atttibute strings to the combo box
//
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"USER_ID");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"IP_ADDRESS");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"EMAIL_NAME");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"FIRST_NAME");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"LAST_NAME");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"CITY_NAME");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"COUNTRY_NAME");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"COMMENT");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"FLAGS");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"APP_NAME");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"APP_MIME_TYPE");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"APP_GUID");

// set default
SendMessage(hwnd, CB_SETCURSEL, 0, 0);

}

//****************************************************************************
//
// void AddProtStrings(HWND hwnd)
//
//****************************************************************************
void AddProtStrings(HWND hwnd)
{
// Add PROTOCOL atttibute strings to the combo box
//
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"PROTOCOL_NAME");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"PROTOCOL_MIME_TYPE");
SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)"PROTOCOL_PORT");

// set default
SendMessage(hwnd, CB_SETCURSEL, 0, 0);

}

//****************************************************************************
//
// void DestroyFilter(HWND hwnd, FILTER_TYPE fType)
//
//  Free the global filters
//
//****************************************************************************
HRESULT DestroyFilter(HWND hwnd, FILTER_USAGE fUsage)
{
HMENU hMenu;

hMenu = GetMenu(hwnd);

if(fUsage == USERFILTER)
{
if(g_pUserFilter)
{
g_pUserFilter->Release();
g_pUserFilter = NULL;
EnableMenuItem(hMenu, IDM_DESTROY_USERFILTER, MF_BYCOMMAND | MF_GRAYED);
MyTextOut(TEXT("User filter destroyed.\r\n"));
}

}
else if(fUsage == PROTFILTER)
{
if(g_pProtFilter)
{
g_pProtFilter->Release();
g_pProtFilter = NULL;
EnableMenuItem(hMenu, IDM_DESTROY_PROTFILTER, MF_BYCOMMAND | MF_GRAYED);
MyTextOut(TEXT("Protocol filter destroyed.\r\n"));
}
}
else
ErrorMessage(hwnd, TEXT("DestroyFilter: usage is indeterminate.\r\n"), E_FAIL);

return S_OK;

}


ILS.IDL

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       ils.idl
//  Content:    This file is a stub for the real IDL
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include <ils.idl>


ILSTEST.H

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       ilstest.h
//  Content:    The main header file.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <olectl.h>
#include "ils.h"
#include "resource.h"
#include <commctrl.h>
#include "oblist.h"
#include "notify.h"
#include <mapidefs.h>

//****************************************************************************
// General Constants
//****************************************************************************
//
#define SERVER_MENU_POS 1
#define SERVER_LIST_POS 2

#define MAX_NAME 260
#define MAX_SERVER_COUNT 20
#define MAX_USER_COUNT 20

#define WM_ILSTEST                  (WM_USER+100)
#define WM_IT_ENUM_NAMES_RESULT     (WM_ILSTEST+0)
#define WM_IT_ENUM_USERS_RESULT     (WM_ILSTEST+1)
#define WM_IT_RESOLVE_USER_RESULT   (WM_ILSTEST+2)
#define WM_IT_RESOLVE_PROT_RESULT   (WM_ILSTEST+3)

//****************************************************************************
// Macros
//****************************************************************************
//
#define ARRAYSIZE(array)    (sizeof(array)/sizeof(array[0]))

//****************************************************************************
// Global Parameters
//****************************************************************************
//
extern HINSTANCE         ghInstance;
extern HWND              g_hwndMain;
extern LPILS_MAIN        g_pIls;
extern IConnectionPoint  *g_pcnp;

extern IIlsFilter        *g_pProtFilter;
extern IIlsFilter        *g_pUserFilter;

extern CHAR              g_szServer[];
extern LPTSTR            g_pszServer;
extern IIlsServer        *g_pServer;
extern COBLIST           *g_pServerList;
extern COBLIST           *g_pUserList;

extern GUID g_GUID;

extern const TCHAR g_szAppID[];
extern const TCHAR g_szProtID[];

enum OBJECT_TYPE {PROTOCOL, USER};
enum FILTER_USAGE {USERFILTER, PROTFILTER};

#define NM_IN_A_CALL   400
#define NM_SEND_AUDIO  501
#define NM_SEND_VIDEO  503
#define NM_RESTRICTION 600

//****************************************************************************
// Useful data structures
//****************************************************************************
//

typedef struct tagServerNode
{
char        szName[MAX_NAME];
IIlsServer  *pSrv;
} SERVERNODE, *PSERVERNODE;

typedef struct tagUserInfo
{
char        szFirstName[MAX_NAME];
char        szLastName[MAX_NAME];
char        szEmailName[MAX_NAME];
char        szCity[MAX_NAME];
char        szCountry[MAX_NAME];
char        szComment[MAX_NAME*2];
char        szIPAddress[MAX_NAME];
BOOLfModify;
} USERINFO, *PUSERINFO;

typedef struct  tagILSUser {
IIlsUser        *pu;
CIlsUserNotify  *puNotify;
COBLIST         *pProtList;
}   USERNODE, *PUSERNODE;

typedef struct  tagServerInfo {
LPTSTR      szServerName;
LPTSTR      szLogonName;
LPTSTR      szPassword;
ILS_ENUM_AUTH_METHOD eam;
LPTSTR      szDomain;
LPTSTR      szCredential;
LPTSTR      szBaseDN;
DWORD       dwTimeout;
}   SERVERINFO, *PSERVERINFO;

typedef struct  tagFilterInfo {

ILS_STD_ATTR_NAME attName1;
LPTSTR            szValue1;
ILS_FILTER_OP     fOp1;

ILS_STD_ATTR_NAME attName2;
LPTSTR            szValue2;
ILS_FILTER_OP     fOp2;

ILS_FILTER_OP     fOpComp;
FILTER_USAGE      fUsage;     // usage: USER, PROTOCOL
ILS_FILTER_TYPE   fType;      // type:  SIMPLE, COMPOSITE

}   FILTERINFO, *PFILTERINFO;

typedef struct  tagProtocolInfo {

LPTSTR     szProtName;
LPTSTR     szProtMime;
ULONG      uPortNumber;

}   PROTOCOLINFO, *PPROTOCOLINFO;

typedef struct  tagEnumProtocol {

USERNODE  *pun;
IIlsProtocol *ppSelected;

}   ENUMPROTINFO, *PENUMPROTINFO;

//****************************************************************************
// Function prototypes
//****************************************************************************
//
// ****  from utils.cpp
void      _cdecl MyTextOut (LPCTSTR szFmt, ...);
void      ErrorMessage(HWND hwnd, LPCTSTR szPrefix, HRESULT hr);
LPTSTR    AllocLPTSTR (ULONG cb);
HRESULT   FreeLPTSTR (LPTSTR pszName);
HRESULT   SetLPTSTR (LPTSTR *ppszName, LPCTSTR pszUserName);
HRESULT   LPTSTR_to_BSTR (BSTR *pbstr, LPCTSTR psz);
HRESULT   BSTR_to_LPTSTR (LPTSTR *ppsz, BSTR bstr);
LPCTSTR   GetErrorString(HRESULT hr);
void      DisplayStdAtt(void *pobj, ILS_STD_ATTR_NAME stdAttr, LPTSTR szDesc, OBJECT_TYPE ot);
void      DisplayExtAtt(void *pobj, DWORD dwExtAttr, LPTSTR szDesc, OBJECT_TYPE ot);
BOOL      InitTreeViewImageLists(HWND hwndTV);
HTREEITEM AddItemToTree(HWND hwndTV, LPSTR lpszItem, int nLevel);
VOID      DisplayAboutBox(HWND hwnd);
HRESULT   DisplayIlsNames(HWND hwnd, IEnumIlsNames *pEnum, LPTSTR szNames);
int       StringFromGuid (REFIID riid, LPTSTR pszBuf);
int       GetRadioButton(HWND hdlg, int idrFirst, int idrLast);
BOOL      MyIsGoodString(LPTSTR psz);
HRESULT   GetNMAttributesObject( IIlsMain *pIls, IIlsAttributes **ppa );

// ****  from users.cpp
HRESULT DisplayIlsUsers(HWND hwnd, IEnumIlsUsers *pEnum);
HRESULT RegisterUser(HWND hwnd, IIlsUser *pu, IIlsServer *ps, LPTSTR szServerName);
HRESULT UnregisterUser(HWND hwnd, IIlsUser *pu, BOOL fSilent);
HRESULT EnumUsers(HWND hwnd, BOOL fShort);
HRESULT OnILSUserAdviseUnadvise(HWND hwnd, PUSERNODE pUserNode, BOOL fAdvise);
HRESULT DisplayIlsUserDialog(HWND hwnd, IIlsUser *pu);

// ****  from svrinfo.cpp
int     ServerDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HRESULT NewServerDialog(HWND hwnd);
HRESULT RemoveServerDialog(HWND hwnd);
void    NewServerNode(IIlsServer *pServer, LPTSTR szName);
int     ServerListDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void    SetServerInfo(HWND hwnd, IIlsServer *ps, PSERVERINFO psi);

// ****  from usrinfo.cpp
HRESULT NewUserDialog(HWND hwnd);
HRESULT ModifyUserDialog(HWND hwnd);
HRESULT CloneUserDialog(HWND hwnd);
HRESULT RemoveUserDialog(HWND hwnd);
HRESULT RegUnregUserDialog(HWND hwnd, BOOL fRegister);
int     UserNameDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HRESULT GetUserInfoDialog(HWND hwnd);
HRESULT GetUserInfoDialog(HWND hwnd, IIlsUser *pu);
int     UserDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void    InitIlsUser(HWND hwnd, IIlsUser *pu);
void    NewUserNode(PUSERNODE pUser);
void    SetUserInfo(HWND hwnd, IIlsUser *pu, USERINFO *pUserInfo);
void    GetUserInfo(HWND hwnd, IIlsUser *pu, USERINFO *pUserInfo);
void    SaveUserInfo(HWND hwnd, PUSERINFO pui);
int     UserListDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void    UnregisterAllUsers(HWND hwnd);
HRESULT ChangeAttribute(IIlsUser *pu, ILS_STD_ATTR_NAME attr, LPTSTR psz);

// ****  from proto.cpp
HRESULT EnumUserProtocols(HWND hwnd, IIlsUser *pu);
int     ProtocolDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void    CreateProtocolDialog(HWND hwnd);
void    SaveProtInfo(HWND hwnd, PPROTOCOLINFO ppi);
void    DestroyProtocolDialog(HWND hwnd);
int     ProtoListDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HRESULT DisplayProtocolInfo(HWND hwnd, IIlsProtocol *pp);
HRESULT DisplayProtocol(HWND hwnd, IIlsUser *pu);
int     ProtoNameDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

// ****  from attrib.cpp
HRESULT EnumUserExAttributes(HWND hwnd, IIlsUser *pu);

// ****  from filter.cpp
HRESULT CreateFilterDialog(HWND hwnd, FILTER_USAGE fUsage);
int     FilterDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HRESULT DestroyFilter(HWND hwnd, FILTER_USAGE fUsage);


MAIN.CPP

//****************************************************************************
//
//  SKIPPY! sample app for Microsoft NetMeeting SDK
//
//  File:       main.cpp
//  Content:    This file contains the application's WinMain and init
//              functions. GUID.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include "ilstest.h"

#define INC_OLE2
#include <initguid.h>
#include <olectl.h>
#include <ilsguid.h>
#include "appguid.h"

#include "notify.h"

//****************************************************************************
// Global parameters
//****************************************************************************

HINSTANCE          ghInstance = NULL;
LPILS_MAIN         g_pIls = NULL;
IConnectionPoint   *g_pcnp = NULL;
CIlsNotify         *g_pIlsNotify = NULL;
HWND               g_hwndMain = NULL;
CHAR               g_szServer[MAX_PATH];
IIlsServer         *g_pServer = NULL;
IIlsFilter         *g_pProtFilter = NULL;
IIlsFilter         *g_pUserFilter = NULL;
COBLIST            *g_pServerList;
COBLIST            *g_pUserList;
GUID               g_GUID;

#define WM_START   (WM_USER+10)

//****************************************************************************
// Global constant strings
//****************************************************************************

const TCHAR g_szOptions[]   = TEXT("Options");
const TCHAR g_szPosition[]  = TEXT("Placement");
const TCHAR g_szIni[]       = TEXT("skippy.ini");
const TCHAR g_szAppID[]   = TEXT("ms-netmeeting"); // Use this string so NM can see you on the server
const TCHAR g_szProtID[]   = TEXT("h323"); // Default to this protocol ID for now

const TCHAR g_szExe[]       = TEXT("skippy.exe");

//****************************************************************************
// Function prototypes
//****************************************************************************

long CALLBACK App_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

//****************************************************************************
//
// HRESULT OnIlsAdviseUnadvise(HWND hwnd, BOOL fAdvise)
//
// This function enables/disables the Ils object's notifications.
//
//****************************************************************************

HRESULT OnIlsAdviseUnadvise(HWND hwnd, BOOL fAdvise)
{
HRESULT hr;

if (fAdvise)
{
DWORD dwCookie;

g_pIlsNotify = new CIlsNotify (hwnd);
hr = g_pcnp->Advise((IUnknown *)g_pIlsNotify, &dwCookie);

if (hr == S_OK)
{
g_pIlsNotify->AddRef();
g_pIlsNotify->SetCookie(dwCookie);
};
}
else
{
hr = g_pcnp->Unadvise(g_pIlsNotify->GetCookie());

if (hr == S_OK)
{
g_pIlsNotify->Release();
g_pIlsNotify = NULL;
};
};
return hr;
}

//****************************************************************************
//
// void InitConnectableObject(HWND hwnd)
//
// This function initializes the Ils connectable object.
//
//****************************************************************************

void InitConnectableObject(HWND hwnd)
{
IConnectionPointContainer *pcnpcnt;
HRESULT hr;

// Get the ConnectionPointContainer
//
hr = g_pIls->QueryInterface(IID_IConnectionPointContainer, (void**)&pcnpcnt);
if (hr != S_OK)
{
ErrorMessage(NULL, TEXT("Cannot query IID_IConnectionPointContainer: returns "),
hr);
}
else
{
IEnumConnectionPoints *pEnum;

// Instead of an easy thing as follows
// hr = pcnpcnt->FindConnectionPoint(IID_IIlsNotify, &g_pcnp);
// Let's do the hard thing through enumeration
//
hr = pcnpcnt->EnumConnectionPoints(&pEnum);
if (hr != S_OK)
{
ErrorMessage(NULL, TEXT("Cannot enumerate ConnectionPoint: returns "),
hr);
}
else
{
IConnectionPoint *pcnp;
ULONG            cFetched;

while(pEnum->Next(1, &pcnp, &cFetched) == S_OK)
{
IID riid;

if ((pcnp->GetConnectionInterface(&riid) == S_OK) &&
(riid == IID_IIlsNotify))
{
g_pcnp = pcnp;
OnIlsAdviseUnadvise(hwnd, TRUE);
break;
};
pcnp->Release();
};

if (g_pcnp == NULL)
{
ErrorMessage(NULL, TEXT("Cannot find ConnectionPoint: returns "),
hr);
};

pEnum->Release();
};
pcnpcnt->Release();
};
}

//****************************************************************************
//
// BOOL InitAppInstance (HINSTANCE hInstance)
//
// This function initializes this instance of the application.
//
//****************************************************************************

BOOL InitAppInstance (HINSTANCE hInstance)
{
HRESULT hr;
LPCLASSFACTORY pcf;
BOOL    fRet = FALSE;

// Remember the application instance
//
ghInstance = hInstance;

// Create the main window with a dialog template
//
InitCommonControls();
g_hwndMain = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DLG_APP),
NULL, (DLGPROC)App_WndProc);

if (g_hwndMain == NULL)
return FALSE;

// Create linked lists for users and servers
//
g_pUserList = new COBLIST;
g_pServerList = new COBLIST;

// Do the OLE thing
//
hr = CoInitialize(NULL);
if (FAILED(hr))
{
ErrorMessage(NULL, TEXT("CoInitialize"), hr);
return FALSE;
}

// Notify the system we want to use the Ils services
// by creating a Ils object
//
hr = CoGetClassObject(CLSID_InternetLocationServices,
CLSCTX_INPROC_SERVER,
NULL,
IID_IClassFactory,
(void**)&pcf);
if (FAILED(hr))
{
ErrorMessage(NULL, TEXT("CoGetClassObject"), hr);
}
else
{
// Get the IlsMain object
//
hr = pcf->CreateInstance(NULL, IID_IIlsMain, (void**)&g_pIls);
if (hr != S_OK)
{
ErrorMessage(NULL, TEXT("Cannot create a IlsMain object: returns "), hr);
}
else
{
// Connect to the IlsMain object
//
InitConnectableObject(g_hwndMain);

// Determine how to start the application and
// start the application
//
PostMessage(g_hwndMain, WM_START, 0, 0);
fRet = TRUE;
};

pcf->Release();
};

return fRet;
}

//****************************************************************************
//
// void DeinitAppInstance (HINSTANCE hInstance)
//
// This function initializes this instance of the application.
//
//****************************************************************************

void DeinitAppInstance(HINSTANCE hInstance)
{
// Unregister all the users we created
UnregisterAllUsers(g_hwndMain);

// Remove the connection points
//
if (g_pcnp != NULL)
{
OnIlsAdviseUnadvise(g_hwndMain, FALSE);
g_pcnp->Release();
};

// Free the main interface
//
if (g_pIls != NULL)
{
g_pIls->Uninitialize();
g_pIls->Release();
};
CoUninitialize();

// clear the user and server lists
//
g_pServerList->EmptyList();
g_pUserList->EmptyList();

delete g_pUserList;
delete g_pServerList;

// Destroy the main window
DestroyWindow(g_hwndMain);

return;
}

//****************************************************************************
//
//    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
//
//    PURPOSE: calls initialization function, processes message loop
//
//    COMMENTS:
//
//        Windows recognizes this function by name as the initial entry point
//        for the program.  This function calls the application initialization
//        routine, if no other instance of the program is running, and always
//        calls the instance initialization routine.  It then executes a message
//        retrieval and dispatch loop that is the top-level control structure
//        for the remainder of execution.  The loop is terminated when a WM_QUIT
//        message is received, at which time this function exits the application
//        instance by returning the value passed by PostQuitMessage().
//
//        If this function must abort before entering the message loop, it
//        returns the conventional value NULL.
//
//****************************************************************************

int PASCAL WinMain(
HINSTANCE hInstance,        /* current instance     */
HINSTANCE hPrevInstance,    /* previous instance    */
LPSTR     lpCmdLine,        /* command line         */
int       nCmdShow)         /* show-window type (open/icon)  */
{
MSG     msg;            /* message              */

if (!InitAppInstance(hInstance)) /* Initialize shared things         */
return (FALSE);              /* Exits if unable to initialize    */

/* Acquire and dispatch messages until a WM_QUIT message is received.*/

while (GetMessage(&msg,    /* message structure                      */
NULL,                  /* handle of window receiving the message */
0,                     /* lowest message to examine              */
0))                    /* highest message to examine             */
{
if (!IsDialogMessage(g_hwndMain, &msg))
{
TranslateMessage(&msg);    /* Translates virtual key codes       */
DispatchMessage(&msg);     /* Dispatches message to window       */
};
}

DeinitAppInstance(hInstance);
return (msg.wParam);       /* Returns the value from PostQuitMessage */
}

//****************************************************************************
//
// void InitializeWindow(HWND hwnd)
//
// This function is called when the main window is initialized.
//
//****************************************************************************

void InitializeWindow(HWND hwnd)
{
HICON hIcon;
HMENU hMenu;
WINDOWPLACEMENT wp;

// Initialize application icon
//
hIcon = LoadIcon(ghInstance, MAKEINTRESOURCE(IDI_APP_ICON));
SendMessage(hwnd, WM_SETICON, TRUE, (LPARAM)hIcon);
SendMessage(hwnd, WM_SETICON, FALSE, (LPARAM)hIcon);

// Get window placement
//
if (GetPrivateProfileStruct(g_szOptions, g_szPosition,
(LPVOID)&wp, sizeof(wp), g_szIni))
{
SetWindowPlacement(hwnd, &wp);
};

// Turn on/off the correct menu items
hMenu = GetMenu(hwnd);
EnableMenuItem(hMenu, IDM_DESTROYUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_DESTROYSERVER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_ENUMUSERS, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_ENUMUSERNAMES, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_REGISTERUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_UNREGISTERUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_CLONEUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_MODIFYUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_GETUSERINFO, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_CREATE_PROT, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_DESTROY_PROT, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_DESTROY_PROTFILTER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_DESTROY_USERFILTER, MF_BYCOMMAND | MF_GRAYED);

return;
}

//****************************************************************************
//
// void TerminateWindow(HWND hwnd)
//
// This function is called when the main window is terminated.
//
//****************************************************************************

void TerminateWindow(HWND hwnd)
{
WINDOWPLACEMENT wp;

// Remember the last position
//
wp.length = sizeof(wp);
if (GetWindowPlacement(hwnd, &wp))
{
WritePrivateProfileStruct(g_szOptions, g_szPosition,
(LPVOID)&wp, sizeof(wp), g_szIni);
};
PostQuitMessage(0);
return;
}

//****************************************************************************
//
// long CALLBACK App_WndProc(HWND hwnd, UINT message,
//                           WPARAM wParam, LPARAM lParam)
//
// This function is the main window procedure.
//
//****************************************************************************

long CALLBACK App_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int iCommand;

switch (message)
{
case WM_INITDIALOG:
InitializeWindow(hwnd);
break;

case WM_CLOSE:
TerminateWindow(hwnd);
break;

case WM_START:
{
HRESULT hr;

// Initialize the Ils object
//
hr = g_pIls->Initialize();
if (FAILED(hr))
ErrorMessage(NULL, TEXT("IIls::Initialize returns"), hr);
}

case WM_COMMAND:
{
// Process the menu selections
//
iCommand = GET_WM_COMMAND_ID(wParam, lParam);

switch (iCommand)
{
case IDM_EXIT:
TerminateWindow(hwnd);
break;

case IDM_NEWUSER:
NewUserDialog(hwnd);
break;

case IDM_MODIFYUSER:
ModifyUserDialog(hwnd);
break;

case IDM_CLONEUSER:
CloneUserDialog(hwnd);
break;

case IDM_DESTROYUSER:
RemoveUserDialog(hwnd);
break;

case IDM_CREATESERVER:
NewServerDialog(hwnd);
break;

case IDM_DESTROYSERVER:
RemoveServerDialog(hwnd);
break;

case IDM_ENUMUSERS:
EnumUsers(hwnd, FALSE);
break;

case IDM_ENUMUSERNAMES:
EnumUsers(hwnd, TRUE);
break;

case IDM_REGISTERUSER:
RegUnregUserDialog(hwnd, TRUE);
break;

case IDM_UNREGISTERUSER:
RegUnregUserDialog(hwnd, FALSE);
break;

case IDM_GETUSERINFO:
GetUserInfoDialog(hwnd);
break;

case IDM_USER_FILTER:
CreateFilterDialog(hwnd, USERFILTER);
break;

case IDM_PROTOCOL_FILTER:
CreateFilterDialog(hwnd, PROTFILTER);
break;

case IDM_DESTROY_USERFILTER:
DestroyFilter(hwnd, USERFILTER);
break;

case IDM_DESTROY_PROTFILTER:
DestroyFilter(hwnd, PROTFILTER);
break;

case IDM_CREATE_PROT:
CreateProtocolDialog(hwnd);
break;

case IDM_DESTROY_PROT:
DestroyProtocolDialog(hwnd);
break;

case IDM_ABOUT:
DisplayAboutBox(hwnd);
break;

default:
break;
};
break;
};

// Handle messages posted from the callbacks
//
case WM_IT_ENUM_NAMES_RESULT:
DisplayIlsNames(hwnd, (IEnumIlsNames *)lParam, "names");
break;

case WM_IT_ENUM_USERS_RESULT:
DisplayIlsUsers(hwnd, (IEnumIlsUsers *)lParam);
break;

case WM_IT_RESOLVE_PROT_RESULT:
DisplayProtocolInfo(hwnd, (IIlsProtocol *)lParam);
break;

case WM_IT_RESOLVE_USER_RESULT:
{
USERNODE un;
PUSERNODE pun = &un;
IIlsUser *pUser = (IIlsUser *)lParam;

pun->pu = pUser;

// Hookup the notification callback
OnILSUserAdviseUnadvise(hwnd, pun, TRUE);

// Display the information dialog
DisplayIlsUserDialog(hwnd, pUser);

// Remove the callback
OnILSUserAdviseUnadvise(hwnd, pun, FALSE);

// release the user object.
pUser->Release();

            }
break;

default:
break;
}

return 0L;
}


NOTIFY.CPP

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       notify.cpp
//  Content:    This file contains notification method implentations.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include "ilstest.h"
#include "notify.h"

//****************************************************************************
// CIlsNotify::CIlsNotify (HWND hwndInit)
//
//****************************************************************************

CIlsNotify::CIlsNotify (HWND hwndInit) :
CNotify(hwndInit)
{
cRef = 0;
return;
}

//****************************************************************************
// STDMETHODIMP
// CIlsNotify::QueryInterface (REFIID riid, void **ppv)
//
//****************************************************************************

STDMETHODIMP
CIlsNotify::QueryInterface (REFIID riid, void **ppv)
{
if (riid == IID_IIlsNotify || riid == IID_IUnknown)
{
*ppv = (IIlsNotify *) this;
AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
};
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CIlsNotify::AddRef (void)
//
//****************************************************************************

STDMETHODIMP_(ULONG)
CIlsNotify::AddRef (void)
{
return cRef++;
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CIlsNotify::Release (void)
//
//****************************************************************************

STDMETHODIMP_(ULONG)
CIlsNotify::Release (void)
{
cRef--;

if (cRef == 0)
{
delete this;
return 0;
}
else
{
return cRef;
};
}

//****************************************************************************
// STDMETHODIMP
// CIlsNotify::GetUserResult(ULONG uReqID, IIlsUser *pUser, HRESULT hResult)
//
//****************************************************************************

STDMETHODIMP
CIlsNotify::GetUserResult(ULONG uReqID, IIlsUser *pUser,
HRESULT hResult)
{
MyTextOut (TEXT("IIlsNotify::GetUserResult(%x) returns %x. [%s]\r\n"),
uReqID, hResult, GetErrorString(hResult) );

if (SUCCEEDED(hResult))
{
pUser->AddRef();
PostMessage(GetWindow(), WM_IT_RESOLVE_USER_RESULT, 0, (LPARAM)pUser);
};
return S_OK;
}

//****************************************************************************
// STDMETHODIMP
// CIlsNotify::EnumUserNamesResult(ULONG uReqID, IEnumIlsNames *pEnumUserName,
//                                 HRESULT hResult)
//
//****************************************************************************

STDMETHODIMP
CIlsNotify::EnumUserNamesResult(ULONG uReqID, IEnumIlsNames *pEnumUserName,
HRESULT hResult)
{
MyTextOut (TEXT("IIlsNotify::EnumUserNamesResult(%x) returns %x [%s].\r\n"),
uReqID, hResult, GetErrorString(hResult) );

if (hResult == NOERROR)
{
pEnumUserName->AddRef();
PostMessage(GetWindow(), WM_IT_ENUM_NAMES_RESULT, 0, (LPARAM)pEnumUserName);
};
return S_OK;
}

//****************************************************************************
// STDMETHODIMP
// CIlsNotify::EnumUsersResult(ULONG uReqID, IEnumIlsUsers *pEnumUser,
//                             HRESULT hResult)
//
//****************************************************************************

STDMETHODIMP
CIlsNotify::EnumUsersResult(ULONG uReqID, IEnumIlsUsers *pEnumUser,
HRESULT hResult)
{
MyTextOut (TEXT("IIlsNotify::EnumUsersResult(%x) returns %x [%s].\r\n"),
uReqID, hResult, GetErrorString(hResult) );

if (hResult == NOERROR)
{
pEnumUser->AddRef();
PostMessage(GetWindow(), WM_IT_ENUM_USERS_RESULT, 0, (LPARAM)pEnumUser);
};
return S_OK;
}

//****************************************************************************
// CIlsUserNotify::CIlsUserNotify (HWND hwndInit)
//
//****************************************************************************

CIlsUserNotify::CIlsUserNotify (HWND hwndInit) :
CNotify(hwndInit)
{
cRef = 0;
return;
}

//****************************************************************************
// STDMETHODIMP
// CIlsUserNotify::QueryInterface (REFIID riid, void **ppv)
//
//****************************************************************************

STDMETHODIMP
CIlsUserNotify::QueryInterface (REFIID riid, void **ppv)
{
if (riid == IID_IIlsUserNotify || riid == IID_IUnknown)
{
*ppv = (IIlsUserNotify *) this;
AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
};
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CIlsUserNotify::AddRef (void)
//
//****************************************************************************

STDMETHODIMP_(ULONG)
CIlsUserNotify::AddRef (void)
{
return cRef++;
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CIlsUserNotify::Release (void)
//
//****************************************************************************

STDMETHODIMP_(ULONG)
CIlsUserNotify::Release (void)
{
cRef--;

if (cRef == 0)
{
delete this;
return 0;
}
else
{
return cRef;
};
}

//****************************************************************************
// STDMETHODIMP
// CIlsUserNotify::StateChanged(ULONG fPrimary, BSTR bstrServerName)
//
//****************************************************************************

STDMETHODIMP
CIlsUserNotify::StateChanged(ULONG fPrimary, BSTR bstrServerName)
{
MyTextOut (TEXT("IIlsUserNotify::StateChanged returns %x on server %s.\r\n"),
fPrimary, bstrServerName);

return S_OK;
}

//****************************************************************************
// STDMETHODIMP
// CIlsUserNotify::RegisterResult(ULONG uReqID, HRESULT hResult)
//
//****************************************************************************

STDMETHODIMP
CIlsUserNotify::RegisterResult(ULONG uReqID, HRESULT hResult)
{
MyTextOut (TEXT("IIlsUserNotify::RegisterResult(%x) returns %x [%s].\r\n"),
uReqID, hResult, GetErrorString(hResult) );
return S_OK;
}

//****************************************************************************
// STDMETHODIMP
// CIlsUserNotify::UpdateResult(ULONG uReqID, HRESULT hResult)
//
//****************************************************************************

STDMETHODIMP
CIlsUserNotify::UpdateResult(ULONG uReqID, HRESULT hResult)
{
MyTextOut (TEXT("IIlsUserNotify::UpdateResult(%x) returns %x [%s].\r\n"),
uReqID, hResult, GetErrorString(hResult) );
return S_OK;
}

//****************************************************************************
// STDMETHODIMP
// CIlsUserNotify::ProtocolChangeResult(ULONG uReqID, HRESULT hResult)
//
//****************************************************************************

STDMETHODIMP
CIlsUserNotify::ProtocolChangeResult(ULONG uReqID, HRESULT hResult)
{
MyTextOut (TEXT("IIlsUserNotify::ProtocolChangeResult(%x) returns %x [%s].\r\n"),
uReqID, hResult, GetErrorString(hResult) );
return S_OK;
}

//****************************************************************************
// STDMETHODIMP
// CIlsUserNotify::EnumProtocolsResult(ULONG uReqID,
//                                    IEnumIlsNames *pEnumProtocol,
//                                    HRESULT hResult)
//
//****************************************************************************

STDMETHODIMP
CIlsUserNotify::EnumProtocolsResult(ULONG uReqID,
IEnumIlsNames *pEnumProtocol,
HRESULT hResult)
{
MyTextOut (TEXT("IIlsUserNotify::EnumProtocolsResult(%x) returns %x [%s].\r\n"),
uReqID, hResult, GetErrorString(hResult) );

if (hResult == NOERROR)
{
pEnumProtocol->AddRef();
PostMessage(GetWindow(), WM_IT_ENUM_NAMES_RESULT, 0, (LPARAM)pEnumProtocol);
};
return S_OK;
}

//****************************************************************************
// STDMETHODIMP
// CIlsUserNotify::GetProtocolResult(ULONG uReqID, IIlsProtocol *pProt,
//                                  HRESULT hResult)
//
//****************************************************************************

STDMETHODIMP
CIlsUserNotify::GetProtocolResult(ULONG uReqID, IIlsProtocol *pProt,
HRESULT hResult)
{
MyTextOut (TEXT("IIlsUserNotify::GetProtocolResult(%x) returns %x [%s].\r\n"),
uReqID, hResult, GetErrorString(hResult) );

if (SUCCEEDED(hResult))
{
pProt->AddRef();
PostMessage(GetWindow(), WM_IT_RESOLVE_PROT_RESULT, 0, (LPARAM)pProt);
};
return S_OK;
}

//****************************************************************************
// CIlsProtocolNotify::CIlsProtocolNotify (HWND hwndInit)
//
//****************************************************************************

CIlsProtocolNotify::CIlsProtocolNotify (HWND hwndInit) :
CNotify(hwndInit)
{
cRef = 0;
return;
}

//****************************************************************************
// STDMETHODIMP
// CIlsProtocolNotify::QueryInterface (REFIID riid, void **ppv)
//
//****************************************************************************

STDMETHODIMP
CIlsProtocolNotify::QueryInterface (REFIID riid, void **ppv)
{
if (riid == IID_IIlsProtocolNotify || riid == IID_IUnknown)
{
*ppv = (IIlsProtocolNotify *) this;
AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
};
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CIlsProtocolNotify::AddRef (void)
//
//****************************************************************************

STDMETHODIMP_(ULONG)
CIlsProtocolNotify::AddRef (void)
{
return cRef++;
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CIlsProtocolNotify::Release (void)
//
//****************************************************************************

STDMETHODIMP_(ULONG)
CIlsProtocolNotify::Release (void)
{
cRef--;

if (cRef == 0)
{
delete this;
return 0;
}
else
{
return cRef;
};
}

//****************************************************************************
// STDMETHODIMP
// CIlsProtocolNotify::UpdateResult(ULONG uReqID, HRESULT hResult)
//
//****************************************************************************

STDMETHODIMP
CIlsProtocolNotify::UpdateResult(ULONG uReqID, HRESULT hResult)
{
MyTextOut (TEXT("IIlsProtocolNotify::UpdateResult(%x) returns %x [%s].\r\n"),
uReqID, hResult, GetErrorString(hResult) );
return S_OK;
}


NOTIFY.H

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       notify.h
//  Content:    This file contains common notification sink objects.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#ifndef __NOTIFY_H__
#define __NOTIFY_H__

// Common notification sink object
//
class CNotify
{
private:
HWND    hwnd;
DWORD   dwCookie;

public:
CNotify (HWND hwnd) {this->hwnd = hwnd; dwCookie = 0xFFFFFFFF; return;}

void                    SetCookie (DWORD dwCookie)  {this->dwCookie = dwCookie; return;}
STDMETHODIMP_(DWORD)    GetCookie (void)            {return dwCookie;}
HWND                    GetWindow(void)             {return hwnd;}
};

//****************************************************************************
// Ils notification sink object
//
//****************************************************************************
class CIlsNotify : public CNotify,
public IIlsNotify
{
private:
UINT    cRef;

public:
CIlsNotify (HWND hwndInit);

// IUnknown
STDMETHODIMP            QueryInterface (REFIID iid, void **ppv);
STDMETHODIMP_(ULONG)    AddRef (void);
STDMETHODIMP_(ULONG)    Release (void);

// IIlsNotify
STDMETHODIMP GetUserResult(ULONG uReqID, IIlsUser *pUser, HRESULT hResult);
STDMETHODIMP EnumUserNamesResult(ULONG uReqID, IEnumIlsNames *pEnumUserName,
HRESULT hResult);
STDMETHODIMP EnumUsersResult(ULONG uReqID, IEnumIlsUsers *pEnumUser,
HRESULT hResult);
};

//****************************************************************************
// IlsProtocol notification sink object
//
//****************************************************************************
class CIlsProtocolNotify : public CNotify,
public IIlsProtocolNotify
{
private:
UINT    cRef;

public:
CIlsProtocolNotify (HWND hwndInit);

// IUnknown
STDMETHODIMP            QueryInterface (REFIID iid, void **ppv);
STDMETHODIMP_(ULONG)    AddRef (void);
STDMETHODIMP_(ULONG)    Release (void);

// IIlsLocalAppProtocolNotify
STDMETHODIMP UpdateResult(ULONG uReqID, HRESULT hResult);
};

//****************************************************************************
// IlsUser notification sink object
//
//****************************************************************************
class CIlsUserNotify : public CNotify,
public IIlsUserNotify
{
private:
UINT    cRef;

public:
CIlsUserNotify (HWND hwndInit);

// IUnknown
STDMETHODIMP            QueryInterface (REFIID iid, void **ppv);
STDMETHODIMP_(ULONG)    AddRef (void);
STDMETHODIMP_(ULONG)    Release (void);

// IIlsUserNotify
STDMETHODIMP StateChanged(ULONG fPrimary, BSTR bstrServerName);
STDMETHODIMP RegisterResult(ULONG uReqID, HRESULT hResult);
STDMETHODIMP UpdateResult(ULONG uReqID, HRESULT hResult);
STDMETHODIMP ProtocolChangeResult(ULONG uReqID, HRESULT hResult);
STDMETHODIMP EnumProtocolsResult(ULONG uReqID, IEnumIlsNames *pEnum,
HRESULT hResult);
STDMETHODIMP GetProtocolResult(ULONG uReqID, IIlsProtocol *pProtocol,
HRESULT hResult);
};

#endif //__NOTIFY_H__


OBLIST.CPP

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       oblist.cpp
//  Content:    This file contains the list class functions.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include <windows.h>
#include "oblist.h"

//****************************************************************************
// void* COBLIST::GetTail()
//
// Returns the end of the list.
//
//****************************************************************************
VOID* COBLIST::GetTail()
{
return m_pTail->pItem;
}

//****************************************************************************
// VOID* COBLIST::GetNext(POSITION& rPos)
//
// Returns the next item of the list.
//
//****************************************************************************
VOID* COBLIST::GetNext(POSITION& rPos)
{
VOID* pReturn = rPos->pItem;
rPos = rPos->pNext;
return pReturn;
}

//****************************************************************************
// VOID* COBLIST::RemoveAt(POSITION Pos)
//
// Removes an item from the list.
//
//****************************************************************************
VOID* COBLIST::RemoveAt(POSITION Pos)
{
VOID* pReturn = NULL;

if (m_pHead)
{
if (m_pHead == Pos)
{
// Removing the first element in the list

m_pHead = Pos->pNext;
pReturn = Pos->pItem;
delete Pos;

if (NULL == m_pHead)
{
// Removing the only element!
m_pTail = NULL;
}
}
else
{
POSITION pCur = m_pHead;

while (pCur && pCur->pNext)
{
if (pCur->pNext == Pos)
{
// Removing

pCur->pNext = Pos->pNext;
if (m_pTail == Pos)
{
m_pTail = pCur;
}
pReturn = Pos->pItem;
delete Pos;
}

pCur = pCur->pNext;
}
}
}

return pReturn;
}

//****************************************************************************
// POSITION COBLIST::AddTail(VOID* pItem)
//
// Add to the end of the list.
//
//****************************************************************************
POSITION COBLIST::AddTail(VOID* pItem)
{
POSITION posRet = NULL;

if (m_pTail)
{
if (m_pTail->pNext = new COBNODE)
{
m_pTail = m_pTail->pNext;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
}
}
else
{
if (m_pHead = new COBNODE)
{
m_pTail = m_pHead;
m_pTail->pItem = pItem;
m_pTail->pNext = NULL;
}
}

return m_pTail;
}

//****************************************************************************
// POSITION COBLIST::EmpytList(VOID* pItem)
//
// Frees the entire list.
//
//****************************************************************************
void COBLIST::EmptyList()
{
while (!IsEmpty()) {
RemoveAt(GetHeadPosition());
}
}

//****************************************************************************
// POSITION COBLIST::GetPosition(VOID* _pItem)
//
// return the position of an item in the list
//
//****************************************************************************
POSITION COBLIST::GetPosition(void* _pItem)
{
POSITION Position = m_pHead;

while (Position) {
if (Position->pItem == _pItem) {
break;
}
GetNext(Position);
}
return Position;
}

//****************************************************************************
// POSITION COBLIST::Lookup(VOID* _pItem)
//
//
//****************************************************************************
POSITION COBLIST::Lookup(void* pComparator)
{
POSITION    Position = m_pHead;

while (Position) {
if (Compare(Position->pItem, pComparator)) {
break;
}
GetNext(Position);
}
return Position;
}

//****************************************************************************
// POSITION COBLIST::SafeGetFromPosition(POSITION Pos)
//
// Safe way to validate that an entry is still in the list,
//
//****************************************************************************
void * COBLIST::SafeGetFromPosition(POSITION Pos)
{
POSITION PosWork = m_pHead;

while (PosWork) {
if (PosWork == Pos) {
return Pos->pItem;
}
GetNext(PosWork);
}
return NULL;
}


OBLIST.H

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       oblist.h
//  Content:    This file contains the list class functions.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#ifndef _OBLIST_H_
#define _OBLIST_H_

// class COBNODE;
#define POSITION COBNODE*

struct COBNODE
{
POSITION  pNext;
void*     pItem;
};

class COBLIST
{
protected:
POSITION        m_pHead;
POSITION        m_pTail;
virtual BOOL    Compare(void* pItemToCompare, void* pComparator)
{return(pItemToCompare == pComparator);};
public:
COBLIST() : m_pHead(NULL), m_pTail(NULL) { };
~COBLIST() {};

POSITION        GetHeadPosition() { return(m_pHead); };
POSITION        GetTailPosition() { return(m_pTail); };
virtual void *  RemoveAt(POSITION rPos);
POSITION        AddTail(void* pItem);
BOOL            IsEmpty() { return(!m_pHead); };
void *          GetTail();
void *          GetNext(POSITION& rPos);
void *          SafeGetFromPosition(POSITION rPos);
POSITION        GetPosition(void* pItem);
POSITION        Lookup(void* pComparator);
void            EmptyList();

void *          GetHead(){return GetFromPosition(GetHeadPosition());};
void *          RemoveHead() { return RemoveAt(m_pHead); };
void *          GetFromPosition(POSITION rPos){return(rPos->pItem);};
};

#endif // ifndef _OBLIST_H_


PROTO.CPP

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       proto.cpp
//  Content:    This file contains protocol functions.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include "ilstest.h"

//****************************************************************************
//
// int EnumUserProtocols(HWND hwnd, IIlsUser *pu)
//
//
//****************************************************************************

HRESULT EnumUserProtocols(HWND hwnd, IIlsUser *pu)
{
HRESULT hr = E_FAIL;
IIlsFilter *pFilter;
IIlsAttributes *pAttributes = NULL;
ULONG uReqID;

// use the global PROT filter, if not defined it will be NULL
//
pFilter = g_pProtFilter;

// The third param is unused and must be NULL
//
hr = pu->EnumProtocols(pFilter, pAttributes, NULL, &uReqID);

if (SUCCEEDED(hr))
MyTextOut(TEXT("IIlsUser::EnumProtocols(%x) pending.\r\n"), uReqID);
else
ErrorMessage(hwnd, TEXT("IIlsUser::EnumProtocols fails."), hr);

return hr;
}

//****************************************************************************
//
// int DisplayProtocol(HWND hwnd, IIlsUser *pu)
//
//
//****************************************************************************
HRESULT DisplayProtocol(HWND hwnd, IIlsUser *pu)
{
HRESULT hr = E_FAIL;
IIlsFilter *pFilter;
IIlsAttributes *pAttributes = NULL;
ULONG uReqID;
BSTR bstrProtID;
char szProtID[MAX_NAME];

// use the global PROT filter, if not defined it will be NULL
//
pFilter = g_pProtFilter;
pAttributes = NULL;

// Ask which user to query (need to get a better dialog box)
//
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_USERNAME),
hwnd, (DLGPROC)ProtoNameDlgProc, (LPARAM)szProtID) == IDOK)
{
LPTSTR_to_BSTR(&bstrProtID, szProtID);

// The third param is unused and must be NULL
//
hr = pu->GetProtocol(bstrProtID, pAttributes, NULL, &uReqID);

if (SUCCEEDED(hr))
MyTextOut(TEXT("IIlsUser::GetProtocol(%x) pending.\r\n"), uReqID);
else
ErrorMessage(hwnd, TEXT("IIlsUser::GetProtocol fails."), hr);
}
else
{
//User cancelled
MyTextOut(TEXT("GetProtocol cancelled.\r\n"));
}

return hr;
}

//****************************************************************************
//
// int ProtocolDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog procedure for the protocol dialog box.
//
//****************************************************************************

int ProtocolDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PROTOCOLINFO *ppi;

switch (msg)
{
case WM_INITDIALOG:
{
SetWindowLong (hwnd, DWL_USER, lParam);
SetFocus (GetDlgItem(hwnd, IDC_PROT_NAME));
break;
}

case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
ppi = (PPROTOCOLINFO)GetWindowLong(hwnd, DWL_USER);
SaveProtInfo(hwnd, ppi);
// FALL THROUGH !!!

case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

default:
break;
};
break;
}

default:
break;
};

return 0;
}

//****************************************************************************
//
// void SaveProtInfo(HWND hwnd, PPROTOCOLINFO ppi)
//
//****************************************************************************
void SaveProtInfo(HWND hwnd, PPROTOCOLINFO ppi)
{

TCHAR szTemp[MAX_PATH];
BOOL fSuccess;

// retrieve the protocol name
//
GetDlgItemText(hwnd, IDC_PROT_NAME, szTemp, MAX_PATH);
ppi->szProtName = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(ppi->szProtName, szTemp);

// retrieve the mime type
//
GetDlgItemText(hwnd, IDC_PROT_MIME, szTemp, MAX_PATH);
ppi->szProtMime = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(ppi->szProtMime, szTemp);

// retrieve the protocol port to use
//
ppi->uPortNumber = GetDlgItemInt(hwnd, IDC_PROT_PORT, &fSuccess, FALSE);

}

//****************************************************************************
//
// void CreateProtocolDialog(HWND hwnd)
//
//****************************************************************************
void CreateProtocolDialog(HWND hwnd)
{
PROTOCOLINFO ProtInfo;
PROTOCOLINFO *pProtInfo = &ProtInfo;
HRESULT hr;
BSTR bstrName, bstrMIME;
IIlsProtocol *pp;
ULONG uReqID;
char szUserName[MAX_NAME];
PUSERNODE pun;
POSITION pos;
LPTSTR psz;

pProtInfo->szProtName = NULL;
pProtInfo->szProtMime = NULL;
pProtInfo->uPortNumber = 0;

// Prompt for the user
//
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)UserListDlgProc, (LPARAM)szUserName) == IDOK)
{

if(!MyIsGoodString(szUserName))
{
MyTextOut(TEXT("User name is empty.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// match name to usernode
//
pos = g_pUserList->GetHeadPosition();

while(pos)
{
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);

hr = pun->pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrName);
BSTR_to_LPTSTR(&psz, bstrName);
if(strcmp(psz, szUserName) == 0)
{
FreeLPTSTR(psz);
break;
}
g_pUserList->GetNext(pos);
FreeLPTSTR(psz);
}

// Got the right user, get the protocol details...
//
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_PROTOCOL),
hwnd, (DLGPROC)ProtocolDlgProc, (LPARAM)pProtInfo) == IDOK)
{

if(!MyIsGoodString(pProtInfo->szProtName))
{
MyTextOut(TEXT("Protocol name is emtpy - no protocol object created.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// Create the protocol for the user
//
LPTSTR_to_BSTR(&bstrName, pProtInfo->szProtName);
LPTSTR_to_BSTR(&bstrMIME, pProtInfo->szProtMime);

hr = pun->pu->CreateProtocol(bstrName, pProtInfo->uPortNumber, bstrMIME, &pp);

SysFreeString(bstrName);
SysFreeString(bstrMIME);

if(SUCCEEDED(hr))
MyTextOut(TEXT("IIlsUser::CreateProtocol succeeded.\r\n"));
else
{
ErrorMessage(hwnd, TEXT("IIlsUser::CreateProtocol fails."), hr);
goto MyExit;
}

// Add the protocol to the user
//
hr = pun->pu->AddProtocol(pp, &uReqID);
if (SUCCEEDED(hr))
{
ILS_STATE pState;

// Adding is sync if the user is not yet registered
//
hr = pun->pu->GetState(&pState);
if(pState == ILS_REGISTERED)
MyTextOut(TEXT("IIlsUser::AddProtocol(%x) pending.\r\n"), uReqID);
else if (pState == ILS_UNREGISTERED)
MyTextOut(TEXT("IIlsUser::AddProtocol returns %s.\r\n"), GetErrorString(hr));
else
MyTextOut(TEXT("Hmm.. not sure of user's state on the server.\r\n"));

// Add this protocol to the list for this user
// TODO - should really post a message in the callback and handle it in the
// main wndproc.
pun->pProtList->AddTail(pp);
}
else
ErrorMessage(hwnd, TEXT("IIlsMain::AddProtocol fails."), hr);

}
else
{
//  User cancelled
MyTextOut(TEXT("Create Protocol cancelled.\r\n"));
}
}
else
{
//  User cancelled
MyTextOut(TEXT("User list cancelled.\r\n"));
}

if(pProtInfo->szProtName != NULL)
FreeLPTSTR(pProtInfo->szProtName);
if(pProtInfo->szProtMime != NULL)
FreeLPTSTR(pProtInfo->szProtMime);

MyExit:
return;
}

//****************************************************************************
//
// void DestroyProtocolDialog(HWND hwnd)
//
//****************************************************************************
void DestroyProtocolDialog(HWND hwnd)
{
PROTOCOLINFO ProtInfo;
PROTOCOLINFO *pProtInfo = &ProtInfo;
HRESULT hr;
BSTR bstrName;
char szUserName[MAX_NAME];
PUSERNODE pun;
POSITION pos;
LPTSTR psz;

ENUMPROTINFO epi;
PENUMPROTINFO pepi = &epi;

// Prompt for the user
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)UserListDlgProc, (LPARAM)szUserName) == IDOK)
{
if(!MyIsGoodString(szUserName))
{
MyTextOut(TEXT("User name is empty.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// match name to usernode
pos = g_pUserList->GetHeadPosition();

while(pos)
{
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);

hr = pun->pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrName);
BSTR_to_LPTSTR(&psz, bstrName);
if(strcmp(psz, szUserName) == 0)
{
FreeLPTSTR(psz);
break;
}
g_pUserList->GetNext(pos);
FreeLPTSTR(psz);
}

// Got the right user, Enum the protocols...
//
pepi->pun = pun;

if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)ProtoListDlgProc, (LPARAM)pepi) == IDOK)
{
// Everything is done in the ProtoListDlgProc function
}
else
{
//User cancelled
MyTextOut(TEXT("Destroy Protocol cancelled.\r\n"));

}
}
else
{
//User cancelled
MyTextOut(TEXT("User list cancelled.\r\n"));
}
MyExit:
return;
}

//****************************************************************************
//
// int ProtoListDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog proc that displays the protocols for the
// current user objects and returns the chosen item.
//
//****************************************************************************
int ProtoListDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND hwndList;
int iIndex;
HRESULT hr = E_FAIL;
ULONG uReqID;
PENUMPROTINFO pepi;
BSTR bstrName;
LPTSTR szName;
POSITION pos;
IIlsProtocol *pp;

pp = NULL;

switch (msg)
{
case WM_INITDIALOG:
{
hwndList = GetDlgItem(hwnd, IDC_LIST);

pepi = (ENUMPROTINFO *)lParam;

SetWindowLong (hwnd, DWL_USER, lParam);
SetWindowText(hwnd, "Protocol List");

// walk the list of Protocols and add the names to the list box
pos = pepi->pun->pProtList->GetHeadPosition();

while(pos)
{
pp = (IIlsProtocol *)pepi->pun->pProtList->GetFromPosition(pos);

hr = pp->GetStandardAttribute(ILS_STDATTR_PROTOCOL_NAME, &bstrName);
BSTR_to_LPTSTR(&szName, bstrName);
SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szName);
pepi->pun->pProtList->GetNext(pos);
FreeLPTSTR(szName);
}

break;
}

case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
{
TCHAR szTemp[256];
hwndList = GetDlgItem(hwnd, IDC_LIST);

// return the name of the user chosen
pepi = (ENUMPROTINFO *)GetWindowLong(hwnd, DWL_USER);
iIndex = SendMessage(hwndList, LB_GETCURSEL, 0,0);
if(iIndex = -1)
iIndex = 0;
iIndex = SendMessage(hwndList, LB_GETTEXT, iIndex, (LPARAM)szTemp);

// walk the list of protocols until we find the one selected
pos = pepi->pun->pProtList->GetHeadPosition();

while(pos)
{
pp = (IIlsProtocol *)pepi->pun->pProtList->GetFromPosition(pos);

hr = pp->GetStandardAttribute(ILS_STDATTR_PROTOCOL_NAME, &bstrName);
BSTR_to_LPTSTR(&szName, bstrName);
if(strcmp(szName, szTemp) == 0)
{
// TODO - really should post a message in the callback and
// handle this in the main wndproc.
FreeLPTSTR(szName);
pepi->pun->pProtList->RemoveAt(pos);
break;
}
pepi->pun->pProtList->GetNext(pos);
FreeLPTSTR(szName);
}

hr = pepi->pun->pu->RemoveProtocol(pp, &uReqID);

if(SUCCEEDED(hr))
{
ILS_STATE pState;

// Removing is sync if the user is not yet registered
//
hr = pepi->pun->pu->GetState(&pState);
if(pState == ILS_REGISTERED)
MyTextOut(TEXT("IIlsUser::RemoveProtocol(%x) pending.\r\n"), uReqID);
else if (pState == ILS_UNREGISTERED)
MyTextOut(TEXT("IIlsUser::RemoveProtocol returns %s.\r\n"), GetErrorString(hr));
else
MyTextOut(TEXT("Hmm.. not sure of user's state on the server.\r\n"));  // TODO - better error handling here.
}
else
{
ErrorMessage(hwnd, TEXT("RemoveProtocol failed"), hr);
}

}

// Fall through to end the dialog!!

case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

default:
break;
};
break;
}

default:
break;
};

return 0;
}

//****************************************************************************
//
// int DisplayProtocolInfo(HWND hwnd, IIlsProtocol *pp)
//
//
//****************************************************************************
HRESULT DisplayProtocolInfo(HWND hwnd, IIlsProtocol *pp)
{
HRESULT hr = E_FAIL;
BSTR bstrTemp = NULL;
LPTSTR szTemp;
BOOL pValue;

MyTextOut(" ---------------\r\n");

// Print out the protocol information
//
hr = pp->IsWritable(&pValue);
if (SUCCEEDED(hr))
{
if(pValue)
MyTextOut("Protocol is writable.\r\n");
else
MyTextOut("Protocol is read only.\r\n");
}
else
{
ErrorMessage(hwnd, TEXT("IIlsProtocol::IsWritable fails."), hr);
}

hr = pp->GetStandardAttribute(ILS_STDATTR_PROTOCOL_NAME, &bstrTemp);
if (SUCCEEDED(hr))
{
BSTR_to_LPTSTR(&szTemp, bstrTemp);
MyTextOut("Protocol name is %s.\r\n", szTemp);
SysFreeString(bstrTemp);
}
else
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);

hr = pp->GetStandardAttribute(ILS_STDATTR_PROTOCOL_MIME_TYPE, &bstrTemp);
if (SUCCEEDED(hr))
{
BSTR_to_LPTSTR(&szTemp, bstrTemp);
MyTextOut("Mime Type is %s.\r\n", szTemp);
SysFreeString(bstrTemp);
}
else
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);

hr = pp->GetStandardAttribute(ILS_STDATTR_PROTOCOL_PORT, &bstrTemp);
if (SUCCEEDED(hr))
{
BSTR_to_LPTSTR(&szTemp, bstrTemp);
MyTextOut("Port is %s.\r\n", szTemp);
SysFreeString(bstrTemp);
}
else
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);

MyTextOut(" ---------------\r\n");

return hr;
}

//****************************************************************************
//
// int ProtoNameDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog proc that prompts the user for a protocol name
//
//****************************************************************************
int ProtoNameDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LPTSTR pszTemp;
HRESULT hr = E_FAIL;

switch (msg)
{
case WM_INITDIALOG:
{
SetWindowLong (hwnd, DWL_USER, lParam);
SetFocus(GetDlgItem(hwnd, IDC_USER_NAME));
SetWindowText(hwnd, "Protocol name");

break;
}

case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
// return the name of the user chosen
pszTemp = (LPTSTR)GetWindowLong(hwnd, DWL_USER);
GetDlgItemText(hwnd, IDC_USER_NAME, pszTemp, MAX_NAME);

// Fall through to end the dialog!!

case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

default:
break;
};
break;
}

default:
break;
};

return 0;
}


APPGUID.H

//****************************************************************************
//
//    SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       appguid.h
//  Content:    This file contains the application GUID.
//
//  Copyright (c) Microsoft Corporation 1997
//    All rights reserved
//
//****************************************************************************

// {54241E5E-9657-11d0-BD01-00C04FC29D47}
DEFINE_GUID(GUID_ILSTest,
0x54241e5e, 0x9657, 0x11d0, 0xbd, 0x1, 0x0, 0xc0, 0x4f, 0xc2, 0x9d, 0x47);


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by skippy.rc
//
#define IDS_VERSION                     1
#define IDI_APP_ICON                    101
#define IDR_MENU1                       102
#define IDD_DLG_APP                     103
#define IDD_DLG_USERINFO                104
#define IDD_DLG_ATTRIBS                 105
#define IDD_DLG_ATTRIB                  106
#define IDD_DLG_APPPROT                 107
#define IDD_DLG_APPPROTS                108
#define IDD_DLG_SERVERNAME              109
#define IDD_DLG_USERNAME                110
#define IDD_DLG_ILSUSER                 111
#define IDD_DLG_ILSAPP                  112
#define IDD_DLG_APPNAME                 113
#define IDB_OPEN                        113
#define IDD_DLG_PROTNAME                114
#define IDB_CLOSED                      114
#define IDD_DLG_PROTOCOL                114
#define IDD_DLG_ILSPROT                 115
#define IDB_EGG                         115
#define IDD_DLG_DESTROYSERVER           116
#define IDD_DLG_SERVERLIST              116
#define IDD_DLG_USERLIST                117
#define IDD_DLG_LIST                    117
#define IDD_DLG_FILTER                  119
#define IDC_ACT_EDIT                    1000
#define IDC_UI_FIRSTNAME                1001
#define IDC_UI_LASTNAME                 1002
#define IDC_UI_EMAIL                    1003
#define IDC_UI_CITY                     1004
#define IDC_UI_COUNTRY                  1005
#define IDC_AT_ATTRIBS                  1005
#define IDC_UI_COMMENT                  1006
#define IDC_AT_ADD                      1006
#define IDC_AT_CHANGE                   1007
#define IDC_UI_IPADD                    1007
#define IDC_AT_DELETE                   1008
#define IDC_ATTR_NAME                   1010
#define IDC_ATTR_VALUE                  1011
#define IDC_LAP_ADD_ATTRS               1012
#define IDC_LAP_DEL_ATTRS               1013
#define IDC_APS_PROTOCOLS               1013
#define IDC_APS_ADD                     1014
#define IDC_APS_CHANGE                  1015
#define IDC_APS_DELETE                  1016
#define IDC_APS_REFRESH                 1017
#define IDC_SVR_NAME                    1018
#define IDC_UU_INFO                     1019
#define IDC_PROT_MIME                   1019
#define IDC_UU_APPS                     1020
#define IDC_UU_PROT                     1020
#define IDC_PROT_PORT                   1020
#define IDC_UA_INFO                     1021
#define IDC_UU_GETPROT                  1021
#define IDC_UU_PROTS                    1022
#define IDC_UA_PROTS                    1022
#define IDC_UU_APPINFO                  1023
#define IDC_UU_EXATTR                   1023
#define IDC_UA_PROTINFO                 1024
#define IDC_UA_ATTRS                    1025
#define IDC_UP_INFO                     1026
#define IDC_UP_ATTRS                    1027
#define IDC_EDIT1                       1028
#define IDC_LOGON_NAME                  1028
#define IDC_VALUE1                      1028
#define IDC_EDIT2                       1029
#define IDC_PASSWORD                    1029
#define IDC_VALUE2                      1029
#define IDC_EDIT3                       1030
#define IDC_AUTH_CRED                   1030
#define IDC_AUTH_METHOD                 1032
#define IDC_ATT_LIST1                   1032
#define IDC_ATT_LIST2                   1033
#define IDC_SPIN1                       1035
#define IDC_EDIT4                       1036
#define IDC_TIMEOUT                     1036
#define IDC_SERVERLIST                  1037
#define IDC_DOMAIN                      1037
#define IDC_USERLIST                    1038
#define IDC_BASEDN                      1038
#define IDC_LIST                        1038
#define IDC_USER_NAME                   1039
#define IDC_OP_EQUAL1                   1040
#define IDC_OP_APPROX1                  1041
#define IDC_OP_LESSTHAN1                1042
#define IDC_OP_GREATERTHAN1             1043
#define IDC_OP_EXIST1                   1044
#define IDC_AND                         1045
#define IDC_OR                          1046
#define IDC_NOT                         1047
#define IDC_OP_EQUAL2                   1048
#define IDC_OP_APPROX2                  1049
#define IDC_OP_LESSTHAN2                1050
#define IDC_OP_GREATERTHAN2             1051
#define IDC_OP_EXIST2                   1052
#define IDC_SIMPLE                      1053
#define IDC_COMPOSITE                   1054
#define IDC_COMP_FRAME                  1055
#define IDC_STD_TXT2                    1056
#define IDC_FIL_OP2                     1057
#define IDC_ATT_TXT2                    1058
#define IDC_ATT_TXT1                    1059
#define IDC_STD_TXT1                    1060
#define IDC_FIL_OP1                     1061
#define IDC_PROT_NAME                   1065
#define IDM_EXIT                        40001
#define IDM_USERINFO                    40002
#define IDM_ATTRIBS                     40003
#define IDM_LA_ADD_ATTRS                40004
#define IDM_LA_DEL_ATTRS                40005
#define IDM_LA_PROTOCOL                 40008
#define IDM_REG                         40009
#define IDM_UNREG                       40010
#define IDM_DIR_SHORT                   40012
#define IDM_DIR_LONG                    40013
#define IDM_USER_RESOLVE                40014
#define IDM_SVRINFO                     40015
#define IDM_ABOUT                       40016
#define IDM_ARBIT_ATTR                  40017
#define IDM_SERVER_INFO                 40018
#define IDM_NEWSERVER                   40019
#define IDM_CREATESERVER                40019
#define IDM_THISUSER                    40020
#define IDM_NEWUSER                     40020
#define IDM_ENUMUSERS                   40021
#define IDM_ENUMUSERNAMES               40022
#define IDM_CLONEUSER                   40023
#define IDM_SERVERLIST                  40200
#define IDM_USERLIST                    40400
#define IDM_DESTROYSERVER               40600
#define IDM_DESTROYUSER                 40601
#define IDM_REGISTERUSER                40602
#define IDM_UNREGISTERUSER              40604
#define IDM_GETUSERINFO                 40605
#define IDM_USER_FILTER                 40608
#define IDM_PROTOCOL_FILTER             40609
#define IDM_DESTROY_USERFILTER          40610
#define IDM_DESTROY_PROTFILTER          40611
#define IDM_CREATE_PROT                 40612
#define IDM_DESTROY_PROT                40613
#define IDM_MODIFYUSER                  40617

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        120
#define _APS_NEXT_COMMAND_VALUE         40618
#define _APS_NEXT_CONTROL_VALUE         1066
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SVRINFO.CPP

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       svrinfo.cpp
//  Content:    This file contains the server info dialog functions.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include "ilstest.h"
#include "notify.h"

//****************************************************************************
//
// HRESULT NewServerDialog(HWND hwnd)
//
// This function starts the server information dialog box for creating a server.
//
//****************************************************************************
HRESULT NewServerDialog(HWND hwnd)
{
HRESULT hr = E_FAIL;
HMENU hMenu;
BSTR bstrServerName;
IIlsServer *ps;
SERVERINFO si;
PSERVERINFO psi;

psi = &si;

// Set up the server info structure
//
psi->szServerName = NULL;
psi->szLogonName = NULL;
psi->szPassword = NULL;
psi->eam = ILS_AUTH_ANONYMOUS;
psi->szDomain = NULL;
psi->szCredential = NULL;
psi->szBaseDN = NULL;
psi->dwTimeout = 0;

// Allow only one dialog at a time
//
hMenu = GetMenu(hwnd);
EnableMenuItem(hMenu, IDM_CREATESERVER, MF_BYCOMMAND | MF_GRAYED);

if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_SERVERNAME),
hwnd, (DLGPROC)ServerDlgProc, (LPARAM)psi) == IDOK)
{

if(!MyIsGoodString(psi->szServerName))
{
MyTextOut(TEXT("Server name is empty - no server object created\r\n"));
hr = E_FAIL;
goto MyExit;
}

LPTSTR_to_BSTR(&bstrServerName, psi->szServerName);

// Create the server object
hr = g_pIls->CreateServer(bstrServerName, &ps);

if (SUCCEEDED(hr))
{
MyTextOut(TEXT("IIls::CreateServer succeeded. %s\r\n"), psi->szServerName);

// Set the server parameters
//
SetServerInfo(hwnd, ps, psi);

// Add this server to the global list of servers
//
NewServerNode(ps, psi->szServerName);

if(!g_pServerList->IsEmpty()) // Check if any menu items should be enabled
{
EnableMenuItem(hMenu, IDM_DESTROYSERVER, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_ENUMUSERS, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_ENUMUSERNAMES, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_GETUSERINFO, MF_BYCOMMAND | MF_ENABLED);
if(!g_pUserList->IsEmpty())  // only enable Reg/Unreg if are users in the list
{
EnableMenuItem(hMenu, IDM_REGISTERUSER, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_UNREGISTERUSER, MF_BYCOMMAND | MF_ENABLED);
}

}

}
else
{
ErrorMessage(hwnd, TEXT("IIls::CreateServer fails."), hr);
};
SysFreeString(bstrServerName);
}
else
{
// User cancelled
//
MyTextOut(TEXT("IIls::CreateServer cancelled.\r\n"));

}

MyExit:

// Clean up
if(psi->szServerName != NULL)
FreeLPTSTR(psi->szServerName);

if(psi->szLogonName != NULL)
FreeLPTSTR(psi->szLogonName);

if(psi->szPassword != NULL)
FreeLPTSTR(psi->szPassword);

if(psi->szDomain != NULL)
FreeLPTSTR(psi->szDomain);

if(psi->szCredential != NULL)
FreeLPTSTR(psi->szCredential);

if(psi->szBaseDN != NULL)
FreeLPTSTR(psi->szBaseDN);

EnableMenuItem(hMenu, IDM_CREATESERVER, MF_BYCOMMAND | MF_ENABLED);
return hr;
}

//****************************************************************************
//
// int ServerDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog procedure for the server prompt dialog box.
//
//****************************************************************************

int ServerDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PSERVERINFO psInfo;
HWND hwndCombo;
BOOL fSuccess;
int iIndex;

switch (msg)
{
case WM_INITDIALOG:
{
hwndCombo = GetDlgItem(hwnd, IDC_AUTH_METHOD);

SetWindowLong (hwnd, DWL_USER, lParam);
SetFocus(GetDlgItem(hwnd, IDC_SVR_NAME));

//  set the auth method strings in the list
//
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)"ANONYMOUS");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)"CLEAR_TEXT");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)"NTLM");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)"DPA");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)"MSN");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)"SICILY");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)"SSPI");

// set default auth method
SendMessage(hwndCombo, CB_SETCURSEL, 0, 0);

break;
}

case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
{
TCHAR szTemp[MAX_NAME];

psInfo = (PSERVERINFO)GetWindowLong(hwnd, DWL_USER);
hwndCombo = GetDlgItem(hwnd, IDC_AUTH_METHOD);

// clear the server info
//
psInfo->szServerName = NULL;
psInfo->szLogonName = NULL;
psInfo->szPassword = NULL;
psInfo->szCredential = NULL;
psInfo->szDomain = NULL;
psInfo->szBaseDN = NULL;

// Get the server name (required)
//
if(0 != GetDlgItemText(hwnd, IDC_SVR_NAME, szTemp, MAX_PATH))
{
psInfo->szServerName = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(psInfo->szServerName, CharUpper(szTemp));
}

// Get the server logon name
//
if(0 != GetDlgItemText(hwnd, IDC_LOGON_NAME, szTemp, MAX_PATH))
{
psInfo->szLogonName = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(psInfo->szLogonName, szTemp);
}

// Get the server password
//
if(0 != GetDlgItemText(hwnd, IDC_PASSWORD, szTemp, MAX_PATH))
{
psInfo->szPassword = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(psInfo->szPassword, szTemp);
}

// Get the server credential to use
//
if(0 != GetDlgItemText(hwnd, IDC_AUTH_CRED, szTemp, MAX_PATH))
{
psInfo->szCredential = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(psInfo->szCredential, szTemp);
}

// Get the domain name use
//
if(0 != GetDlgItemText(hwnd, IDC_DOMAIN, szTemp, MAX_PATH))
{
psInfo->szDomain = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(psInfo->szDomain, szTemp);
}

// Get the Base Distinguished Name use
//
if(0 != GetDlgItemText(hwnd, IDC_BASEDN, szTemp, MAX_PATH))
{
psInfo->szBaseDN = AllocLPTSTR(lstrlen(szTemp) + 1);
strcpy(psInfo->szBaseDN, szTemp);
}

// Get the server timeout to use
//
psInfo->dwTimeout = GetDlgItemInt(hwnd, IDC_TIMEOUT, &fSuccess, FALSE);

// Get the currently selected authentication method
//
iIndex = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
psInfo->eam = (ILS_ENUM_AUTH_METHOD)iIndex;

// Fall through to end the dialog!!
}
case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

default:
break;
};
break;
}

default:
break;
};

return 0;
}

//****************************************************************************
//
// void NewServerNode (IlsServer *pServer, LPTSTR szName)
//
//****************************************************************************
void NewServerNode(IIlsServer *pServer, LPTSTR szName)
{
PSERVERNODE psn;

// Alloc memory for the new node
//
psn = (PSERVERNODE)LocalAlloc(LMEM_FIXED, sizeof(SERVERNODE));

// set the pointers to the new server
psn->pSrv = pServer;
strcpy(psn->szName, szName);

// add to the list
g_pServerList->AddTail( (void *)psn );

}

//****************************************************************************
//
// HRESULT RemoveServerDialog(HWND hwnd)
//
// This function starts the server information dialog box for
// destroying a server.
//
//****************************************************************************
HRESULT RemoveServerDialog(HWND hwnd)
{
HRESULT hr = E_FAIL;
HMENU hMenu;
PSERVERNODE psn;
char szServerName[MAX_NAME];
POSITION pos;

if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)ServerListDlgProc, (LPARAM)szServerName) == IDOK)
{
if(!MyIsGoodString(szServerName))
{
MyTextOut(TEXT("Server name is empty.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// match name to servernode
pos = g_pServerList->GetHeadPosition();

while(pos)
{
psn = (PSERVERNODE)g_pServerList->GetFromPosition(pos);
if(strcmp(psn->szName, szServerName) == 0)
break;
g_pServerList->GetNext(pos);
}

// free server object
psn->pSrv->Release();
psn->pSrv = NULL;

// free server name string
LocalFree(psn->szName);

// remove node from list
g_pServerList->RemoveAt(pos);

// if the list is now empty, disable the menu item
if(g_pServerList->IsEmpty())
{
hMenu = GetMenu(hwnd);
EnableMenuItem(hMenu, IDM_DESTROYSERVER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_ENUMUSERS, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_ENUMUSERNAMES, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_REGISTERUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_UNREGISTERUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_GETUSERINFO, MF_BYCOMMAND | MF_GRAYED);
}

MyTextOut(TEXT("Server %s removed.\r\n"), szServerName);

}
else
{
//User cancelled
MyTextOut(TEXT("Destroy server cancelled.\r\n"));
}

MyExit:

return hr;
}

//****************************************************************************
//
// int ServerListDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog procedure for the remove server prompt dialog box.
//
//****************************************************************************

int ServerListDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PSERVERNODE psn;
LPTSTR pszTemp;
POSITION pos;
HWND hwndList;
int iIndex;

switch (msg)
{
case WM_INITDIALOG:
{
SetWindowLong (hwnd, DWL_USER, lParam);
hwndList = GetDlgItem(hwnd, IDC_LIST);

// Walk the list of servers and fill the listbox with names
pos = g_pServerList->GetHeadPosition();
SetWindowText(hwnd, "Server List");

while(pos)
{
psn = (PSERVERNODE)g_pServerList->GetFromPosition(pos);
SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)psn->szName);
g_pServerList->GetNext(pos);
}

break;
}

case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
// return the name of the server chosen
hwndList = GetDlgItem(hwnd, IDC_LIST);
pszTemp = (LPTSTR)GetWindowLong(hwnd, DWL_USER);
iIndex = SendMessage(hwndList, LB_GETCURSEL, 0,0);
if(iIndex == -1)
iIndex = 0;
iIndex = SendMessage(hwndList, LB_GETTEXT, iIndex, (LPARAM)pszTemp);

// Fall through to end the dialog!!

case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

default:
break;
};
break;
}

default:
break;
};

return 0;
}

//****************************************************************************
//
// int SetServerInfo (HWND hwnd, IIlsServer *ps, PSERVERINFO psi)
//
// set the server options based on the server dialog settings
//
//****************************************************************************
void SetServerInfo(HWND hwnd, IIlsServer *ps, PSERVERINFO psi)
{
HRESULT hr = E_FAIL;
BSTR bstrTemp;

// Auth method
//
hr = ps->SetAuthenticationMethod(psi->eam);
if(SUCCEEDED(hr))
MyTextOut("IIlsServer::SetAuthentication method succeeded.\r\n");
else
ErrorMessage(hwnd, TEXT("IIlsServer::SetAuthenticationMethod fails."), hr);

// Logon name
//
if(psi->szLogonName)
{
LPTSTR_to_BSTR(&bstrTemp, psi->szLogonName);
hr = ps->SetLogonName(bstrTemp);
SysFreeString(bstrTemp);

if(SUCCEEDED(hr))
MyTextOut("IIlsServer::SetLogonName, set to %s.\r\n", psi->szLogonName);
else
ErrorMessage(hwnd, TEXT("IIlsServer::SetLogonName fails."), hr);

FreeLPTSTR(psi->szLogonName);
psi->szLogonName = NULL;

}

// Password
//
if(psi->szPassword)
{
LPTSTR_to_BSTR(&bstrTemp, psi->szPassword);
hr = ps->SetLogonPassword(bstrTemp);
SysFreeString(bstrTemp);

if(SUCCEEDED(hr))
MyTextOut("IIlsServer::SetLogonPassword, set to %s.\r\n", psi->szPassword);
else
ErrorMessage(hwnd, TEXT("IIlsServer::SetLogonPassword fails."), hr);

FreeLPTSTR(psi->szPassword);
psi->szPassword = NULL;

}

// Domain
//
if(psi->szDomain)
{
LPTSTR_to_BSTR(&bstrTemp, psi->szDomain);
hr = ps->SetDomain(bstrTemp);
SysFreeString(bstrTemp);

if(SUCCEEDED(hr))
MyTextOut("IIlsServer::SetDomain method, set to %s.\r\n", psi->szDomain);
else
ErrorMessage(hwnd, TEXT("IIlsServer::SetDomain fails."), hr);

FreeLPTSTR(psi->szDomain);
psi->szDomain = NULL;

}

// BaseDN
//
if(psi->szBaseDN)
{
LPTSTR_to_BSTR(&bstrTemp, psi->szBaseDN);
hr = ps->SetBaseDN(bstrTemp);
SysFreeString(bstrTemp);

if(SUCCEEDED(hr))
MyTextOut("IIlsServer::SetBaseDN method, set to %s.\r\n", psi->szBaseDN);
else
ErrorMessage(hwnd, TEXT("IIlsServer::SetBaseDN fails."), hr);

FreeLPTSTR(psi->szBaseDN);
psi->szBaseDN = NULL;

}

// Time out
//
if(psi->dwTimeout != 0)
{
hr = ps->SetTimeout(psi->dwTimeout);

if(SUCCEEDED(hr))
MyTextOut("IIlsServer::SetTimeout, set to %d seconds.\r\n", psi->dwTimeout);
else
ErrorMessage(hwnd, TEXT("IIlsServer::SetTimeout fails."), hr);
}

}


USERS.CPP

//*************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       users.cpp
//  Content:    This file contains user object manipulation functions.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include "ilstest.h"
#include "notify.h"

//****************************************************************************
//
// HRESULT EnumUsers(HWND hwnd, BOOL fShort)
//
// This function starts the user information display dialogs
//
//****************************************************************************

HRESULT EnumUsers(HWND hwnd, BOOL fShort)
{
HRESULT hr = E_FAIL;
ULONG uReqID;
IIlsFilter *pFilter;
SERVERNODE *pNode;
POSITION pos;

// Use the global USER filter
pFilter = g_pUserFilter;

if(g_pServerList->IsEmpty())
MyTextOut(TEXT("No server objects defined.\r\n"));
else
{

// Do the enumerations for all server objects
//
pos = g_pServerList->GetHeadPosition();

while(pos)
{
pNode = (PSERVERNODE)g_pServerList->GetFromPosition(pos);

if (fShort) // we are only interested in user names
{
hr = g_pIls->EnumUserNames(pNode->pSrv, pFilter, NULL, &uReqID);

if (SUCCEEDED(hr))
{
MyTextOut(TEXT("IIls::EnumUserNames(%x) pending for server %s.\r\n"),
uReqID, pNode->szName);
}
else
{
ErrorMessage(hwnd, TEXT("IIls::EnumUserNames fails."), hr);
}
}
else // retrieve the entire user object
{
// set up the attributes object here so the
// desired extended attributes get returned.
IIlsAttributes *pAttrib = NULL;
hr = GetNMAttributesObject(g_pIls, &pAttrib);

if (SUCCEEDED(hr))
{

hr = g_pIls->EnumUsers(pNode->pSrv, pFilter, pAttrib, NULL, &uReqID);

if (SUCCEEDED(hr))
{
MyTextOut(TEXT("IIls::EnumUsers(%x) pending for server %s.\r\n"), uReqID, pNode->szName);
}
else
{
ErrorMessage(hwnd, TEXT("IIls::EnumUsers fails."), hr);
}
}
}
g_pServerList->GetNext(pos);
}
}
return hr;
};

//****************************************************************************
//
// HRESULT RegisterUser(HWND hwnd, IIlsUser *pu, IIlsServer *ps, LPTSTR szServerName)
//
// This function registers a specific user on all servers
//
//****************************************************************************

HRESULT RegisterUser(HWND hwnd, IIlsUser *pu, IIlsServer *ps, LPTSTR szServerName)
{
HRESULT hr = E_FAIL;
ULONG uReqID;
LPTSTR szUserName;
BSTR bstrUserName;

// Get the user ID
//
hr = pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrUserName);

if(SUCCEEDED(hr))
BSTR_to_LPTSTR(&szUserName, bstrUserName);
else
strcpy(szUserName, TEXT("<unknown>"));

// Register the user
//
hr = pu->Register(ps, &uReqID);

if (SUCCEEDED(hr))
{
MyTextOut(TEXT("IIlsUser::Register(%x) pending for user %s on server %s.\r\n"),
uReqID, szUserName, szServerName);
}
else
ErrorMessage(hwnd, TEXT("IIlsUser::Register fails."), hr);

return hr;
};

//****************************************************************************
//
// HRESULT UnregisterUser(HWND hwnd, IIlsUser *pu, BOOL fSilent)
//
// This function registers a specific user on all servers
//
//****************************************************************************

HRESULT UnregisterUser(HWND hwnd, IIlsUser *pu, BOOL fSilent)
{
HRESULT hr = E_FAIL;
ULONG uReqID;
LPTSTR szUserName;
BSTR bstrUserName;

// Get the user ID
//
hr = pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrUserName);

if(SUCCEEDED(hr))
BSTR_to_LPTSTR(&szUserName, bstrUserName);
else
strcpy(szUserName, TEXT("<unknown>"));

// Unregister the user
//
hr = pu->Unregister(&uReqID);

if (SUCCEEDED(hr))
{
MyTextOut(TEXT("IIlsUser::Unregister(%x) pending for user %s.\r\n"),
uReqID, szUserName);
}
else
{
if(!fSilent) // Do we really want error messages?
ErrorMessage(hwnd, TEXT("IIlsUser::Unregister fails."), hr);
}

return hr;
};

//****************************************************************************
//
// HRESULT OnILSUserAdviseUnadvise(HWND hwnd, PILSUSER pIlsUser, BOOL fAdvise)
//
// This function enables/disables the user object notifications.
//
//****************************************************************************

HRESULT OnILSUserAdviseUnadvise(HWND hwnd, PUSERNODE pUserNode, BOOL fAdvise)
{
IConnectionPointContainer *pcnpcnt;
HRESULT hr;

// Get the ConnectionPointContainer
//
hr = pUserNode->pu->QueryInterface(IID_IConnectionPointContainer,
(void**)&pcnpcnt);
if (FAILED(hr))
{
ErrorMessage(NULL, TEXT("Cannot query IID_IConnectionPointContainer: returns "),
hr);
}
else
{
IConnectionPoint *pcnp;

// Instead of an easy thing as follows
// Let's do the hard thing through enumeration
//
hr = pcnpcnt->FindConnectionPoint(IID_IIlsUserNotify, &pcnp);

if (hr != S_OK)
{
ErrorMessage(NULL, TEXT("Cannot find ConnectionPoint: returns "),
hr);
}
else
{
CIlsUserNotify *pun;

if (fAdvise)
{
DWORD dwCookie;

pun = new CIlsUserNotify (hwnd);
hr = pcnp->Advise((IUnknown *)pun, &dwCookie);

if (hr == S_OK)
{
MyTextOut(TEXT("User Advise succeeded.\r\n"));
pun->AddRef();
pun->SetCookie(dwCookie);
pUserNode->puNotify = pun;
};
}
else
{
pun = pUserNode->puNotify;

if (pun != NULL)
{
hr = pcnp->Unadvise(pun->GetCookie());

if (hr == S_OK)
{
MyTextOut(TEXT("User Unadvise succeeded.\r\n"));
pun->Release();
pUserNode->puNotify = NULL;
};
};
};
pcnp->Release();
};
pcnpcnt->Release();
};
return hr;
}

//****************************************************************************
//
// int DisplayIlsUserInfo(HWND hwnd, IIlsUser *pu)
//
// Displays the "easy" attributes for a user.
// for a more detailed look, use "Get User Info"
//****************************************************************************

int DisplayIlsUserInfo(HWND hwnd, IIlsUser *pu)
{
HRESULT hr = E_FAIL;

MyTextOut(" ======================== \r\n");

// The standard attributes...
DisplayStdAtt(pu, ILS_STDATTR_IP_ADDRESS,   "IP address  ", USER);
DisplayStdAtt(pu, ILS_STDATTR_EMAIL_NAME,   "Email name  ", USER);
DisplayStdAtt(pu, ILS_STDATTR_FIRST_NAME,   "First name  ", USER);
DisplayStdAtt(pu, ILS_STDATTR_LAST_NAME,    "Last name  ",  USER);
DisplayStdAtt(pu, ILS_STDATTR_CITY_NAME,    "City name  ",  USER);
DisplayStdAtt(pu, ILS_STDATTR_COUNTRY_NAME, "Ctry name ",   USER);
DisplayStdAtt(pu, ILS_STDATTR_COMMENT,      "Comment  ",    USER);
DisplayStdAtt(pu, ILS_STDATTR_APP_NAME,     "App name  ",   USER);
DisplayStdAtt(pu, ILS_STDATTR_APP_MIME_TYPE,"App Mime  ",   USER);

MyTextOut(" ---------------------- \r\n");

// The extended attributes...
DisplayExtAtt(pu, NM_RESTRICTION,"User type  ",   USER);
DisplayExtAtt(pu, NM_IN_A_CALL,"In a call  ",    USER);
DisplayExtAtt(pu, NM_SEND_AUDIO,"Video capable  ",   USER);
DisplayExtAtt(pu, NM_SEND_VIDEO,"Audio capable  ",   USER);

MyTextOut(" ======================== \r\n");

  return 1;
}

//****************************************************************************
//
// HRESULT DisplayIlsUsers(HWND hwnd, IEnumIlsUsers *pEnum)
//
// This function displays user information for all users
//
//****************************************************************************

HRESULT DisplayIlsUsers(HWND hwnd, IEnumIlsUsers *pEnum)
{
IIlsUser *pu;
BOOL fCancel = FALSE;

// For each user name...
//
while (!fCancel && (pEnum->Next(1, &pu, NULL) == NOERROR))
{
fCancel = (DisplayIlsUserInfo(hwnd, pu) == IDCANCEL);
pu->Release();
};
pEnum->Release();

return NOERROR;
}


USRINFO.CPP

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       usrinfo.cpp
//  Content:    This file contains the user info dialog functions.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include "ilstest.h"
#include "notify.h"
#include "appguid.h"

//****************************************************************************
//
// int NewUserDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog procedure for the user dialog box.
//
//****************************************************************************

int NewUserDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
USERINFO *pUserInfo;

pUserInfo = (USERINFO *)GetWindowLong(hwnd, DWL_USER);

switch (msg)
{
case WM_INITDIALOG:
{
SetWindowLong (hwnd, DWL_USER, lParam);
pUserInfo = (USERINFO *)lParam;

SetWindowText(GetDlgItem(hwnd, IDC_UI_FIRSTNAME), pUserInfo->szFirstName);
SetWindowText(GetDlgItem(hwnd, IDC_UI_LASTNAME),  pUserInfo->szLastName);
SetWindowText(GetDlgItem(hwnd, IDC_UI_CITY),      pUserInfo->szCity);
SetWindowText(GetDlgItem(hwnd, IDC_UI_COMMENT),   pUserInfo->szComment);
SetWindowText(GetDlgItem(hwnd, IDC_UI_IPADD),     pUserInfo->szIPAddress);
SetWindowText(GetDlgItem(hwnd, IDC_UI_EMAIL),     pUserInfo->szEmailName);
SetWindowText(GetDlgItem(hwnd, IDC_UI_COUNTRY),   pUserInfo->szCountry);

// Can't change email or country once set.
// See if we are creating or modifying
if(pUserInfo->fModify)
{
EnableWindow(GetDlgItem(hwnd, IDC_UI_EMAIL), FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_UI_COUNTRY), FALSE);
}

SetFocus (GetDlgItem(hwnd, IDC_UI_FIRSTNAME));
break;
}
case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
SaveUserInfo(hwnd, pUserInfo);

case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

default:
break;
};
break;
}

default:
break;
};

return 0;
}

//****************************************************************************
//
// HRESULT NewUserDialog(HWND hwnd)
//
// This function starts the user information dialog box.
//
//****************************************************************************

HRESULT NewUserDialog(HWND hwnd)
{
HRESULT hr = E_FAIL;
IIlsUser *pUser;
USERINFO UserInfo;
BSTR bstrAppID, bstrUserID;
USERNODE un;
PUSERNODE pun;

HMENU hMenu;

pun = &un;

// clear the UserInfo structure
strcpy(UserInfo.szFirstName,"");
strcpy(UserInfo.szLastName,"");
strcpy(UserInfo.szEmailName,"");
strcpy(UserInfo.szCity,"");
strcpy(UserInfo.szCountry,"");
strcpy(UserInfo.szComment,"");
strcpy(UserInfo.szIPAddress,"");

UserInfo.fModify = FALSE;

// Allow only one dialog at a time
//
hMenu = GetMenu(hwnd);
EnableMenuItem(hMenu, IDM_NEWUSER, MF_BYCOMMAND | MF_GRAYED);

if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_USERINFO),
hwnd, (DLGPROC)NewUserDlgProc, (LPARAM)&UserInfo) == IDOK)
{

if(!MyIsGoodString(UserInfo.szEmailName))
{
MyTextOut(TEXT("Email name is empty - no user object created.\r\n"));
hr = E_FAIL;
goto MyExit;
}
// Convert the necessary strings to BSTRs
//
LPTSTR_to_BSTR(&bstrUserID, UserInfo.szEmailName);
LPTSTR_to_BSTR(&bstrAppID, g_szAppID); // Use "ms-netmeeting" so NM can see this user

// Create the user object
hr = g_pIls->CreateUser(bstrUserID, bstrAppID, &pUser);

if(SUCCEEDED(hr))
{
MyTextOut(TEXT("IIls::CreateUser succeeded. %s\r\n"), UserInfo.szEmailName);

pun->pu = pUser;

// Hookup the notification callback
OnILSUserAdviseUnadvise(hwnd, pun, TRUE);

// Set the rest of the user info fields
//
SetUserInfo(hwnd, pun->pu, &UserInfo);

// Add to the list of users
NewUserNode(pun);

if(!g_pUserList->IsEmpty()) // The user list is not empty, enable menu items
{
EnableMenuItem(hMenu, IDM_DESTROYUSER, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_CLONEUSER, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_MODIFYUSER, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_CREATE_PROT, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_DESTROY_PROT, MF_BYCOMMAND | MF_ENABLED);
if(!g_pServerList->IsEmpty())  // only enable Reg/Unreg if there servers in the list
{
EnableMenuItem(hMenu, IDM_REGISTERUSER, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_UNREGISTERUSER, MF_BYCOMMAND | MF_ENABLED);
}
}

}
else
{
ErrorMessage(hwnd, TEXT("IIls::CreateUser fails."), hr);
}

// Clean up
SysFreeString(bstrAppID);
SysFreeString(bstrUserID);

}
else
{
// User cancelled
//
MyTextOut(TEXT("Create User cancelled.\r\n"));

};

MyExit:

EnableMenuItem(hMenu, IDM_NEWUSER, MF_BYCOMMAND | MF_ENABLED);
return hr;
};

//****************************************************************************
//
// HRESULT CloneUserDialog(HWND hwnd)
//
// This function starts the user information dialog box to copy a user.
//
//****************************************************************************

HRESULT CloneUserDialog(HWND hwnd)
{
HRESULT hr = E_FAIL;

BSTR bstrName;
USERNODE un;
PUSERNODE pun;
char szUserName[MAX_NAME];
LPTSTR psz;
POSITION pos;
USERNODE unClone;
PUSERNODE punClone;
HMENU hMenu;

IIlsUser *pUserClone;

pun = &un;
punClone = &unClone;

// Allow only one dialog at a time
//
hMenu = GetMenu(hwnd);
EnableMenuItem(hMenu, IDM_CLONEUSER, MF_BYCOMMAND | MF_GRAYED);

if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)UserListDlgProc, (LPARAM)&szUserName) == IDOK)
{
if(!MyIsGoodString(szUserName))
{
MyTextOut(TEXT("User name is empty.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// Find the specified user in the list
pos = g_pUserList->GetHeadPosition();

while(pos)
{
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);

hr = pun->pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrName);
BSTR_to_LPTSTR(&psz, bstrName);
if(strcmp(psz, szUserName) == 0)
{
FreeLPTSTR(psz);
break;
}
g_pUserList->GetNext(pos);
FreeLPTSTR(psz);
}
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);

// Clone the specified user object
hr = pun->pu->Clone(&pUserClone);

if(SUCCEEDED(hr))
{
// the user name is in psz and bstrName now
//
MyTextOut(TEXT("IIlsUser::Clone succeeded for user %s\r\n"), psz);

punClone->pu = pUserClone;

// Hookup the notification callback
OnILSUserAdviseUnadvise(hwnd, punClone, TRUE);

// Add to the list of users
NewUserNode(punClone);

}
else
{
ErrorMessage(hwnd, TEXT("IIlsUser::CloneUser fails."), hr);
}
}
else
{
// User cancelled
//
MyTextOut(TEXT("Clone User cancelled.\r\n"));

};
MyExit:

EnableMenuItem(hMenu, IDM_CLONEUSER, MF_BYCOMMAND | MF_ENABLED);
return hr;
};

//****************************************************************************
//
// void NewUserNode (PUSERNODE pUser)
//
// Create a new node and add to the linked list
//****************************************************************************
void NewUserNode(PUSERNODE pUserNode)
{
PUSERNODE pun;

pun = (PUSERNODE)LocalAlloc(LMEM_FIXED, sizeof(USERNODE));

pun->pu = pUserNode->pu;
pun->puNotify = pUserNode->puNotify;
pun->pProtList = new COBLIST;

g_pUserList->AddTail( (void *)pun );
}

//****************************************************************************
//
// void SetUserInfo (HWND hwnd, IlsUser *pUser, USERINFO *pUserInfo)
//
//****************************************************************************
void SetUserInfo(HWND hwnd, IIlsUser *pu, USERINFO *pUserInfo)
{
HRESULT hr = E_FAIL;
BSTR bstrFirstName = NULL, bstrLastName = NULL, bstrEmailName = NULL;
BSTR bstrCity = NULL, bstrCountry = NULL, bstrComment = NULL, bstrIPAddress = NULL;
GUID *pGUID;

pGUID = (GUID *)&GUID_ILSTest;

// FirstName
//
LPTSTR_to_BSTR(&bstrFirstName, pUserInfo->szFirstName);
hr = pu->SetStandardAttribute(ILS_STDATTR_FIRST_NAME, bstrFirstName);

if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::SetStandardAttribute fails."), hr);
goto MyExit;
}

// LastName
//
LPTSTR_to_BSTR(&bstrLastName, pUserInfo->szLastName);
hr = pu->SetStandardAttribute(ILS_STDATTR_LAST_NAME, bstrLastName);

if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::SetStandardAttribute fails."), hr);
goto MyExit;
}

// EmailName
//
LPTSTR_to_BSTR(&bstrEmailName, pUserInfo->szEmailName);
hr = pu->SetStandardAttribute(ILS_STDATTR_EMAIL_NAME, bstrEmailName);

if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::SetStandardAttribute fails."), hr);
goto MyExit;
}

// City/State
//
LPTSTR_to_BSTR(&bstrCity, pUserInfo->szCity);
hr = pu->SetStandardAttribute(ILS_STDATTR_CITY_NAME, bstrCity);

if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::SetStandardAttribute fails."), hr);
goto MyExit;
}

// Country
//
LPTSTR_to_BSTR(&bstrCountry, pUserInfo->szCountry);
hr = pu->SetStandardAttribute(ILS_STDATTR_COUNTRY_NAME, bstrCountry);

if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::SetStandardAttribute fails."), hr);
goto MyExit;
}

// IP_Address
//
LPTSTR_to_BSTR(&bstrIPAddress, pUserInfo->szIPAddress);
hr = pu->SetStandardAttribute(ILS_STDATTR_IP_ADDRESS, bstrIPAddress);

if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::SetStandardAttribute fails."), hr);
goto MyExit;
}

// Comment
//
LPTSTR_to_BSTR(&bstrComment, pUserInfo->szComment);
hr = pu->SetStandardAttribute(ILS_STDATTR_COMMENT, bstrComment);

if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::SetStandardAttribute fails."), hr);
goto MyExit;
}

// Make us visible to the rest of the world
//
hr = pu->SetVisible(0x01);

// Set the GUID for the user
//
hr = pu->SetGuid(pGUID);

MyExit:
// Cleanup
if(bstrFirstName)
SysFreeString(bstrFirstName);
if(bstrLastName)
SysFreeString(bstrLastName);
if(bstrEmailName)
SysFreeString(bstrEmailName);
if(bstrCity)
SysFreeString(bstrCity);
if(bstrCountry)
SysFreeString(bstrCountry);
if(bstrComment)
SysFreeString(bstrComment);
if(bstrIPAddress)
SysFreeString(bstrIPAddress);

}

//****************************************************************************
//
// void GeUserInfo (HWND hwnd, IlsUser *pUser, USERINFO *pUserInfo)
//
//****************************************************************************
void GetUserInfo(HWND hwnd, IIlsUser *pu, USERINFO *pUserInfo)
{
HRESULT hr = E_FAIL;
BSTR bstrName = NULL;
GUID *pGUID;
LPTSTR szTemp;

pGUID = (GUID *)&GUID_ILSTest;

// FirstName
hr = pu->GetStandardAttribute(ILS_STDATTR_FIRST_NAME, &bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);
goto MyExit;
}
BSTR_to_LPTSTR(&szTemp, bstrName);
strcpy(pUserInfo->szFirstName, szTemp);

if(bstrName)
SysFreeString(bstrName);

// LastName
hr = pu->GetStandardAttribute(ILS_STDATTR_LAST_NAME, &bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);
goto MyExit;
}
BSTR_to_LPTSTR(&szTemp, bstrName);
strcpy(pUserInfo->szLastName, szTemp);

if(bstrName)
SysFreeString(bstrName);

// EMailName
hr = pu->GetStandardAttribute(ILS_STDATTR_EMAIL_NAME, &bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);
goto MyExit;
}
BSTR_to_LPTSTR(&szTemp, bstrName);
strcpy(pUserInfo->szEmailName, szTemp);

if(bstrName)
SysFreeString(bstrName);

// City
hr = pu->GetStandardAttribute(ILS_STDATTR_CITY_NAME, &bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);
goto MyExit;
}
BSTR_to_LPTSTR(&szTemp, bstrName);
strcpy(pUserInfo->szCity, szTemp);

if(bstrName)
SysFreeString(bstrName);

// Country
hr = pu->GetStandardAttribute(ILS_STDATTR_COUNTRY_NAME, &bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);
goto MyExit;
}
BSTR_to_LPTSTR(&szTemp, bstrName);
strcpy(pUserInfo->szCountry, szTemp);

if(bstrName)
SysFreeString(bstrName);

// IP Address
hr = pu->GetStandardAttribute(ILS_STDATTR_IP_ADDRESS, &bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);
goto MyExit;
}
BSTR_to_LPTSTR(&szTemp, bstrName);
strcpy(pUserInfo->szIPAddress, szTemp);

if(bstrName)
SysFreeString(bstrName);

// Comment
hr = pu->GetStandardAttribute(ILS_STDATTR_COMMENT, &bstrName);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::GetStandardAttribute fails."), hr);
goto MyExit;
}
BSTR_to_LPTSTR(&szTemp, bstrName);
strcpy(pUserInfo->szComment, szTemp);

if(bstrName)
SysFreeString(bstrName);
MyExit:

return;
}

//****************************************************************************
//
// void SaveUserInfo(HWND hwnd, PUSERINFO pUserInfo)
//
// This function saves the changes in the user information.
// to the USERINFO structure
//
//****************************************************************************
void SaveUserInfo(HWND hwnd, PUSERINFO pUserInfo)
{
HWND    hCtrl;
TCHAR   szText[MAX_NAME];

// Get each information field
//
hCtrl = GetDlgItem(hwnd, IDC_UI_FIRSTNAME);
Edit_GetText(hCtrl, (LPTSTR)szText, ARRAYSIZE(szText));
strcpy(pUserInfo->szFirstName, szText);

hCtrl = GetDlgItem(hwnd, IDC_UI_LASTNAME);
Edit_GetText(hCtrl, (LPTSTR)szText, ARRAYSIZE(szText));
strcpy(pUserInfo->szLastName, szText);

hCtrl = GetDlgItem(hwnd, IDC_UI_EMAIL);
Edit_GetText(hCtrl, (LPTSTR)szText, ARRAYSIZE(szText));
strcpy(pUserInfo->szEmailName, szText);

hCtrl = GetDlgItem(hwnd, IDC_UI_CITY);
Edit_GetText(hCtrl, (LPTSTR)szText, ARRAYSIZE(szText));
strcpy(pUserInfo->szCity, szText);

hCtrl = GetDlgItem(hwnd, IDC_UI_COUNTRY);
Edit_GetText(hCtrl, (LPTSTR)szText, ARRAYSIZE(szText));
strcpy(pUserInfo->szCountry, szText);

hCtrl = GetDlgItem(hwnd, IDC_UI_COMMENT);
Edit_GetText(hCtrl, (LPTSTR)szText, ARRAYSIZE(szText));
strcpy(pUserInfo->szComment, szText);

hCtrl = GetDlgItem(hwnd, IDC_UI_IPADD);
Edit_GetText(hCtrl, (LPTSTR)szText, ARRAYSIZE(szText));
strcpy(pUserInfo->szIPAddress, szText);

}

//****************************************************************************
//
// HRESULT RemoveUserDialog(HWND hwnd)
//
// This function starts the dialog box for
// destroying a user object.
//
//****************************************************************************
HRESULT RemoveUserDialog(HWND hwnd)
{
HRESULT hr = E_FAIL;
HMENU hMenu;
char szUserName[MAX_NAME];
char *psz;
POSITION pos;
USERNODE *pun;
BSTR bstrName;

szUserName[0] = NULL;

if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)UserListDlgProc, (LPARAM)szUserName) == IDOK)
{
if(!MyIsGoodString(szUserName))
{
MyTextOut(TEXT("User name is empty.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// match name to usernode
pos = g_pUserList->GetHeadPosition();

while(pos)
{
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);

hr = pun->pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrName);
BSTR_to_LPTSTR(&psz, bstrName);
if(strcmp(psz, szUserName) == 0)
{
FreeLPTSTR(psz);
break;
}
g_pUserList->GetNext(pos);
FreeLPTSTR(psz);
}

// Disconnect the notification callbacks
//
OnILSUserAdviseUnadvise(hwnd, pun, FALSE);

// free user object
pun->pu->Release();
pun->pu = NULL;

// free the protocol list
delete pun->pProtList;

// remove node from list
g_pUserList->RemoveAt(pos);

LocalFree(pun);
pun = NULL;

// if the list is now empty, disable the menu item
if(g_pUserList->IsEmpty())
{
hMenu = GetMenu(hwnd);
EnableMenuItem(hMenu, IDM_DESTROYUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_REGISTERUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_UNREGISTERUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_CLONEUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_MODIFYUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_CREATE_PROT, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_DESTROY_PROT, MF_BYCOMMAND | MF_GRAYED);

}
MyTextOut(TEXT("User %s removed.\r\n"), szUserName);
}
else
{
//User cancelled
MyTextOut(TEXT("Destroy user cancelled.\r\n"));
}
MyExit:

return hr;
}

//****************************************************************************
//
// int UserListDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog proc that displays the current user objects
// and returns the chosen item.
//
//****************************************************************************
int UserListDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

USERNODE *pun;
LPTSTR pszTemp;
POSITION pos;
HWND hwndList;
char *psz;
int iIndex;
HRESULT hr = E_FAIL;
BSTR bstrName;

switch (msg)
{
case WM_INITDIALOG:
{
hwndList = GetDlgItem(hwnd, IDC_LIST);
SetWindowLong (hwnd, DWL_USER, lParam);

// Walk the list of servers and fill the listbox with names
pos = g_pUserList->GetHeadPosition();
SetWindowText(hwnd, "User List");

while(pos)
{
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);
hr = pun->pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrName);
BSTR_to_LPTSTR(&psz, bstrName);
SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)psz);
g_pUserList->GetNext(pos);
FreeLPTSTR(psz);
}

SetFocus(GetDlgItem(hwnd, IDC_LIST));
break;
}

case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
hwndList = GetDlgItem(hwnd, IDC_LIST);

// return the name of the user chosen
pszTemp = (LPTSTR)GetWindowLong(hwnd, DWL_USER);
iIndex = SendMessage(hwndList, LB_GETCURSEL, 0,0);
if(iIndex == -1) // default to the first item in the list if none selected
iIndex = 0;
iIndex = SendMessage(hwndList, LB_GETTEXT, iIndex, (LPARAM)pszTemp);

// Fall through to end the dialog!!

case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

default:
break;
};
break;
}

default:
break;
};

return 0;
}

//****************************************************************************
//
// HRESULT RegisterUserDialog(HWND hwnd, BOOL fRegister)
//
// This function starts the dialog box for
// registering or unregistering a user object on a server.
//
//****************************************************************************
HRESULT RegUnregUserDialog(HWND hwnd, BOOL fRegister)
{
HRESULT hr = E_FAIL;
char szUserName[MAX_NAME];
char szServerName[MAX_NAME];
char *psz;
POSITION pos;
BSTR bstrName;
PUSERNODE pun;
PSERVERNODE psn;

// Ask which user to reg/unreg
//
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)UserListDlgProc, (LPARAM)szUserName) == IDOK)
{

if(!MyIsGoodString(szUserName))
{
MyTextOut(TEXT("User name is empty.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// match name to usernode
pos = g_pUserList->GetHeadPosition();

while(pos)
{
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);

hr = pun->pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrName);
BSTR_to_LPTSTR(&psz, bstrName);
if(strcmp(psz, szUserName) == 0)
{
FreeLPTSTR(psz);
break;
}
g_pUserList->GetNext(pos);
FreeLPTSTR(psz);
}

if(fRegister)
{
// Got the user, now ask which server to use
//
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)ServerListDlgProc, (LPARAM)szServerName) == IDOK)
{

if(!MyIsGoodString(szServerName))
{
MyTextOut(TEXT("Server name is empty.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// match name to servernode
pos = g_pServerList->GetHeadPosition();

while(pos)
{
psn = (PSERVERNODE)g_pServerList->GetFromPosition(pos);
if(strcmp(psn->szName, (LPCTSTR)szServerName) == 0)
break;
g_pServerList->GetNext(pos);
}
}

RegisterUser(hwnd, pun->pu, psn->pSrv, psn->szName);
}
else
{
UnregisterUser(hwnd, pun->pu, FALSE);
}

}
else
{
//User cancelled
MyTextOut(TEXT("Reg/Unreg cancelled.\r\n"));
}

MyExit:

return hr;
}

//****************************************************************************
//
// HRESULT GetUserInfoDialog(HWND hwnd)
//
// This function displays user information for all users
//
//****************************************************************************
HRESULT GetUserInfoDialog(HWND hwnd)
{
ULONG uReqID;
BOOL fCancel = FALSE;
HRESULT hr = E_FAIL;
char szUserID[MAX_NAME];
char szServerName[MAX_NAME];
POSITION pos;
PSERVERNODE psn;
BSTR bstrUserID = NULL, bstrAppID = NULL, bstrProtID = NULL;

// Ask which user to query
//
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_USERNAME),
hwnd, (DLGPROC)UserNameDlgProc, (LPARAM)szUserID) == IDOK)
{

if(!MyIsGoodString(szUserID))
{
MyTextOut(TEXT("User ID is empty.\r\n"));
hr = E_FAIL;
goto MyExit;
}

LPTSTR_to_BSTR(&bstrUserID, (LPCTSTR)szUserID);

// Got the user, now ask which server to use
//
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)ServerListDlgProc, (LPARAM)szServerName) == IDOK)
{

if(!MyIsGoodString(szServerName))
{
MyTextOut(TEXT("Server name is empty.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// match name to servernode
pos = g_pServerList->GetHeadPosition();

while(pos)
{
psn = (PSERVERNODE)g_pServerList->GetFromPosition(pos);
if(strcmp(psn->szName, (LPCTSTR)szServerName) == 0)
break;
g_pServerList->GetNext(pos);
}

LPTSTR_to_BSTR(&bstrProtID, g_szProtID); // TODO - allow user to specify protocol ID here

LPTSTR_to_BSTR(&bstrAppID, g_szAppID); // Use "ms-netmeeting"

// set up the attributes object here so the
// desired extended attributes get returned.
IIlsAttributes *pAttrib = NULL;
hr = GetNMAttributesObject(g_pIls, &pAttrib);

hr = g_pIls->GetUser(psn->pSrv, bstrUserID, bstrAppID, bstrProtID, pAttrib, NULL, &uReqID);

if (SUCCEEDED(hr))
{
MyTextOut(TEXT("IIlsMain::GetUser(%x) pending for user %s on server %s.\r\n"),
uReqID, szUserID, szServerName);
}
else
ErrorMessage(hwnd, TEXT("IIlsMain::GetUser fails."), hr);

}
else
{
//User cancelled
MyTextOut(TEXT("Server List cancelled.\r\n"));
}
}
else
{
//User cancelled
MyTextOut(TEXT("Get User cancelled.\r\n"));
}

MyExit:
// Cleanup
//
if(bstrUserID)
SysFreeString(bstrUserID);
if(bstrAppID)
SysFreeString(bstrAppID);
if(bstrProtID)
SysFreeString(bstrProtID);

return hr;
}

//****************************************************************************
//
// int UserNameDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog proc that displays the current user objects
// and returns the chosen item.
//
//****************************************************************************
int UserNameDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LPTSTR pszTemp;
HRESULT hr = E_FAIL;

switch (msg)
{
case WM_INITDIALOG:
{
SetWindowLong (hwnd, DWL_USER, lParam);
SetFocus(GetDlgItem(hwnd, IDC_USER_NAME));
break;
}

case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
// return the name of the user chosen
pszTemp = (LPTSTR)GetWindowLong(hwnd, DWL_USER);
GetDlgItemText(hwnd, IDC_USER_NAME, pszTemp, MAX_NAME);

// Fall through to end the dialog!!

case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

default:
break;
};
break;
}

default:
break;
};

return 0;
}

//****************************************************************************
//
// HRESULT DisplayIlsUserDialog(HWND hwnd, IIlsUser *pu)
//
// This function displays user information for all users
//
//****************************************************************************
HRESULT DisplayIlsUserDialog(HWND hwnd, IIlsUser *pu)
{

HRESULT hr = E_FAIL;

// Display the specified user details
//
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_ILSUSER),
hwnd, (DLGPROC)UserDlgProc, (LPARAM)pu) != IDOK)
{
//User cancelled
MyTextOut(TEXT("Get User cancelled.\r\n"));
}

return hr;
}

//****************************************************************************
//
// int UserDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// This function is the dialog proc that displays the current user objects
// and returns the chosen item.
//
//****************************************************************************
int UserDlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
IIlsUser *pu;

switch (msg)
{
case WM_INITDIALOG:
{
pu = (IIlsUser *)lParam;
SetWindowLong (hwnd, DWL_USER, lParam);
InitIlsUser(hwnd, pu);
break;
}

case WM_COMMAND:
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
case IDCANCEL:
EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam));
break;

case IDC_UU_PROT:  // Display the protocols in the main window
{
IIlsUser *pu;

pu = (IIlsUser *)GetWindowLong(hwnd, DWL_USER);
EnumUserProtocols(hwnd, pu);
break;
}
case IDC_UU_EXATTR:  // display the extended attributes in the main window
{
IIlsUser *pu;

pu = (IIlsUser *)GetWindowLong(hwnd, DWL_USER);
EnumUserExAttributes(hwnd, pu);
break;
}

case IDC_UU_GETPROT:  // Display the protocol information in the main window
{
IIlsUser *pu;

pu = (IIlsUser *)GetWindowLong(hwnd, DWL_USER);
DisplayProtocol(hwnd, pu);
break;
}

case WM_IT_ENUM_NAMES_RESULT:
DisplayIlsNames(hwnd, (IEnumIlsNames *)lParam, "protocols");
break;

default:
break;
};
break;
}

default:
break;
};

return 0;
}

//****************************************************************************
//
// void InitIlsUser(HWND hwnd, IIlsUser *pu)
//
// Retrieve the standard attributes and display them in a dialog.
//
//****************************************************************************
void InitIlsUser(HWND hwnd, IIlsUser *pu)
{

LPTSTR szID, szFirstName, szLastName, szEmailName, szCityName, szCountryName;
LPTSTR szComment, szAppID, szAppMimeType, szIPAdd;
TCHAR szVisible[MAX_NAME];
TCHAR szGuid[MAX_NAME];
HRESULT hr = E_FAIL;
BSTR bstrTemp;
TCHAR szText[512];
DWORD fVisible;
GUID guid;

pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrTemp);
BSTR_to_LPTSTR(&szID, bstrTemp);
SysFreeString(bstrTemp);

pu->GetStandardAttribute(ILS_STDATTR_IP_ADDRESS, &bstrTemp);
BSTR_to_LPTSTR(&szIPAdd, bstrTemp);
SysFreeString(bstrTemp);

pu->GetStandardAttribute(ILS_STDATTR_EMAIL_NAME, &bstrTemp);
BSTR_to_LPTSTR(&szEmailName, bstrTemp);
SysFreeString(bstrTemp);

pu->GetStandardAttribute(ILS_STDATTR_FIRST_NAME, &bstrTemp);

BSTR_to_LPTSTR(&szFirstName, bstrTemp);
SysFreeString(bstrTemp);

pu->GetStandardAttribute(ILS_STDATTR_LAST_NAME, &bstrTemp);
BSTR_to_LPTSTR(&szLastName, bstrTemp);
SysFreeString(bstrTemp);

pu->GetStandardAttribute(ILS_STDATTR_CITY_NAME, &bstrTemp);
BSTR_to_LPTSTR(&szCityName, bstrTemp);
SysFreeString(bstrTemp);

pu->GetStandardAttribute(ILS_STDATTR_COUNTRY_NAME, &bstrTemp);
BSTR_to_LPTSTR(&szCountryName, bstrTemp);
SysFreeString(bstrTemp);

pu->GetStandardAttribute(ILS_STDATTR_COMMENT, &bstrTemp);
BSTR_to_LPTSTR(&szComment, bstrTemp);
SysFreeString(bstrTemp);

pu->GetStandardAttribute(ILS_STDATTR_APP_NAME, &bstrTemp);
BSTR_to_LPTSTR(&szAppID, bstrTemp);
SysFreeString(bstrTemp);

pu->GetStandardAttribute(ILS_STDATTR_APP_MIME_TYPE, &bstrTemp);
BSTR_to_LPTSTR(&szAppMimeType, bstrTemp);
SysFreeString(bstrTemp);

// English interpretation of Visibile flags
hr = pu->GetVisible(&fVisible);
if(SUCCEEDED(hr))
strcpy(szVisible, (fVisible == 0x01 ? TEXT("visible") : TEXT("hidden")));
else
strcpy(szVisible, TEXT("ERROR"));

hr = pu->GetGuid(&guid);
if(SUCCEEDED(hr))
StringFromGuid(guid, szGuid);
else
strcpy(szGuid, TEXT("ERROR"));

wsprintf(szText,
TEXT("ID:\t\t%s\nFirst:\t\t%s\nLast:\t\t%s\nE-mail:\t\t%s\nCity:\t\t%s\nCountry:\t\t%s\nComment:\t%s\nIP:\t\t%s\nAppID:\t\t%s\nMime Type:\t%s\nVisibility:\t\t%s\nGUID:\t%s"),
szID, szFirstName, szLastName, szEmailName, szCityName, szCountryName,
szComment, szIPAdd, szAppID, szAppMimeType, szVisible, szGuid);

SetDlgItemText(hwnd, IDC_UU_INFO, szText);

FreeLPTSTR(szID);
FreeLPTSTR(szIPAdd);
FreeLPTSTR(szEmailName);
FreeLPTSTR(szFirstName);
FreeLPTSTR(szLastName);
FreeLPTSTR(szCityName);
FreeLPTSTR(szCountryName);
FreeLPTSTR(szComment);
FreeLPTSTR(szAppID);
FreeLPTSTR(szAppMimeType);

}

//****************************************************************************
//
// HRESULT UnregisterAllUsers(HWND hwnd)
//
// This function removes all our users from the servers
//
//****************************************************************************

void UnregisterAllUsers(HWND hwnd)
{
POSITION pos;
PUSERNODE pun;

// Walk the list of users
pos = g_pUserList->GetHeadPosition();

while(pos)
{
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);
UnregisterUser(hwnd, pun->pu, TRUE);

g_pUserList->GetNext(pos);
}
return;
};

//****************************************************************************
//
// HRESULT ModifyUserDialog(HWND hwnd)
//
// This function starts the user information modify dialog box.
//
//****************************************************************************

HRESULT ModifyUserDialog(HWND hwnd)
{
HRESULT hr = E_FAIL;
USERINFO UserInfo;
BSTR bstrName = NULL;
LPTSTR psz = NULL;
char szUserName[MAX_NAME];
USERNODE un;
PUSERNODE pun;
POSITION pos;
ULONG uReqID;
HMENU hMenu;

pun = &un;

// Allow only one dialog at a time
//
hMenu = GetMenu(hwnd);
EnableMenuItem(hMenu, IDM_NEWUSER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_MODIFYUSER, MF_BYCOMMAND | MF_GRAYED);

// Ask which user to query
//
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_LIST),
hwnd, (DLGPROC)UserListDlgProc, (LPARAM)&szUserName) == IDOK)
{

// Check the user name
if(!MyIsGoodString(szUserName))
{
MyTextOut(TEXT("User Name is invalid.\r\n"));
hr = E_FAIL;
goto MyExit;
}

// Find the specified user in the list
pos = g_pUserList->GetHeadPosition();

while(pos)
{
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);

hr = pun->pu->GetStandardAttribute(ILS_STDATTR_USER_ID, &bstrName);
BSTR_to_LPTSTR(&psz, bstrName);
if(strcmp(psz, szUserName) == 0)
{
FreeLPTSTR(psz);
break;
}
g_pUserList->GetNext(pos);
FreeLPTSTR(psz);
}
pun = (PUSERNODE)g_pUserList->GetFromPosition(pos);

// Get the user info stuff here...
GetUserInfo(hwnd, pun->pu, &UserInfo);

// Mark this for modification
UserInfo.fModify = TRUE;

// Show the user info dialog box and allow the user make changes
if(DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_DLG_USERINFO),
hwnd, (DLGPROC)NewUserDlgProc, (LPARAM)&UserInfo) == IDOK)
{
// For now we assume that if they've pressed OK, they actually changed something

// Can't change EMAIL_NAME or COUNTRY_NAME -- see SDK documentation
//
//ChangeAttribute(pun->pu, ILS_STDATTR_EMAIL_NAME,   UserInfo.szEmailName);
//ChangeAttribute(pun->pu, ILS_STDATTR_COUNTRY_NAME, UserInfo.szCountry);

ChangeAttribute(pun->pu, ILS_STDATTR_FIRST_NAME,   UserInfo.szFirstName);
ChangeAttribute(pun->pu, ILS_STDATTR_LAST_NAME,    UserInfo.szLastName);
ChangeAttribute(pun->pu, ILS_STDATTR_CITY_NAME,    UserInfo.szCity);
ChangeAttribute(pun->pu, ILS_STDATTR_COMMENT,      UserInfo.szComment);
ChangeAttribute(pun->pu, ILS_STDATTR_IP_ADDRESS,   UserInfo.szIPAddress);

// Now we've got the local user changed, write it to the server
hr = pun->pu->Update(&uReqID);
if(FAILED(hr))
{
ErrorMessage(hwnd, TEXT("IIlsUser::Update fails."), hr);
goto MyExit;
}
}
else
{
// User cancelled
//
MyTextOut(TEXT("Modify User cancelled.\r\n"));
};
}
MyExit:
EnableMenuItem(hMenu, IDM_NEWUSER, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_MODIFYUSER, MF_BYCOMMAND | MF_ENABLED);
return hr;
};

//****************************************************************************
//
// HRESULT ChangeAttribute(ILS_STD_ATTR_NAME attr, LPTSTR psz)
//
// This function removes all our users from the servers
//
//****************************************************************************
HRESULT ChangeAttribute(IIlsUser *pu, ILS_STD_ATTR_NAME attr, LPTSTR psz)
{
BSTR bstr;
HRESULT hr = E_FAIL;

if(MyIsGoodString(psz))
{
LPTSTR_to_BSTR(&bstr, psz);
hr = pu->SetStandardAttribute(attr, bstr);

if(SUCCEEDED(hr))
SysFreeString(bstr);
}

return hr;
}


UTILS.CPP

//****************************************************************************
//
//  SKIPPY! sample for Microsoft NetMeeting SDK
//
//  File:       utils.cpp
//  Content:    This file contains utility functions.
//
//  Copyright (c) Microsoft Corporation 1997
//  All rights reserved
//
//****************************************************************************

#include "ilstest.h"

//
int g_nOpen, g_nClosed, g_nDocument;

//****************************************************************************
//
// void _cdecl MyTextOut (LPCTSTR szFmt, ...)
//
// This function displays the textual message in the application log box.
//
//****************************************************************************

void _cdecl MyTextOut (LPCTSTR szFmt, ...)
{
HWND hEdit;
va_list VarArg;
TCHAR   szText[256];

va_start(VarArg,szFmt);
wvsprintf(szText, szFmt, VarArg);

hEdit = GetDlgItem(g_hwndMain, IDC_ACT_EDIT);
Edit_SetSel(hEdit, 0xFFFF, 0xFFFF);
Edit_ReplaceSel(hEdit, szText);
return;
}

//****************************************************************************
//
// void ErrorMessage(HWND hwnd, LPCTSTR szPrefix, HRESULT hr)
//
// This function prompts the end-user with the error message box.
//
//****************************************************************************

void ErrorMessage(HWND hwnd, LPCTSTR szPrefix, HRESULT hr)
{
TCHAR   szMessage[256];

if (HRESULT_FACILITY(hr) == FACILITY_WINDOWS)
hr = HRESULT_CODE(hr);

wsprintf (szMessage, TEXT("\r\n%s: returns %s (%lx)\r\n"), szPrefix, GetErrorString(hr), hr);
MessageBox(hwnd, szMessage, TEXT("Function return"), MB_OK | MB_ICONERROR);
return;
}

//****************************************************************************
//
// LPTSTR AllocLPTSTR (ULONG cb)
//
// This function allocates a LPTSTR
//
//****************************************************************************

LPTSTR AllocLPTSTR (ULONG cb)
{
LPTSTR psz;

psz = (LPTSTR)LocalAlloc(LMEM_FIXED, cb*sizeof(TCHAR));
return psz;
}

//****************************************************************************
//
// HRESULT FreeLPTSTR (LPTSTR psz)
//
// This function frees the LPTSTR
//
//****************************************************************************

HRESULT FreeLPTSTR (LPTSTR psz)
{
HLOCAL hRet;
hRet = LocalFree(psz);
return( hRet == NULL ? S_OK : E_FAIL);
}

//****************************************************************************
//
// HRESULT SetLPTSTR (LPTSTR *ppszName, LPCTSTR pszUserName)
//
// This function creates a name buffer
//
//****************************************************************************

HRESULT SetLPTSTR (LPTSTR *ppszName, LPCTSTR pszUserName)
{
LPTSTR pszNew;

// Allocate a name buffer
//
pszNew = AllocLPTSTR(lstrlen(pszUserName)+1);

// It would be bad if the name buffer cannot be allocated,
// but we can live with it.
//
if (pszNew != NULL)
{
lstrcpy(pszNew, pszUserName);

// Free the old name
//
if (*ppszName != NULL)
{
FreeLPTSTR(*ppszName);
};
*ppszName = pszNew;
return S_OK;
}
else
{
return E_OUTOFMEMORY;
};
}

//****************************************************************************
//
// HRESULT LPTSTR_to_BSTR (BSTR *pbstr, LPCTSTR psz)
//
//****************************************************************************

HRESULT LPTSTR_to_BSTR (BSTR *pbstr, LPCTSTR psz)
{
#ifndef UNICODE

BSTR bstr;
int i;
HRESULT hr;

// compute the length of the required BSTR
//
i =  MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
if (i <= 0)
{
return E_UNEXPECTED;
};

// allocate the widestr, +1 for terminating null
//
bstr = SysAllocStringLen(NULL, i-1); // SysAllocStringLen adds 1

if (bstr != NULL)
{
MultiByteToWideChar(CP_ACP, 0, psz, -1, (LPWSTR)bstr, i);
((LPWSTR)bstr)[i - 1] = 0;
*pbstr = bstr;
hr = S_OK;
}
else
{
hr = E_OUTOFMEMORY;
};
return hr;

#else

BSTR bstr;

bstr = SysAllocString(psz);

if (bstr != NULL)
{
*pbstr = bstr;
return S_OK;
}
else
{
return E_OUTOFMEMORY;
};

#endif // UNICODE
}

//****************************************************************************
//
// HRESULT BSTR_to_LPTSTR (LPTSTR *ppsz, BSTR bstr)
//
//****************************************************************************

HRESULT BSTR_to_LPTSTR (LPTSTR *ppsz, BSTR bstr)
{
#ifndef UNICODE

LPTSTR psz;
int i;
HRESULT hr;

// Whoa... handle the NULL string first.
//
if(bstr == NULL)  // if bstr is NULL we return E_UNEXPECTED and pass a default string
{
psz = AllocLPTSTR(lstrlen("")+1);
if(psz != NULL)
{
strcpy(psz, "");
*ppsz = psz;
}
return E_UNEXPECTED;
}

// compute the length of the required BSTR
//
i =  WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, NULL, 0, NULL, NULL);
if (i <= 0)
{
return E_UNEXPECTED;
};

// allocate the widestr, +1 for terminating null
//
psz = AllocLPTSTR(i);

if (psz != NULL)
{
WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, psz, i, NULL, NULL);
*ppsz = psz;
hr = S_OK;
}
else
{
hr = E_OUTOFMEMORY;
};
return hr;

#else

LPTSTR psz = NULL;
HRESULT hr;

hr = SetName(&psz, (LPTSTR)bstr);

if (hr == S_OK)
{
*ppsz = psz;
};
return hr;

#endif // UNICODE
}

//****************************************************************************
//
// HRESULT DisplayIlsNames(HWND hwnd, IEnumIlsNames *pEnum, LPTSTR szString)
//
// This function displays enumerated text in the output
//
//****************************************************************************

HRESULT DisplayIlsNames(HWND hwnd, IEnumIlsNames *pEnum, LPTSTR szString)
{
BSTR bstrName;
LPTSTR pszName;
ULONG iIndex = 1;

// For each name
//
MyTextOut(TEXT("******** Found the following %s ************\r\n"), szString);

while (pEnum->Next(1, &bstrName, NULL) == NOERROR)
{
BSTR_to_LPTSTR(&pszName, bstrName);
MyTextOut(TEXT("\t%d> %s\r\n"), iIndex, pszName);
iIndex++;
SysFreeString(bstrName);
FreeLPTSTR(pszName);
};
pEnum->Release();

MyTextOut(TEXT("*****************************************************\r\n"));
return NOERROR;
}

//****************************************************************************
//
// HRESULT DisplayAboutBox(HWND hwnd)
//
// This function displays the application's about box
//
//****************************************************************************

VOID DisplayAboutBox(HWND hwnd)
{
char szVersion[MAX_PATH];

LoadString(ghInstance, IDS_VERSION, szVersion, sizeof(szVersion));

ShellAbout(hwnd, szVersion, NULL, LoadIcon(ghInstance, MAKEINTRESOURCE(IDI_APP_ICON)));
}

// STRING_CASE turns a #define into a case statement
#define STRING_CASE(val)  case val: pcsz = #val; break

//****************************************************************************
//
// int GetErrorString(HRESULT hr)
//
// This function returns a pointer a string describing the error condition
// The error strings are lifted from ULS.H
//
//****************************************************************************
LPCTSTR GetErrorString(HRESULT hr)
{
LPCTSTR pcsz;
static CHAR sz[MAX_PATH];

switch (hr)
{
default:
wsprintf(sz, "(unknown)");
pcsz = sz;
break;

STRING_CASE(S_OK);
STRING_CASE(S_FALSE);

// The one success code

STRING_CASE(ILS_S_SERVER_MAY_NOT_SUPPORT);

// overloaded error codes

STRING_CASE(ILS_E_FAIL);
STRING_CASE(ILS_E_POINTER);
STRING_CASE(ILS_E_HANDLE);
STRING_CASE(ILS_E_ABORT);
STRING_CASE(ILS_E_ACCESS_DENIED);
STRING_CASE(ILS_E_NOT_IMPL);
STRING_CASE(ILS_E_NO_INTERFACE);
STRING_CASE(ILS_E_MEMORY);
STRING_CASE(ILS_E_PARAMETER);

//  General error codes

STRING_CASE(ILS_E_SERVER_SERVICE);
STRING_CASE(ILS_E_SERVER_NAME);
STRING_CASE(ILS_E_TIMEOUT);
STRING_CASE(ILS_E_BIND);
STRING_CASE(ILS_E_THREAD);
STRING_CASE(ILS_E_SERVER_EXEC);
STRING_CASE(ILS_E_WINSOCK);
STRING_CASE(ILS_E_NO_MORE);
STRING_CASE(ILS_E_NOT_INITIALIZED);
STRING_CASE(ILS_E_NOTIFY_ID);
STRING_CASE(ILS_E_NO_SUCH_OBJECT);
STRING_CASE(ILS_E_NEED_RELOGON);
STRING_CASE(ILS_E_NAME_CONFLICTS);
STRING_CASE(ILS_E_NETWORK_DOWN);
STRING_CASE(ILS_E_FILTER_STRING);
STRING_CASE(ILS_E_FILTER_OBJECT);
STRING_CASE(ILS_E_FILTER_TYPE);
STRING_CASE(ILS_E_ACCESS_CONTROL);
STRING_CASE(ILS_E_NOT_REGISTERED);
STRING_CASE(ILS_E_NO_SUCH_MEMBER);
STRING_CASE(ILS_E_NO_WRITE_ACCESS);
STRING_CASE(ILS_E_ALREADY_REGISTERED);

//  LDAP specific error codes

STRING_CASE(ILS_E_LDAP_OPERATIONS_ERROR);
STRING_CASE(ILS_E_LDAP_PROTOCOL_ERROR);
STRING_CASE(ILS_E_LDAP_TIMELIMIT_EXCEEDED);
STRING_CASE(ILS_E_LDAP_SIZELIMIT_EXCEEDED);
STRING_CASE(ILS_E_LDAP_COMPARE_FALSE);
STRING_CASE(ILS_E_LDAP_COMPARE_TRUE);
STRING_CASE(ILS_E_LDAP_AUTH_METHOD_NOT_SUPPORTED);
STRING_CASE(ILS_E_LDAP_STRONG_AUTH_REQUIRED);
STRING_CASE(ILS_E_LDAP_REFERRAL_V2);
STRING_CASE(ILS_E_LDAP_PARTIAL_RESULTS);
STRING_CASE(ILS_E_LDAP_REFERRAL);
STRING_CASE(ILS_E_LDAP_ADMIN_LIMIT_EXCEEDED);
STRING_CASE(ILS_E_LDAP_UNAVAILABLE_CRIT_EXTENSION);

STRING_CASE(ILS_E_LDAP_NO_SUCH_ATTRIBUTE);
STRING_CASE(ILS_E_LDAP_UNDEFINED_TYPE);
STRING_CASE(ILS_E_LDAP_INAPPROPRIATE_MATCHING);
STRING_CASE(ILS_E_LDAP_CONSTRAINT_VIOLATION);
STRING_CASE(ILS_E_LDAP_ATTRIBUTE_OR_VALUE_EXISTS);
STRING_CASE(ILS_E_LDAP_INVALID_SYNTAX);

STRING_CASE(ILS_E_LDAP_ALIAS_PROBLEM);
STRING_CASE(ILS_E_LDAP_INVALID_DN_SYNTAX);
STRING_CASE(ILS_E_LDAP_IS_LEAF);
STRING_CASE(ILS_E_LDAP_ALIAS_DEREF_PROBLEM);

STRING_CASE(ILS_E_LDAP_INAPPROPRIATE_AUTH);
STRING_CASE(ILS_E_LDAP_INVALID_CREDENTIALS);
STRING_CASE(ILS_E_LDAP_INSUFFICIENT_RIGHTS);
STRING_CASE(ILS_E_LDAP_BUSY);
STRING_CASE(ILS_E_LDAP_UNAVAILABLE);
STRING_CASE(ILS_E_LDAP_UNWILLING_TO_PERFORM);
STRING_CASE(ILS_E_LDAP_LOOP_DETECT);

STRING_CASE(ILS_E_LDAP_NAMING_VIOLATION);
STRING_CASE(ILS_E_LDAP_OBJECT_CLASS_VIOLATION);
STRING_CASE(ILS_E_LDAP_NOT_ALLOWED_ON_NONLEAF);
STRING_CASE(ILS_E_LDAP_NOT_ALLOWED_ON_RDN);
STRING_CASE(ILS_E_LDAP_NO_OBJECT_CLASS_MODS);
STRING_CASE(ILS_E_LDAP_RESULTS_TOO_LARGE);
STRING_CASE(ILS_E_LDAP_AFFECTS_MULTIPLE_DSAS);

STRING_CASE(ILS_E_LDAP_OTHER);
STRING_CASE(ILS_E_LDAP_SERVER_DOWN);
STRING_CASE(ILS_E_LDAP_LOCAL_ERROR);
STRING_CASE(ILS_E_LDAP_ENCODING_ERROR);
STRING_CASE(ILS_E_LDAP_DECODING_ERROR);
STRING_CASE(ILS_E_LDAP_TIMEOUT);
STRING_CASE(ILS_E_LDAP_AUTH_UNKNOWN);
STRING_CASE(ILS_E_LDAP_FILTER_ERROR);
STRING_CASE(ILS_E_LDAP_USER_CANCELLED);
STRING_CASE(ILS_E_LDAP_NO_MEMORY);
}
return pcsz;
}

//**********************************************************
// DisplayStdAttr - display all the standard attribute information
//
//
//**********************************************************
void DisplayStdAtt(void *pobj, ILS_STD_ATTR_NAME stdAttr, LPTSTR szDesc, OBJECT_TYPE ot)
{
HRESULT hr = E_FAIL;
BSTR bstr = NULL;
LPTSTR sz = NULL;

switch(ot)
{
case USER:  // user objects
{
IIlsUser *pObject = (IIlsUser *)pobj;
hr = pObject->GetStandardAttribute(stdAttr, &bstr);
break;
}
case PROTOCOL:  // protocol objects
{
IIlsProtocol *pObject = (IIlsProtocol *)pobj;
hr = pObject->GetStandardAttribute(stdAttr, &bstr);
break;
}
default:
MyTextOut(TEXT("ERROR - hit default in DumpStdAttr, no type?"));
}

if(SUCCEEDED(hr))
{
// retrieved the correct object, print out its value
hr = BSTR_to_LPTSTR(&sz, bstr);
if(!sz)
MyTextOut(TEXT("%s    = <NULL>\r\n"), szDesc);
else
MyTextOut(TEXT("%s    = %s\r\n"), szDesc, sz);
}
else
{
MyTextOut(TEXT("ERROR retrieving %s, returns %s.\r\n"), szDesc, GetErrorString(hr));
}

if(sz)
FreeLPTSTR(sz);

}

//**********************************************************
// DisplayExtAttr - display the requested extended attribute
// information
//
//
//**********************************************************
void DisplayExtAtt(void *pobj, DWORD dwExtAttr, LPTSTR szDesc, OBJECT_TYPE ot)
{
HRESULT hr = E_FAIL;
BSTR bstr = NULL, bstrName = NULL;
LPTSTR sz = NULL;
TCHAR szProperty[100];

switch(ot)
{
case USER:  // user objects
{
IIlsUser *pObject = (IIlsUser *)pobj;
wsprintf(szProperty, TEXT("%lu"), PROP_TAG(PT_STRING8, dwExtAttr));
LPTSTR_to_BSTR(&bstrName, szProperty);
hr = pObject->GetExtendedAttribute(bstrName, &bstr);
break;
}
case PROTOCOL:  // protocol objects
{
IIlsProtocol *pObject = (IIlsProtocol *)pobj;
wsprintf(szProperty, TEXT("%lu"), PROP_TAG(PT_STRING8, dwExtAttr));
LPTSTR_to_BSTR(&bstrName, szProperty);
hr = pObject->GetExtendedAttribute(bstrName, &bstr);
break;
}
default:
MyTextOut(TEXT("ERROR - hit default in DumpExtAttr, no type?"));
}

if(SUCCEEDED(hr))
{
// retrieved the correct object, convert back to a string
hr = BSTR_to_LPTSTR(&sz, bstr);

if(!sz)  // retrieved an empty string for some reason !!!
MyTextOut(TEXT("%s    = <NULL>\r\n"), szDesc);
else
if( dwExtAttr == NM_RESTRICTION)
{
CHAR szType[MAX_PATH];

switch (sz[0])
{
case '1':
wsprintf(szType, "Business");
break;
case '2':
wsprintf(szType, "Personal");
break;
case '4':
wsprintf(szType, "Adult");
break;
default:
wsprintf(szType, "(unknown)");

}

MyTextOut(TEXT("%s    = %s\r\n"), szDesc, szType);

}
else
MyTextOut(TEXT("%s    = %s\r\n"), szDesc,
(lstrcmp(sz, "1") == 0) ? "yes" : "no");
}
else
{
MyTextOut(TEXT("ERROR retrieving %s, returns %s.\r\n"), szDesc, GetErrorString(hr));
}

if(sz)
FreeLPTSTR(sz);

}

//****************************************************************************
//
// int StringFromGuid (REFIID riid, LPTSTR pszBuf)
//
// This function returns a string from a CLSID or GUID
//
//****************************************************************************

int StringFromGuid (REFIID   riid, LPTSTR pszBuf)
{
return wsprintf(pszBuf,
TEXT("{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
riid.Data1, riid.Data2, riid.Data3, riid.Data4[0], riid.Data4[1],
riid.Data4[2], riid.Data4[3], riid.Data4[4], riid.Data4[5],
riid.Data4[6], riid.Data4[7]);
}

//****************************************************************************
//
// int GetRadioButton(HWND hdlg, int idrFirst, int idrLast);
//
// return the id of the radio button in a given range
//
//****************************************************************************
int GetRadioButton(HWND hdlg, int idrFirst, int idrLast)
{
int id;
for (id = idrFirst; id<=idrLast; id++)
{
if(IsDlgButtonChecked(hdlg, id))
return id;
}

return idrFirst;
}

//****************************************************************************
//
// BOOL MyIsGoodString(LPTSTR psz)
//
// determine if a string is NULL or empty
//
//****************************************************************************
BOOL MyIsGoodString(LPTSTR psz)
{
if((psz == NULL) || 0 == strcmp(psz, ""))
return FALSE;
else
return TRUE;

}

//****************************************************************************
//
// HRESULT GetNMAttributesObject( IIlsAttributes **ppa )
//
// return a pointer to an Attribute object with NetMeeting
// ExtendedAttributes specified.
//
//****************************************************************************
HRESULT GetNMAttributesObject( IIlsMain *pIls, IIlsAttributes **ppa )
{
HRESULT hr = E_FAIL;
IIlsAttributes *pAttrib = NULL;

hr = pIls->CreateAttributes(ILS_ATTRTYPE_NAME_VALUE, &pAttrib);

if (SUCCEEDED(hr))
{

// got an Attributes object, now fill it in.
BSTR bstrEmpty = NULL, bstrName = NULL;
TCHAR szProperty[100];

LPTSTR_to_BSTR(&bstrEmpty, "");

wsprintf(szProperty, TEXT("%lu"), PROP_TAG(PT_STRING8, NM_IN_A_CALL));  // 400
LPTSTR_to_BSTR(&bstrName, szProperty);
hr = pAttrib->SetAttribute(bstrName, bstrEmpty);

wsprintf(szProperty, TEXT("%lu"), PROP_TAG(PT_STRING8, NM_RESTRICTION));  // 600
LPTSTR_to_BSTR(&bstrName, szProperty);
hr = pAttrib->SetAttribute(bstrName, bstrEmpty);

wsprintf(szProperty, TEXT("%lu"), PROP_TAG(PT_STRING8, NM_SEND_AUDIO));  // 501
LPTSTR_to_BSTR(&bstrName, szProperty);
hr = pAttrib->SetAttribute(bstrName, bstrEmpty);

wsprintf(szProperty, TEXT("%lu"), PROP_TAG(PT_STRING8, NM_SEND_VIDEO));  // 503
LPTSTR_to_BSTR(&bstrName, szProperty);
hr = pAttrib->SetAttribute(bstrName, bstrEmpty);

}
*ppa = pAttrib;

return hr;
}


README.TXT

------------------------------------------------------------------
         Microsoft NetMeeting SDK 2.1 Vbcard Readme File
                           November 1997
------------------------------------------------------------------

             (c) Copyright Microsoft Corporation, 1996, 1997

CONTENTS
========

OVERVIEW OF THE VBCARD SAMPLE
BUILDING THE VBCARD SAMPLE

OVERVIEW OF THE VBCARD SAMPLE
=============================

Vbcard is a data conferencing application that uses the ActiveX
Control. It is written in Visual Basic. Features include the exchange
of bitmaps to display pictures of the participants to each other, and
the exchange of business card information.

BUILDING THE VBCARD SAMPLE
=============================

Building the sample Visual Basic application requires Visual Basic Version
5.0 or later.

Open the project file (Vbcard.vbp) as a Microsoft Visual Basic project, and
build as usual.

Note: You must have Microsoft NetMeeting installed in order to run this
example.


MODULE1.BAS

Attribute VB_Name = "Module1"
'****************************************************************************
'
'  Module:     VBCARD.EXE
'  File:       Module1.frm
'  Content:    Implementation of the VbCard processes
'
'  Copyright (c) Microsoft Corporation 1995-1997
'
' 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.
'****************************************************************************

Option Explicit

Public Const BIZCARD_DATA_SIZE As Integer = 568
Const SIZE_OFFSET As Integer = 0
Const NAME_OFFSET As Integer = 4
Const TITLE_OFFSET As Integer = 84
Const COMPANY_OFFSET As Integer = 164
Const ADDRESS_OFFSET As Integer = 244
Const PHONE_OFFSET As Integer = 324
Const FAX_OFFSET As Integer = 404
Const EMAIL_OFFSET As Integer = 484
Const IMAGESIZE_OFFSET As Integer = 564
Const STRING_LENGTH As Integer = 80

' Beta2 NetMeeting breaks up data transfers into 4K packets
Const MAX_DATA_PACKET As Integer = 4096

' of course this is a bad thing
Const PictureDir As String = "c:\"

Private Type LongBytes
    b0 As Byte
    b1 As Byte
    b2 As Byte
    b3 As Byte
End Type

Private Type LongType
    l As Long
End Type

Public Type BizCardInfo
    Name As String
    Title As String
    Company As String
    Address As String
    Phone As String
    Fax As String
    EMail As String
    PhotoFile As String
End Type

Public Type MemberInfo
    Name As String
    Card As BizCardInfo
    Photo As Picture
    ImageSize As Long
    FileOpen As Boolean
End Type

Public MyCard As BizCardInfo
Public ActiveCard As BizCardInfo
Public MemberInfoArray(15) As MemberInfo
Public ActiveIndex As Integer

Public Sub ReadBizCard( _
    App As String, _
    Name As String, _
    Card As BizCardInfo)

    Card.Name = GetSetting(App, Name, "Name", "")
    Card.Title = GetSetting(App, Name, "Title", "")
    Card.Company = GetSetting(App, Name, "Company", "")
    Card.Address = GetSetting(App, Name, "Address", "")
    Card.Phone = GetSetting(App, Name, "Phone", "")
    Card.Fax = GetSetting(App, Name, "Fax", "")
    Card.EMail = GetSetting(App, Name, "EMail", "")
    Card.PhotoFile = GetSetting(App, Name, "Photo", "")
End Sub

Public Sub WriteBizCard( _
    App As String, _
    Name As String, _
    Card As BizCardInfo)

    SaveSetting App, Name, "Name", Card.Name
    SaveSetting App, Name, "Title", Card.Title
    SaveSetting App, Name, "Company", Card.Company
    SaveSetting App, Name, "Address", Card.Address
    SaveSetting App, Name, "Phone", Card.Phone
    SaveSetting App, Name, "Fax", Card.Fax
    SaveSetting App, Name, "EMail", Card.EMail
    SaveSetting App, Name, "Photo", Card.PhotoFile
End Sub

Private Sub UpdateActivePhoto()
    VbCard.Photo.Picture = MemberInfoArray(ActiveIndex).Photo
End Sub

Public Sub UpdateActiveIndex(Name As String)
    ActiveIndex = GetMemberIndex(Name)
    If ActiveIndex < 0 Then
        ActiveIndex = 0
    End If

    UpdateActivePhoto
End Sub

Public Function GetActiveCard() As BizCardInfo
    If ActiveIndex >= 0 Then
        GetActiveCard = MemberInfoArray(ActiveIndex).Card
    End If
End Function

Private Sub InitMember(Index As Integer)
    MemberInfoArray(Index).Name = ""
    MemberInfoArray(Index).FileOpen = False
    Set MemberInfoArray(Index).Photo = VbCard.Unknown.Picture
End Sub

Public Sub InitMemberArray()
    Dim Index As Integer

    For Index = 0 To 15
        InitMember Index
    Next
End Sub

Public Function GetMemberIndex(Name As String) As Integer
    Dim Index As Integer

    GetMemberIndex = -1
    For Index = 0 To 15
        If MemberInfoArray(Index).Name = Name Then
            GetMemberIndex = Index
            Exit For
        End If
    Next
End Function

Public Sub DeleteMember(Member As IConfMemberX)
    Dim Index As Integer

    Index = GetMemberIndex(Member.Name)
    If Index >= 0 Then
        InitMember Index
    End If
End Sub

Public Sub AddMember(Member As IConfMemberX)
    Dim Index As Integer

    Index = GetMemberIndex(Member.Name)
    If Index >= 0 Then
        ' already a member
        Exit Sub
    End If

    ' find next empty slot
    Index = GetMemberIndex("")
    If Index >= 0 Then
        MemberInfoArray(Index).Name = Member.Name

        ' If adding self, need to load user's picture
        If Member.IsSelf Then
            Dim MyPict As Picture

            MemberInfoArray(Index).Card = MyCard
            On Error Resume Next
            Set MyPict = LoadPicture(MyCard.PhotoFile)
            If Not Err Then
                Set MemberInfoArray(Index).Photo = MyPict
            End If
        End If
    End If
End Sub

Public Sub ReceiveImage(Name As String, DataTransfer As IConfDataTransferX)
    Dim Size As Integer
    Dim Index As Integer
    Dim FileNumber As Integer

    ' Create the member's picture from the transferred data
    ' Allows for Beta2 NetMeeting 4K packet breakup

    Index = GetMemberIndex(Name)
    FileNumber = Index + 1

    ' Make sure that the temporary file is open
    If Not MemberInfoArray(Index).FileOpen Then
        On Error Resume Next
        MemberInfoArray(Index).Card.PhotoFile = PictureDir & Name & ".bmp"
        Open MemberInfoArray(Index).Card.PhotoFile For Binary As #FileNumber
        If Err Then
            Exit Sub
        End If
        MemberInfoArray(Index).FileOpen = True
    End If

    Size = DataTransfer.BytesTransferred
    ReDim buf(1 To Size) As Byte
    buf = DataTransfer.Array(1)
    Put #FileNumber, , buf

    ' Assumes complete if the packet is not 4K
    ' This will cause problems if the picture is an exact multiple of 4K
    If Size <> MAX_DATA_PACKET Then
        'Close the file
        Close #FileNumber
        MemberInfoArray(Index).FileOpen = False

        ' Load the picture
        Set MemberInfoArray(Index).Photo = _
            LoadPicture(MemberInfoArray(Index).Card.PhotoFile)

        ' Delete the file
        Kill MemberInfoArray(Index).Card.PhotoFile

        ' If this is the current member, update the picture
        If Index = ActiveIndex Then
            UpdateActivePhoto
        End If
    End If
End Sub

Private Function getVbString( _
    Buffer() As Byte, _
    first As Integer, _
    length As Integer)

    ' Convert a fixed length array of Bytes into a VB string
    ' (each Byte is an 8 bit ASCII character)

    Dim i As Integer
    Dim j As Integer
    ReDim szText(1 To length) As Byte

    j = first
    For i = 1 To length
        szText(i) = Buffer(j)
        j = j + 1
    Next
    getVbString = StrConv(szText, vbUnicode)
End Function

Public Sub ReceiveBizCard( _
    Name As String, _
    DataTransfer As IConfDataTransferX)

    ' Copy transfered data into the member's BizCardInfo structure

    Dim Index

    Index = GetMemberIndex(Name)
    If Index >= 0 Then
        Dim Data() As Byte
        Data = DataTransfer.Array(1)

        MemberInfoArray(Index).Card.Name = getVbString(Data, _
            NAME_OFFSET, STRING_LENGTH)
        MemberInfoArray(Index).Card.Title = getVbString(Data, _
            TITLE_OFFSET, STRING_LENGTH)
        MemberInfoArray(Index).Card.Company = getVbString(Data, _
            COMPANY_OFFSET, STRING_LENGTH)
        MemberInfoArray(Index).Card.Address = getVbString(Data, _
           ADDRESS_OFFSET, STRING_LENGTH)
        MemberInfoArray(Index).Card.Phone = getVbString(Data, _
            PHONE_OFFSET, STRING_LENGTH)
        MemberInfoArray(Index).Card.Fax = getVbString(Data, _
            FAX_OFFSET, STRING_LENGTH)
        MemberInfoArray(Index).Card.EMail = getVbString(Data, _
            EMAIL_OFFSET, STRING_LENGTH)
    End If

End Sub

Private Sub setVbString( _
    strText As String, _
    Buffer() As Byte, _
    first As Integer, _
    length As Integer)

    ' Convert a VB String into an fixed length array of Bytes
    ' (each Byte is an 8 bit ASCII character)

    Dim i As Integer
    Dim j As Integer
    Dim szText() As Byte

    szText = StrConv(strText, vbFromUnicode)

    ' just in case szText is ""
    Buffer(first) = 0

    j = first
    For i = LBound(szText) To UBound(szText)
        Buffer(j) = szText(i)
        j = j + 1
    Next
End Sub

Public Sub BizCardToBytes(ByRef Card As BizCardInfo, _
    Buffer() As Byte, _
    Size As Integer)

    ' Convert a BizCardInfo structure into an array of Bytes

    Dim bSize As LongBytes
    Dim lSize As LongType
    ReDim Buffer(BIZCARD_DATA_SIZE - 1) As Byte

    lSize.l = BIZCARD_DATA_SIZE
    LSet bSize = lSize

    Buffer(SIZE_OFFSET) = bSize.b0
    Buffer(SIZE_OFFSET + 1) = bSize.b1
    Buffer(SIZE_OFFSET + 2) = bSize.b2
    Buffer(SIZE_OFFSET + 3) = bSize.b3

    lSize.l = Size
    LSet bSize = lSize

    Buffer(IMAGESIZE_OFFSET) = bSize.b0
    Buffer(IMAGESIZE_OFFSET + 1) = bSize.b1
    Buffer(IMAGESIZE_OFFSET + 2) = bSize.b2
    Buffer(IMAGESIZE_OFFSET + 3) = bSize.b3

    setVbString Card.Name, Buffer, _
        NAME_OFFSET, STRING_LENGTH
    setVbString Card.Title, Buffer, _
        TITLE_OFFSET, STRING_LENGTH
    setVbString Card.Company, Buffer, _
        COMPANY_OFFSET, STRING_LENGTH
    setVbString Card.Address, Buffer, _
        ADDRESS_OFFSET, STRING_LENGTH
    setVbString Card.Phone, Buffer, _
        PHONE_OFFSET, STRING_LENGTH
    setVbString Card.Fax, Buffer, _
        FAX_OFFSET, STRING_LENGTH
    setVbString Card.EMail, Buffer, _
        EMAIL_OFFSET, STRING_LENGTH
End Sub

Public Function PictureToBytes( _
    FileName As String, _
    Buffer() As Byte) As Integer
    Dim FileNumber As Integer

    ' Convert a bitmap file into an array of Bytes

    FileNumber = 99
    On Error Resume Next
    Open FileName For Binary As #FileNumber
    If Err Then
        ' check for empty file name here
        If FileName = "" Then
            MsgBox "Picture file name is empty"
        Else
            MsgBox "Unable to open " & FileName
        End If
        PictureToBytes = 0
    Else
        Dim Size As Integer

        Size = LOF(FileNumber)
        ReDim Buffer(1 To Size) As Byte
        Get #FileNumber, , Buffer()
        Close #FileNumber
        PictureToBytes = Size
    End If
End Function


OPTIONS.FRM

VERSION 5.00
Object = "{F9043C88-F6F2-101A-A3C9-08002B2F49FB}#1.1#0"; "COMDLG32.OCX"
Object = "{6B7E6392-850A-101B-AFC0-4210102A8DA7}#1.2#0"; "COMCTL32.OCX"
Begin VB.Form Options
   Caption         =   "Options"
   ClientHeight    =   5640
   ClientLeft      =   7470
   ClientTop       =   2940
   ClientWidth     =   5025
   LinkTopic       =   "Form1"
   PaletteMode     =   1  'UseZOrder
   ScaleHeight     =   5640
   ScaleWidth      =   5025
   Begin VB.PictureBox Conference
      BorderStyle     =   0  'None
      Height          =   4455
      Left            =   240
      ScaleHeight     =   4455
      ScaleWidth      =   4575
      TabIndex        =   21
      Top             =   480
      Width           =   4575
      Begin VB.Frame Frame1
         Caption         =   "Call with:"
         Height          =   1215
         Left            =   120
         TabIndex        =   22
         Top             =   120
         Width           =   4335
         Begin VB.OptionButton ConfData
            Caption         =   "&Data only"
            Height          =   255
            Left            =   240
            TabIndex        =   24
            Top             =   720
            Value           =   -1  'True
            Width           =   1575
         End
         Begin VB.OptionButton ConfAll
            Caption         =   "&Voice and data"
            Height          =   255
            Left            =   240
            TabIndex        =   23
            Top             =   360
            Width           =   2055
         End
      End
   End
   Begin VB.PictureBox BizCard
      BorderStyle     =   0  'None
      Height          =   4455
      Left            =   240
      ScaleHeight     =   4455
      ScaleWidth      =   4575
      TabIndex        =   3
      Top             =   480
      Width           =   4575
      Begin VB.TextBox Photo
         Height          =   300
         Left            =   960
         TabIndex        =   12
         Top             =   3480
         Width           =   3495
      End
      Begin VB.TextBox EMail
         Height          =   300
         Left            =   960
         TabIndex        =   11
         Top             =   3000
         Width           =   3495
      End
      Begin VB.TextBox Fax
         Height          =   300
         Left            =   960
         TabIndex        =   10
         Top             =   2520
         Width           =   3495
      End
      Begin VB.TextBox Phone
         Height          =   300
         Left            =   960
         TabIndex        =   9
         Top             =   2040
         Width           =   3495
      End
      Begin VB.TextBox Address
         Height          =   300
         Left            =   960
         TabIndex        =   8
         Top             =   1560
         Width           =   3495
      End
      Begin VB.TextBox Company
         Height          =   300
         Left            =   960
         TabIndex        =   7
         Top             =   1080
         Width           =   3495
      End
      Begin VB.TextBox Title
         Height          =   300
         Left            =   960
         TabIndex        =   6
         Top             =   600
         Width           =   3495
      End
      Begin VB.TextBox BizName
         Height          =   300
         Left            =   960
         TabIndex        =   5
         Top             =   120
         Width           =   3495
      End
      Begin VB.CommandButton Browse
         Caption         =   "Browse..."
         Height          =   375
         Left            =   3240
         TabIndex        =   4
         Top             =   3960
         Width           =   1215
      End
      Begin VB.Label Label8
         Caption         =   "P&hoto"
         Height          =   255
         Left            =   120
         TabIndex        =   20
         Top             =   3480
         Width           =   855
      End
      Begin VB.Label Label7
         Caption         =   "&E-Mail:"
         Height          =   255
         Left            =   120
         TabIndex        =   19
         Top             =   3000
         Width           =   855
      End
      Begin VB.Label Label6
         Caption         =   "&Fax:"
         Height          =   255
         Left            =   120
         TabIndex        =   18
         Top             =   2520
         Width           =   855
      End
      Begin VB.Label Label5
         Caption         =   "&Phone:"
         Height          =   255
         Left            =   120
         TabIndex        =   17
         Top             =   2040
         Width           =   855
      End
      Begin VB.Label Label4
         Caption         =   "&Address:"
         Height          =   255
         Left            =   120
         TabIndex        =   16
         Top             =   1560
         Width           =   855
      End
      Begin VB.Label Label3
         Caption         =   "&Company:"
         Height          =   255
         Left            =   120
         TabIndex        =   15
         Top             =   1080
         Width           =   855
      End
      Begin VB.Label Label2
         Caption         =   "&Title:"
         Height          =   255
         Left            =   120
         TabIndex        =   14
         Top             =   600
         Width           =   855
      End
      Begin VB.Label Label1
         Caption         =   "&Name:"
         Height          =   255
         Left            =   120
         TabIndex        =   13
         Top             =   120
         Width           =   855
      End
   End
   Begin VB.CommandButton Cancel
      Cancel          =   -1  'True
      Caption         =   "Cancel"
      Height          =   375
      Left            =   3840
      TabIndex        =   2
      Top             =   5160
      Width           =   1095
   End
   Begin VB.CommandButton OK
      Caption         =   "OK"
      Default         =   -1  'True
      Height          =   375
      Left            =   2640
      TabIndex        =   1
      Top             =   5160
      Width           =   1095
   End
   Begin MSComDlg.CommonDialog CommonDialog1
      Left            =   360
      Top             =   5160
      _ExtentX        =   847
      _ExtentY        =   847
      _Version        =   327680
      DefaultExt      =   "*.bmp"
      DialogTitle     =   "Open"
      Filter          =   "Pictures (*.bmp)|*.bmp|All Files (*.*)|*.*"
      Flags           =   5
   End
   Begin ComctlLib.TabStrip TabStrip1
      Height          =   4935
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   4815
      _ExtentX        =   8493
      _ExtentY        =   8705
      _Version        =   327680
      BeginProperty Tabs {0713E432-850A-101B-AFC0-4210102A8DA7}
         NumTabs         =   2
         BeginProperty Tab1 {0713F341-850A-101B-AFC0-4210102A8DA7}
            Caption         =   "Conference"
            Key             =   ""
            Object.Tag             =   ""
            Object.ToolTipText     =   ""
            ImageVarType    =   2
         EndProperty
         BeginProperty Tab2 {0713F341-850A-101B-AFC0-4210102A8DA7}
            Caption         =   "Business Card"
            Key             =   ""
            Object.Tag             =   ""
            Object.ToolTipText     =   ""
            ImageVarType    =   2
         EndProperty
      EndProperty
      MouseIcon       =   "Options.frx":0000
   End
End
Attribute VB_Name = "Options"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'****************************************************************************
'
'  Module:     VBCARD.EXE
'  File:       Options.frm
'  Content:    Implementation of the Options form
'
'  Copyright (c) Microsoft Corporation 1995-1997
'
' 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.
'****************************************************************************

Option Explicit

Private Sub Browse_Click()
    ' Display a FileOpen dialog so that user can select picture
    On Error Resume Next
    CommonDialog1.ShowOpen
    If Err <> cdlCancel Then
        Photo = CommonDialog1.FileName
    End If
End Sub

Private Sub Cancel_Click()
    ' Close form window
    Unload Options
End Sub

Private Sub Form_Load()
    ' Initialize the form
    BizName.text = MyCard.Name
    Title.text = MyCard.Title
    Company.text = MyCard.Company
    Address.text = MyCard.Address
    Phone.text = MyCard.Phone
    Fax.text = MyCard.Fax
    EMail.text = MyCard.EMail
    Photo.text = MyCard.PhotoFile
End Sub

Private Sub OK_Click()
    ' Read and store all values on sheets
    MyCard.Name = BizName.text
    MyCard.Title = Title.text
    MyCard.Company = Company.text
    MyCard.Address = Address.text
    MyCard.Phone = Phone.text
    MyCard.Fax = Fax.text
    MyCard.EMail = EMail.text
    MyCard.PhotoFile = Photo.text

    ' Close form window
    Unload Options
End Sub

Private Sub TabStrip1_Click()
    ' Display the selected property page
    If TabStrip1.SelectedItem.Index = 1 Then
        Conference.ZOrder 0
    Else
        BizCard.ZOrder 0
    End If
End Sub


BIZCARD.FRM

VERSION 5.00
Begin VB.Form BizCard
   BorderStyle     =   3  'Fixed Dialog
   Caption         =   "Business Card"
   ClientHeight    =   4080
   ClientLeft      =   7425
   ClientTop       =   5805
   ClientWidth     =   4620
   LinkTopic       =   "Form1"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   PaletteMode     =   1  'UseZOrder
   ScaleHeight     =   4080
   ScaleWidth      =   4620
   ShowInTaskbar   =   0   'False
   Begin VB.CommandButton Close
      Caption         =   "Close"
      Default         =   -1  'True
      Height          =   375
      Left            =   3240
      TabIndex        =   14
      Top             =   3480
      Width           =   1215
   End
   Begin VB.TextBox BizName
      Enabled         =   0   'False
      Height          =   300
      Left            =   960
      TabIndex        =   6
      Top             =   120
      Width           =   3495
   End
   Begin VB.TextBox Title
      Enabled         =   0   'False
      Height          =   300
      Left            =   960
      TabIndex        =   5
      Top             =   600
      Width           =   3495
   End
   Begin VB.TextBox Company
      Enabled         =   0   'False
      Height          =   300
      Left            =   960
      TabIndex        =   4
      Top             =   1080
      Width           =   3495
   End
   Begin VB.TextBox Address
      Enabled         =   0   'False
      Height          =   300
      Left            =   960
      TabIndex        =   3
      Top             =   1560
      Width           =   3495
   End
   Begin VB.TextBox Phone
      Enabled         =   0   'False
      Height          =   300
      Left            =   960
      TabIndex        =   2
      Top             =   2040
      Width           =   3495
   End
   Begin VB.TextBox Fax
      Enabled         =   0   'False
      Height          =   300
      Left            =   960
      TabIndex        =   1
      Top             =   2520
      Width           =   3495
   End
   Begin VB.TextBox EMail
      Enabled         =   0   'False
      Height          =   300
      Left            =   960
      TabIndex        =   0
      Top             =   3000
      Width           =   3495
   End
   Begin VB.Label Label1
      Caption         =   "&Name:"
      Height          =   255
      Left            =   120
      TabIndex        =   13
      Top             =   120
      Width           =   855
   End
   Begin VB.Label Label2
      Caption         =   "&Title:"
      Height          =   255
      Left            =   120
      TabIndex        =   12
      Top             =   600
      Width           =   855
   End
   Begin VB.Label Label3
      Caption         =   "&Company:"
      Height          =   255
      Left            =   120
      TabIndex        =   11
      Top             =   1080
      Width           =   855
   End
   Begin VB.Label Label4
      Caption         =   "&Address:"
      Height          =   255
      Left            =   120
      TabIndex        =   10
      Top             =   1560
      Width           =   855
   End
   Begin VB.Label Label5
      Caption         =   "&Phone:"
      Height          =   255
      Left            =   120
      TabIndex        =   9
      Top             =   2040
      Width           =   855
   End
   Begin VB.Label Label6
      Caption         =   "&Fax:"
      Height          =   255
      Left            =   120
      TabIndex        =   8
      Top             =   2520
      Width           =   855
   End
   Begin VB.Label Label7
      Caption         =   "&E-Mail:"
      Height          =   255
      Left            =   120
      TabIndex        =   7
      Top             =   3000
      Width           =   855
   End
End
Attribute VB_Name = "BizCard"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'****************************************************************************
'
'  Module:     VBCARD.EXE
'  File:       BizCard.frm
'  Content:    Implementation of the BizCard form
'
'  Copyright (c) Microsoft Corporation 1995-1997
'
' 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.
'****************************************************************************

Option Explicit

Private Sub Close_Click()
    Unload BizCard
End Sub

Private Sub Form_Load()
    Dim Card As BizCardInfo

    Card = GetActiveCard
    BizName.text = Card.Name
    Title.text = Card.Title
    Company.text = Card.Company
    Address.text = Card.Address
    Phone.text = Card.Phone
    Fax.text = Card.Fax
    EMail.text = Card.EMail
End Sub


VBCARD.FRM

VERSION 5.00
Object = "{F9043C88-F6F2-101A-A3C9-08002B2F49FB}#1.1#0"; "COMDLG32.OCX"
Object = "{6B7E6392-850A-101B-AFC0-4210102A8DA7}#1.2#0"; "COMCTL32.OCX"
Object = "{53D22821-D7E8-11CF-AD0A-0080C7137C82}#1.0#0"; "xmsconf.ocx"
Begin VB.Form VbCard
   BorderStyle     =   3  'Fixed Dialog
   Caption         =   "Conference Card"
   ClientHeight    =   4125
   ClientLeft      =   1515
   ClientTop       =   3375
   ClientWidth     =   6375
   Icon            =   "VbCard.frx":0000
   LinkTopic       =   "Form1"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   PaletteMode     =   1  'UseZOrder
   ScaleHeight     =   4125
   ScaleWidth      =   6375
   ShowInTaskbar   =   0   'False
   Begin ComctlLib.Toolbar Toolbar1
      Align           =   1  'Align Top
      Height          =   390
      Left            =   0
      TabIndex        =   5
      Top             =   0
      Width           =   6375
      _ExtentX        =   11245
      _ExtentY        =   688
      ImageList       =   "ImageList1"
      _Version        =   327680
      BeginProperty Buttons {0713E452-850A-101B-AFC0-4210102A8DA7}
         NumButtons      =   8
         BeginProperty Button1 {0713F354-850A-101B-AFC0-4210102A8DA7}
            Enabled         =   0   'False
            Caption         =   ""
            Key             =   ""
            Description     =   ""
            Object.ToolTipText     =   ""
            Object.Tag             =   ""
            Style           =   3
         EndProperty
         BeginProperty Button2 {0713F354-850A-101B-AFC0-4210102A8DA7}
            Enabled         =   0   'False
            Caption         =   ""
            Key             =   "hangup"
            Description     =   ""
            Object.ToolTipText     =   ""
            Object.Tag             =   ""
            ImageIndex      =   1
         EndProperty
         BeginProperty Button3 {0713F354-850A-101B-AFC0-4210102A8DA7}
            Enabled         =   0   'False
            Caption         =   ""
            Key             =   ""
            Description     =   ""
            Object.ToolTipText     =   ""
            Object.Tag             =   ""
            Style           =   3
         EndProperty
         BeginProperty Button4 {0713F354-850A-101B-AFC0-4210102A8DA7}
            Enabled         =   0   'False
            Caption         =   ""
            Key             =   "sendfile"
            Description     =   ""
            Object.ToolTipText     =   ""
            Object.Tag             =   ""
            ImageIndex      =   2
         EndProperty
         BeginProperty Button5 {0713F354-850A-101B-AFC0-4210102A8DA7}
            Enabled         =   0   'False
            Caption         =   ""
            Key             =   "whiteboard"
            Description     =   ""
            Object.ToolTipText     =   ""
            Object.Tag             =   ""
            ImageIndex      =   3
         EndProperty
         BeginProperty Button6 {0713F354-850A-101B-AFC0-4210102A8DA7}
            Enabled         =   0   'False
            Caption         =   ""
            Key             =   "chat"
            Description     =   ""
            Object.ToolTipText     =   ""
            Object.Tag             =   ""
            ImageIndex      =   4
         EndProperty
         BeginProperty Button7 {0713F354-850A-101B-AFC0-4210102A8DA7}
            Enabled         =   0   'False
            Caption         =   ""
            Key             =   ""
            Description     =   ""
            Object.ToolTipText     =   ""
            Object.Tag             =   ""
            Style           =   3
         EndProperty
         BeginProperty Button8 {0713F354-850A-101B-AFC0-4210102A8DA7}
            Enabled         =   0   'False
            Caption         =   ""
            Key             =   "sendcard"
            Description     =   ""
            Object.ToolTipText     =   ""
            Object.Tag             =   ""
            ImageIndex      =   5
         EndProperty
      EndProperty
      MouseIcon       =   "VbCard.frx":0442
   End
   Begin VB.ComboBox UserName
      Height          =   315
      Left            =   120
      TabIndex        =   4
      Top             =   480
      Width           =   5415
   End
   Begin VB.CommandButton StartConf
      Caption         =   "Call"
      Height          =   615
      Left            =   5640
      TabIndex        =   2
      Top             =   480
      Width           =   615
   End
   Begin VB.TextBox State
      BackColor       =   &H8000000F&
      Height          =   300
      Left            =   120
      TabIndex        =   1
      Top             =   3720
      Width           =   3015
   End
   Begin MSComDlg.CommonDialog CommonDialog1
      Left            =   3480
      Top             =   720
      _ExtentX        =   847
      _ExtentY        =   847
      _Version        =   327680
      DialogTitle     =   "Send File"
      Filter          =   "All Files (*.*)|*.*"
      Flags           =   5
   End
   Begin ComctlLib.ListView MemberList
      Height          =   2415
      Left            =   120
      TabIndex        =   3
      Top             =   1200
      Width           =   3855
      _ExtentX        =   6800
      _ExtentY        =   4260
      View            =   3
      LabelWrap       =   -1  'True
      HideSelection   =   -1  'True
      _Version        =   327680
      ForeColor       =   -2147483640
      BackColor       =   -2147483643
      Appearance      =   1
      MouseIcon       =   "VbCard.frx":045E
      NumItems        =   2
      BeginProperty ColumnHeader(1) {0713E8C7-850A-101B-AFC0-4210102A8DA7}
         Key             =   ""
         Object.Tag             =   ""
         Text            =   "Name"
         Object.Width           =   2540
      EndProperty
      BeginProperty ColumnHeader(2) {0713E8C7-850A-101B-AFC0-4210102A8DA7}
         Key             =   ""
         Object.Tag             =   ""
         Text            =   "Connection"
         Object.Width           =   2540
      EndProperty
   End
   Begin ComctlLib.ProgressBar Progress
      Height          =   300
      Left            =   3240
      TabIndex        =   0
      Top             =   3720
      Width           =   3015
      _ExtentX        =   5318
      _ExtentY        =   529
      _Version        =   327680
      Appearance      =   1
      MouseIcon       =   "VbCard.frx":047A
   End
   Begin VB.Image Unknown
      Height          =   2355
      Left            =   4680
      Picture         =   "VbCard.frx":0496
      Top             =   1200
      Visible         =   0   'False
      Width           =   2100
   End
   Begin ActiveXConferenceCtl.ActiveXConference ConfMgr
      Left            =   2880
      Top             =   720
   End
   Begin VB.Image Photo
      BorderStyle     =   1  'Fixed Single
      Height          =   2415
      Left            =   4080
      Top             =   1200
      Width           =   2175
   End
   Begin ComctlLib.ImageList ImageList1
      Left            =   3960
      Top             =   720
      _ExtentX        =   1005
      _ExtentY        =   1005
      BackColor       =   -2147483643
      ImageWidth      =   16
      ImageHeight     =   16
      MaskColor       =   12632256
      _Version        =   327680
      BeginProperty Images {0713E8C2-850A-101B-AFC0-4210102A8DA7}
         NumListImages   =   5
         BeginProperty ListImage1 {0713E8C3-850A-101B-AFC0-4210102A8DA7}
            Picture         =   "VbCard.frx":3140
            Key             =   ""
         EndProperty
         BeginProperty ListImage2 {0713E8C3-850A-101B-AFC0-4210102A8DA7}
            Picture         =   "VbCard.frx":3252
            Key             =   ""
         EndProperty
         BeginProperty ListImage3 {0713E8C3-850A-101B-AFC0-4210102A8DA7}
            Picture         =   "VbCard.frx":3364
            Key             =   ""
         EndProperty
         BeginProperty ListImage4 {0713E8C3-850A-101B-AFC0-4210102A8DA7}
            Picture         =   "VbCard.frx":3476
            Key             =   ""
         EndProperty
         BeginProperty ListImage5 {0713E8C3-850A-101B-AFC0-4210102A8DA7}
            Picture         =   "VbCard.frx":3588
            Key             =   ""
         EndProperty
      EndProperty
   End
   Begin VB.Menu mnuConference
      Caption         =   "&Conference"
      Begin VB.Menu mnuStart
         Caption         =   "&Start..."
      End
      Begin VB.Menu mnuLeave
         Caption         =   "&Leave"
         Enabled         =   0   'False
      End
      Begin VB.Menu mnuConfSep1
         Caption         =   "-"
      End
      Begin VB.Menu mnuOptions
         Caption         =   "&Options..."
      End
      Begin VB.Menu mnuConfSep2
         Caption         =   "-"
      End
      Begin VB.Menu mnuExit
         Caption         =   "&Exit"
      End
   End
   Begin VB.Menu mnuTools
      Caption         =   "&Tools"
      Begin VB.Menu mnuSendFile
         Caption         =   "Send &File..."
         Enabled         =   0   'False
      End
      Begin VB.Menu mnuWhiteboard
         Caption         =   "&Whiteboard"
         Enabled         =   0   'False
      End
      Begin VB.Menu mnuChat
         Caption         =   "&Chat"
         Enabled         =   0   'False
      End
      Begin VB.Menu mnuStartRemote
         Caption         =   "Start &Remote"
         Enabled         =   0   'False
      End
      Begin VB.Menu mnuToolsSep1
         Caption         =   "-"
      End
      Begin VB.Menu mnuSendCard
         Caption         =   "&Send Card"
         Enabled         =   0   'False
      End
      Begin VB.Menu mnuShowCard
         Caption         =   "S&how Card"
         Enabled         =   0   'False
      End
   End
   Begin VB.Menu mnuHelp
      Caption         =   "&Help"
      Enabled         =   0   'False
   End
End
Attribute VB_Name = "VbCard"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'****************************************************************************
'
'  Module:     VBCARD.EXE
'  File:       VbCard.frm
'  Content:    Implementation of the main form
'
'  Copyright (c) Microsoft Corporation 1995-1997
'
' 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.
'****************************************************************************

Option Explicit

Const AppID As String = _
    "{54A4A060-CF42-11cf-8796-444553540000}"
Const AppName As String = "HotTalk"

Dim Conference As IConferenceX

Private Sub ConfMgr_ConferenceCreated( _
    ByVal pcnf As IConferenceX)

    Set Conference = pcnf
    Conference.Advise
End Sub

Private Sub ConfMgr_DataReceived( _
    ByVal pDataExchange As IConfDataExchangeX, _
    ByVal pMember As IConfMemberX, _
    ByVal pDataTransfer As IConfDataTransferX, _
    ByVal hr As Long)

    If pDataTransfer.BytesTransferred = BIZCARD_DATA_SIZE Then
        ReceiveBizCard pMember.Name, pDataTransfer
    Else
        ReceiveImage pMember.Name, pDataTransfer
    End If
End Sub

Private Sub ConfMgr_DataSent( _
    ByVal pDataExchange As IConfDataExchangeX, _
    ByVal pMember As IConfMemberX, _
    ByVal pDataTransfer As IConfDataTransferX, _
    ByVal hr As Long)

    If pDataTransfer.BytesTransferred = BIZCARD_DATA_SIZE Then
        State = "Bizcard sent"
    Else
        State = "Picture sent"
    End If
End Sub

Private Sub ConfMgr_FileReceived( _
    ByVal pFileExchange As IConfFileExchangeX, _
    ByVal pMember As IConfMemberX, _
    ByVal pFileTransfer As IConfFileTransferX, _
    ByVal hr As Long)

    FileRxTx pMember, pFileTransfer, False
End Sub

Private Sub ConfMgr_FileSent( _
    ByVal pFileExchange As IConfFileExchangeX, _
    ByVal pMember As IConfMemberX, _
    ByVal pFileTransfer As IConfFileTransferX, _
    ByVal hr As Long)

    FileRxTx pMember, pFileTransfer, True
End Sub

Private Sub ConfMgr_MemberChanged( _
    ByVal pConference As IConferenceX, _
    ByVal pMember As IConfMemberX, _
    ByVal lNotify As Long)

    Select Case lNotify
        Case CNF_MEMB_ADDED
            'member is now added in UpdateMemberList
            'callee doesn't get added notifications on startup
            'AddMember pMember
        Case CNF_MEMB_REMOVED
            DeleteMember pMember
    End Select

    UpdateMemberList
End Sub

Private Sub ConfMgr_StateChanged( _
    ByVal pConference As IConferenceX, _
    ByVal lConfState As Long, _
    ByVal hr As Long)

    Select Case lConfState
        Case CNF_STATE_IDLE
            State.text = "Conference is idle."
        Case CNF_STATE_INIT
            State.text = "Conference is initialized."
        Case CNF_STATE_START
            State.text = "Conference has started."
            OnConferenceRunning
        Case CNF_STATE_STOP
            State.text = "Conference has stopped."
            OnConferenceStopped
        Case CNF_STATE_OFFERED
            State.text = "Conference has been offered."
    End Select
End Sub

Private Sub Form_Load()
    Dim Conferences As IConferences

    Set Conference = Nothing

    InitChannels

    InitMemberArray

    UpdateMemberList
    UpdateButtons False

    ReadBizCard AppName, "Self", MyCard

    ConfMgr.Initialize AppID
    ConfMgr.Advise

    On Error Resume Next
    Set Conferences = ConfMgr.Conferences
    If Not (Conferences Is Nothing) Then
        ' get the first conference
        Set Conference = Conferences(0)

        If Not (Conference Is Nothing) Then
            Conference.Advise
            OnConferenceRunning
        End If
    End If
End Sub

Private Sub Form_Unload(Cancel As Integer)
    OnConferenceStopped
    ConfMgr.Unadvise

    WriteBizCard AppName, "Self", MyCard
End Sub

Private Sub MemberList_ItemClick(ByVal Item As ListItem)
    UpdateActiveIndex Item.text
End Sub

Private Sub mnuChat_Click()
    LaunchChat
End Sub

Private Sub mnuExit_Click()
    Unload VbCard
End Sub

Private Sub mnuOptions_Click()
    Options.Show 1
End Sub

Private Sub mnuSendFile_Click()
    SendFile
End Sub

Private Sub mnuStart_Click()
    Start
End Sub

Private Sub mnuLeave_Click()
    Leave
End Sub

Private Sub mnuWhiteboard_Click()
    LaunchWhiteboard
End Sub

Private Sub mnuSendCard_Click()
    SendCard
End Sub

Private Sub mnuShowCard_Click()
    ShowCard
End Sub

Private Sub Photo_DblClick()
    ShowCard
End Sub

Private Sub StartConf_Click()
    Start
End Sub

Private Sub Toolbar1_ButtonClick(ByVal Button As Button)
    Select Case Button.Key
        Case "hangup"
            Leave
        Case "sendfile"
            SendFile
        Case "whiteboard"
            LaunchWhiteboard
        Case "chat"
            LaunchChat
        Case "sendcard"
            SendCard
    End Select

End Sub

Private Sub UpdateButtons(State As Boolean)
    Toolbar1.Buttons("hangup").Enabled = State
    Toolbar1.Buttons("sendfile").Enabled = State
    Toolbar1.Buttons("whiteboard").Enabled = State
    Toolbar1.Buttons("chat").Enabled = State
    Toolbar1.Buttons("sendcard").Enabled = State
    mnuLeave.Enabled = State
    mnuSendFile.Enabled = State
    mnuWhiteboard.Enabled = State
    mnuChat.Enabled = State
    mnuStartRemote.Enabled = State
    mnuSendCard.Enabled = State
    mnuShowCard.Enabled = State
End Sub

Private Sub Start()
    Dim User As IConfUserX

    On Error Resume Next
    If Conference Is Nothing Then
        Set Conference = ConfMgr.CreateConference("MyConf", CNF_CAPS_DATA)
        If Conference Is Nothing Then
            ErrorMsg "failed to create conference"
            Exit Sub
        End If

        Conference.Advise
    End If

    Set User = ConfMgr.CreateUser(UserName.text, CNF_USER_MACHINENAME)
    If User Is Nothing Then
        ErrorMsg "failed to create user"
    Else
        If Not Conference.Invite(User) Then
            ErrorMsg "failed to invite user"
        End If
    End If

    UpdateButtons True
End Sub

Private Sub Leave()
    On Error Resume Next
    If Not (Conference Is Nothing) Then
        Conference.Leave
    End If
    UpdateButtons False
End Sub

Private Sub SendFile()
    Dim FileXfer As IConfFileTransferX

    On Error Resume Next
    CommonDialog1.ShowOpen
    If Err <> cdlCancel Then
        If FileChannel Is Nothing Then
            MsgBox "No File Exchange channel"
        Else
            Set FileXfer = FileChannel.SendFile(Nothing, Nothing, _
                CommonDialog1.FileName, CNFF_SFOPT_NONE)
            If FileXfer Is Nothing Then
                ErrorMsg "Failed to send file"
            End If
        End If
    End If
End Sub

Private Sub LaunchChat()
    MsgBox "Launch Chat"
End Sub

Private Sub LaunchWhiteboard()
    MsgBox "Launch Whiteboard"
End Sub

Private Sub FileRxTx( _
    ByVal pMember As IConfMemberX, _
    ByVal pFileTransfer As IConfFileTransferX, _
    fSend As Boolean)

    Dim MemberName As String

    If pMember Is Nothing Then
        MemberName = "somebody"
    Else
        MemberName = pMember.Name
    End If

    Select Case pFileTransfer.State
        Case CNF_FILE_IDLE
        Case CNF_FILE_INITIALIZING
        Case CNF_FILE_PROGRESS
        Case CNF_FILE_COMPLETE
            If pFileTransfer.TotalSize = pFileTransfer.BytesTransferred Then
                If fSend Then
                    MsgBox pFileTransfer.Name & " was sent to " & MemberName
                Else
                    MsgBox pFileTransfer.Name & " was received from " & MemberName
                End If
            Else
                MsgBox "File transfer canceled"
            End If
    End Select

    Progress.Max = pFileTransfer.TotalSize
    Progress.Value = pFileTransfer.BytesTransferred
End Sub

Private Sub OnConferenceStopped()
    If Not (Conference Is Nothing) Then
        Conference.Unadvise
        Set Conference = Nothing
    End If

    CloseChannels

    UpdateMemberList
    UpdateButtons False
End Sub

Private Sub OnConferenceRunning()
    OpenChannels Conference
    UpdateMemberList
    UpdateButtons True
End Sub

Private Sub ErrorMsg(text As String)
    MsgBox "Error " & Err.Number & ": " & text
End Sub

Private Sub UpdateMemberList()
    Dim Members As IConfMembers
    Dim Member As IConfMemberX
    Dim itmX As ListItem

    MemberList.ListItems.Clear

    On Error Resume Next
    If Not (Conference Is Nothing) Then
        Set Members = Conference.Members
        If Not (Members Is Nothing) Then
            For Each Member In Members
                Set itmX = MemberList.ListItems.Add(, , Member.Name)
                itmX.SubItems(1) = ConfCapsToString(Member.ConferenceCapabilities)
                AddMember Member    'NOP if already added
            Next
        End If
    End If

    If MemberList.ListItems.Count > 0 Then
        MemberList.ListItems(1).Selected = True
        UpdateActiveIndex MemberList.ListItems(1).text
    Else
        UpdateActiveIndex ""
    End If
End Sub

Private Sub SendCard()
    Dim Buffer1() As Byte
    Dim Buffer2() As Byte
    Dim Size As Integer

    If Not (DataChannel Is Nothing) Then
        Size = PictureToBytes(MyCard.PhotoFile, Buffer2)
        If Not Size = 0 Then
            BizCardToBytes MyCard, Buffer1, Size

            On Error Resume Next
            If Not DataChannel.SendData(Nothing, Nothing, Buffer1, CNFF_SDOPT_NONE) Then
                MsgBox "Unable to send bizcard"
            Else
                If Not DataChannel.SendData(Nothing, Nothing, Buffer2, CNFF_SDOPT_NONE) Then
                    MsgBox "Unable to send picture"
                End If
            End If
        Else
            MsgBox "Unable to send picture"
        End If
    End If
End Sub

Private Sub ShowCard()
    BizCard.Show 1
End Sub


XMSCONF.BAS

Attribute VB_Name = "XMSConf"
'****************************************************************************
'
'  Module:     VBCARD.EXE
'  File:       XMSConf.frm
'  Content:    Implementation of the Conference control globals
'
'  Copyright (c) Microsoft Corporation 1995-1997
'
' 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.
'****************************************************************************

Option Explicit

Public Const IIDIConfDataExchangeX As String = _
    "{be5b8707-92e1-11cf-8796-444553540000}"
Public Const IIDIConfFileExchangeX As String = _
    "{be5b8708-92e1-11cf-8796-444553540000}"
Public Const IIDIConfAppSharingX As String = _
    "{be5b8709-92e1-11cf-8796-444553540000}"
Public Const IIDIConfAppControlX As String = _
    "{be5b870a-92e1-11cf-8796-444553540000}"

Public Const CNF_SHR_UNKNOWN As Long = 0
Public Const CNF_SHR_SHARED As Long = 1
Public Const CNF_SHR_UNSHARED As Long = 2

Public Const CNF_FILE_IDLE As Long = 0
Public Const CNF_FILE_INITIALIZING As Long = 1
Public Const CNF_FILE_PROGRESS As Long = 2
Public Const CNF_FILE_COMPLETE As Long = 3

Public Const CNF_MEMB_ADDED As Long = 0
Public Const CNF_MEMB_REMOVED As Long = 1
Public Const CNF_MEMB_UPDATED As Long = 2

Public Const CNF_STATE_IDLE As Long = 0
Public Const CNF_STATE_INIT As Long = 1
Public Const CNF_STATE_START As Long = 2
Public Const CNF_STATE_STOP As Long = 3
Public Const CNF_STATE_OFFERED As Long = 4

Public Const CNF_CAPS_NONE As Long = 0
Public Const CNF_CAPS_DATA As Long = 1
Public Const CNF_CAPS_AUDIO As Long = 2
Public Const CNF_CAPS_VIDEO As Long = 4

Public Const CNF_USER_UNKNOWN As Long = 0
Public Const CNF_USER_IP As Long = 1
Public Const CNF_USER_MACHINENAME As Long = 2
Public Const CNF_USER_PSTN As Long = 3

Public Const CNFF_SDOPT_NONE As Long = 0

Public Const CNFF_SFOPT_NONE As Long = 0
Public Const CNFF_SFOPT_NOUI As Long = 2
Public Const CNFF_SFOPT_NOCOMPRESS As Long = 4

Public DataChannel As IConfDataExchangeX
Public FileChannel As IConfFileExchangeX

Public Sub InitChannels()
    Set DataChannel = Nothing
    Set FileChannel = Nothing
End Sub

Public Sub OpenChannels(Conf As IConferenceX)
    Dim Channels As IConfChannels
    Dim Channel As IConfChannelX

    On Error Resume Next
    Set Channels = Conf.Channels
    If Not (Channels Is Nothing) Then
        For Each Channel In Channels
            Dim Interface As String

            Interface = Channel.Interface
            If Interface = IIDIConfDataExchangeX Then
                Set DataChannel = Channel.object
                DataChannel.Advise
            ElseIf Interface = IIDIConfFileExchangeX Then
                Set FileChannel = Channel.object
                FileChannel.Advise
            End If
        Next
    End If
End Sub

Public Sub CloseChannels()
    If Not (DataChannel Is Nothing) Then
        DataChannel.Unadvise
        Set DataChannel = Nothing
    End If
    If Not (FileChannel Is Nothing) Then
        FileChannel.Unadvise
        Set FileChannel = Nothing
    End If
End Sub

Public Function ConfCapsToString(lCaps As Long) As String
    Select Case lCaps
        Case CNF_CAPS_NONE
            ConfCapsToString = "None."
        Case CNF_CAPS_DATA
            ConfCapsToString = "Data Only."
        Case CNF_CAPS_AUDIO
            ConfCapsToString = "Voice Only."
        Case CNF_CAPS_AUDIO + CNF_CAPS_DATA
            ConfCapsToString = "Voice and Data."
    End Select
End Function


README.TXT

//-------------------------------------------------------------------------
// Readme.txt
//-------------------------------------------------------------------------
// Spruuids: A sample ActiveX Scripting host, which hosts VB Script.
//-------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//-------------------------------------------------------------------------

FILES SUPPLIED
EXTERNAL HEADER FILES
ActivScp.h  -- ActiveX Scripting header file, used by Game.cpp
VBSGuids.h  -- Defines CLSID for VB Script, used by Game.cpp
CompMgr.h   -- Component Manager interfaces, used by App.cpp, MsgLoop.cpp
PROGRAM FILES
App.cpp, App.h
            -- Implements Main App window via a dialog.  CApp is responsible
               for creating the CMsgLoop object, and CGame object.
MsgLoop.cpp, MsgLoop.h
            -- Implements .exe's message loop via IOleComponentManager.  CApp
               acts as a client to the ComponentManager, implementing
               IOleComponent.
OleAuto.cpp, OleAuto.h
            -- Small framework for creating OLE Automation objects which have
               dual interfaces.
Game.cpp, Game.h
            -- Orchestrates the show, responsible for creating and coordinating
               CScore, CDisplay (from Spr.cpp), and CSpriteClass's.  Handles
               high-level game behaviors, such as starting and ending games,
               the menus, and creating, loading, and managing VB Script through
               ActiveX Scripting.
GameOA.cpp
            -- OLE Automation implementation for "Game" object in object model.
Score.cpp, Score.h
            -- Tracks score, level, # ships left.
Spr.cpp, Spr.h
            -- Core "sprite" engine, implementing CDisplay, CSpriteClass, and
               CSprite.
            -- CDisplay implements the display surface on which the
               sprites are drawn, and handles drawing the sprites, updating
               their positions on timer ticks, and tracking collisions between
               the sprites.
            -- CSpriteClass implements a set of functionality shared between
               common kinds of sprites, such as the starting image used, how
               sprites of this class interact with the border, etc.
            -- CSprite implements a "sprite" which basically a repository for
               position, velocity, image, etc.
SprOA.cpp
            -- Implements the "SpriteClass" and "Sprite" OLE Automation
               interfaces for the CSpriteClass and CSprite classes,
               respectively.
Guids.cpp, Guids.h
            -- Simple file to get all necessary GUIDs to the linker.
Helpers.h
            -- Implements some obligatory ANSI/Unicode helpers.
Main.h
            -- Main include file included by everyone.
SpruuidP.Pix, SpruuidP.hh, SpruuidP.h
            -- Helper files which tells the sprite engine about the graphics
               found within the main sprite bitmap, Spruuids.bmp
DispIDs.h
            -- Defines the DISPIDs used for events.
Spruuids.odl
            -- Declares the object model for the Spruuids.exe application.
    BITMAPS
        Spruuids.bmp  -- Contains all of the interesting images.
        Ship.bmp      -- Contains the image used to display the # ships left.
        Plus.bmp      -- Graphics used to display a "+" when not enough room
                         to display # ships left.
SAMPLE SPRITE.EXE INPUT FILES
Game1.txt     -- Simple samples illustrating how to use some
Game2.txt        primitives from the Game object model.
Game3.txt
GameFull.txt  -- Complete fun-to-play game.

    CLASSES IN SPRUUIDS' IMPLEMENTATION
    CApp implements
            IUnknown
            IDispatch Using COleAuto
    SpruuidsApp
            IOleComponent
    CGame implements
            IUnknown
            IDispatch Using COleAuto
    Game
    IActiveScriptSite
            IProvideClassInfo
            IProvideClassInfo2
            IConnectionPointContainer
            IConnectionPoint via an embedded class
    CMsgLoop implements
    IOleComponentManager
    CScore
    CDisplay
    CSpriteClass implements
            IUnknown
            IDispatch Using COleAuto
    SpriteClass
            IProvideClassInfo
            IProvideClassInfo2
            IConnectionPointContainer
            IConnectionPoint via an embedded class
    CSprite
            IUnknown
            IDispatch Using COleAuto
    Sprite

RUNNING SPRUUIDS.EXE
Command Line:
Spruuids <VB Script File>
where
<VB Script File> is the relative or full path to a VB Script
file containing the source code which Spruuids is to load and run.  See
Game*.txt for samples of VB Script programs written against Spruuids.exe.

COMPILING SPRITE.EXE
Use MkTypLib.exe to compile "Spruuids.odl" into "Spruuids.h" and
        "Spruuids.tlb".  NOTE THAT Spruuids.tlb MUST BE GENERATED BEFORE THE .RC
        FILE CAN BE COMPILED WITH RC.EXE!
Compile each of the *.cpp files.
Use RC.exe to compile the "Spruuids.rc" into "Spruuids.res", noting that
"Spruuids.tlb" must be included as a resource of type "TYPELIB",
so it must have been created first.
Link everything togther, and use RC.exe to add the .res to the .exe.

SPRUUIDS' OBJECT MODEL
Application As SpruuidsApp
        R/O Properties
            Application - Returns the Application object (returns itself).
            Parent - Returns the Application object (returns itself).
            Game - Returns the "Game" object, described below.
        Methods
            Quit() - Causes Spruuids to terminate.

Game As Game
        R/O Properties
            Application - Returns the Application object.
            Parent - Returns the Application object.
            ShipClass As SpriteClass - Returns the SpriteClass for the ship.
            BubbleClass As SpriteClass - Returns the SpriteClass for bubbles.
            AmmoClass As SpriteClass - Returns the SpriteClass for ammo.
            ExtraClass As SpriteClass - Returns the SpriteClass for extra,
                special effects, or general use.
       R/W Properties
            Caption As String - Sets the caption of the main Spruuids window.
            StatusText As String - Sets the status text of Spruuids.
            Width As Integer - Width, in pixels, of the display area.
            Height As Integer - Height, in pixels, of the display area.
            Paused As Boolean - Sets or clears the paused game.
            Score As Integer - Current score.  Setting this will not compute
                extra ships gained.  Use AddScore() to ensure this calcualtion.
            Level As Integer - Current level.
            ShipCount As Integer - Current number of ships left.
            ScoreFirst1Up As Integer - Score at which the first extra ship is
                awarded.
            ScoreSecond1Up As Integer - Score at which the second extra ship
                is awarded.
            DScoreNext1Up As Integer - Delta score for each extra ship award
                after the second.
            ShipsStart As Integer - Number of ships to start game with.
            Tag As Variant - General storage for the user.
        Methods
            StartGame() - Starts a new game, propting the user first, if a
                game is already in progress.
            EndGame() - Ends the current game.
            RemoveAllSprites() - Destroys all sprites.
            NextLevel() - Increments the Level number, updating the level
                display
            Refresh() - Forces the display to be refreshed.
            AddScore(amt As Integer) - Adds the given amount to the score,
                rounding up to zero, if necessary.  Also computes to see
                if an extra ship should be awarded.
            StdBorderBounce(s As Sprite, brd As Integer) - Ensures that
                the sprite's velocities will cause the sprite to bounce.
            StdBorderWrap(s As Sprite, brd As Integer) - Move the sprite
                to the other edge of the screen.
            StdInitRand(s As Sprite, u As Variant) - Moves the sprite to some
                random location on the display at least u pixels away from
                the center.  Ensures that the sprite does not start out
                overlapping a sprite it would cause a Collide event with.
            StdInitEdge(s As Sprite, u As Variant) - Moves the sprite to some
                random location around the edge of the display.  u is ignored.
        Events
            NewGame() - Fired when a new game is started.
            NewLevel() - Fired when NextLevel is called.
            NewShip() - Fired when a new ship is awarded.
            Tick() - Fired every timer tick.
            Collide(s1 As Sprite, s2 As Sprite, coll As Integer) - Fired when
                two sprites move so they overlap.  S1 is always the sprite
                with the lower id (ShipClass < BubbleClass < AmmoClass <
                ExtraClass).  Coll is the bit-wise AND of s1.Parent.Collide
                with s2.Parent.Collide.  So with judicious setting of the
                Collide propertis, coll is enough to tell what types of
                sprites collided, and what s1 and s2 are.  Bit 1 is special
                and is used to indicate that a sprite should generate this
                event when it hits sprites of its own class.
            KeyDown(vk As Integer) - Fired when key is depressed.  vk contains
                the virtual key code, as defined in Windows.h.
            KeyPress(ascii As Integer) - Fired when key is depressed.  ascii
                contains the ascii value of the key pressed.
            KeyUp(vk As Integer) - Fired when key is released.  vk contains
                the virtual key code, as defined in Windows.h.
            MouseMove(x As Integer, y As Integer, keys As Integer, button As
                Integer) - Fired when mouse moves over the play surface of
                Spruuids.  x and y are the pixel location of the mouse relative
                to the upper left corner of the play surface.  keys indicates
                whether the SHIFT key is depressed (bit 1) or the CONTROL key
                is depressed (bit 2).  button indicates which buttons are
                currently depressed (any combination of: 1=left, 2=right,
                4=middle).
            MouseDown(x As Integer, y As Integer, keys As Integer, button As
                Integer) - Fired when the user depresses a mouse button.
                button indicates which button was depressed (one of: 1=left,
                2=right, 4=middle).
            MouseUp(x As Integer, y As Integer, keys As Integer, button As
                Integer) - Fired when the user releases a mouse button.
                button indicates which button was released (one of: 1=left,
                2=right, 4=middle).

    SpriteClass Objects
        R/O Properties
            Application - Returns the Application object.
            Parent - Returns the Game object.
            SpriteCount As Integer - The number of existing sprites of
                this kind.
        R/W Properties
            Image As Integer - A number indicating the default graphic
                to be used for sprites of this class.
            Brd As Integer - A bitfield indicating the display borders with
                which sprites of this class are interested in being notified
                (via the Border event) when then touch them.  See Spr.h for
                the definition of this bitfield.
            Collide As Integer - a bitfield used to determine whether to fire
                the Game_Collide event, as described above.
            Friction As Single - Indicates the amount of friction sprites of
                this class should default to.  Values 0<n<1 slow the ship.  The
                value 1 is the same as no friction (default).  Values >1 will
                cause the ship to automatically accelerate.  Values <0, who
                knows?
            Tag As Variant - General storage for the user.
        Methods
            CreateSprite(Left As Integer, Top As Integer, u As Variant) As Sprite
                A function which creates a new instance of a sprite of this
                class, at the given location.  U is passed to the Init event,
                allowing the user to pass information into that event.  This
                function returns a reference to the sprite object created.
        Events
                Init(s As Sprite, u As Variant) - Fired when sprite is first
                    created.  U is the parameter passed to CreateSprite().
                Tick(s As Sprite) - Fired every s.TickEvt timer ticks.
                Border(s As Sprite, brd As Border) - Fired when sprite touches
                    a display border indicated by the Brd property.  Calling
                    s.IgnoreMove() during this event will cause the sprite
                    to return to its position it was at before the move which
                    caused this event.
                Term(s As Sprite) - Called before sprite object is destroyed.
                LastTerm() - Fired when no more sprites of this sprite class
                    exist.

    Sprite Objects
        R/O Properties
            Application - Returns the Application object.
            Parent - Returns the Game object.
            Width As Integer - Width of sprite's graphic in pixels.
            Height As Integer - Height of sprite's graphic in pixels.
        R/W Properties
            Left As Single - Left position in pixels (may be fractional).
            Top As Single - Top position in pixels (may be fractional).
            Vx As Single - Horizontal velocity in pixels per TickMove ticks
                (may be fractional).
            Vy As Single - Vertical velocity in pixels per TickMove ticks
                (may be fractional).
            Ax As Single - Horizontal acceleration in pixels per TickMove ticks
                (may be fractional).
            Ay As Single - Vertical accekeration in pixels per TickMove ticks
                (may be fractional).
            FrictionX As Single - Horizontal friction.  See description of
                Friction for SpriteClass (may be fractional).
            FrictionY As Single - Vertical friction.  See description of
                Friction for SpriteClass (may be fractional).
            Image As Integer - Graphic associated with this sprite.  See
                SpruuidP.pix and SpruuidP.hh for a list of numbers and their
                graphics.
            TickMove As Integer - The number of ticks between times the sprite
                engine move this sprite by Vx and Vy.
            TickEvt As Integer - The number of ticks between SpriteClass_Tick
                events.
            Visible As Boolean - Indicates whether this sprite is visible or
                not.  Invisible sprites never cause Collide events.
            Tag As Variant - General storage for the user.
        Methods
            MoveTo(Left As Single, Top As Single) - Move the sprite to the
                given location.
            MoveBy(Left As Single, Top As Single) - Move the sprite by the
                given amount.
            Remove() - Destroys the sprite.
            Refresh() - Forces the sprite to repaint, if it is visible.
            IgnoreMove() - Called from within the Collide or Border events
                to indicate that the move which caused this sprite to collide
                or touch a border should not be made.

//--- EOF -----------------------------------------------------------------


APP.H

//---------------------------------------------------------------------------
// App.h
//---------------------------------------------------------------------------
// Sample spr program
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Globals
//---------------------------------------------------------------------------
extern class CApp *g_papp;
extern char       *g_pszCodeFile;
extern char       *g_pszRecFile;
extern int         g_mode;

#define MODE_NORMAL     0
#define MODE_PLAYBACK   1
#define MODE_RECORD     2

//---------------------------------------------------------------------------
// Events on client of App
//---------------------------------------------------------------------------
BOOL    AppEvt_Pause(int pause);
BOOL    AppEvt_FQueryTerminate(BOOL fPromptUser);
void    AppEvt_NewGame();
BOOL    AppEvt_DlgProc(  HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
LRESULT AppEvt_PSWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);

//---------------------------------------------------------------------------
// App class
//---------------------------------------------------------------------------
class CApp : public ISpruuidsApp, public IOleComponent, public COleAuto
  {
public:
  // *** Constructor / Descructor ***
  static HRESULT CreateApp(HINSTANCE hinst);
  CApp(HINSTANCE hinst);
  ~CApp();

  // *** IUnknown methods ***
  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj);
  STDMETHOD_(ULONG, AddRef)(void);
  STDMETHOD_(ULONG, Release)(void);
  inline IUnknown  *GetUnknown(void)  {return (ISpruuidsApp *)this;};
  inline IDispatch *GetDispatch(void) {return (ISpruuidsApp *)this;};

  // *** IDispatch methods ***
  DECLARE_STANDARD_DISPATCH();
  // Overrides required by COleAuto:
  virtual ITypeInfo **GetTinfoClsAddr(void) { return &m_ptinfoCls; };
  virtual ITypeInfo **GetTinfoIntAddr(void) { return &m_ptinfoInt; };
  virtual HRESULT GetTypeLibInfo(HINSTANCE *phinstOut, const GUID **pplibidOut,
                                 SHORT *pwMajLibOut, SHORT *pwMinLibOut,
                                 const CLSID **ppclsidOut, const IID **ppiidOut,
                                 ITypeLib ***ppptlOut);
  virtual IDispatch *GetPrimary() {return this->GetDispatch(); };

  // *** SpruuidsApp methods ****
  STDMETHOD(get_Application)(ISpruuidsApp** lppaReturn);
  STDMETHOD(get_Parent)(ISpruuidsApp** lppaReturn);
  STDMETHOD(Quit)(void);
  STDMETHOD(get_Game)(IGame** lppaReturn);

  // *** IComponent methods ***
  STDMETHOD_(BOOL, FPreTranslateMessage)(MSG* pMsg);
  STDMETHOD_(void, OnEnterState)(ULONG uStateID, BOOL fEnter);
  STDMETHOD_(void, OnAppActivate)(BOOL fActive, DWORD dwOtherThreadID);
  STDMETHOD_(void, OnLoseActivation)(void);
  STDMETHOD_(BOOL, FDoIdle)(DWORD grfidlef);
  STDMETHOD_(BOOL, FContinueMessageLoop)(ULONG uReason, void* pvLoopData);
  STDMETHOD_(BOOL, FQueryTerminate)(BOOL fPromptUser);
  STDMETHOD_(void, Terminate)(void);

  // *** Public methods ***
  HRESULT MainMsgLoop(void);
  HRESULT LoadGame(void);
  void    CloseGame(void);
  void    CausePause(int p);
  HRESULT CheckTypeInfo(UINT itinfo);
  HRESULT Playback(void);

  // *** Public member variables ***
  class CMsgLoop *m_pmsgloop;
  class CGame    *m_pgame;
  HWND       m_hwndDlg;
  HWND       m_hwndPS;
  HWND       m_hwndStat;
  HWND       m_hwndAbout;
  HINSTANCE  m_hinst;
  DWORD      m_idcomp;
  BOOL       m_fRegisteredComp;
  ULONG      m_cmodal;
  BOOL       m_fQuit;
  ULONG      m_cref;
  ITypeInfo *m_ptinfoCls;   // This OLE Automation object's coclass   TypeInfo
  ITypeInfo *m_ptinfoInt;   // This OLE Automation object's interface TypeInfo

  // DEBUG info
  static HRESULT s_hr;
  #define SIG_App 'App '
  DECLARE_SIGNATURE(SIG_App);
  };

//--- EOF -------------------------------------------------------------------


DEBUG.H

//---------------------------------------------------------------------------
// Debug.H
//---------------------------------------------------------------------------
// Contains the various macros and the like which are only useful in DEBUG
// builds.
//---------------------------------------------------------------------------
// (C) Copyright 1995-1997 by Microsoft Corporation. All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// All the things required to handle our ASSERT mechanism
//---------------------------------------------------------------------------
#undef DEBUG
// ;begin_internal
// BUGBUG DisplayAssert is not coded in this release,
// until it is, debug builds do not get these debug functions
// ;end_internal

#ifdef DEBUG

VOID DisplayAssert(LPSTR pszMsg, LPSTR pszAssert, LPSTR pszFile, UINT line);

// *** Include this macro at the top of any source file using *ASSERT*() macros ***
#define SZTHISFILEstatic char _szThisFile[] = __FILE__;

// Our versions of the ASSERT and FAIL macros.
#define ASSERT(fTest, szMsg)                                \
    if (!(fTest))                                           \
        {                                                   \
        static char szMsgCode[] = szMsg;                    \
        static char szAssert[] = #fTest;                    \
        DisplayAssert(szMsgCode, szAssert, _szThisFile, __LINE__); \
        }

#define FAIL(szMsg)                                         \
        { static char szMsgCode[] = szMsg;                    \
        DisplayAssert(szMsgCode, "FAIL", _szThisFile, __LINE__); }

#else  // DEBUG

#define SZTHISFILE
#define ASSERT(fTest, err)
#define FAIL(err)

#endif// DEBUG

//---------------------------------------------------------------------------
// Macro that checks a pointer for validity on input
//---------------------------------------------------------------------------
#ifdef DEBUG
#define CHECK_POINTER(val) if (!(val) || IsBadWritePtr((void *)(val), sizeof(void *))) return E_POINTER
#else  // DEBUG
#define CHECK_POINTER(val)
#endif // DEBUG

//---------------------------------------------------------------------------
// Track signatures on objects for debugging
//---------------------------------------------------------------------------
#ifdef DEBUG

#define SIG_destroyed 'XxXx'
#define DECLARE_SIGNATURE(x)    DWORD __sig
#define CHECK_SIGNATURE(x)      ASSERT(this->__sig == (x), "Bad signature")
#define INIT_SIGNATURE(x)       this->__sig = (x)
#define DESTROY_SIGNATURE(x)    this->__sig = SIG_destroyed

#else // DEBUG

#define DECLARE_SIGNATURE(x)
#define CHECK_SIGNATURE(x)
#define INIT_SIGNATURE(x)
#define DESTROY_SIGNATURE(x)

#endif // DEBUG

//--- EOF -------------------------------------------------------------------


DISPIDS.H

//---------------------------------------------------------------------------
// Dispids.h
//---------------------------------------------------------------------------
// Dispids for our servers.
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#define DISPID_GameEvents_NewGame         1
#define DISPID_GameEvents_NewLevel        2
#define DISPID_GameEvents_NewShip         3
#define DISPID_GameEvents_Collide         4
#define DISPID_GameEvents_Tick            5
#define DISPID_GameEvents_KeyDown         6
#define DISPID_GameEvents_KeyPress        7
#define DISPID_GameEvents_KeyUp           8
#define DISPID_GameEvents_MouseMove       9
#define DISPID_GameEvents_MouseDown      10
#define DISPID_GameEvents_MouseUp        11

#define DISPID_SpriteClassEvents_Init     1
#define DISPID_SpriteClassEvents_Tick     2
#define DISPID_SpriteClassEvents_Border   3
#define DISPID_SpriteClassEvents_Term     4
#define DISPID_SpriteClassEvents_LastTerm 5

//--- EOF -------------------------------------------------------------------


GAME.CPP

//---------------------------------------------------------------------------
// Game.cpp
//---------------------------------------------------------------------------
// Sample spr program
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop
#include "Game.h"
#include "App.h"
#include "Spr.h"
#include "Score.h"
#include "SpruuidP.h"
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE

//---------------------------------------------------------------------------
// Forward Declares
//---------------------------------------------------------------------------
int DisplayScriptError(HINSTANCE hinst, HWND hwndParent, LPCSTR pszError, int ichError);

//---------------------------------------------------------------------------
// Image data
//---------------------------------------------------------------------------
#define ADDPIX(    id,idNext,x,y,cx,cy) {iimg##idNext, x, y, cx, cy},
#define ADDPIXLAST(id,idNext,x,y,cx,cy) {iimg##idNext, x, y, cx, cy}
#define ADDPIXIMG
IMG Game_img[] = {
#include "SpruuidP.pix"
};
#undef ADDPIX
#undef ADDPIXLAST
#undef ADDPIXIMG

//---------------------------------------------------------------------------
// Various Globals
//---------------------------------------------------------------------------
CGame     *g_pgame            = NULL;
ITypeLib  *g_ptlGameSubObj    = NULL;
ITypeInfo *g_ptinfoClsGame    = NULL;
ITypeInfo *g_ptinfoIntGame    = NULL;
char      *CGame::s_pszError  = NULL;
ULONG      g_clineOffset;

//---------------------------------------------------------------------------
// Cover for contstructor.  Use instead of new CGame!
//---------------------------------------------------------------------------
HRESULT CGame::s_hr = E_FAIL;

HRESULT CGame::CreateGame
(
  HINSTANCE hinst,
  HWND      hwndDlg,
  HWND      hwndPS,
  HWND      hwndStat,
  IOleComponentManager *pcm,
  CGame   **ppgameOut
)
{
  CGame  *pgame;
  HRESULT hr;

  *ppgameOut = NULL;
  pgame = new CGame(hinst, hwndDlg, hwndPS, hwndStat, pcm);
  if (!pgame)
    return E_OUTOFMEMORY;
  else if (pgame->s_hr)
    {
    delete pgame;
    return s_hr;
    }

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
// Add the Game as a Named Item & load its code
  g_clineOffset = 0;
  hr = pgame->ParseFile(g_pszCodeFile, L"Game");
  if (hr)
    {
    MessageBox(g_papp->m_hwndDlg, s_pszError ? s_pszError : "Unspecified Error", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
    return hr;
    }
// #####  END  ACTIVEX SCRIPTING SUPPORT #####

  // Create sprite classes based upon $OBJECT lines in file
  for (int i=0; i<pgame->m_csubobj; i++)
    {
    CHECK(CSpriteClass::Create(pgame->m_pdisp, &pgame->m_rgpsc[i]));
    pgame->m_rgpdispSubObjs[i] = pgame->m_rgpsc[i]->GetDispatch();
    pgame->m_rgpdispSubObjs[i]->AddRef();
    }

  if (hr)
    {
CleanUp:
    delete pgame;
    return hr;
    }

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
  // Start the script running...
  hr = pgame->m_ps->SetScriptState(SCRIPTSTATE_CONNECTED);
  if (hr)
    {
    delete pgame;
    return hr;
    }
// #####  END  ACTIVEX SCRIPTING SUPPORT #####

  // Success
  *ppgameOut = pgame;
  return S_OK;
}

//---------------------------------------------------------------------------
// Constructor
//---------------------------------------------------------------------------
CGame::CGame
(
  HINSTANCE hinst,
  HWND      hwndDlg,
  HWND      hwndPS,
  HWND      hwndStat,
  IOleComponentManager *pcm
)
{
  INIT_SIGNATURE(SIG_Game);

  // Setup globals
  g_pgame       = this;
  s_hr          = S_OK;

  // Init members
  m_hinst        = hinst;
  m_hwndDlg      = hwndDlg;
  m_hwndPS       = hwndPS;
  m_hwndStat     = hwndStat;
  m_cref         = 1;
  m_fPaused      = FALSE;
  m_fShipDead    = FALSE;
  m_fShipRestart = FALSE;
  m_fGameOver    = TRUE;
  m_pcm          = pcm;
  m_ps           = NULL;
  m_psp          = NULL;
  VariantInit(&m_varTag);
  m_pscore       = NULL;
  m_pdisp        = NULL;
  m_pdispBaseObject = NULL;
  m_pgameoa      = NULL;
  m_csubobj      = 0;

  CoCreateGuid(&m_libidSubObj);
  CoCreateGuid(&m_clsidSubObj);
  CoCreateGuid(&m_iidSubObj);

  m_pgameoa = new CGameOA(this);
  if (!m_pgameoa)
    {
    s_hr = E_OUTOFMEMORY;
    return;
    }
  this->SetBaseObject(m_pgameoa->GetDispatch());

  m_pdisp = new CDisplay(hinst, hwndPS, GetDC(hwndPS),
                         this->GetUnknown(), CGameOA::FireCollide,
                         sizeof(Game_img)/sizeof(IMG), Game_img, ID_BMP_SPRITES);
  if (!m_pdisp)
    {
    s_hr = E_OUTOFMEMORY;
    return;
    }

  s_hr = CScore::CreateScore(
                      m_hinst,       // hinst to load resources from
                      m_hwndDlg,     // hwnd on which to draw score
                      m_hwndPS,      // hwnd of PlaySurface
                      m_hwndStat,    // hwnd of Status bar
                      2500,          // scoreFirst1Up
                      5000,          // scoreSecond1Up
                      2500,          // dscoreNext1Up
                      3,             // cshipStart;
                      ID_BMP_SHIP,   // idbmpShip;
                      ID_BMP_PLUS,   // idbmpPlus;
                      &m_pscore);
  if (s_hr)
    return;
  ASSERT(m_pscore, "Good hr, but m_pscore==NULL");

  // Default to VBScript
  extern const CLSID CLSID_VBScript;
  m_clsidEngine = CLSID_VBScript;

  // Success
  s_hr = S_OK;
}

//---------------------------------------------------------------------------
// Clean up allocated resources
//---------------------------------------------------------------------------
CGame::~CGame
(
  void
)
{
  CHECK_SIGNATURE(SIG_Game);
  this->Close();
  if (m_pgameoa)
    delete m_pgameoa;
  DESTROY_SIGNATURE(SIG_Game);
}

//---------------------------------------------------------------------------
// Clean up allocated resources
//---------------------------------------------------------------------------
void CGame::Close
(
  void
)
{
  CHECK_SIGNATURE(SIG_Game);

  if (g_mode != MODE_PLAYBACK)
    KillTimer(m_hwndPS, 0);

  g_pgame = NULL;

  // Clear this, since it may be an object
  VariantClear(&m_varTag);

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
  // Release the language engine, since it may hold on to us
  if (m_psp)
    m_psp->Release();
  if (m_ps)
    {
    m_ps->Close();
    m_ps->Release();
    }
// #####  END  ACTIVEX SCRIPTING SUPPORT #####

  // Drop all sink's that have Advise()'d us, since they might keep us alive
  m_pgameoa->m_cp.Close();

  // Release all existing sprites
  if (m_pdisp)
    m_pdisp->DestroyAll();

  // Close & Release the SpriteClass Sub Objects
  for (int i=0; i<m_csubobj; i++)
    {
    delete [] m_rgpwszSubObjs[i];
    if (m_rgpdispSubObjs[i])
      {
      m_rgpdispSubObjs[i]->Release();
      m_rgpdispSubObjs[i] = NULL;
      }
    if (m_rgpsc[i])
      {
      m_rgpsc[i]->Close();
      m_rgpsc[i]->Release();
      m_rgpsc[i] = NULL;
      }
    }

  // Now destroy the Score & Display objects
  if (m_pscore)
    {
    delete m_pscore;
    m_pscore = NULL;
    }
  if (m_pdisp)
    {
    delete m_pdisp;
    m_pdisp = NULL;
    }
}

//***************************************************************************
// IUnknown Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::QueryInterface
(
  REFIID  iid,
  LPVOID* ppvObjOut
)
{
  if (!ppvObjOut)
    return E_INVALIDARG;

  *ppvObjOut = NULL;

  if (iid == IID_IUnknown)
    *ppvObjOut = this->GetUnknown();
  else if (iid == IID_IDispatch)
    *ppvObjOut = this->GetDispatch();
  else if (iid == IID_IGameSubObjects)
    *ppvObjOut = this->GetDispatch();
  else if (iid == IID_IActiveScriptSite)
    *ppvObjOut = (IActiveScriptSite *)this;
  else if (iid == IID_IActiveScriptSiteWindow)
    *ppvObjOut = (IActiveScriptSiteWindow *)this;
  else if (iid == IID_IProvideClassInfo)
    *ppvObjOut = (IProvideClassInfo *)this;
  else if (iid == IID_IProvideMultipleClassInfo)
    *ppvObjOut = (IProvideMultipleClassInfo *)this;
  else if (iid == IID_IGame)
    *ppvObjOut = (IGame *)(this->m_pgameoa);
  else if (iid == IID_IConnectionPointContainer)
    *ppvObjOut = (IConnectionPointContainer *)(this->m_pgameoa);

  if (*ppvObjOut)
    {
    this->AddRef();
    return S_OK;
    }

  return E_NOINTERFACE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CGame::AddRef
(
  void
)
{
  return ++m_cref;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CGame::Release
(
  void
)
{
  ASSERT(m_cref, "bad m_cref");
  m_cref--;
  if (!m_cref)
    {
    m_pgameoa->m_cp.Close();     // Make sure nobody's holding on to us because we're holding their sink
    if (!m_cref && !m_pgameoa->m_cp.m_cref)
      {
      delete this;
      return 0;
      }
    }
  return m_cref;
}

//***************************************************************************
// IDispatch Interface
//***************************************************************************

//---------------------------------------------------------------------------
// Re-implement COleAuto's GetIDsOfNames(), so we can implement the dynamic
// properties we added for the $OBJECT=SpriteClassName lines.
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetIDsOfNames
(
  REFIID    iid,
  OLECHAR** rgszNames,
  UINT      cNames,
  LCID      lcid,
  DISPID*   prgdispid
)
{
  // Validate Args
  if (iid != IID_NULL)
      return E_INVALIDARG;

  // See if a sub-object matches the name passed in.
  for (int i=0; i<m_csubobj; i++)
    if (!wcscmp(*rgszNames, m_rgpwszSubObjs[i]))
      {
      if (cNames != 1)
        return DISP_E_NONAMEDARGS;
      *prgdispid = 0x80010000 | i;
      return S_OK;
      }

  return COleAuto::GetIDsOfNames(iid, rgszNames, cNames, lcid, prgdispid);
}

//---------------------------------------------------------------------------
// Re-implement COleAuto's Invoke(), so we can implement the dynamic
// properties we added for the $OBJECT=SpriteClassName lines.
//---------------------------------------------------------------------------
STDMETHODIMP CGame::Invoke
(
  DISPID      dispid,
  REFIID      riid,
  LCID        lcid,
  WORD        wFlags,
  DISPPARAMS* pdispparams,
  VARIANT*    pvarResult,
  EXCEPINFO*  pexcepinfo,
  UINT*       puArgErr)
{
  IDispatch *pdisp = NULL;

  // Validate Args
  if (pvarResult)
    VariantInit(pvarResult);
  if (puArgErr)
    *puArgErr = 0;
  SetErrorInfo(0L, NULL);

  // If dispid isn't a dynamic property, call COleAuto's dispatch,
  // and it will forward the request on to m_pdispBaseObject.
  if ((dispid & 0x80010000) != 0x80010000)
    return COleAuto::Invoke(dispid, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
  // It's for one of our property objects...

  // Make sure we have either a PropGet or a Method
  if (!(wFlags & (DISPATCH_METHOD | DISPATCH_PROPERTYGET)))
    return DISP_E_MEMBERNOTFOUND;

  dispid &= 0xffff;
  if (dispid >= m_csubobj)
    return DISP_E_MEMBERNOTFOUND;

  if (pdispparams->cArgs)
    return DISP_E_BADPARAMCOUNT;

  if (pdispparams->cNamedArgs)
    return DISP_E_NONAMEDARGS;

  // Get an addref'd IDispatch to the property object
  pdisp = m_rgpdispSubObjs[dispid];
  ASSERT(pdisp, "NULL m_rgpdispSubObjs[]");

  if (pvarResult)
    {
    pvarResult->vt = VT_DISPATCH;
    pvarResult->pdispVal = pdisp;
    pdisp->AddRef();// We're returning this disp ptr
    }

  return S_OK;
}

//---------------------------------------------------------------------------
// Method needed by COleAuto, so it can implement IDispatch for us.
//---------------------------------------------------------------------------
HRESULT CGame::GetTypeLibInfo
(
  HINSTANCE    *phinstOut,
  const GUID  **pplibidOut,
  SHORT        *pwMajLib,
  SHORT        *pwMinLib,
  const CLSID **ppclsidOut,
  const IID   **ppiidOut,
  ITypeLib   ***ppptlOut
)
{
  HRESULT hr = this->BuildTypeInfo();
  if (hr)
    return hr;
  *phinstOut  = NULL;
  *pplibidOut = &m_libidSubObj;
  *pwMajLib   = 1;
  *pwMinLib   = 0;
  *ppclsidOut = &m_clsidSubObj;
  *ppiidOut   = &m_iidSubObj;
  *ppptlOut   = &g_ptlGameSubObj;
  return S_OK;
}

//***************************************************************************
// IProvideClassInfo Interfaces
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetClassInfo
(
  ITypeInfo** pptinfoOut
)
{
  HRESULT hr = ((COleAuto *)this)->CheckTypeInfo(0, 0x0409);
  if (hr)
    return hr;
  *pptinfoOut = g_ptinfoClsGame;
  (*pptinfoOut)->AddRef();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetGUID
(
  DWORD dwGuidKind,
  GUID* pGUID
)
{
  if (pGUID)
    return E_INVALIDARG;

  if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
    return E_INVALIDARG;

  *pGUID = DIID_IGameEvents;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetMultiTypeInfoCount
(
  ULONG *pcti
)
{
  if (!pcti)
    return E_INVALIDARG;

  *pcti = 2;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetInfoOfIndex
(
  ULONG       iti,
  DWORD       dwFlags,
  ITypeInfo** pptiCoClass,
  DWORD*      pdwTIFlags,
  ULONG*      pcdispidReserved,
  IID*        piidPrimary,
  IID*        piidSource
)
{
  HRESULT    hr        = S_OK;
  ITypeInfo *ptinfoOut = NULL;

  if (iti > 1)
    hr = E_FAIL;

//  if (dwFlags & ~MULTICLASSINFO_ALL)
//    hr = E_INVALIDARG;

  if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
    {
    if (!pptiCoClass || !pdwTIFlags)
      hr = E_INVALIDARG;
    else
      {
      *pdwTIFlags  = 0L;
      *pptiCoClass = NULL;
      if (iti == 0)
        {
        hr = ((COleAuto *)this)->CheckTypeInfo(0, 0x0409);
        if (hr)
          return hr;
        ptinfoOut = g_ptinfoClsGame;
        }
      else
        {
        hr = ((COleAuto *)m_pgameoa)->CheckTypeInfo(0, 0x0409);
        if (hr)
          return hr;
        ptinfoOut = g_ptinfoClsGameOA;
        }
      }
    }

  if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
    {
    if (!pcdispidReserved)
      hr = E_INVALIDARG;
    else
      *pcdispidReserved = 256;
    }

  if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY)
    {
    if (!piidPrimary)
      hr = E_INVALIDARG;
    else if (iti == 0)
      *piidPrimary = IID_IGameSubObjects;
    else
      *piidPrimary = IID_IGame;
    }

  if (dwFlags & MULTICLASSINFO_GETIIDSOURCE)
    {
    if (!piidSource)
      hr = E_INVALIDARG;
    else if (iti == 0)
      *piidSource = IID_NULL;
    else
      *piidSource = DIID_IGameEvents;
    }

  // Fill in ptinfo out param
  if (hr)
    {
    if (pptiCoClass)
      *pptiCoClass = NULL;
    }
  else if (pptiCoClass)
    {
    *pptiCoClass = ptinfoOut;
    (*pptiCoClass)->AddRef();
    }

  return hr;
}

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
//***************************************************************************
// IActiveScriptSite Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetLCID
(
  LCID *plcid
)
{
  return E_NOTIMPL;     // Use system settings
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetItemInfo
(
  LPCOLESTR   pstrName,
  DWORD       dwReturnMask,
  IUnknown**  ppunkItemOut,
  ITypeInfo** pptinfoOut
)
{
  HRESULT hr;

  if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
    {
    if (!pptinfoOut)
      return E_INVALIDARG;
    *pptinfoOut = NULL;
    }

  if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
    {
    if (!ppunkItemOut)
      return E_INVALIDARG;
    *ppunkItemOut = NULL;
    }

  if (!_wcsicmp(L"game", pstrName))
    {
    if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
      {
      hr = ((COleAuto *)this)->CheckTypeInfo(0, 0x0409);
      if (hr)
        return hr;
      *pptinfoOut = g_ptinfoClsGame;
      (*pptinfoOut)->AddRef();      // because returning
      }
    if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
      {
      *ppunkItemOut = this->GetUnknown();
      (*ppunkItemOut)->AddRef();    // because returning
      }
    return S_OK;
    }

  return TYPE_E_ELEMENTNOTFOUND;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetDocVersionString
(
  BSTR *pbstrVersion
)
{
  return E_NOTIMPL;   // UNDONE: Implement this method
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::RequestItems
(
  void
)
{
  return m_ps->AddNamedItem(L"Game", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::RequestTypeLibs
(
  void
)
{
  return m_ps->AddTypeLib(LIBID_SPRUUIDS, 1, 0, 0);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnScriptTerminate
(
  const VARIANT   *pvarResult,
  const EXCEPINFO *pexcepinfo
)
{
  // UNDONE: Put up error dlg here
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnStateChange
(
  SCRIPTSTATE ssScriptState
)
{
  // Don't care about notification
  return S_OK;
}

//---------------------------------------------------------------------------
// Display the error
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnScriptError
(
  IActiveScriptError *pse
)
{
  char      szError[1024];
  char     *pszArrow = NULL;
  BOOL      fAlloc = FALSE;
  EXCEPINFO ei;
  DWORD     dwSrcContext;
  ULONG     ulLine;
  LONG      ichError;
  BSTR      bstrLine = NULL;
  HRESULT   hr;

  CHECK(pse->GetExceptionInfo(&ei));
  CHECK(pse->GetSourcePosition(&dwSrcContext, &ulLine, &ichError));
  ulLine += g_clineOffset;    // Adjust for $ENGINE/$OBJECT/etc. lines
  hr = pse->GetSourceLineText(&bstrLine);
  if (hr)
    hr = S_OK;  // Ignore this error, there may not be source available

  if (!hr)
    {
    MAKE_ANSIPTR_FROMWIDE(pszSrc,  ei.bstrSource);
    MAKE_ANSIPTR_FROMWIDE(pszDesc, ei.bstrDescription);
    MAKE_ANSIPTR_FROMWIDE(pszLine, bstrLine);
    if (ichError > 0 && ichError < 255)
      {
      pszArrow = new char[ichError+1];
      memset(pszArrow, '-', ichError);
      pszArrow[ichError-1] = 'v';
      pszArrow[ichError]   = 0;
      fAlloc = TRUE;
      }
    else
      pszArrow = "";

    wsprintf(szError, "Source:'%s'\nFile:'%s'  Line:%d  Char:%d\nError:%d  '%s'\n%s\n%s",
                      pszSrc, g_pszCodeFile, ulLine, ichError,
                      (int)ei.wCode, pszDesc, pszArrow, pszLine);
    DisplayScriptError(g_hinst, m_hwndDlg, szError, ichError);
    }

CleanUp:
  if (fAlloc)
    delete [] pszArrow;
  if (bstrLine)
    SysFreeString(bstrLine);

  return hr;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnEnterScript
(
  void
)
{
  // No need to do anything
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::OnLeaveScript
(
  void
)
{
  // No need to do anything
  return S_OK;
}

//***************************************************************************
// IActiveScriptSiteWindow Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::GetWindow
(
  HWND *phwndOut
)
{
  if (!phwndOut)
    return E_INVALIDARG;
  *phwndOut = m_hwndDlg;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGame::EnableModeless
(
  BOOL fEnable
)
{
  if (fEnable)
    m_pcm->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
  else
    m_pcm->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
  return S_OK;
}
// #####  END  ACTIVEX SCRIPTING SUPPORT #####

//***************************************************************************
// Misc WndProc-related Methods
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL AppEvt_DlgProc
(
  HWND   hwnd,
  UINT   msg,
  WPARAM wp,
  LPARAM lp
)
{
  if (!g_pgame)
    return FALSE;

  return g_pgame->DlgProc(hwnd, msg, wp, lp);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CGame::DlgProc
(
  HWND   hwnd,
  UINT   msg,
  WPARAM wp,
  LPARAM lp
)
{
  switch (msg)
    {
    case WM_ERASEBKGND:
      if (m_pscore)
        {
        m_pscore->Paint((HDC)wp);
        return TRUE;
        }
      break;
    }
  return FALSE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
LRESULT AppEvt_PSWndProc
(
  HWND   hwnd,
  UINT   msg,
  WPARAM wp,
  LPARAM lp
)
{
  if (!g_pgame)
    return DefWindowProc(hwnd, msg, wp, lp);

  return g_pgame->PSWndProc(hwnd, msg, wp, lp);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
LRESULT CGame::PSWndProc
(
  HWND   hwnd,
  UINT   msg,
  WPARAM wp,
  LPARAM lp
)
{
  switch (msg)
    {
    case WM_PAINT:
      if (m_pdisp)
        {
        if (wp)
          m_pdisp->Paint((HDC)wp);
        else
          {
          PAINTSTRUCT ps;

          BeginPaint(hwnd, &ps);
          m_pdisp->Paint(ps.hdc);
          EndPaint(hwnd, &ps);
          }
        return 1L;
        }
      break;

    case WM_ERASEBKGND:
      SelectObject((HDC)wp, (HGDIOBJ)(m_pdisp->m_hbrushBack ? (HBRUSH)(m_pdisp->m_hbrushBack) : (HBRUSH)g_hbrushStock));
      PatBlt((HDC)wp, 0, 0, 32000, 32000, PATCOPY);
      SelectObject((HDC)wp, g_hbrushStock);
      return TRUE;

    case WM_SIZE:
      {
      RECT rect;

      GetClientRect(hwnd, &rect);
      m_pdisp->m_cx = rect.right;
      m_pdisp->m_cy = rect.bottom;
      break;
      }

    case WM_TIMER:
      {
      if (m_fGameOver)
        break;

      m_pdisp->Timer();

      if (m_fPaused)
        break;

      if (m_fShipRestart)
        {
        m_pdisp->Refresh();
        break;
        }

      if (!m_fShipDead)
        {
        static cRefresh = 0;

        m_pgameoa->FireTick();
        if (++cRefresh > 5)      // What's a reasonable number?
          {
          m_pdisp->Refresh();
          cRefresh = 0;
          }
        }
      else
        {
        m_pscore->ShipKilled();
        InvalidateRect(m_hwndDlg, NULL, TRUE);
        if (m_pscore->GetCShip() <= 0)
          {
          this->GameOver();
          break;
          }

        m_fShipDead = FALSE;
        m_fShipRestart = TRUE;
        InvalidateRect(hwnd, NULL, TRUE);
        }
      break;
      }

    case WM_KEYDOWN:
      m_pgameoa->FireKeyDown(wp);
      break;

    case WM_CHAR:
      m_pgameoa->FireKeyPress(wp);
      break;

    case WM_KEYUP:
      m_pgameoa->FireKeyUp(wp);
      break;

    case WM_MOUSEMOVE:
      m_pgameoa->FireMouseMove(LOWORD(lp), HIWORD(lp), wp);
      break;

    case WM_LBUTTONDOWN:
      m_pgameoa->FireMouseDown(LOWORD(lp), HIWORD(lp), wp, MK_LBUTTON);
      break;

    case WM_RBUTTONDOWN:
      m_pgameoa->FireMouseDown(LOWORD(lp), HIWORD(lp), wp, MK_RBUTTON);
      break;

    case WM_MBUTTONDOWN:
      m_pgameoa->FireMouseDown(LOWORD(lp), HIWORD(lp), wp, MK_MBUTTON);
      break;

    case WM_LBUTTONUP:
      m_pgameoa->FireMouseUp(LOWORD(lp), HIWORD(lp), wp, MK_LBUTTON);
      break;

    case WM_RBUTTONUP:
      m_pgameoa->FireMouseUp(LOWORD(lp), HIWORD(lp), wp, MK_RBUTTON);
      break;

    case WM_MBUTTONUP:
      m_pgameoa->FireMouseUp(LOWORD(lp), HIWORD(lp), wp, MK_MBUTTON);
      break;
    }

  return DefWindowProc(hwnd, msg, wp, lp);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
LRESULT CALLBACK ScriptErrorDialog
(
  HWND   hwnd,
  UINT   msg,
  WPARAM wp,
  LPARAM lp
)
{
  LPCSTR pszError;

  switch (msg)
    {
    case WM_INITDIALOG:
      pszError = (LPCSTR)lp;
      ShowWindow(hwnd, SW_HIDE);
      SendDlgItemMessage(hwnd, ID_CTL_ERROR, WM_SETFONT,
                         (WPARAM)GetStockObject(ANSI_FIXED_FONT), MAKELPARAM(FALSE,0));
      SendDlgItemMessage(hwnd, ID_CTL_ERROR, WM_SETTEXT, 0, (LPARAM)pszError);
      ShowWindow(hwnd, SW_SHOW);
      return (TRUE);

     case WM_COMMAND:
       switch(LOWORD(wp))
         {
         case IDOK:
         case IDCANCEL:

goto Destroy;
         }
       break;
    }
  return FALSE;

Destroy:
  EndDialog(hwnd, 0);
  return TRUE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
int DisplayScriptError
(
  HINSTANCE hinst,
  HWND      hwndParent,
  LPCSTR    pszError,
  int       ichError
)
{
  int   result;
  UINT  len = lstrlen(pszError);
  UINT  newlen;
  UINT  newindex;
  UINT  i;
  LPSTR pszNewError;

  // Figure out how long the real string will be
  newlen = 0;
  for (i=0; i<len; i++)
    {
    CHAR c = pszError[i];

    if (c == '\n')
      newlen +=1;               // Convert \n to \r\n
    else if (c == '\r')
      {
      if (pszError[i+1] == '\n')
        {
        newlen++;               // Leave \r\n alone
        i++;
        }
      else
        newlen++;               // Convert \r to \r\n
      }
    newlen++;
    }

  // Create the real string:
  pszNewError = new char[newlen+1];
  if (!pszNewError)
    return -1;

  newindex = 0;
  for (i=0; i<len; i++)
    {
    CHAR c = pszError[i];

    if (c == '\n')
      pszNewError[newindex++] = '\r';             // Convert \n to \r\n
    else if (c == '\r')
      {
      if (pszError[i+1] == '\n')
        {
        pszNewError[newindex++] = pszError[i++];  // Leave \r\n alone
        i++;
        }
      else
        {
        pszNewError[newindex++] = c;              // Convert \r to \r\n
        c = '\n';
        }
      }
    pszNewError[newindex++] = c;
    }
  pszNewError[newindex] = '\0';

  // Create the dialog box
  g_pgame->m_pcm->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
  g_pgame->Pause(1);
  result = DialogBoxParam(
  hinst,                         // Handle to application instance
  MAKEINTRESOURCE(ID_DLG_ERROR), // Identifies dialog box template name
  hwndParent,                    // Handle to owner window
  (DLGPROC)ScriptErrorDialog,    // Pointer to dialog box procedure
  (DWORD)pszNewError);           // Context
  g_pgame->m_pcm->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
  SetFocus(g_pgame->m_hwndPS);

  delete[] pszNewError;

  return result;
}

//***************************************************************************
// Misc General Methods
//***************************************************************************

//---------------------------------------------------------------------------
// New level
//---------------------------------------------------------------------------
void CGame::NewLevel
(
  void
)
{
  g_pgame->m_pdisp->DestroyAll();
  m_pscore->NextLevel();
  m_fShipRestart = TRUE;
  InvalidateRect(m_hwndPS,  NULL, TRUE);
  InvalidateRect(m_hwndDlg, NULL, TRUE);
  m_pgameoa->FireNewLevel();
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void AppEvt_NewGame
(
  void
)
{
  g_pgame->NewGame();
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGame::NewGame
(
  void
)
{
  m_pdisp->DestroyAll();
  m_pscore->NewGame();
  m_fGameOver    = FALSE;
  m_fShipRestart = TRUE;
  m_pgameoa->FireNewGame();
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL AppEvt_Pause
(
  int pause
)
{
  return g_pgame->Pause(pause);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CGame::Pause
(
  int pause
)
{
  if (pause == -1)            // Toggle
    pause = !m_fPaused;

  switch (pause)
    {
    case 1:                   // Pause
      if (m_fGameOver)
        goto UnPause;
      if (g_mode != MODE_PLAYBACK)
        KillTimer(m_hwndPS, 0);
      m_fPaused = TRUE;
      break;

    case 0:                   // UnPause
UnPause:
      if (g_mode != MODE_PLAYBACK)
        SetTimer(m_hwndPS, 0, 40, NULL);
      m_fPaused = FALSE;
      break;
    }

  if (m_fPaused)
    m_pscore->SetStatusText("Game Paused.  Press F3 to Continue.");
  else
    m_pscore->SetStatusText("");

  return m_fPaused;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL AppEvt_FQueryTerminate
(
  BOOL fPromptUser
)
{
  return g_pgame->FQueryTerminate(fPromptUser);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CGame::FQueryTerminate
(
  BOOL fPromptUser
)
{
  // UNDONE
  return TRUE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGame::GameOver
(
  void
)
{
  m_fGameOver = TRUE;
  InvalidateRect(m_hwndPS, NULL, TRUE);
  m_pscore->SetStatusText("Game Over.  Press F2 to Play a New Game.");
}

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
HRESULT CGame::CreateScriptEngine
(
  LPCOLESTR pstrItemName
)
{
  HRESULT hr;

  if (m_ps)
    return S_FALSE;   // Already created it

  // Create the ActiveX Scripting Engine
  hr = CoCreateInstance(m_clsidEngine, NULL, CLSCTX_INPROC_SERVER, IID_IActiveScript, (void **)&m_ps);
  if (hr)
    {
    s_pszError = "Creating the ActiveX Scripting engine failed.  Scripting engine is probably not correctly registered or CLSID incorrect.";
    return E_FAIL;
    }
  // Script Engine must support IActiveScriptParse for us to use it
  hr = m_ps->QueryInterface(IID_IActiveScriptParse, (void **)&m_psp);
  if (hr)
    {
    s_pszError = "ActiveX Scripting engine does not support IActiveScriptParse";
    return hr;
    }
  hr = m_ps->SetScriptSite(this);
  if (hr)
    return hr;
  // InitNew the object:
  hr = m_psp->InitNew();
  if (hr)
    return hr;
  hr = m_ps->AddNamedItem(L"Game", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE);
  return hr;
}
// #####  END  ACTIVEX SCRIPTING SUPPORT #####

//***************************************************************************
// Parsing support
//***************************************************************************

//---------------------------------------------------------------------------
// Skip white space, counting lines.  Treat \r & \n as white space only if
// fSkipNewLines is TRUE.
//---------------------------------------------------------------------------
void SkipWhite
(
  char **ppsz,
  BOOL   fSkipNewLines
)
{
  // Skip white space
  while (**ppsz && **ppsz<=' ')
    {
    if (**ppsz == '\r' || **ppsz == '\n')
      {
      if (!fSkipNewLines)
        return;
      if (**ppsz == '\r' && (*ppsz)[1] == '\n')
        ++*ppsz;
      g_clineOffset++;
      }
    ++*ppsz;
    }
}

//---------------------------------------------------------------------------
// Finds $OBJECT, $ENGINE, $INCLUDE, etc, parses past '=' to argument.  NULL
// terminates the argument, then returns a ptr to begining of arg & to beg
// of next line.
//---------------------------------------------------------------------------
struct
  {
  int   cch;
  char *psz;
  } g_rgtoken[] =
    {
      {7, "$ENGINE"},
      {7, "$OBJECT"},
      {8, "$INCLUDE"},
      {0, ""},
    };

int FindToken
(
  char    *pszCur,
  char   **ppszBeginArg,
  char   **ppszNextLine,
  HRESULT *phr,
  char   **ppszError
)
{
  char chQuote = 0;
  char ch;

  *phr = S_OK;
  SkipWhite(&pszCur, TRUE);
  if (*pszCur != '$')
    {
    *ppszNextLine = pszCur;
    return 0;   // No token found
    }

  for (int i=0; g_rgtoken[i].cch; i++)
    {
    if (!strncmp(pszCur, g_rgtoken[i].psz, g_rgtoken[i].cch))
      {
      pszCur += g_rgtoken[i].cch;
      SkipWhite(&pszCur, FALSE);
      if (*pszCur != '=')
        {
        *ppszError = "Missing '=' on $ENGINE, $OBJECT, or $INCLUDE line";
        *phr = E_FAIL;
        return 0;
        }
      pszCur++;
      SkipWhite(&pszCur, FALSE);
      if (*pszCur <= ' ')
        {
        *ppszError = "Missing argument on $ENGINE, $OBJECT, or $INCLUDE line";
        *phr = E_FAIL;
        return 0;
        }
      if (*pszCur=='"' || *pszCur=='\'' || *pszCur=='<')
        {
        chQuote = *pszCur;
        if (chQuote == '<')
          chQuote = '>';
        pszCur++;
        }
      *ppszBeginArg = pszCur;
      if (chQuote)
        {
        // Look for End of String, EOL, or End of Quote
        while (*pszCur && *pszCur!='\n' && *pszCur!='\r' && *pszCur!=chQuote)
          pszCur++;
        }
      else
        {
        // Skip non-white space
        while (*pszCur > ' ')
          pszCur++;
        }
      ch = *pszCur;
      *pszCur = 0;
NextChar:
      switch (ch)
        {
        default:
          // Look for EOL
          pszCur++;
          ch = *pszCur;
          goto NextChar;

        case '\r':
          // Skip the cr we're at
          g_clineOffset++;
          pszCur++;
          if (*pszCur == '\n')
            pszCur++;
          break;

        case '\n':
          // Skip the newline we're at
          g_clineOffset++;
          pszCur++;
          break;

        case 0:
           break;
        }

      *ppszNextLine = pszCur;
      return i+1;
      }
    }

  *ppszError = "unrecognized token; must be $ENGINE, $OBJECT, or $INCLUDE";
  *phr = E_FAIL;
  return 0;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
HRESULT CGame::ParseFile
(
  char     *pszFileName,
  LPCOLESTR pstrItemName
)
{
  struct _stat stat;
  size_t       cch;
  EXCEPINFO    ei;
  char        *pszT;
  char        *pszAlloc;
  FILE        *pfile;
  HRESULT      hr = S_OK;
  int          tk = 0;
  char        *pszArg;

  // Find the length of the file
  if (_stat(pszFileName, &stat))
    return E_FAIL;
  cch = stat.st_size;

  // Allocate a buffer to read the file
  pszAlloc = new char[cch+1];
  if (!pszAlloc)
    return E_OUTOFMEMORY;
  memset(pszAlloc, 0, cch);   // HACK: Since cch may be too large, zero-fill string

  // Open & read the file
  pfile = fopen(pszFileName, "rb");
  if (!pfile)
    {
    s_pszError = "Bad file name";
    hr = E_FAIL;
    goto Error;
    }
  fread(pszAlloc, cch, 1, pfile);
  fclose(pfile);

  // Parse lines begining w/$
  pszAlloc[cch] = 0;
  pszT = pszAlloc;
  while (tk=FindToken(pszT, &pszArg, &pszT, &hr, &s_pszError))
    {
    switch (tk)
      {
      case 1:   // $ENGINE
        {
        MAKE_WIDEPTR_FROMANSI(pwszCLSID, pszArg);
        hr = CLSIDFromString(pwszCLSID, &m_clsidEngine);
        if (hr)
          {
          s_pszError = "Bad $ENGINE={xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} syntax";
          goto Error;
          }
        hr = this->CreateScriptEngine(pstrItemName);
        if (hr == S_FALSE)
          {
          s_pszError = "$ENGINE must be first line; Script Engine already created.";
          goto Error;
          }
        else if (hr)
          goto Error;
        break;
        }

      case 2:  // $OBJECT
        if (m_csubobj >= csubobjMAX)
          {
          s_pszError = "Too many $OBJECT lines";
          goto Error;
          }

        m_rgpwszSubObjs[m_csubobj] = new WCHAR [strlen(pszArg)+1];
        if (!m_rgpwszSubObjs[m_csubobj])
          {
          s_pszError = "Out Of Memory";
          goto Error;
          }
        UNICODE_FROM_ANSI(m_rgpwszSubObjs[m_csubobj], pszArg, 2*strlen(pszArg));
        m_rgpdispSubObjs[m_csubobj] = NULL;
        m_rgpsc[m_csubobj] = NULL;
        m_csubobj++;
        break;

      case 3:   // $INCLUDE
        hr = this->ParseFile(pszArg, pstrItemName);
        if (hr)
          goto Error;
        break;
      }
    }

  // If there's anything left, assume it's code
  if (*pszT)
    {
// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
    // Make sure we've created the scripting engine
    if (!m_ps)
      {
      hr = this->CreateScriptEngine(pstrItemName);
      if (hr)
        goto Error;
      }

    // Hand it the code
    MAKE_WIDEPTR_FROMANSI(pwszCode, pszT);
    hr = m_psp->ParseScriptText(pwszCode, pstrItemName, NULL, NULL, 0, 0, 0L, NULL, &ei);
    hr = S_OK;    // Ignore parse errors, so user can press F9 to view source...
// #####  END  ACTIVEX SCRIPTING SUPPORT #####
    }

Error:
  delete [] pszAlloc;
  return hr;
}

//***************************************************************************
// Dynamic object model support
//***************************************************************************

//---------------------------------------------------------------------------
// Builds the typeinfo for the container object from the list of property
// objects.
//---------------------------------------------------------------------------
HRESULT CGame::BuildTypeInfo
(
  void
)
{
  static GUID guidStdOle = {0x00020430,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46};
  ITypeLib*        ptlStdOle   = NULL;
  ITypeInfo*       ptinfoIDisp = NULL;
  ICreateTypeLib * pcreatetl   = NULL;
  ICreateTypeInfo* pctiCoClass = NULL;// For creating the CoClass
  ICreateTypeInfo* pctiProps   = NULL;// For creating the Primary (Property) Dispinterface
  FUNCDESC         funcdesc    = {0};
  TYPEDESC         tdescUser   = {0};
  ELEMDESC         edArg       = {0};
  HREFTYPE         hreftype;
  int              index;
  int              i;
  HRESULT          hr;

  if (g_ptinfoIntGame && g_ptinfoClsGame)
    return S_OK;

  // Create TypeLib
  CHECKSZ(CreateTypeLib(SYS_WIN32, L"c:\\SprdSubO.tlb" /* szFile */, &pcreatetl), "CreateTypeLib FAILED");

  // Misc. global props of the TypeLib
  CHECKSZ(pcreatetl->SetGuid(m_libidSubObj), "TL SetGuid FAILED");
  CHECKSZ(pcreatetl->SetVersion(1, 0), "TL SetVersion FAILED");
  CHECKSZ(pcreatetl->SetName(L"Sprds"), "TL SetName FAILED");
  CHECKSZ(pcreatetl->SetLcid(LOCALE_SYSTEM_DEFAULT), "TL SetLcid FAILED");

  // Get Information on IDispatch
  CHECKSZ(LoadRegTypeLib(guidStdOle, STDOLE_MAJORVERNUM, STDOLE_MINORVERNUM, STDOLE_LCID, &ptlStdOle), "LoadRegTypeLib FAILED");
  CHECKSZ(ptlStdOle->GetTypeInfoOfGuid(IID_IDispatch, &ptinfoIDisp), "GetTypeInfoOfGuid (IID_IDispatch) FAILED");

  // Create TypeInfo
  CHECKSZ(pcreatetl->CreateTypeInfo(L"GameSubObjs", TKIND_DISPATCH, &pctiProps), "Create TI FAILED");
  CHECKSZ(pctiProps->SetGuid(m_iidSubObj), "cti SetGuid FAILED");
  CHECKSZ(pctiProps->SetVersion(1, 0), "cti SetVersion FAILED");
  CHECKSZ(pctiProps->AddRefTypeInfo(ptinfoIDisp, &hreftype), "cti AddRefTypeInfoo FAILED"); // Reference StdOLE
  CHECKSZ(pctiProps->AddImplType(0, hreftype), "cti AddImplType FAILED");

  funcdesc.funckind = FUNC_DISPATCH;
  funcdesc.invkind  = INVOKE_PROPERTYGET;
  funcdesc.callconv = CC_STDCALL;
  funcdesc.cScodes  = -1;
  funcdesc.elemdescFunc.tdesc.vt = VT_PTR;
  funcdesc.elemdescFunc.tdesc.lptdesc = &tdescUser;
  tdescUser.vt = VT_USERDEFINED;

  // Loop through all of the SpriteClasses ($OBJECTs) in this game, adding a
  // property to pctiProps which corresponds to the control.
  index = 0;
  for (i=0; i<m_csubobj; i++)
    {
    // Need to make sure this starts out cleared
    if (!g_ptinfoClsSpriteClass)
      {
      CHECKCL(LoadTypeInfo(g_hinst, 0, 1, 0, 0x0409,
                           LIBID_SPRUUIDS, CLSID_SpriteClass, IID_ISpriteClass, FALSE,
                           &g_ptlMain, &g_ptinfoClsSpriteClass, &g_ptinfoIntSpriteClass));
      }
    CHECKCL(pctiProps->AddRefTypeInfo(g_ptinfoClsSpriteClass, &tdescUser.hreftype));
    funcdesc.memid = 0x80010000 | i;
    funcdesc.wFuncFlags = FUNCFLAG_FSOURCE;

    CHECKCL(pctiProps->AddFuncDesc(index, &funcdesc));
    CHECKCL(pctiProps->SetFuncAndParamNames(index, &m_rgpwszSubObjs[i], 1));

    index++;
    continue;

CheckLoop:
    // Do any clean up here

    CHECKSZ(hr,"BuildTypeInfo FAILED");
    break;
    }

  // Finish off the TI for the Props
  CHECKSZ(pctiProps->LayOut(), "BuildTypeInfo FAILED");
  CHECKSZ(pctiProps->QueryInterface(IID_ITypeInfo, (void **)&g_ptinfoIntGame), "BuildTypeInfo FAILED");

  // Now we have to create a coclass for the interface
  CHECKSZ(pcreatetl->CreateTypeInfo(L"Game", TKIND_COCLASS, &pctiCoClass), "BuildTypeInfo FAILED");
  CHECKSZ(pctiCoClass->SetGuid(m_clsidSubObj), "BuildTypeInfo FAILED");
  CHECKSZ(pctiCoClass->SetVersion(1, 0), "BuildTypeInfo FAILED");

  // Add the Page Property dispinterface to coclass
  CHECKSZ(pctiCoClass->AddRefTypeInfo(g_ptinfoIntGame, &hreftype), "BuildTypeInfo FAILED");
  CHECKSZ(pctiCoClass->AddImplType(0, hreftype), "BuildTypeInfo FAILED");
  CHECKSZ(pctiCoClass->SetImplTypeFlags(0, IMPLTYPEFLAG_FDEFAULT), "BuildTypeInfo FAILED");

  // Finish off the CoClass
  CHECKSZ(pctiCoClass->LayOut(), "BuildTypeInfo FAILED");
  CHECKSZ(pctiCoClass->QueryInterface(IID_ITypeInfo, (void **)&g_ptinfoClsGame), "BuildTypeInfo FAILED");

  // Finish off the Library
  CHECKSZ(pcreatetl->QueryInterface(IID_ITypeLib, (void **)&g_ptlGameSubObj), "BuildTypeLib FAILED");

CleanUp:
  // Code to release refs aquired during page build.
  if (pctiCoClass)
    pctiCoClass->Release();
  if (pctiProps)
    pctiProps->Release();
  if (ptinfoIDisp)
    ptinfoIDisp->Release();
  if (ptlStdOle)
    ptlStdOle->Release();
  if (pcreatetl)
    pcreatetl->Release();

  return hr;
};

//--- EOF -------------------------------------------------------------------


GAME.H

//---------------------------------------------------------------------------
// Game.h
//---------------------------------------------------------------------------
// Sample spr program
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Forward Delcares
//---------------------------------------------------------------------------
class CDisplay;
class CSpriteClass;
class CSprite;
class CScore;
class CGameOA;

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
DEFINE_GUID(IID_IGameSubObjects, 0xb54f3741, 0x5b07, 0x11cf, 0xa4, 0xb0, 0x0, 0xaa, 0x0, 0x4a, 0x55, 0xe8);

//---------------------------------------------------------------------------
// the CGame class
//---------------------------------------------------------------------------
class CGame : public COleAuto,
              public IActiveScriptSite, public IActiveScriptSiteWindow,
              public IProvideMultipleClassInfo
  {
  friend class CGameOA;

public:
  static HRESULT CGame::CreateGame(HINSTANCE hinst, HWND hwnd, HWND hwndPS, HWND hwndStat,
                                   IOleComponentManager *pcm, CGame **ppgameOut);
  CGame(HINSTANCE hinst, HWND hwnd, HWND hwndPS, HWND hwndStat, IOleComponentManager *pcm);
  ~CGame();
  void Close(void);

  // *** IUnknown methods ***
  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj);
  STDMETHOD_(ULONG, AddRef)(void);
  STDMETHOD_(ULONG, Release)(void);
  inline IUnknown  *GetUnknown(void)  {return (IDispatch *)(COleAuto *)this;};
  inline IDispatch *GetDispatch(void) {return (IDispatch *)(COleAuto *)this;};

  // *** IDispatch methods ***
  STDMETHOD(GetTypeInfoCount)(UINT *pctinfo)
    { return COleAuto::GetTypeInfoCount(pctinfo); };
  STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo **pptinfoOut)
    { return COleAuto::GetTypeInfo(itinfo, lcid, pptinfoOut); };
  STDMETHOD(GetIDsOfNames)(REFIID iid, OLECHAR **rgszNames, UINT cnames, LCID lcid, DISPID *rgdispid);
  STDMETHOD(Invoke)(DISPID dispid, REFIID iid, LCID lcid, WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pVarResult, EXCEPINFO *pexcepinfo, UINT *puArgErr);

  // Override required by COleAuto:
  virtual ITypeInfo **GetTinfoClsAddr(void) { return &g_ptinfoClsGame; };
  virtual ITypeInfo **GetTinfoIntAddr(void) { return &g_ptinfoIntGame; };
  virtual HRESULT GetTypeLibInfo(HINSTANCE *phinstOut, const GUID **pplibidOut,
                                 SHORT *pwMajLibOut, SHORT *pwMinLibOut,
                                 const CLSID **ppclsidOut, const IID **ppiidOut,
                                 ITypeLib ***ppptlOut);
  virtual IDispatch *GetPrimary() {return this->GetDispatch(); };

  // *** IProvideClassInfo methods ***
  STDMETHOD(GetClassInfo)(ITypeInfo** pptinfoOut);
  // *** IProvideClassInfo2 methods ***
  STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID* pGUID);
  // *** IProvideMultipleClassInfo methods ***
  STDMETHOD(GetMultiTypeInfoCount)(ULONG *pcti);
  STDMETHOD(GetInfoOfIndex)(ULONG iti, DWORD dwFlags, ITypeInfo** pptiCoClass, DWORD* pdwTIFlags, ULONG* pcdispidReserved, IID* piidPrimary, IID* piidSource);

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
  // *** IActiveScriptSite methods ***
  STDMETHOD(GetLCID)(LCID *plcid);
  STDMETHOD(GetItemInfo)(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti);
  STDMETHOD(GetDocVersionString)(BSTR *pszVersion);
  STDMETHOD(RequestItems)(void);
  STDMETHOD(RequestTypeLibs)(void);
  STDMETHOD(OnScriptTerminate)(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo);
  STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState);
  STDMETHOD(OnScriptError)(IActiveScriptError *pscripterror);
  STDMETHOD(OnEnterScript)(void);
  STDMETHOD(OnLeaveScript)(void);

  // *** IActiveScriptSiteWindow methods ***
  STDMETHOD(GetWindow)(HWND *phwnd);
  STDMETHOD(EnableModeless)(BOOL fEnable);
// #####  END  ACTIVEX SCRIPTING SUPPORT #####

  class CGameOA *m_pgameoa;

  // *** General Methods ***
  BOOL    DlgProc(  HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
  LRESULT PSWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
  void    NewLevel(void);
  void    NewGame(void);
  BOOL    Pause(int pause);
  BOOL    FQueryTerminate(BOOL fPromptUser);
  void    GameOver(void);
  HRESULT ParseFile(char *pszFileName, LPCOLESTR pstrItemName);
  HRESULT CreateScriptEngine(LPCOLESTR pstrItemName);
  HRESULT BuildTypeInfo(void);

  // *** Member Variables ***
  HINSTANCE  m_hinst;
  HWND       m_hwndDlg;
  HWND       m_hwndPS;
  HWND       m_hwndStat;
  CScore    *m_pscore;
  CDisplay  *m_pdisp;
  UINT       m_cref;
  VARIANT    m_varTag;
  IOleComponentManager *m_pcm;
// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####
  IActiveScript        *m_ps;
  IActiveScriptParse   *m_psp;
// #####  END  ACTIVEX SCRIPTING SUPPORT #####

  CLSID          m_clsidEngine;
  static char   *s_pszError;

  // Sub-Object support
#define csubobjMAX    30
  int           m_csubobj;
  LPOLESTR      m_rgpwszSubObjs[csubobjMAX];   // Should really dynamically allocate this array
  IDispatch    *m_rgpdispSubObjs[csubobjMAX];  // Should really dynamically allocate this array
  CSpriteClass *m_rgpsc[csubobjMAX];
  GUID          m_libidSubObj;
  CLSID         m_clsidSubObj;
  IID           m_iidSubObj;

  // These reset every new game
  BOOL m_fPaused:1;
  BOOL m_fShipDead:1;
  BOOL m_fShipRestart:1;
  BOOL m_fGameOver:1;

  // DEBUG info
  static HRESULT s_hr;
  #define SIG_Game 'Game'
  DECLARE_SIGNATURE(SIG_Game);
  };

//---------------------------------------------------------------------------
// the CGameOA class; Ole automation implementation for static members
// of the IGame interface.
//---------------------------------------------------------------------------
class CGameOA : public IGame, public COleAuto, public IProvideClassInfo,
                public IConnectionPointContainer
  {
public:
  // *** Constructor / Destructor ***
  CGameOA::CGameOA(CGame *);

  // *** IUnknown methods ***
  STDMETHOD(QueryInterface)(REFIID iid, void** ppvObj) {return m_pgame->QueryInterface(iid, ppvObj);};
  STDMETHOD_(ULONG, AddRef) (void)                     {return m_pgame->AddRef();};
  STDMETHOD_(ULONG, Release)(void)                     {return m_pgame->Release();};
  inline IDispatch *GetDispatch(void) {return (IGame *)this;};

  // *** IDispatch methods ***
  DECLARE_STANDARD_DISPATCH();
  // Override required by COleAuto:
  virtual ITypeInfo **GetTinfoClsAddr(void) { return &g_ptinfoClsGameOA; };
  virtual ITypeInfo **GetTinfoIntAddr(void) { return &g_ptinfoIntGameOA; };
  virtual HRESULT GetTypeLibInfo(HINSTANCE *phinstOut, const GUID **pplibidOut,
                                 SHORT *pwMajLibOut, SHORT *pwMinLibOut,
                                 const CLSID **ppclsidOut, const IID **ppiidOut,
                                 ITypeLib ***ppptlOut);
  virtual IDispatch *GetPrimary() {return this->GetDispatch(); };

  // *** IProvideClassInfo methods ***
  STDMETHOD(GetClassInfo)(ITypeInfo** pptinfoOut);

  // *** IConnectionPointContainer methods ***
  STDMETHOD(EnumConnectionPoints)(LPENUMCONNECTIONPOINTS* ppEnum);
  STDMETHOD(FindConnectionPoint)(REFIID iid, LPCONNECTIONPOINT* ppCP);

  // *** IGame methods ***
  STDMETHOD(get_Caption)(BSTR* lpbstrReturn);
  STDMETHOD(put_Caption)(BSTR bstr);
  STDMETHOD(get_Application)(ISpruuidsApp** lppaReturn);
  STDMETHOD(get_Parent)(ISpruuidsApp** lppaReturn);
  STDMETHOD(StartGame)(void);
  STDMETHOD(EndGame)(void);
  STDMETHOD(RemoveAllSprites)(void);
  STDMETHOD(NextLevel)(void);
  STDMETHOD(Refresh)(void);
  STDMETHOD(AddScore)(int val);
  STDMETHOD(StdBorderBounce)(ISprite* pspr, int brd);
  STDMETHOD(StdBorderWrap)(  ISprite* pspr, int brd);
  STDMETHOD(StdInitRand)(ISprite* pspr, VARIANT user);
  STDMETHOD(StdInitEdge)(ISprite* pspr, VARIANT user);
  STDMETHOD(get_Paused)(VARIANT_BOOL* pRet);
  STDMETHOD(put_Paused)(VARIANT_BOOL val);
  STDMETHOD(get_Width)(int* pRet);
  STDMETHOD(put_Width)(int val);
  STDMETHOD(get_Height)(int* pRet);
  STDMETHOD(put_Height)(int val);
  STDMETHOD(get_BackColor)(long* pRet);
  STDMETHOD(put_BackColor)(long val);
  STDMETHOD(get_Score)(int* pRet);
  STDMETHOD(put_Score)(int val);
  STDMETHOD(get_Level)(int* pRet);
  STDMETHOD(put_Level)(int val);
  STDMETHOD(get_ShipCount)(int* pRet);
  STDMETHOD(put_ShipCount)(int val);
  STDMETHOD(get_ScoreFirst1Up)(int* pRet);
  STDMETHOD(put_ScoreFirst1Up)(int val);
  STDMETHOD(get_ScoreSecond1Up)(int* pRet);
  STDMETHOD(put_ScoreSecond1Up)(int val);
  STDMETHOD(get_DScoreNext1Up)(int* pRet);
  STDMETHOD(put_DScoreNext1Up)(int val);
  STDMETHOD(get_ShipsStart)(int* pRet);
  STDMETHOD(put_ShipsStart)(int val);
  STDMETHOD(get_Tag)(VARIANT* pRet);
  STDMETHOD(put_Tag)(VARIANT val);
  STDMETHOD(get_StatusText)(BSTR* lpbstrReturn);
  STDMETHOD(put_StatusText)(BSTR bstr);

  class XCP : public IConnectionPoint
    {
    inline CGameOA *PGAMEOA(void) { return ((CGameOA *)((char *)this - (char *)&(((CGameOA *)0)->m_cp))); }

  public:
    XCP();
    void Close(void);

    // *** IUnknown methods ***
    STDMETHOD(QueryInterface)(REFIID iid, void** ppvObjOut);
    STDMETHOD_(ULONG, AddRef)(void);
    STDMETHOD_(ULONG, Release)(void);
    inline IUnknown *GetUnknown(void) {return (IConnectionPoint *)this;};

    // *** IConnectionPoint methods ***
    STDMETHOD(GetConnectionInterface)(IID* piid);
    STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer** ppCPC);
    STDMETHOD(Advise)(IUnknown* punkSink, DWORD* pdwCookie);
    STDMETHOD(Unadvise)(DWORD dwCookie);
    STDMETHOD(EnumConnections)(LPENUMCONNECTIONS* ppEnum);

    // *** Member Variables ***
    ULONG     m_cref;
#define GAME_cADVISE 5
    IDispatch *m_rgpdisp[GAME_cADVISE];     // Array of pdisps to call.
    } m_cp;

  // *** Event Methods ***
  void FireNewGame(void);
  void FireNewLevel(void);
  void FireNewShip(void);
  static void FireCollide(CSprite *psprMoved, CSprite *psprHit, int);
  void FireTick(void);
  void FireKeyDown(int vk);
  void FireKeyPress(int ascii);
  void FireKeyUp(int vk);
  void FireMouseMove(int x, int y, long mk);
  void FireMouseDown(int x, int y, long mk, long button);
  void FireMouseUp(  int x, int y, long mk, long button);

  // *** Member Variable ***
  CGame *m_pgame;
  };

//--- EOF -------------------------------------------------------------------


GAMEOA.CPP

//---------------------------------------------------------------------------
// GameOA.cpp
//---------------------------------------------------------------------------
// Sample spr program, OLE Automation implementation
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop
#include "Game.h"
#include "App.h"
#include "Spr.h"
#include "Score.h"
#include "DispIDs.h"
#include <math.h>

//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE

//---------------------------------------------------------------------------
// Various Globals
//---------------------------------------------------------------------------
extern CGame *g_pgame;
ITypeInfo *g_ptinfoClsGameOA = NULL;
ITypeInfo *g_ptinfoIntGameOA = NULL;

//***************************************************************************
// Constructor / Destructor
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
CGameOA::CGameOA
(
  CGame *pgame
)
{
  m_pgame           = pgame;
  m_pdispBaseObject = NULL;
}

//***************************************************************************
// Fire IGameEvents Events
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireNewGame
(
  void
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_NewGame, NULL, 0);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireNewLevel
(
  void
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_NewLevel, NULL, 0);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireNewShip
(
  void
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_NewShip, NULL, 0);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireCollide
(
  CSprite *psprLowId,
  CSprite *psprHighId,
  int      maskCollide
)
{
  IDispatch **ppdisp = g_pgame->m_pgameoa->m_cp.m_rgpdisp;
  VARIANTARG  var[3];

  VariantInit(&var[2]);
  var[2].vt = VT_DISPATCH;
  var[2].pdispVal = psprLowId->GetDispatch();
  psprLowId->AddRef();
  VariantInit(&var[1]);
  var[1].vt = VT_DISPATCH;
  var[1].pdispVal = psprHighId->GetDispatch();
  psprHighId->AddRef();
  VariantInit(&var[0]);
  var[0].vt = VT_I4;
  var[0].lVal = maskCollide;

  for (; ppdisp < &g_pgame->m_pgameoa->m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_Collide, var, 3);

  psprLowId->Release();     // For var[2].pdispVal
  psprHighId->Release();    // For var[1].pdispVal
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireTick
(
  void
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_Tick, NULL, 0);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireKeyDown
(
  int vk
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[2];

  VariantInit(&var[0]);
  var[0].vt   = VT_I4;
  var[0].lVal = vk;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_KeyDown, var, 1);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireKeyPress
(
  int ascii
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[1];

  VariantInit(&var[0]);
  var[0].vt   = VT_I4;
  var[0].lVal = ascii;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_KeyPress, var, 1);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireKeyUp
(
  int vk
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[2];

  VariantInit(&var[0]);
  var[0].vt   = VT_I4;
  var[0].lVal = vk;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_KeyUp, var, 1);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireMouseMove
(
  int  x,
  int  y,
  long mk
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[4];

  VariantInit(&var[3]);
  var[3].vt = VT_I4;
  var[3].lVal = (mk & MK_SHIFT   ? 1 : 0) |
                (mk & MK_CONTROL ? 2 : 0);
  VariantInit(&var[2]);
  var[2].vt = VT_I4;
  var[2].lVal = (mk & MK_LBUTTON ? 1 : 0) |
                (mk & MK_RBUTTON ? 2 : 0) |
                (mk & MK_MBUTTON ? 4 : 0);
  VariantInit(&var[1]);
  var[1].vt = VT_I4;
  var[1].lVal = x;
  VariantInit(&var[0]);
  var[0].vt = VT_I4;
  var[0].lVal = y;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_MouseMove, var, 4);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireMouseDown
(
  int  x,
  int  y,
  long mk,
  long button
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[4];

  VariantInit(&var[3]);
  var[3].vt = VT_I4;
  var[3].lVal = (mk & MK_SHIFT   ? 1 : 0) |
                (mk & MK_CONTROL ? 2 : 0);
  VariantInit(&var[2]);
  var[2].vt = VT_I4;
  var[2].lVal = (button & MK_LBUTTON ? 1 : 0) |
                (button & MK_RBUTTON ? 2 : 0) |
                (button & MK_MBUTTON ? 4 : 0);
  VariantInit(&var[1]);
  var[1].vt = VT_I4;
  var[1].lVal = x;
  VariantInit(&var[0]);
  var[0].vt = VT_I4;
  var[0].lVal = y;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_MouseDown, var, 4);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::FireMouseUp
(
  int  x,
  int  y,
  long mk,
  long button
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[4];

  VariantInit(&var[3]);
  var[3].vt = VT_I4;
  var[3].lVal = (mk & MK_SHIFT   ? 1 : 0) |
                (mk & MK_CONTROL ? 2 : 0);
  VariantInit(&var[2]);
  var[2].vt = VT_I4;
  var[2].lVal = (button & MK_LBUTTON ? 1 : 0) |
                (button & MK_RBUTTON ? 2 : 0) |
                (button & MK_MBUTTON ? 4 : 0);
  VariantInit(&var[1]);
  var[1].vt = VT_I4;
  var[1].lVal = x;
  VariantInit(&var[0]);
  var[0].vt = VT_I4;
  var[0].lVal = y;

  for (; ppdisp < &m_cp.m_rgpdisp[GAME_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_GameEvents_MouseUp, var, 4);
}

//***************************************************************************
// IGame Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_Caption
(
  BSTR* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  UINT cch = GetWindowTextLength(m_pgame->m_hwndDlg);
  char *pszT = new char[cch+1];
  if (!pszT)
    return E_OUTOFMEMORY;
  *pRet = SysAllocStringLen(NULL, cch);
  if (!*pRet)
    {
    delete [] pszT;
    return E_OUTOFMEMORY;
    }
  GetWindowText(m_pgame->m_hwndDlg, pszT, cch+1);
  MultiByteToWideChar(CP_ACP, 0, pszT, -1, *pRet, cch+1);
  delete [] pszT;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_Caption
(
  BSTR bstr
)
{
  WCHAR *pwsz = bstr ? bstr : L"";
  MAKE_ANSIPTR_FROMWIDE(pszT, pwsz);
  SetWindowText(m_pgame->m_hwndDlg, pszT);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_StatusText
(
  BSTR* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  UINT cch = GetWindowTextLength(m_pgame->m_hwndStat);
  char *pszT = new char[cch+1];
  if (!pszT)
    return E_OUTOFMEMORY;
  *pRet = SysAllocStringLen(NULL, cch);
  if (!*pRet)
    {
    delete [] pszT;
    return E_OUTOFMEMORY;
    }
  GetWindowText(m_pgame->m_hwndStat, pszT, cch+1);
  MultiByteToWideChar(CP_ACP, 0, pszT, -1, *pRet, cch+1);
  delete [] pszT;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_StatusText
(
  BSTR bstr
)
{
  WCHAR *pwsz = bstr ? bstr : L"";
  MAKE_ANSIPTR_FROMWIDE(pszT, pwsz);
  m_pgame->m_pscore->SetStatusText(pszT);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_Application
(
  ISpruuidsApp** lppaReturn
)
{
  return g_papp->QueryInterface(IID_ISpruuidsApp, (void **)lppaReturn);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_Parent
(
  ISpruuidsApp** lppaReturn
)
{
  return g_papp->QueryInterface(IID_ISpruuidsApp, (void **)lppaReturn);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::StartGame
(
  void
)
{
  m_pgame->NewGame();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::EndGame
(
  void
)
{
  m_pgame->GameOver();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::RemoveAllSprites
(
  void
)
{
  m_pgame->m_pdisp->DestroyAll();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::NextLevel
(
  void
)
{
  m_pgame->NewLevel();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::Refresh
(
  void
)
{
  m_pgame->m_pdisp->Refresh();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::AddScore
(
  int val
)
{
  m_pgame->m_pscore->Add(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::StdBorderBounce
(
  ISprite *psprite,
  int      brd
)
{
  CSprite *pspr = SPRITEOFI(psprite);

  if ((brd & SPR_brdTOP) && pspr->m_vyFull < 0)
    {
    pspr->m_vyFull = -pspr->m_vyFull;
    pspr->IgnoreMove();
    }
  if ((brd & SPR_brdLEFT) && pspr->m_vxFull < 0)
    {
    pspr->m_vxFull = -pspr->m_vxFull;
    pspr->IgnoreMove();
    }
  if ((brd & SPR_brdBOTTOM) && pspr->m_vyFull > 0)
    {
    pspr->m_vyFull = -pspr->m_vyFull;
    pspr->IgnoreMove();
    }
  if ((brd & SPR_brdRIGHT) && pspr->m_vxFull > 0)
    {
    pspr->m_vxFull = -pspr->m_vxFull;
    pspr->IgnoreMove();
    }
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::StdBorderWrap
(
  ISprite *psprite,
  int      brd
)
{
  CSprite *pspr = SPRITEOFI(psprite);

  if ((brd & SPR_brdTOP) && pspr->m_vyFull < 0)
    pspr->m_y = pspr->m_pdisp->m_cy;
  if ((brd & SPR_brdLEFT) && pspr->m_vxFull < 0)
    pspr->m_x = pspr->m_pdisp->m_cx;
  if ((brd & SPR_brdBOTTOM) && pspr->m_vyFull > 0)
    pspr->m_y = -pspr->m_pimg->cy;
  if ((brd & SPR_brdRIGHT) && pspr->m_vxFull > 0)
    pspr->m_x = -pspr->m_pimg->cx;
  return S_OK;
}

//---------------------------------------------------------------------------
// Generate a random number between 0 and n.
//---------------------------------------------------------------------------
int MyRand
(
  int n
)
{
  int t, u;
  static nLast=0, tLast=0;

  if (n <= 1)
    return 0;

  n &= RAND_MAX;
  if (n == nLast)
    t = tLast;
  else
    {
    nLast = n;
    for (t=2; t<n; t<<=1)
      ;
    t--;
    tLast = t;
    }

  do
    {
    u = t & rand();
    } while (u >= n);
  return u;
}

//---------------------------------------------------------------------------
// Init to random position which doesn't cause a collision event.
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::StdInitRand
(
  ISprite *psprite,
  VARIANT  varUser
)
{
  CSprite *pspr = SPRITEOFI(psprite);
  CSprite *psprT;
  LONG     x, y, dx, dy;
  int      cx, cy;
  int      lUser = (varUser.vt == VT_I4) ? varUser.lVal : 0;
  LONG     dist = 8128;

  cx = pspr->m_pimg->cx;
  cy = pspr->m_pimg->cy;
  do
    {
Loop:
    x = MyRand(pspr->m_pdisp->m_cx - cx);
    y = MyRand(pspr->m_pdisp->m_cy - cy);
    for (psprT=pspr->m_pdisp->m_psprFirst; psprT; psprT=psprT->m_psprNext)
      {
      if (!(psprT->m_psc->m_maskCollide & pspr->m_psc->m_maskCollide) && // if potential collision doesn't matter
          x <= psprT->m_x+psprT->m_pimg->cx && x+cx >= psprT->m_x &&  // and we collide
          y <= psprT->m_y+psprT->m_pimg->cy && y+cy >= psprT->m_y)
goto Loop;                              // then try another location
      }
    dx = (x - (pspr->m_pdisp->m_cx>>1));
    dy = (y - (pspr->m_pdisp->m_cy>>1));
    } while (dx*dx + dy*dy < dist);
  pspr->m_x = (int)x;
  pspr->m_y = (int)y;
  return S_OK;
}

//---------------------------------------------------------------------------
// Init to random position along edge of disp.
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::StdInitEdge
(
  ISprite *psprite,
  VARIANT  varUser
)
{
  CSprite *pspr = SPRITEOFI(psprite);

  if (rand() & 1)
    {
    pspr->m_x = MyRand(pspr->m_pdisp->m_cx - pspr->m_pimg->cx);
    if (rand() & 1)
      pspr->m_y = pspr->m_pdisp->m_cy - pspr->m_pimg->cy;
    }
  else
    {
    pspr->m_y = MyRand(pspr->m_pdisp->m_cy - pspr->m_pimg->cy);
    if (rand() & 1)
      pspr->m_x = pspr->m_pdisp->m_cx - pspr->m_pimg->cx;
    }
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_Paused
(
  VARIANT_BOOL* pRet
)
{
  *pRet = m_pgame->m_fPaused;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_Paused
(
  VARIANT_BOOL val
)
{
  m_pgame->Pause(!!val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_Width
(
  int* pRet
)
{
  *pRet = m_pgame->m_pdisp->m_cx;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_Width
(
  int val
)
{
  if (val < 20 || val > 1024)
    return E_FAIL;
  m_pgame->m_pscore->Size(val, m_pgame->m_pdisp->m_cy);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_Height
(
  int* pRet
)
{
  *pRet = m_pgame->m_pdisp->m_cy;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_Height
(
  int val
)
{
  if (val < 20 || val > 1024)
    return E_FAIL;
  m_pgame->m_pscore->Size(m_pgame->m_pdisp->m_cx, val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_BackColor
(
  long* pRet
)
{
  *pRet = m_pgame->m_pdisp->m_colorBack;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_BackColor
(
  long val
)
{
  if (val & 0xff000000)
    return E_FAIL;
  m_pgame->m_pdisp->SetBackColor(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_Score
(
  int* pRet
)
{
  *pRet = m_pgame->m_pscore->GetScore();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_Score
(
  int val
)
{
  m_pgame->m_pscore->SetScore(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_Level
(
  int* pRet
)
{
  *pRet = m_pgame->m_pscore->GetLevel();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_Level
(
  int val
)
{
  m_pgame->m_pscore->SetLevel(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_ShipCount
(
  int* pRet
)
{
  *pRet = m_pgame->m_pscore->GetCShip();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_ShipCount
(
  int val
)
{
  m_pgame->m_pscore->SetCShip(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_ScoreFirst1Up
(
  int* pRet
)
{
  *pRet = m_pgame->m_pscore->m_scoreFirst1Up;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_ScoreFirst1Up
(
  int val
)
{
  if (val < 0)
    return E_FAIL;
  m_pgame->m_pscore->m_scoreFirst1Up = val;
  if (m_pgame->m_pscore->m_scoreSecond1Up < val)
    m_pgame->m_pscore->m_scoreSecond1Up = val;
  if (m_pgame->m_pscore->GetScore() < m_pgame->m_pscore->m_scoreFirst1Up)
    m_pgame->m_pscore->m_scoreNext1Up = val;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_ScoreSecond1Up
(
  int* pRet
)
{
  *pRet = m_pgame->m_pscore->m_scoreSecond1Up;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_ScoreSecond1Up
(
  int val
)
{
  if (val < 0)
    return E_FAIL;
  m_pgame->m_pscore->m_scoreSecond1Up = val;
  if (val < m_pgame->m_pscore->m_scoreFirst1Up)
    m_pgame->m_pscore->m_scoreFirst1Up = val;
  if (m_pgame->m_pscore->GetScore() > m_pgame->m_pscore->m_scoreFirst1Up  &&
      m_pgame->m_pscore->GetScore() < m_pgame->m_pscore->m_scoreSecond1Up)
    m_pgame->m_pscore->m_scoreNext1Up = val;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_DScoreNext1Up
(
  int* pRet
)
{
  *pRet = m_pgame->m_pscore->m_dscoreNext1Up;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_DScoreNext1Up
(
  int val
)
{
  if (val < 0)
    return E_FAIL;
  m_pgame->m_pscore->m_dscoreNext1Up = val;
  if (m_pgame->m_pscore->GetScore() > m_pgame->m_pscore->m_scoreFirst1Up && m_pgame->m_pscore->GetScore() > 0)
    {
    m_pgame->m_pscore->m_scoreNext1Up = m_pgame->m_pscore->m_scoreFirst1Up;
    while (m_pgame->m_pscore->m_scoreNext1Up < m_pgame->m_pscore->GetScore())
      m_pgame->m_pscore->m_scoreNext1Up += m_pgame->m_pscore->m_dscoreNext1Up;
    }
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_ShipsStart
(
  int* pRet
)
{
  *pRet = m_pgame->m_pscore->m_cshipStart;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_ShipsStart
(
  int val
)
{
  if (val < 0)
    return E_FAIL;
  m_pgame->m_pscore->m_cshipStart = val;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::get_Tag
(
  VARIANT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  VariantInit(pRet);
  return VariantCopy(pRet, &m_pgame->m_varTag);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::put_Tag
(
  VARIANT val
)
{
  return VariantCopy(&m_pgame->m_varTag, &val);
}

//***************************************************************************
// IDispatch Interface
//***************************************************************************

//---------------------------------------------------------------------------
// Method needed by COleAuto, so it can implement IDispatch for us.
//---------------------------------------------------------------------------
HRESULT CGameOA::GetTypeLibInfo
(
  HINSTANCE    *phinstOut,
  const GUID  **pplibidOut,
  SHORT        *pwMajLib,
  SHORT        *pwMinLib,
  const CLSID **ppclsidOut,
  const IID   **ppiidOut,
  ITypeLib   ***ppptlOut
)
{
  *phinstOut  = g_hinst;
  *pplibidOut = &LIBID_SPRUUIDS;
  *pwMajLib   = 1;
  *pwMinLib   = 0;
  *ppclsidOut = &CLSID_Game;
  *ppiidOut   = &IID_IGame;
  *ppptlOut   = &g_ptlMain;
  return S_OK;
}

//***************************************************************************
// IProvideClassInfo Interfaces
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::GetClassInfo
(
  ITypeInfo** pptinfoOut
)
{
  HRESULT hr = ((COleAuto *)this)->CheckTypeInfo(0, 0x0409);
  if (hr)
    return hr;
  *pptinfoOut = g_ptinfoClsGameOA;
  (*pptinfoOut)->AddRef();
  return S_OK;
}

//***************************************************************************
// IConnectionPointContainer Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::EnumConnectionPoints
(
  LPENUMCONNECTIONPOINTS* ppEnum
)
{
  return E_NOTIMPL;   // UNDONE: Implement this method
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::FindConnectionPoint
(
  REFIID             iid,
  LPCONNECTIONPOINT* ppCpOut
)
{
  if (!ppCpOut)
    return E_INVALIDARG;

  if (iid == DIID_IGameEvents || iid == IID_IDispatch)
    {
    *ppCpOut = &m_cp;
    (*ppCpOut)->AddRef();
    return S_OK;
    }

  return E_NOINTERFACE;
}

//***************************************************************************
// Embedded IConnectionPoint Class
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
CGameOA::XCP::XCP
(
  void
)
{
  m_cref = 1;
  for (int i=0; i<GAME_cADVISE; i++)
    m_rgpdisp[i] = NULL;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CGameOA::XCP::Close

(
  void
)
{
  for (int i=0; i<SC_cADVISE; i++)
    if (m_rgpdisp[i])
      {
      m_rgpdisp[i]->Release();
      m_rgpdisp[i] = NULL;
      }
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::XCP::QueryInterface
(
  REFIID  iid,
  LPVOID* ppvObjOut
)
{
  if (!ppvObjOut)
    return E_INVALIDARG;

  *ppvObjOut = NULL;

  if (iid == IID_IUnknown)
    *ppvObjOut = this->GetUnknown();
  else if (iid == IID_IConnectionPoint)
    *ppvObjOut = (IConnectionPoint *)this;

  if (*ppvObjOut)
    {
    this->AddRef();
    return S_OK;
    }

  return E_NOINTERFACE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CGameOA::XCP::AddRef
(
  void
)
{
  return ++m_cref;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CGameOA::XCP::Release
(
  void
)
{
  CGame *pgame = this->PGAMEOA()->m_pgame;
  ASSERT(m_cref, "bad m_cref");
  m_cref--;
  if (!m_cref && !pgame->m_cref)
    {
    delete pgame;
    return 0;
    }
  return m_cref;
}

//***************************************************************************
// IConnectionPoint Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::XCP::GetConnectionInterface
(
  IID* piid
)
{
  if (!piid)
    return E_INVALIDARG;

  memcpy(piid, &DIID_ISpriteClassEvents, sizeof(IID));
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::XCP::GetConnectionPointContainer
(
  IConnectionPointContainer** ppCPC
)
{
  if (!ppCPC)
    return E_INVALIDARG;

  *ppCPC = this->PGAMEOA();
  (*ppCPC)->AddRef();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::XCP::Advise
(
  IUnknown* punkSink,
  DWORD*    pdwCookie
)
{
  IDispatch  *psce = NULL;
  IDispatch **ppdisp;
  HRESULT     hr;

  if (!punkSink || !pdwCookie)
    return E_INVALIDARG;
  *pdwCookie = 0;

  // Look for empty slot
  for (ppdisp=m_rgpdisp; *ppdisp && ppdisp<&m_rgpdisp[GAME_cADVISE]; ppdisp++)
    ;
  // Did we find one?
  if (ppdisp >= &m_rgpdisp[GAME_cADVISE])
    return E_FAIL;          // UNDONE: Error?

  // Now see if sink supports correct interface
  hr = punkSink->QueryInterface(DIID_ISpriteClassEvents, (void **)&psce);
  if (hr == E_NOINTERFACE)
    {
    hr = punkSink->QueryInterface(IID_IDispatch, (void **)&psce);
    if (hr)
      return hr;
    }
  ASSERT(psce, "QI but no ptr");

  // Finish advise by stashing punkSink QI'd to our interface
  *ppdisp = psce;       // AddRef'd from QI
  *pdwCookie = (ppdisp - m_rgpdisp) + 1;

  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::XCP::Unadvise
(
  DWORD dwCookie
)
{
  if (!dwCookie || dwCookie > GAME_cADVISE)
    return E_INVALIDARG;

  m_rgpdisp[dwCookie-1]->Release();
  m_rgpdisp[dwCookie-1] = NULL;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CGameOA::XCP::EnumConnections
(
  LPENUMCONNECTIONS* ppEnum
)
{
  return E_NOTIMPL;   // UNDONE: Implement this method
}

//--- EOF -------------------------------------------------------------------


GUIDS.CPP

//---------------------------------------------------------------------------
// Guids.cpp
//---------------------------------------------------------------------------
// Contains the guids we will define ourselves.
//---------------------------------------------------------------------------
// (C) Copyright 1995-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#define INITGUID                   // define all the guids.
#include "Main.h"
#pragma hdrstop
#include "Game.h"
#include <MultInfo.h>


GUIDS.H

//---------------------------------------------------------------------------
// Guids.H
//---------------------------------------------------------------------------
// Guids that we define locally
//---------------------------------------------------------------------------
// (C) Copyright 1995-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Blah, blah blah blech blah.
//---------------------------------------------------------------------------
// {149BBC40-2A4E-11cf-A4B0-00AA004A55E8}
DEFINE_GUID(IID_IFoo, 0x149bbc40, 0x2a4e, 0x11cf, 0xa4, 0xb0, 0x0, 0xaa, 0x0, 0x4a, 0x55, 0xe8);

//--- EOF -------------------------------------------------------------------


HELPERS.H

//---------------------------------------------------------------------------
// Helpers.h
//---------------------------------------------------------------------------
// General OLE Automation helper functions.
//---------------------------------------------------------------------------
// (C) Copyright 1995-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// To make the OLE-related code easier to read
//---------------------------------------------------------------------------
#define CHECK(x)       { hr = (x);  if (hr) { FAIL("Bad hr"); goto CleanUp; } }
#define CHECKSZ(x,sz)  { hr = (x);  if (hr) { FAIL(sz);       goto CleanUp; } }
#define CHECKCL(x)     { hr = (x);  if (hr) { FAIL("Bad hr"); goto CheckLoop; } }
#define ARGSZ(f,sz)    {            if (f)  { FAIL(sz);       return E_INVALIDARG; } }
#define CHECKOS(x) { if (ERROR_SUCCESS!=(x)) { FAIL("Bad"); hr=E_FAIL; goto CleanUp; } }

#define RELEASEPTRTYPE(p,typ) \
  {                           \
    if (p)                    \
      {                       \
      typ *__punk = (p);      \
      (p) = NULL;             \
      __punk->Release();      \
      }                       \
  }

#define RELEASEPTR(p)     RELEASEPTRTYPE(p,IUnknown)

//---------------------------------------------------------------------------
// Allocates a temporary buffer that will disappear when it goes out of scope
// NOTE: Be careful of that-- make sure you use the string in the same or
// nested scope in which you created this buffer.  People should not use this
// class directly; use the macro(s) below.
//---------------------------------------------------------------------------
class TempBuffer
  {
  public:
    TempBuffer(ULONG cb)
      {
      m_fAlloc = (cb > 120);
      if (m_fAlloc)
        m_pbBuf = new char[cb];
      else
        m_pbBuf = &m_szBufT;
      }
    ~TempBuffer()
      { if (m_pbBuf && m_fAlloc) delete m_pbBuf; }
    void *GetBuffer(void)
      { return m_pbBuf; }

  private:
    void *m_pbBuf;
    char  m_szBufT[120];  // We'll use this temp buffer for small cases.
    int   m_fAlloc;
  };

//---------------------------------------------------------------------------
// String helpers.
//---------------------------------------------------------------------------
// Given and ANSI String, copy it into a wide buffer.
// NOTE: Be careful about scoping when using this macro!
//
// How to use the below two macros:
//
//  ...
//  LPSTR pszA;
//  pszA = MyGetpszAnsiingRoutine();
//  MAKE_WIDEPTR_FROMANSI(pwsz, pszA);
//  MyUseWideStringRoutine(pwsz);
//  ...
//
// Similarily for MAKE_ANSIPTR_FROMWIDE().  Note that the first param does
// not have to be declared, and no clean up must be done.
//---------------------------------------------------------------------------
#define UNICODE_FROM_ANSI(pwszUnicode, pszAnsi, cb) \
    MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, pwszUnicode, cb);

#define MAKE_WIDEPTR_FROMANSI(ptrname, pszAnsi) \
    char *__psz##ptrname = pszAnsi?pszAnsi:""; \
    long __l##ptrname = (lstrlen(__psz##ptrname) + 1) * sizeof(WCHAR); \
    TempBuffer __TempBuffer##ptrname(__l##ptrname); \
    MultiByteToWideChar(CP_ACP, 0, __psz##ptrname, -1, (LPWSTR)__TempBuffer##ptrname.GetBuffer(), __l##ptrname); \
    LPWSTR ptrname = (LPWSTR)__TempBuffer##ptrname.GetBuffer()

#define MAKE_ANSIPTR_FROMWIDE(ptrname, pwszUnicode) \
    WCHAR *__pwsz##ptrname = pwszUnicode?pwszUnicode:L""; \
    long __l##ptrname = (lstrlenW(__pwsz##ptrname) + 1) * sizeof(char); \
    TempBuffer __TempBuffer##ptrname(__l##ptrname); \
    WideCharToMultiByte(CP_ACP, 0, __pwsz##ptrname, -1, (LPSTR)__TempBuffer##ptrname.GetBuffer(), __l##ptrname, NULL, NULL); \
    LPSTR ptrname = (LPSTR)__TempBuffer##ptrname.GetBuffer()

//--- EOF -------------------------------------------------------------------


MAIN.H

//---------------------------------------------------------------------------
// Main.h
//---------------------------------------------------------------------------
// Main header file for all source files.  Hopefully, we'll then pre-compile
// this one header...
//---------------------------------------------------------------------------
// (C) Copyright 1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#pragma warning(disable:4201) // nameless struct/union
#pragma warning(disable:4100) // unreferenced formal parameter
#pragma warning(disable:4514) // unreferenced inline function has been removed

// External Headers
#define  STRICT
#include <Windows.h>
#include <StdLib.h>
#include <Memory.h>
#include <OleCtl.h>

// ActiveX Scripting related headers
#include "ActivScp.h"
#include "MultInfo.h"
#include "VBSGuids.h"
#include "CompMgr.h"

// VC++ Created Headers
#include "Resource.h"     // From Spruuids.rc  via RC.exe
#include "Spruuids.h"     // From Spruuids.odl via MkTypeLib.exe

// Globals
extern HINSTANCE g_hinst;
extern ITypeLib *g_ptlMain;
extern ITypeLib *g_ptlGameSubObj;
extern IUnknown *g_punkApplicationNA;   // For .Application properties (not AddRef'd)
extern HBRUSH    g_hbrushStock;

// Global TypeInformation
extern ITypeInfo *g_ptinfoClsGame;
extern ITypeInfo *g_ptinfoIntGame;
extern ITypeInfo *g_ptinfoClsGameOA;
extern ITypeInfo *g_ptinfoIntGameOA;
extern ITypeInfo *g_ptinfoClsSpriteClass;
extern ITypeInfo *g_ptinfoIntSpriteClass;
extern ITypeInfo *g_ptinfoClsSprite;
extern ITypeInfo *g_ptinfoIntSprite;

// Internal Headers
#include "Debug.h"
#include "Helpers.h"
#include "OleAuto.h"

//--- EOF -------------------------------------------------------------------


MSGLOOP.CPP

//---------------------------------------------------------------------------
// MsgLoop.cpp
//---------------------------------------------------------------------------
// Implementation of message loop for application.  Includes implementation
// of IOleComponentManager.
//---------------------------------------------------------------------------
// (C) Copyright 1995-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop
#include "MsgLoop.h"

//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
CMsgLoop::CMsgLoop
(
  HWND hwnd
)
  {
  INIT_SIGNATURE(SIG_MsgLoop);

  for (int i=0; i<cCOMP; i++)
    m_rgcomp[i].pcomp = NULL;

  m_icompActive  = icompNIL;
  m_fInState     = FALSE;
  m_icompState   = icompNIL;
  m_uStateID     = 0;
  m_fScrptReset  = FALSE;
  m_fQuitting    = FALSE;
  m_hwnd         = hwnd;
  m_haccel       = NULL;     // Load accel table here
  m_icompMsgLoop =icompNIL;

  m_cref        = 1;
  m_cMsgLoop    = 0;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
CMsgLoop::~CMsgLoop
(
  void
)
  {
  CHECK_SIGNATURE(SIG_MsgLoop);

  for (int i=0; i<cCOMP; i++)
    if (m_rgcomp[i].pcomp)
      {
      m_rgcomp[i].pcomp->Terminate();
      RELEASEPTR(m_rgcomp[i].pcomp);
      }

  DESTROY_SIGNATURE(SIG_MsgLoop);
  }

//***************************************************************************
// IUnknown methods
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CMsgLoop::QueryInterface
(
  REFIID   riid,
  void   **ppvObj
)
  {
  *ppvObj = NULL;

  if (riid == IID_IUnknown)
    *ppvObj = (LPVOID)(IUnknown *)(IOleComponentManager *)this;
  else if (riid == IID_IOleComponentManager)
    *ppvObj = (LPVOID)(IOleComponentManager *)this;

  if (*ppvObj)
    {
    this->AddRef();
    return S_OK;
    }

  return E_NOINTERFACE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CMsgLoop::AddRef
(
  void
)
  {
  CHECK_SIGNATURE(SIG_MsgLoop);
  return ++m_cref;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CMsgLoop::Release
(
  void
)
  {
  CHECK_SIGNATURE(SIG_MsgLoop);
  if (--m_cref)
    return m_cref;

  delete this;
  return 0;
  }

//***************************************************************************
// IOleComponentManager Methods
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CMsgLoop::QueryService
(
  REFGUID guidService,
  REFIID  iid,
  void  **ppvObjOut
)
  {
  *ppvObjOut = NULL;
  return E_NOINTERFACE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FRegisterComponent
(
  IOleComponent   *piComponent,
  const CMGRRINFO *pcrinfo,
  DWORD           *pdwCompIDOut
)
  {
  for (int i=0; i<cCOMP; i++)
    if (!m_rgcomp[i].pcomp)
      {
      piComponent->AddRef();
      m_rgcomp[i].pcomp = piComponent;
      *pdwCompIDOut = i;
      return TRUE;
      }

  FAIL("Out of room!");
  return FALSE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FRevokeComponent
(
  DWORD icomp
)
  {
  ASSERT(m_rgcomp[icomp].pcomp, "Bogus icomp");
  RELEASEPTR(m_rgcomp[icomp].pcomp);
  if (m_icompActive == icomp)
    m_icompActive = icompNIL;
  if (m_icompMsgLoop == icomp)
    m_icompMsgLoop = icompNIL;
  return TRUE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FUpdateComponentRegistration
(
  DWORD            dwComponentID,
  const CMGRRINFO *pcrinfo
)
  {
  // Don't track info anyway, so pretend it worked:
  return TRUE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CMsgLoop::OnComponentActivate
(
  DWORD icomp
)
  {
  ASSERT(m_rgcomp[icomp].pcomp, "Bad icomp");
  m_icompActive = icomp;
  return;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FSetTrackingComponent
(
  DWORD dwComponentID,
  BOOL  fTrack
)
  {
  // UNDONE: Need to support
  return FALSE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CMsgLoop::OnComponentEnterState
(
  DWORD icomp,
  ULONG uStateID,
  ULONG uContext,
  ULONG cpicmExclude,
  IOleComponentManager **rgpicmExclude,
  DWORD dwReserved
)
  {
  ASSERT(m_rgcomp[icomp].pcomp, "Bad icomp");
  ASSERT(uStateID==cmgrstateModal,  "Not modal state");
  ASSERT(!m_fInState, "Nested modal states");

  m_icompState = icomp;
  m_fInState   = TRUE;
  m_uStateID   = uStateID;
  if (uStateID == cmgrstateModal)
    EnableWindow(m_hwnd, FALSE);
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FOnComponentExitState
(
  DWORD icomp,
  ULONG uStateID,
  ULONG uContext,
  ULONG cpicmExclude,
  IOleComponentManager **rgpicmExclude
)
  {
  ASSERT(m_rgcomp[icomp].pcomp, "Bad icomp");
  ASSERT(icomp==(DWORD)m_icompState, "icomp didn't own state");
  ASSERT(uStateID==cmgrstateModal,  "Not modal state");
  ASSERT(uStateID==m_uStateID, "non-matching modal states on pop");
  ASSERT(m_fInState, "underflow of modal state");

  m_fInState   = FALSE;
  m_icompState = icompNIL;
  if (uStateID == cmgrstateModal)
    EnableWindow(m_hwnd, TRUE);
  return TRUE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FInState
(
  ULONG uStateID,
  void *pvoid
)
  {
  return m_fInState && uStateID==m_uStateID;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FContinueIdle
(
  void
)
  {
  return FALSE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FPushMessageLoop
(
  DWORD icomp,
  ULONG uReason,
  void *pvLoopData
)
  {
  MessageLoop(icomp, uReason, pvLoopData);
  return TRUE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FCreateSubComponentManager
(
  IUnknown *piunkOuter,
  IUnknown *piunkServProv,
  REFIID    riid,
  void    **ppvObj
)
  {
  ASSERT(!piunkOuter, "Don't support aggre. SubCompMgr");
  HRESULT hr = this->QueryInterface(riid, ppvObj);
  return !FAILED(hr);
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FGetParentComponentManager
(
  IOleComponentManager **ppicm
)
  {
  // UNDONE: Need to implement
  return FALSE;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CMsgLoop::FGetActiveComponent
(
  DWORD           dwReserved,
  IOleComponent **ppcompOut
)
  {
  // Do we have an active component?
  if (m_icompActive == icompNIL)
    {
    *ppcompOut = NULL;
    return FALSE;
    }

  // Yes, return it
  *ppcompOut = m_rgcomp[m_icompActive].pcomp;
  (*ppcompOut)->AddRef();       // for *ppcompOut
  return TRUE;
  }

//***************************************************************************
// Message Loop Implementation
//***************************************************************************

//---------------------------------------------------------------------------
// This pushes a new message loop.
//---------------------------------------------------------------------------
void CMsgLoop::MessageLoop
(
  DWORD icomp,
  ULONG uReason,
  void *pvLoopData
)
  {
  BOOL  fExitLoop = FALSE;

  m_cMsgLoop++;

  while (!fExitLoop)
    fExitLoop = PumpMessage(icomp, uReason, pvLoopData);

  m_cMsgLoop--;

  if (!m_cMsgLoop)
    m_fScrptReset = FALSE;
  }

//---------------------------------------------------------------------------
// This processes one message.
// Returns TRUE if it is time to exit the current message loop.
//---------------------------------------------------------------------------
BOOL CMsgLoop::PumpMessage
(
  DWORD icomp,
  ULONG uReason,
  void *pvLoopData
)
  {
  BOOL fExitLoop = FALSE;
  BOOL fMsg;
  MSG  msg;

  fMsg = PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE);
  if (fMsg)
    {
    if (msg.message == WM_QUIT)
      {
      // Since we're not really an app, re-post the WM_QUIT message, until
      // we can pop any msgloops we've pushed.
      if (m_cMsgLoop)
       PostQuitMessage(msg.wParam);
      return TRUE;
      }
    else
      ProcessMessage(&msg);
    }

  if (m_fScrptReset)
    return TRUE;

  // A Component told us to push this message loop, and so we
  // ask them whether it's time yet to exit the loop
  fExitLoop = !m_rgcomp[icomp].pcomp->FContinueMessageLoop(uReason, pvLoopData);

  if (!fMsg && !fExitLoop)
    {
    // There weren't any messages and we don't need to exit this message
    // loop yet.  Therefore, now is the right time to do idle processing
    DoIdleProcessing();
    WaitMessage();
    }

  return fExitLoop;
  }

//---------------------------------------------------------------------------
// This processes one message.
//---------------------------------------------------------------------------
void CMsgLoop::ProcessMessage
(
  MSG  *pmsg
)
  {
  // When a component is active give it a chance to translate
  // its accelerators.
  if (m_icompActive != icompNIL)
    {
    if (!m_rgcomp[m_icompActive].pcomp->FPreTranslateMessage(pmsg))
      {
      TranslateMessage(pmsg);    // Translates virtual key codes
      DispatchMessage(pmsg);    // Dispatches message to window
      }
    }
  else
    {
    if (!TranslateAccelerator(m_hwnd, m_haccel, pmsg))
      {
      TranslateMessage(pmsg); // Translates virtual key codes
      DispatchMessage(pmsg);  // Dispatches message to window
      }
    }
  }

//---------------------------------------------------------------------------
// This is called at idle time.
//---------------------------------------------------------------------------
void CMsgLoop::DoIdleProcessing
(
  void
)
  {
  MSG  msg;
  BOOL fContinue = TRUE;

  while (fContinue && !PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
    {
    fContinue = DoIdle();
    }
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CMsgLoop::DoIdle
(
  void
)
  {
  int  i;
  BOOL fDidWork = FALSE;

  // if we have periodic idle tasks that to be done,
  // do them and return TRUE.
  for (i=0; i<cCOMP; i++)
    {
    if (m_rgcomp[i].pcomp)
      if (m_rgcomp[i].pcomp->FDoIdle(cmgridlefPeriodic))
        fDidWork = TRUE;
    if (fDidWork)
      return TRUE;
    }

  // if any high priority idle tasks need to be done,
  // do it, and return TRUE.
  for (i=0; i<cCOMP; i++)
    {
    if (m_rgcomp[i].pcomp)
      if (m_rgcomp[i].pcomp->FDoIdle(cmgridlefPriority))
        fDidWork = TRUE;
    if (fDidWork)
      return TRUE;
    }

  // if any lower priority idle tasks need to be done,
  // do it, and return TRUE.
  for (i=0; i<cCOMP; i++)
    {
    if (m_rgcomp[i].pcomp)
      if (m_rgcomp[i].pcomp->FDoIdle(cmgridlefNonPeriodic))
        fDidWork = TRUE;
    if (fDidWork)
      return TRUE;
    }

  return FALSE;
  }

//--- EOF -------------------------------------------------------------------


MSGLOOP.H

//---------------------------------------------------------------------------
// MsgLoop.h
//---------------------------------------------------------------------------
// (C) Copyright 1995-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// In we need to track per Component.
//---------------------------------------------------------------------------
typedef struct tagCOMP
  {
  IOleComponent *pcomp;
  } COMP;

#define cCOMP     10
#define icompNIL  0xffffffff

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
class CMsgLoop : public IOleComponentManager
  {
public:
  // *** Constructor / Destructor ***
  CMsgLoop(HWND hwnd);
  virtual ~CMsgLoop();

  // *** IUnknown methods ***
  STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  STDMETHOD_(ULONG, AddRef)(void);
  STDMETHOD_(ULONG, Release)(void);

  // *** IOleComponentManager Methods
  STDMETHOD(QueryService)(REFGUID guidService, REFIID iid, void **ppvObj);
  STDMETHOD_(BOOL, FRegisterComponent)(IOleComponent *piComponent, const CMGRRINFO *pcrinfo, DWORD *pdwComponentID);
  STDMETHOD_(BOOL, FRevokeComponent)(DWORD dwComponentID);
  STDMETHOD_(BOOL, FUpdateComponentRegistration)(DWORD dwComponentID, const CMGRRINFO *pcrinfo);
  STDMETHOD_(void, OnComponentActivate)(DWORD dwComponentID);
  STDMETHOD_(BOOL, FSetTrackingComponent)(DWORD dwComponentID, BOOL fTrack);
  STDMETHOD_(void, OnComponentEnterState)(DWORD dwComponentID, ULONG uStateID, ULONG uContext, ULONG cpicmExclude, IOleComponentManager **rgpicmExclude, DWORD dwReserved);
  STDMETHOD_(BOOL, FOnComponentExitState)(DWORD dwComponentID, ULONG uStateID, ULONG uContext, ULONG cpicmExclude, IOleComponentManager **rgpicmExclude);
  STDMETHOD_(BOOL, FInState)(ULONG uStateID, void *pvoid);
  STDMETHOD_(BOOL, FContinueIdle)(void);
  STDMETHOD_(BOOL, FPushMessageLoop)(DWORD dwComponentID, ULONG uReason, void *pvLoopData);
  STDMETHOD_(BOOL, FCreateSubComponentManager)(IUnknown *piunkOuter, IUnknown *piunkServProv, REFIID riid, void **ppvObj);
  STDMETHOD_(BOOL, FGetParentComponentManager)(IOleComponentManager **ppicm);
  STDMETHOD_(BOOL, FGetActiveComponent)(DWORD dwReserved, IOleComponent **ppic);

  // *** Implementation of FPushMessageLoop from IOleComponentManager
  BOOL PumpMessage(DWORD icomp, ULONG uReason, void *pvLoopData);
  void ProcessMessage(MSG *pmsg);
  void MessageLoop(DWORD icomp, ULONG uReason, void *pvLoopData);
  void DoIdleProcessing(void);
  BOOL DoIdle(void);

  // *** Publicly available state ***
  COMP   m_rgcomp[cCOMP]; // Array of components for our compmgr
  DWORD  m_icompActive;
  BOOL   m_fInState;
  DWORD  m_icompState;
  ULONG  m_uStateID;
  BOOL   m_fScrptReset;
  BOOL   m_fQuitting;
  HWND   m_hwnd;
  HACCEL m_haccel;

private:
  ULONG m_cref;
  ULONG m_cMsgLoop;
  DWORD m_icompMsgLoop;

  // DEBUG info
  #define SIG_MsgLoop 'MsgL'
  DECLARE_SIGNATURE(SIG_MsgLoop);
  };

//--- EOF -------------------------------------------------------------------


OLEAUTO.CPP

//---------------------------------------------------------------------------
// OleAuto.cpp
//---------------------------------------------------------------------------
// Simple class for doing dual OLE Automation interfaces
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop

#define NUM_RESERVED_EXTENDER_DISPIDS 500
#define EXTENDER_DISPID_BASE ((ULONG)(0x80010000))
#define IS_EXTENDER_DISPID(x) ( ( (ULONG)(x) & 0xFFFF0000 ) == EXTENDER_DISPID_BASE )

SZTHISFILE

//---------------------------------------------------------------------------
// A little utility which simplifies firing dispatch events.
//---------------------------------------------------------------------------
HRESULT InvokeEvent
(
  IDispatch  *pdisp,    // IDispatch of Sink
  DISPID      dispid,   // DISPID of event
  VARIANTARG *pvararg,  // Args to event
  UINT        carg      // # args
)
{
  DISPPARAMS dp;
  EXCEPINFO  ei;
  UINT       uArgErr = 0;

  dp.rgvarg            = pvararg;
  dp.rgdispidNamedArgs = NULL;
  dp.cArgs             = carg;
  dp.cNamedArgs        = 0;
  return pdisp->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dp, NULL, &ei, &uArgErr);
}

//---------------------------------------------------------------------------
// This routine will load a TypeLib and (optionally) find the TypeInfo inside
// which matches the given clsid.  The TypeLib and TypeInfo pointers are
// in/out so you can simply:
//      hr = LoadTypeInfo(..., &g_pMyTypeLib, &m_pMyObjectsTypeInfo);
// and it will fill in g_pMyTypeLib and m_pMyObjectsTypeInfo, if necessary.
//---------------------------------------------------------------------------
HRESULT LoadTypeInfo
(
  HINSTANCE   hinst,             // hinst of where to load TypeLib from, if not found
  UINT        itinfo,            // index of TypeInfo requested, only 0 supported
  USHORT      dwMaj,             // Maj version # of TypeLib
  USHORT      dwMin,             // Min version # of TypeLib
  LCID        lcid,              // Locale of TypeLib to load
  REFGUID     libid,             // LIBID of TypeLib  to find
  REFCLSID    clsid,             // CLSID of TypeInfo to find
  REFIID      iid,               // IID   of TypeInfo to find
  BOOL        fDispOnly,         // TRUE=ensure *ptinfoIntInOut is a TKIND_DISPATCH, not vtbl
  ITypeLib  **pptlibInOut,       // Ptr to cache of pTypeLib, typically &g_ptlib
  ITypeInfo **pptinfoClassInOut, // Ptr to cache of pTypeInfo, typically &s_ptinfoCls
  ITypeInfo **pptinfoIntInOut    // Ptr to cache of pTypeInfo, typically &s_ptinfoInt
)
{
  HRESULT    hr        = S_OK;
  ITypeInfo *ptinfoT   = NULL;
  TYPEATTR  *ptypeattr = NULL;

  // Arg checking
  if (itinfo != 0)
    return DISP_E_BADINDEX;

  if (!pptlibInOut)
    return E_POINTER;

  // Check to see if we have a TypeLib, load it if not.
  if (!*pptlibInOut)
    {
    hr = LoadRegTypeLib(libid, dwMaj, dwMin, lcid, pptlibInOut);
    if (hr)
      {
      ASSERT(hinst, "hinst==NULL means in-memory TL; shouldn't have to load from resource");

      // If, for some reason, we failed to load the type library this way,
      // we're going to try and load the type library directly out of our
      // resources.  This has the advantage of going and re-setting all the
      // registry information again for us.
      DWORD dwPathLen;
      char  szDllPath[MAX_PATH];

      dwPathLen = GetModuleFileName(hinst, szDllPath, MAX_PATH);
      if (!dwPathLen)
        return E_FAIL;

      MAKE_WIDEPTR_FROMANSI(pwsz, szDllPath);
      CHECK(LoadTypeLib(pwsz, pptlibInOut));
      }
    }
  ASSERT(*pptlibInOut, "Don't have a pTypeLib");

  // If the user wants a coclass TypeInfo...
  if (pptinfoClassInOut)
    {
    // ...Check to see if we already have it, and find it in the TypeLib, if not.
    if (!*pptinfoClassInOut)
      {
      // Get coclass TypeInfo
      CHECK((*pptlibInOut)->GetTypeInfoOfGuid(clsid, pptinfoClassInOut));
      }
    }

  // If the user wants an interface TypeInfo...
  if (pptinfoIntInOut)
    {
    // ...Check to see if we already have it, and find it in the TypeLib, if not.
    if (!*pptinfoIntInOut)
      {
      // Get interface TypeInfo
      CHECK((*pptlibInOut)->GetTypeInfoOfGuid(iid, &ptinfoT));
      CHECK(ptinfoT->GetTypeAttr(&ptypeattr));

      // If we want a TKIND_DISPATCH, but we have a TKIND_INTERFACE, then switch if it's dual
      if (fDispOnly && ptypeattr->typekind == TKIND_INTERFACE)
        {
        // We're on a TKIND_INTERFACE but want a dispatch.  If this is not a dual interface,
        // we're out of luck, so stuff a NULL, and return S_FALSE.
        if (!(ptypeattr->wTypeFlags & TYPEFLAG_FDUAL))
          {
          *pptinfoIntInOut = NULL;
          hr = S_FALSE;
          }

        // This is a dual interface, so get the corresponding Disp interface;
        // 0xffffffff is a magic number which does this for us.
        HREFTYPE hrefType;
        CHECK(ptinfoT->GetRefTypeOfImplType(0xffffffff, &hrefType));
        CHECK(ptinfoT->GetRefTypeInfo(hrefType, pptinfoIntInOut));
        }
      else
        {
        *pptinfoIntInOut = ptinfoT;
        (*pptinfoIntInOut)->AddRef();
        }
      }
    }

CleanUp:
  // Release all frame variables
  if (ptypeattr)
    ptinfoT->ReleaseTypeAttr(ptypeattr);
  if (ptinfoT)
    ptinfoT->Release();

  return hr;
}

//***************************************************************************
// IDispatch Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetTypeInfoCount
(
  UINT* pctinfo
)
{
  // Validate args
  if (!pctinfo)
    return E_INVALIDARG;

  // Return requested info
  *pctinfo = 1;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetTypeInfo
(
  UINT        itinfo,
  LCID        lcid,
  ITypeInfo** pptinfoOut
)
{
  HRESULT hr = S_OK;

  // Validate args
  if (!pptinfoOut)
    return E_INVALIDARG;
  *pptinfoOut = NULL;

  // Make sure we have the TypeInfo
  CHECK(this->CheckTypeInfo(itinfo, lcid));

  // Return requested info
  *pptinfoOut = *this->GetTinfoIntAddr();
  (*pptinfoOut)->AddRef();    // For *pptiOut

CleanUp:
  return hr;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
HRESULT COleAuto::CheckTypeInfo
(
  UINT itinfo,
  LCID lcid
)
{
  HRESULT      hr;
  HINSTANCE    hinst;
  SHORT        wMaj, wMin;
  const GUID  *plibid;
  const CLSID *pclsid;
  const IID   *piid;
  ITypeLib   **pptl;
  ITypeInfo  **pptinfoCls;
  ITypeInfo  **pptinfoInt;

  pptinfoCls = this->GetTinfoClsAddr();
  pptinfoInt = this->GetTinfoIntAddr();
  if (*pptinfoCls && *pptinfoInt)
    return S_OK;

  hr = GetTypeLibInfo(&hinst, &plibid, &wMaj, &wMin, &pclsid, &piid, &pptl);
  if (hr)
    return hr;

  return LoadTypeInfo(hinst, itinfo, wMaj, wMin, lcid, *plibid, *pclsid, *piid, FALSE,
                      pptl, pptinfoCls, pptinfoInt);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetIDsOfNames
(
  REFIID    iid,
  OLECHAR** rgszNames,
  UINT      cNames,
  LCID      lcid,
  DISPID*   prgdispid
)
{
  HRESULT hr;

  // Validate Args
  if (iid != IID_NULL)
      return E_INVALIDARG;

  // Make sure we have the TypeInfo
  CHECK(this->CheckTypeInfo(0, lcid));

  // Use the standard provided routines to do all the work for us.
  hr = (*this->GetTinfoIntAddr())->GetIDsOfNames(rgszNames, cNames, prgdispid);

  // Try the base object iff there is a base object and the previous call failed
  if (FAILED(hr) && m_pdispBaseObject)
    {
    hr = m_pdispBaseObject->GetIDsOfNames(iid, rgszNames, cNames, lcid, prgdispid);
    if (SUCCEEDED(hr))
      {
      // Adjust extender dispid.
      if (IS_EXTENDER_DISPID(prgdispid[0]))
        prgdispid[0] += NUM_RESERVED_EXTENDER_DISPIDS;
      }
    }

CleanUp:
  return hr;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::Invoke
(
  DISPID      dispidMember,
  REFIID      iid,
  LCID        lcid,
  WORD        wFlags,
  DISPPARAMS* pdispparams,
  VARIANT*    pvarResult,
  EXCEPINFO*  pexcepinfo,
  UINT*       puArgErr
)
{
  HRESULT hr;

  // Validate args, clear out params
  if (!pdispparams)
    return E_INVALIDARG;
  if (pvarResult)
    VariantInit(pvarResult);
  SetErrorInfo(0L, NULL);
  if (puArgErr)
    *puArgErr = 0;
  if (iid != IID_NULL)
    return E_INVALIDARG;

  // Make sure we have the TypeInfo
  CHECK(this->CheckTypeInfo(0, lcid));

  // Use the standard provided routines to do all the work for us.
  hr = (*this->GetTinfoIntAddr())->Invoke(this->GetPrimary(), dispidMember, wFlags,
                                          pdispparams, pvarResult, pexcepinfo, puArgErr);
  // Try the base object iff there is a base object and the previous invoke failed
  if (FAILED(hr) && m_pdispBaseObject)
    {
    if (IS_EXTENDER_DISPID(dispidMember))
      dispidMember -= NUM_RESERVED_EXTENDER_DISPIDS;

    hr = m_pdispBaseObject->Invoke(dispidMember, iid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
    }

CleanUp:
  return hr;
}

//***************************************************************************
// IProvideMultipleClassInfo Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetMultiTypeInfoCount
(
  ULONG *pc
)
  {
  HRESULT hr;
  ULONG count;
  IProvideClassInfo         *pclsinfo   = NULL;
  IProvideMultipleClassInfo *pclsinfoex = NULL;

  // This should not be possible!
  ASSERT(m_pdispBaseObject != NULL, "");

  hr = m_pdispBaseObject->QueryInterface(IID_IProvideMultipleClassInfo, (void**)&pclsinfoex);
  if (SUCCEEDED(hr))
    {
    hr = pclsinfoex->GetMultiTypeInfoCount(&count);
    if (SUCCEEDED(hr))
      *pc = count + 1;
    }
  else
    {
    hr = m_pdispBaseObject->QueryInterface(IID_IProvideClassInfo, (void**)&pclsinfo);
    if (FAILED(hr))
      goto Exit;
    *pc = 2;
    }

Exit:
  // Release the temp refs
  if (pclsinfoex)
    pclsinfoex->Release();
  if (pclsinfo)
    pclsinfo->Release();
  return hr;
  }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetInfoOfIndex
(
  ULONG       itinfo,
  DWORD       dwFlags,
  ITypeInfo** pptinfoCoClass,
  DWORD*      pdwTIFlags,
  ULONG*      pcdispidReserved,
  IID*        piidPrimary,
  IID*        piidSource
)
  {
  HRESULT                    hr         = S_OK;
  IProvideClassInfo         *pclsinfo   = NULL;
  IProvideMultipleClassInfo *pclsinfoex = NULL;

  // This should not be possible!
  ASSERT(m_pdispBaseObject != NULL, "");

  if (itinfo == 0)
    {
    // Return info on ourselves
    if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
      {
      // Make sure we have the TypeInfo
      hr = this->CheckTypeInfo(0, 0x0409);
      if (hr)
        goto Exit;

      // Return requested info
      *pptinfoCoClass = *this->GetTinfoClsAddr();
      (*pptinfoCoClass)->AddRef();    // For *pptiOut
      if (pdwTIFlags)
        *pdwTIFlags = 0;
      }
    if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
      *pcdispidReserved = NUM_RESERVED_EXTENDER_DISPIDS;
    if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY)
      // REVIEW: Not implemented
      *piidPrimary = IID_NULL;
    if (dwFlags & MULTICLASSINFO_GETIIDSOURCE)
      // REVIEW: Not implemented
      *piidSource = IID_NULL;
    else
      {
      // Look for an extender
      hr = m_pdispBaseObject->QueryInterface(IID_IProvideMultipleClassInfo, (void**)&pclsinfoex);
      if (SUCCEEDED(hr))
        {
        hr = pclsinfoex->GetInfoOfIndex(itinfo-1, dwFlags, pptinfoCoClass, pdwTIFlags,
                                       pcdispidReserved, piidPrimary, piidSource);
        if (SUCCEEDED(hr) && (dwFlags & MULTICLASSINFO_GETTYPEINFO))
          *pdwTIFlags = TIFLAGS_EXTENDDISPATCHONLY;
}
      else
        {
        if (itinfo != 1)
          hr = TYPE_E_ELEMENTNOTFOUND;

        hr = m_pdispBaseObject->QueryInterface(IID_IProvideClassInfo, (void**)&pclsinfo);
        if (FAILED(hr))
          goto Exit;

        // Return info on unextended base object
        if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
          {
          hr = pclsinfo->GetClassInfo(pptinfoCoClass);
          if (FAILED(hr))
            goto Exit;
          if (pdwTIFlags)
            *pdwTIFlags = TIFLAGS_EXTENDDISPATCHONLY;
          }
        if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
          *pcdispidReserved = 0;
        if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY)
          // REVIEW: Not implemented
          *piidPrimary = IID_NULL;
        if (dwFlags & MULTICLASSINFO_GETIIDSOURCE)
          // REVIEW: Not implemented
          *piidSource = IID_NULL;
        }
      }
    }

Exit:
  // Release the temp refs
  if (pclsinfoex)
    pclsinfoex->Release();
  if (pclsinfo)
    pclsinfo->Release();
  return hr;
  }

//***************************************************************************
// Misc
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
HRESULT COleAuto::SetBaseObject
(
  IDispatch *pdisp
)
  {
  if (!pdisp)
    {
    if (m_pdispBaseObject)
      {
      IDispatch* pdispTemp = m_pdispBaseObject;

      m_pdispBaseObject = NULL;
      pdispTemp->Release();
      }
    }
  else
    {
    // Only allow the base object to be set once
    if (m_pdispBaseObject)
      return E_UNEXPECTED;
    m_pdispBaseObject = pdisp;
    m_pdispBaseObject->AddRef();
    }

  return S_OK;
  }

//--- EOF -------------------------------------------------------------------


OLEAUTO.H

//---------------------------------------------------------------------------
// OleAuto.h
//---------------------------------------------------------------------------
// Simple class for doing dual OLE Automation interfaces
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// A little utility which simplifies firing dispatch events.
//---------------------------------------------------------------------------
HRESULT InvokeEvent
  (
  IDispatch  *pdisp,    // IDispatch of Sink
  DISPID      dispid,   // DISPID of event
  VARIANTARG *pvararg,  // Args to event
  UINT        carg      // # args
  );

//---------------------------------------------------------------------------
// This routine will load a TypeLib and (optionally) find the TypeInfo inside
// which matches the given clsid.  The TypeLib and TypeInfo pointers are
// in/out so you can simply:
//      hr = LoadTypeInfo(..., &g_pMyTypeLib, &m_pMyObjectsTypeInfo);
// and it will fill in g_pMyTypeLib and m_pMyObjectsTypeInfo, if necessary.
//---------------------------------------------------------------------------
HRESULT LoadTypeInfo
  (
  HINSTANCE   hinst,             // hinst of where to load TypeLib from, if not found
  UINT        itinfo,            // index of TypeInfo requested, only 0 supported
  USHORT      dwMaj,             // Maj version # of TypeLib
  USHORT      dwMin,             // Min version # of TypeLib
  LCID        lcid,              // Locale of TypeLib to load
  REFGUID     libid,             // LIBID of TypeLib  to find
  REFCLSID    clsid,             // CLSID of TypeInfo to find
  REFIID      iid,               // IID   of TypeInfo to find
  BOOL        fDispOnly,         // TRUE=ensure *ptinfoIntInOut is a TKIND_DISPATCH, not vtbl
  ITypeLib  **pptlibInOut,       // Ptr to cache of pTypeLib, typically &g_ptlib
  ITypeInfo **pptinfoClassInOut, // Ptr to cache of pTypeInfo, typically &m_ptinfoCls
  ITypeInfo **pptinfoIntInOut    // Ptr to cache of pTypeInfo, typically &m_ptinfoInt
  );

//---------------------------------------------------------------------------
// Derrive from this class to get the standard implementation of IDispatch
// over a vtable.  Assumes that "this" is the proper vtable, so to use, your
// class should first derive from IMyCustomDualInterface, then from COleAuto.
// Then implement GetTypeLibInfo().
//---------------------------------------------------------------------------
class COleAuto : public IDispatch
  {
public:
  // *** IUnknown methods ***
  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj) PURE;
  STDMETHOD_(ULONG, AddRef)(void) PURE;
  STDMETHOD_(ULONG, Release)(void) PURE;

  // *** IDispatch methods ***
  STDMETHOD(GetTypeInfoCount)(UINT* pctinfo);
  STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfoOut);
  STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR** rgszNames, UINT cNames, LCID lcid, DISPID* prgdispid);
  STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr);

  // *** IProvideMultipleClassInfo methods ***
  STDMETHOD(GetMultiTypeInfoCount)(ULONG *pc);
  STDMETHOD(GetInfoOfIndex)(ULONG itinfo, DWORD dwFlags, ITypeInfo** pptinfoCoClass, DWORD* pdwTIFlags, ULONG* pcdispidReserved, IID* piidPrimary, IID* piidSource);

  // *** Pure virtual methods for derived class to implement ***
  virtual ITypeInfo **GetTinfoClsAddr(void) PURE;
  virtual ITypeInfo **GetTinfoIntAddr(void) PURE;
  virtual HRESULT GetTypeLibInfo(HINSTANCE *phinstOut, const GUID **pplibidOut,
                                 SHORT *pwMajLibOut, SHORT *pwMinLibOut,
                                 const CLSID **ppclsidOut, const IID **ppiidOut,
                                 ITypeLib ***ppptlOut) PURE;
  virtual IDispatch *GetPrimary(void) PURE;

  // *** Other methods for derived class to call ***
  HRESULT CheckTypeInfo(UINT itinfo, LCID lcid);
  HRESULT SetBaseObject(IDispatch *pdisp);

  // Member Variables
  IDispatch *m_pdispBaseObject;   // The object this class is extending
  };

//---------------------------------------------------------------------------
// Standard Dispatch
//---------------------------------------------------------------------------
// All objects should declare these in their class definitions so that they
// get standard implementations of IDispatch and ISupportErrorInfo.
//---------------------------------------------------------------------------
#define DECLARE_STANDARD_DISPATCH() \
    STDMETHOD(GetTypeInfoCount)(UINT *pctinfo) \
      { return COleAuto::GetTypeInfoCount(pctinfo); } \
    STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo **pptinfoOut) \
      { return COleAuto::GetTypeInfo(itinfo, lcid, pptinfoOut); } \
    STDMETHOD(GetIDsOfNames)(REFIID iid, OLECHAR **rgszNames, UINT cnames, LCID lcid, DISPID *rgdispid) \
      { return COleAuto::GetIDsOfNames(iid, rgszNames, cnames, lcid, rgdispid); } \
    STDMETHOD(Invoke)(DISPID dispid, REFIID iid, LCID lcid, WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pVarResult, EXCEPINFO *pexcepinfo, UINT *puArgErr) \
      { return COleAuto::Invoke(dispid, iid, lcid, wFlags, pdispparams, pVarResult, pexcepinfo, puArgErr); }

//--- EOF -------------------------------------------------------------------


APP.CPP

//---------------------------------------------------------------------------
// App.cpp
//---------------------------------------------------------------------------
// Shell for sample spr program
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop
#include "App.h"
#include "MsgLoop.h"
#include "Game.h"
#include <stdio.h>

//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE

//---------------------------------------------------------------------------
// Prototypes
//---------------------------------------------------------------------------
BOOL CALLBACK AppDlgProc(  HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
LONG CALLBACK PSWndProc(   HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);

//---------------------------------------------------------------------------
// Global Variables
//---------------------------------------------------------------------------
HINSTANCE g_hinst             = NULL;
ITypeLib *g_ptlMain           = NULL;   // Cache of TypeLib
CApp     *g_papp              = NULL;
IUnknown *g_punkApplicationNA = NULL;   // Not AddRef()'d
char     *g_pszCodeFile       = NULL;
char     *g_pszRecFile        = NULL;
FILE     *g_pfileRec          = NULL;
int       g_mode              = MODE_NORMAL;
CMGRRINFO App_crinfo =
  {
  sizeof(CMGRRINFO),      // size of CMGRRINFO structure in bytes.
  0,                      // Don't need idle time
  cmgrrfPreTranslateAll,  // Need pretranslate
  cmgradvfModal           // Need modal notifications
  };
HRESULT CApp::s_hr = E_FAIL;

//---------------------------------------------------------------------------
// Helpers for little vs. bit endian
//---------------------------------------------------------------------------

#undef BIGENDIAN // change for big-endian systems

typedef union
  {
  DWORD i4;
  struct
    {
    char b1;
    char b2;
    char b3;
    char b4;
    };
  } I4_SW;

#ifndef BIGENDIAN
  #define NORMALIZE_I4(i4)  (i4)
#else
  #define NORMALIZE_I4(i4) \
    {                      \
    I4_SW l=i4;            \
    (i4).b1 = l.b4;        \
    (i4).b2 = l.b3;        \
    (i4).b3 = l.b2;        \
    (i4).b4 = l.b1;        \
    }
#endif

typedef struct
  {
  char   win;
  char   msg;
  I4_SW  wp;
  I4_SW  lp;
  } REC;

//---------------------------------------------------------------------------
// Write out one message.
//---------------------------------------------------------------------------
#define RECORD(win, msg, wp, lp)    {if (g_mode == MODE_RECORD) Record(win,msg,wp,lp);}
#define WIN_DLG   ((char)0xdd)  // Dlg
#define WIN_PS    ((char)0xee)  // playsurfacE
#define WIN_ABOUT ((char)0xaa)  // About

void Record
(
  char   win,
  UINT   msg,
  WPARAM wp,
  LPARAM lp
)
{
  REC rec;

  if (!g_pfileRec)
    {
    g_pfileRec = fopen(g_pszRecFile, "wb");
    if (!g_pfileRec)
      {
      MessageBox(NULL, "Could not open record file", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
      exit(1);
      }
    }

  switch (msg)
    {
    default:
      return;

    case WM_COMMAND:
      msg = 1;
      break;
    case WM_SYSCOMMAND:
      msg = 2;
      break;
    case WM_TIMER:
      msg = 3;
      break;
    case WM_CHAR:
      msg = 4;
      break;
    case WM_KEYDOWN:
      msg = 5;
      break;
    case WM_KEYUP:
      msg = 6;
      break;
    case WM_MOUSEMOVE:
      msg = 7;
      break;
    case WM_LBUTTONUP:
      msg = 8;
      break;
    case WM_RBUTTONUP:
      msg = 9;
      break;
    case WM_MBUTTONUP:
      msg = 10;
      break;
    case WM_LBUTTONDOWN:
      msg = 11;
      break;
    case WM_RBUTTONDOWN:
      msg = 12;
      break;
    case WM_MBUTTONDOWN:
      msg = 13;
      break;
    }

  rec.win = win;
  rec.msg = (char)msg;
  rec.wp.i4 = wp;
  NORMALIZE_I4(rec.wp);    // Handle byte-swapping for little vs. big endian.
  rec.lp.i4 = lp;
  NORMALIZE_I4(rec.lp);    // Handle byte-swapping for little vs. big endian.
  fwrite(&rec, sizeof(rec), 1, g_pfileRec);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
HRESULT CApp::Playback
(
  void
)
{
  REC    rec;
  HWND   hwnd;
  UINT   msg;

  if (!g_pfileRec)
    {
    g_pfileRec = fopen(g_pszRecFile, "rb");
    if (!g_pfileRec)
      {
      MessageBox(NULL, "Could not open playback file", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
      return E_FAIL;
      }
    }

  while (TRUE)
    {
    size_t cch = fread(&rec, sizeof(rec), 1, g_pfileRec);
    if (cch != 1)
      {
      if (feof(g_pfileRec))
        {
        fclose(g_pfileRec);
        g_pfileRec = NULL;
        g_mode = MODE_NORMAL;
        m_fQuit = TRUE;
        return S_OK;
        }
      MessageBox(NULL, "Unexpected read error in playback file", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
      return E_FAIL;
      }

    switch (rec.win)
      {
      case WIN_DLG:
        hwnd = m_hwndDlg;
        break;

      case WIN_PS:
        hwnd = m_hwndPS;
        break;

      case WIN_ABOUT:
        hwnd = m_hwndAbout;
        break;
      }

    UINT mpimsg[] = {0, WM_COMMAND, WM_SYSCOMMAND, WM_TIMER, WM_CHAR, WM_KEYDOWN, WM_KEYUP,
                        WM_MOUSEMOVE, WM_LBUTTONUP, WM_RBUTTONUP, WM_MBUTTONUP, WM_LBUTTONDOWN,
                        WM_RBUTTONDOWN, WM_MBUTTONDOWN};
    if (rec.msg <= 0 || rec.msg >= sizeof(mpimsg)/sizeof(mpimsg[0]))
      {
      MessageBox(NULL, "Bad msg # in playback file", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
      return E_FAIL;
      }
    msg = mpimsg[rec.msg];
    NORMALIZE_I4(rec.wp);    // Handle byte-swapping for little vs. big endian.
    NORMALIZE_I4(rec.lp);    // Handle byte-swapping for little vs. big endian.

    SendMessage(hwnd, msg, (WPARAM)rec.wp.i4, (LPARAM)rec.lp.i4);
    if (!m_pmsgloop->FPushMessageLoop(g_papp->m_idcomp, cmgrloopDoEvents, NULL))
      return E_FAIL;
    }

  return S_OK;
}

//---------------------------------------------------------------------------
// Helper to parse command-line parameters
//---------------------------------------------------------------------------
HRESULT ParseCmdLine
(
  char *pszCmdLine
)
{
  if (!pszCmdLine)
    goto DoError;

  if (pszCmdLine[0] == '/')
    {
    if (pszCmdLine[1] == 'P')
      {
      g_mode = MODE_PLAYBACK;
      goto GetFile;
      }
    else if (pszCmdLine[1] == 'R')
      {
      g_mode = MODE_RECORD;
GetFile:
      if (pszCmdLine[2] <= ' ')
        goto DoError;
      pszCmdLine += 2;
      g_pszRecFile = pszCmdLine;
      while (*pszCmdLine > ' ')   // Skip non-white space
        pszCmdLine++;
      if (!*pszCmdLine)   // End Of String
        goto DoError;     // Needed <fileCode>
      *pszCmdLine = 0;
      pszCmdLine++;
      srand(1);           // Seed random # generator
      }
    else
      goto DoError;
    }

  // Skip white space
  while (*pszCmdLine && *pszCmdLine <= ' ')
    pszCmdLine++;
  if (!*pszCmdLine)
    {
DoError:
    MessageBox(NULL, "Usage: Spruuids [/P<filePlay> | /R<fileRec>] <fileCode>, where <fileCode> is a VB Script", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
    return E_FAIL;
    }

  // Rest of Cmd Line should be source file
  g_pszCodeFile = pszCmdLine;
  return S_OK;
}

//---------------------------------------------------------------------------
// Main program
//---------------------------------------------------------------------------
int WINAPI WinMain
(
  HINSTANCE hinst,        // Instance handle of current instance
  HINSTANCE hinstPrev,    // Instance handle of previous instance
  LPSTR     lpszCmdLine,  // Null-terminated command line
  int    iCmdShow      // How window should be initially displayed
)
{
  int     ret   = 1;      // Assume non-normal exit
  BOOL    fInit = FALSE;
  HRESULT hr;

  // Stuff hinst in global for all to see
  g_hinst      = hinst;
  hr = ParseCmdLine(lpszCmdLine);
  if (hr)
    goto CleanUp;

  // Initialize OLE
  hr = CoInitialize(NULL);
  if (hr)
    goto CleanUp;
  fInit = TRUE;

  // Create the CApp object, since it runs the show, note it inits g_papp
  hr = CApp::CreateApp(hinst);
  if (hr)
    goto CleanUp;
  ASSERT(g_papp, "!hr but g_papp==NULL");

  // Start a new game
  g_papp->CausePause(0);
  AppEvt_NewGame();

  // Either Playback or push the main msg loop
  if (g_mode == MODE_PLAYBACK)
    hr = g_papp->Playback();
  else
    hr = g_papp->MainMsgLoop();
  if (hr)
    goto CleanUp;

  // Normal exit
  ret = 0;

  // Cleanup, if we haven't already done so
CleanUp:
  if (g_pfileRec)
    {
    fclose(g_pfileRec);
    g_pfileRec = NULL;
    }
  if (g_papp)
    delete g_papp;
  if (fInit)
    CoUninitialize();
  return ret;
}

//***************************************************************************
// Constructor and Destructor support for CApp
//***************************************************************************

//---------------------------------------------------------------------------
// Creates an instance of CApp.  Use this instead of "new", as this returns
// errors, etc.
//---------------------------------------------------------------------------
HRESULT CApp::CreateApp
(
  HINSTANCE hinst
)
{
  if (g_papp)
    return E_UNEXPECTED;

  CApp *papp = new CApp(hinst);
  if (!papp)
    return E_OUTOFMEMORY;
  if (papp->s_hr)
    return papp->s_hr;

  return S_OK;
}

//---------------------------------------------------------------------------
// Constructor
//---------------------------------------------------------------------------
CApp::CApp
(
  HINSTANCE hinst
)
{
  WNDCLASS cls;
  RECT     rect;
  int      cx, cy;

  INIT_SIGNATURE(SIG_App);

  // Init globals
  g_papp              = this;
  g_punkApplicationNA = this->GetUnknown(); // GetUnknown() doesn't AddRef, but we don't want to anyway.
  s_hr                = E_FAIL;             // Assume failure

  // Init members
  m_pmsgloop        = NULL;
  m_pgame           = NULL;
  m_hwndDlg         = NULL;
  m_hwndPS          = NULL;
  m_hwndStat        = NULL;
  m_hwndAbout       = NULL;
  m_hinst           = hinst;
  m_idcomp          = 0xffffffff;
  m_fRegisteredComp = FALSE;
  m_cmodal          = 0;
  m_fQuit           = FALSE;
  m_cref            = 1;
  m_ptinfoCls       = NULL;
  m_ptinfoInt       = NULL;
  m_pdispBaseObject = NULL;

  // Add new class for dialog which is our application:
  cls.style         = 0;
  cls.lpfnWndProc   = DefDlgProc;
  cls.cbClsExtra    = 0;
  cls.cbWndExtra    = 0;
  cls.hInstance     = hinst;
  cls.hIcon         = LoadIcon(hinst, MAKEINTRESOURCE(ID_ICO_APP));
  cls.hCursor       = NULL;
  cls.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
  cls.lpszMenuName  = NULL;
  cls.lpszClassName = "DlgClass";
  if (!RegisterClass(&cls))
    return;

  // Add new class for play surface:
  cls.style         = CS_OWNDC;
  cls.lpfnWndProc   = PSWndProc;
  cls.cbClsExtra    = 0;
  cls.cbWndExtra    = 0;
  cls.hInstance     = hinst;
  cls.hIcon         = NULL;
  cls.hCursor       = LoadCursor(hinst, MAKEINTRESOURCE(ID_CUR_PLAYSURF));
  cls.hbrBackground = NULL;
  cls.lpszMenuName  = NULL;
  cls.lpszClassName = "PlaySurface";
  if (!RegisterClass(&cls))
    return;

  // Now that we've registerd the window classes, create the main window
  m_hwndDlg = CreateDialog(hinst, MAKEINTRESOURCE(ID_DLG_APP), NULL, AppDlgProc);
  if (!m_hwndDlg)
    {
    s_hr = E_OUTOFMEMORY;
    return;
    }

  // Center Dialog:
  GetWindowRect(m_hwndDlg, &rect);
  rect.right  -= rect.left;
  rect.bottom -= rect.top;
  cx = GetSystemMetrics(SM_CXSCREEN);
  cy = GetSystemMetrics(SM_CYSCREEN);
  MoveWindow(m_hwndDlg, (cx - rect.right) >> 1, (cy - rect.bottom) >> 1, rect.right, rect.bottom, TRUE);

  // Create the Component Manager
  m_pmsgloop = new CMsgLoop(m_hwndDlg);
  if (!m_pmsgloop)
    {
    s_hr = E_OUTOFMEMORY;
    return;
    }

  m_fRegisteredComp = m_pmsgloop->FRegisterComponent(this, &App_crinfo, &m_idcomp);
  if (!m_fRegisteredComp)
    return;

  HRESULT hr = this->LoadGame();
  if (hr)
    {
    s_hr = hr;
    return;
    }

  // Success
  s_hr = S_OK;
  ShowWindow(m_hwndDlg, SW_NORMAL);
  return;
}

//---------------------------------------------------------------------------
// Destructor
//---------------------------------------------------------------------------
CApp::~CApp
(
  void
)
{
  CHECK_SIGNATURE(SIG_App);

  g_papp = NULL;
  g_punkApplicationNA = NULL;   // Wasn't AddRef()'d, so don't Release().

  if (m_hwndDlg)
    {
    ShowWindow(m_hwndDlg, SW_HIDE);
    ReleaseCapture();
    DestroyWindow(m_hwndDlg);
    m_hwndDlg = NULL;
    }

  if (m_pgame)
    {
    delete m_pgame;
    m_pgame = NULL;
    }

  if (m_fRegisteredComp)
    {
    ASSERT(m_pmsgloop, "m_idcomp, but !m_pmsgloop");
    BOOL f = m_pmsgloop->FRevokeComponent(m_idcomp);
    ASSERT(!f, "!f");
    m_fRegisteredComp = FALSE;
    }

  if (m_pmsgloop)
    {
    m_pmsgloop->Release();
    m_pmsgloop = NULL;
    }

  // Free dynamically created TypeInfo / TypeLib stuff
  if (g_ptinfoClsGame)
    g_ptinfoClsGame->Release();
  if (g_ptinfoIntGame)
    g_ptinfoIntGame->Release();

  if (g_ptlGameSubObj)
    g_ptlGameSubObj->Release();

  // Free TypeInfo / TypeLib loaded from our resources
  if (g_ptinfoClsGameOA)
    g_ptinfoClsGameOA->Release();
  if (g_ptinfoIntGameOA)
    g_ptinfoIntGameOA->Release();

  if (g_ptinfoClsSpriteClass)
    g_ptinfoClsSpriteClass->Release();
  if (g_ptinfoIntSpriteClass)
    g_ptinfoIntSpriteClass->Release();

  if (g_ptinfoClsSprite)
    g_ptinfoClsSprite->Release();
  if (g_ptinfoIntSprite)
    g_ptinfoIntSprite->Release();

  if (m_ptinfoCls)
    m_ptinfoCls->Release();
  if (m_ptinfoInt)
    m_ptinfoInt->Release();

  if (g_ptlMain)
    g_ptlMain->Release();

  DESTROY_SIGNATURE(SIG_App);
}

//***************************************************************************
// General methods
//***************************************************************************

//---------------------------------------------------------------------------
// Enter main message loop for application
//---------------------------------------------------------------------------
HRESULT CApp::MainMsgLoop
(
  void
)
{
  if (!m_pmsgloop->FPushMessageLoop(g_papp->m_idcomp, cmgrloopDoEvents, NULL))
    return E_FAIL;

  return S_OK;
}

//---------------------------------------------------------------------------
// Load a game from the given file.
//---------------------------------------------------------------------------
HRESULT CApp::LoadGame
(
  void
)
{
  if (m_pgame)
    return E_UNEXPECTED;

  return CGame::CreateGame(g_hinst, m_hwndDlg, m_hwndPS, m_hwndStat, m_pmsgloop, &m_pgame);
}

//---------------------------------------------------------------------------
// Get rid of an existing game, if present.
//---------------------------------------------------------------------------
void CApp::CloseGame
(
  void
)
{
  if (m_pgame)
    {
    m_pgame->Close();
    m_pgame->Release();
    m_pgame = NULL;
    }
}

//---------------------------------------------------------------------------
// Called when pausing or unpausing
//---------------------------------------------------------------------------
void CApp::CausePause
(
  int p   // -1=Toggle, 0=UnPause, 1=Pause
)
{
  // Invoke game.cpp's Pause event.  Returns whether or not app is paused
  p = AppEvt_Pause(p);
}

//***************************************************************************
// DlgProcs, WndProcs
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CALLBACK AppDlgProc
(
  HWND   hwnd,
  UINT   msg,
  WPARAM wp,
  LPARAM lp
)
{
  switch (msg)
    {
    case WM_INITDIALOG:
      {
      g_papp->m_hwndDlg  = hwnd;
      g_papp->m_hwndPS   = GetDlgItem(hwnd, ID_CTL_PLAYSURF);
      g_papp->m_hwndStat = GetDlgItem(hwnd, ID_CTL_STATUS);
      break;
      }

    case WM_ACTIVATE:
      if (LOWORD(wp) != WA_INACTIVE)
        {
        g_papp->m_pmsgloop->OnComponentActivate(g_papp->m_idcomp);
        SetFocus(g_papp->m_hwndPS);
        return TRUE;
        }
      break;

    case WM_SETFOCUS:
      SetFocus(g_papp->m_hwndPS);
      return TRUE;

    case WM_QUERYDRAGICON:
      return (BOOL)LoadIcon(g_papp->m_hinst, MAKEINTRESOURCE(ID_ICO_APP));

    case WM_PAINT:
      {
      HDC hdc;
      PAINTSTRUCT ps;

      if (!IsIconic(hwnd))
        return FALSE;
      hdc = wp ? (HDC)wp : BeginPaint(hwnd, &ps);
      DrawIcon(hdc, 0, 0, LoadIcon(g_papp->m_hinst, MAKEINTRESOURCE(ID_ICO_APP)));
      if (!wp)
        EndPaint(hwnd, &ps);
      return TRUE;
      }

    case WM_ERASEBKGND:
      {
      if (IsIconic(hwnd))
        {
        DefWindowProc(hwnd, msg, wp, lp);
        return TRUE;
        }

      // Draw fancy boarders
      RECT rect;

      GetClientRect(hwnd, &rect);
      DrawEdge((HDC)wp, &rect, 0, BF_RECT | BF_MIDDLE);

      GetWindowRect(g_papp->m_hwndPS, &rect);
      ScreenToClient(hwnd, (LPPOINT)&rect);
      ScreenToClient(hwnd, (LPPOINT)&rect.right);
      InflateRect(&rect, 2, 2);
      DrawEdge((HDC)wp, &rect, EDGE_SUNKEN, BF_RECT);

      GetWindowRect(g_papp->m_hwndStat, &rect);
      InflateRect(&rect, 2, 2);
      ScreenToClient(hwnd, (LPPOINT)&rect);
      ScreenToClient(hwnd, (LPPOINT)&rect.right);
      DrawEdge((HDC)wp, &rect, BDR_SUNKENOUTER, BF_RECT);
      break;
      }

    case WM_SYSCOMMAND:
      RECORD(WIN_DLG, msg, wp, lp);
      switch (wp & 0xfff0)
        {
        case SC_CLOSE:
          if (g_papp->FQueryTerminate(TRUE))
            g_papp->m_fQuit = TRUE;
          break;
        }
      return FALSE;

    case WM_SIZE:
      if (IsIconic(hwnd))
        g_papp->CausePause(TRUE);
      break;

    case WM_COMMAND:
      RECORD(WIN_DLG, msg, wp, lp);
      if (!LOWORD(lp) || HIWORD(lp) == 1)
        switch (wp)
          {
          case ID_MENU_NEWGAME:
            g_papp->CausePause(1);
            if (!g_papp->m_pgame->m_fGameOver && g_mode != MODE_PLAYBACK)
              {
              int ret;
              g_papp->m_pmsgloop->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
              ret = MessageBox(g_papp->m_hwndDlg, "Do you really want to start a new game?", "Spruuids", MB_YESNO | MB_ICONQUESTION);
              g_papp->m_pmsgloop->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
              SetFocus(g_papp->m_hwndPS);
              if (ret == IDYES)
                goto NewGame;
              }
            else
              {
NewGame:      g_papp->CausePause(0);
              AppEvt_NewGame();
              }
            return TRUE;

          case ID_MENU_EDIT:
            {
            char sz[255];
            // Shell notepad to edit file we loaded.
            g_papp->CausePause(1);
            wsprintf(sz, "Notepad.exe %s", g_pszCodeFile);
            WinExec(sz, SW_SHOWNORMAL);
            return TRUE;
            }

          case ID_MENU_RELOAD:
            g_papp->CausePause(1);
            if (g_papp->m_pgame && !g_papp->m_pgame->m_fGameOver)
              {
              int ret;
              g_papp->m_pmsgloop->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
              ret = MessageBox(g_papp->m_hwndDlg, "Do you really want to start a new game?", "Spruuids", MB_YESNO | MB_ICONQUESTION);
              g_papp->m_pmsgloop->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
              SetFocus(g_papp->m_hwndPS);
              if (ret == IDYES)
                goto OpenGame;
              }
            else
              {
OpenGame:     g_papp->CloseGame();
              g_papp->LoadGame();
              g_papp->CausePause(0);
              AppEvt_NewGame();    // Start a new game
              }
            return TRUE;

          case ID_MENU_EXIT:
            if (g_papp->FQueryTerminate(TRUE))
              g_papp->m_fQuit = TRUE;
            return TRUE;

          case ID_MENU_PAUSE:
            g_papp->CausePause(-1);
            break;

          case ID_MENU_ABOUT:
            g_papp->CausePause(1);
            g_papp->m_pmsgloop->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
            DialogBox(g_papp->m_hinst, MAKEINTRESOURCE(ID_DLG_ABOUT), g_papp->m_hwndDlg, AboutDlgProc);
            g_papp->m_pmsgloop->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
            SetFocus(g_papp->m_hwndPS);
            return TRUE;

          case ID_MENU_HIGHSCORES:
          case ID_MENU_HELPINDEX:
          case ID_MENU_HELPPLAY:
          case ID_MENU_HELPCOMMANDS:
          case ID_MENU_HELPHELP:
            return TRUE;
          }
      break;
    }

  return AppEvt_DlgProc(hwnd, msg, wp, lp);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CALLBACK AboutDlgProc
(
  HWND   hwnd,
  UINT   msg,
  WPARAM wp,
  LPARAM lp
)
{
  switch (msg)
    {
    case WM_INITDIALOG:
      {
      RECT rect;
      RECT rect2;

      g_papp->m_hwndAbout = hwnd;
      GetWindowRect(g_papp->m_hwndDlg, &rect);
      GetWindowRect(hwnd, &rect2);
      MoveWindow(hwnd, (rect.right+rect.left)/2-(rect2.right-rect2.left)/2,
                       (rect.bottom+rect.top)/2-(rect2.bottom-rect2.top)/2,
                       rect2.right-rect2.left,
                       rect2.bottom-rect2.top,
                       TRUE);
      return TRUE;
      }

    case WM_COMMAND:
      RECORD(WIN_ABOUT, msg, wp, lp);
      g_papp->m_hwndAbout = NULL;
      EndDialog(hwnd, TRUE);
      return TRUE;
    }
  return FALSE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
LRESULT CALLBACK PSWndProc
(
  HWND   hwnd,
  UINT   msg,
  WPARAM wp,
  LPARAM lp
)
{
  switch (msg)
    {
    case WM_TIMER:
    case WM_CHAR:
    case WM_KEYUP:
    case WM_MOUSEMOVE:
    case WM_LBUTTONUP:
    case WM_RBUTTONUP:
    case WM_MBUTTONUP:
      RECORD(WIN_PS, msg, wp, lp);
      break;

    case WM_LBUTTONDOWN:
    case WM_RBUTTONDOWN:
    case WM_MBUTTONDOWN:
      RECORD(WIN_PS, msg, wp, lp);
      SetFocus(hwnd);
      break;

    case WM_GETDLGCODE:
      return DLGC_WANTALLKEYS | DLGC_WANTARROWS | DLGC_WANTCHARS | DLGC_WANTMESSAGE | DLGC_WANTTAB;

    case WM_KEYDOWN:
      RECORD(WIN_PS, msg, wp, lp);
      switch (wp)
        {
        case VK_ESCAPE:
          ShowWindow(g_papp->m_hwndDlg, SW_MINIMIZE);
          break;

        case VK_F1:
          SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_HELPINDEX, 0L);
          break;

        case VK_F2:
          SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_NEWGAME, 0L);
          break;

        case VK_PAUSE:
        case VK_F3:
          SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_PAUSE, 0L);
          break;

        case VK_F5:
          SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_RELOAD, 0L);
          break;

        case VK_F9:
          SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_EDIT, 0L);
          break;
        }
      break;
    }

  return AppEvt_PSWndProc(hwnd, msg, wp, lp);
}

//***************************************************************************
// IUnknown Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::QueryInterface
(
  REFIID iid,
  void **ppvObjOut
)
{
  if (!ppvObjOut)
    return E_INVALIDARG;

  *ppvObjOut = NULL;

  if (iid == IID_IUnknown)
    *ppvObjOut = this->GetUnknown();
  else if (iid == IID_IDispatch)
    *ppvObjOut = this->GetDispatch();
  else if (iid == IID_ISpruuidsApp)
    *ppvObjOut = (ISpruuidsApp *)this;
  else if (iid == IID_IOleComponent)
    *ppvObjOut = (IOleComponent *)this;

  if (*ppvObjOut)
    {
    this->AddRef();
    return S_OK;
    }

  return E_NOINTERFACE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CApp::AddRef
(
  void
)
{
  ASSERT(m_cref, "bad m_cref");
  return ++m_cref;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CApp::Release
(
  void
)
{
  ASSERT(m_cref, "bad m_cref");
  m_cref--;
  if (!m_cref)
    {
    delete this;
    return 0;
    }
  return m_cref;
}

//***************************************************************************
// IOleComponent Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CApp::FPreTranslateMessage
(
  MSG *pmsg
)
{
  return IsDialogMessage(m_hwndDlg, pmsg);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CApp::OnEnterState
(
  ULONG state,
  BOOL  fEnter
)
{
  // TODO: We only care about Modal notifications.  You application will likely
  // TODO: need to support some of the others.

  if (state == cmgrstateModal)
    {
    if (fEnter)
      {
      if (!m_cmodal)
        {
        this->CausePause(1);              // Pause for the user, since we're modal
        EnableWindow(m_hwndDlg, FALSE);   // Entering modal state, so disable window
        }
      m_cmodal++;
      }
    else if (m_cmodal)    // Don't decrement if zero!
      {
      m_cmodal--;
      if (!m_cmodal)
        EnableWindow(m_hwndDlg, TRUE);   // Leaving modal state, so re-enable window
      }
    }
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CApp::OnAppActivate
(
  BOOL  fActive,
  DWORD dwOtherThreadID
)
{
  // TODO: If you have floating palettes, etc., show them here.
  SetFocus(m_hwndPS);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CApp::OnLoseActivation
(
  void
)
{
  // TODO: If you have floating palettes, etc., hide them here.
  this->CausePause(1);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------

STDMETHODIMP_(BOOL) CApp::FDoIdle
(
  DWORD grfidlef
)
{
  // TODO: If you have need idle-time processing, do so here
  return FALSE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CApp::FContinueMessageLoop
(
  ULONG uReason,
  void *pvLoopData
)
{
  MSG msg;

  // If we're playing back a recording, return from msg loop once there are
  // no more msgs.
  if (g_mode == MODE_PLAYBACK && !PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
    return FALSE;

  // Since the only time we push a message loop is for the Main message loop,
  // we don't want that one popped until we decide to exit the application.
  return !m_fQuit;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CApp::FQueryTerminate
(
  BOOL fPromptUser
)
{
  // Disallow termination if modal dlg is up
  if (m_cmodal)
    {
    if (fPromptUser)
      MessageBox(NULL, "Please terminate Dlg before exiting", "Spruuids",
                 MB_TASKMODAL | MB_ICONEXCLAMATION | MB_OK);
    return FALSE;
    }

  return AppEvt_FQueryTerminate(fPromptUser);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CApp::Terminate
(
  void
)
{
  // Unregister component & release all refs on Component Manager
  if (m_fRegisteredComp)
    {
    ASSERT(m_pmsgloop, "m_idcomp, but !m_pmsgloop");
    BOOL f = m_pmsgloop->FRevokeComponent(m_idcomp);
    ASSERT(!f, "!f");
    m_fRegisteredComp = FALSE;
    }

  if (m_pmsgloop)
    {
    m_pmsgloop->Release();
    m_pmsgloop = NULL;
    }

  m_fQuit = TRUE;
}

//***************************************************************************
// IDispatch Interface
//***************************************************************************

//---------------------------------------------------------------------------
// Method needed by COleAuto, so it can implement IDispatch for us.
//---------------------------------------------------------------------------
HRESULT CApp::GetTypeLibInfo
(
  HINSTANCE    *phinstOut,
  const GUID  **pplibidOut,
  SHORT        *pwMajLib,
  SHORT        *pwMinLib,
  const CLSID **ppclsidOut,
  const IID   **ppiidOut,
  ITypeLib   ***ppptlOut
)
{
  *phinstOut  = g_hinst;
  *pplibidOut = &LIBID_SPRUUIDS;
  *pwMajLib   = 1;
  *pwMinLib   = 0;
  *ppclsidOut = &CLSID_SpruuidsApp;
  *ppiidOut   = &IID_ISpruuidsApp;
  *ppptlOut   = &g_ptlMain;
  return S_OK;
}

//***************************************************************************
// SpruuidsApp Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::get_Application
(
  ISpruuidsApp** lppaReturn
)
{
  return this->QueryInterface(IID_ISpruuidsApp, (void **)lppaReturn);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::get_Parent
(
  ISpruuidsApp** lppaReturn
)
{
  return this->QueryInterface(IID_ISpruuidsApp, (void **)lppaReturn);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::Quit
(
  void
)
{
  m_fQuit = TRUE;

  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::get_Game
(
  IGame** lppaReturn
)
{
  return m_pgame->QueryInterface(IID_IGame, (void **)lppaReturn);
}

//--- EOF -------------------------------------------------------------------


RESOURCE.H

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Spruuids.rc
//
#define IDI_ICO_APP                     101
#define ID_CUR_PLAYSURF                 102
#define ID_DLG_APP                      103
#define ID_DLG_ABOUT                    201
#define ID_ICO_APP                      202
#define ID_DLG_ERROR                    304
#define ID_CTL_ERROR                    305
#define ID_CTL_LABEL                    306
#define ID_CTL_ICON                     307
#define ID_MENU_APP                     401
#define ID_MENU_NEWGAME                 402
#define ID_MENU_HIGHSCORES              403
#define ID_MENU_PAUSE                   404
#define ID_MENU_EXIT                    405
#define ID_MENU_HELPINDEX               406
#define ID_MENU_HELPPLAY                407
#define ID_MENU_HELPCOMMANDS            408
#define ID_MENU_HELPHELP                409
#define ID_MENU_ABOUT                   410
#define ID_CTL_PLAYSURF                 1000
#define ID_BMP_SPRITES                  1000
#define ID_BMP_GUY                      1001
#define ID_BMP_SHIP                     1001
#define ID_BMP_PLUS                     1002
#define ID_CTL_PAUSED                   1003
#define ID_CTL_STATUS                   1003
#define ID_MENU_RELOAD                  40001
#define ID_MENU_EDIT                    40002

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_3D_CONTROLS                     1
#define _APS_NEXT_RESOURCE_VALUE        124
#define _APS_NEXT_COMMAND_VALUE         40003
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


SCORE.CPP

//---------------------------------------------------------------------------
// Score.cpp
//---------------------------------------------------------------------------
// Score object to track score, # lives, level.
//---------------------------------------------------------------------------
// (C) Copyright 1995-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop
#include "Score.h"
#include "Spr.h"
#include "Game.h"
extern CGame *g_pgame;

//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE

//---------------------------------------------------------------------------
// Initialize score stuff
//---------------------------------------------------------------------------
HRESULT CScore::s_hr = E_FAIL;

HRESULT CScore::CreateScore
(
  HINSTANCE hinst,          // Needed to load bitmaps
  HWND      hwnd,           // hwnd on which to draw the Score, Level, # Lives
  HWND      hwndPS,         // hwnd of PlaySurface
  HWND      hwndStat,       // hwnd of Status Bar
  long      scoreFirst1Up,  // Score to first new life
  long      scoreSecond1Up, // Score to 2nd new life
  long      dscoreNext1Up,  // Score to next new life
  int       cship,          // Number lives to start
  IDBMP     idbmpShip,      // Bitmap for display of # lives
  IDBMP     idbmpPlus,      // Bitmap for "+" when out of room for # lives
  CScore  **ppscoreOut      // Return value: new score object
)
{
  // Initialize output parameters
  *ppscoreOut = NULL;

  // Validate required args, etc.
  if (!idbmpShip || !hwnd)
    return E_INVALIDARG;

  *ppscoreOut = new CScore(hinst, hwnd, hwndPS, hwndStat, scoreFirst1Up, scoreSecond1Up, dscoreNext1Up, cship, idbmpShip, idbmpPlus);
  if (!ppscoreOut)
    return E_OUTOFMEMORY;
  if (s_hr)
    {
    delete *ppscoreOut;
    return s_hr;
    }

  return S_OK;
}

//---------------------------------------------------------------------------
// Initialize score stuff
//---------------------------------------------------------------------------
CScore::CScore
(
  HINSTANCE hinst,          // Needed to load bitmaps
  HWND      hwnd,           // hwnd on which to draw the Score, Level, # Lives
  HWND      hwndPS,         // hwnd of PlaySurface
  HWND      hwndStat,       // hwnd of Status Bar
  long      scoreFirst1Up,  // Score to first new life
  long      scoreSecond1Up, // Score to 2nd new life
  long      dscoreNext1Up,  // Score to next new life
  int       cshipStart,     // Number lives to start
  IDBMP     idbmpShip,      // Bitmap for display of # lives
  IDBMP     idbmpPlus       // Bitmap for "+" when out of room for # lives
)
{
  BITMAP bmp;
  RECT   rectStat;

  INIT_SIGNATURE(SIG_Score);

  // Assume success
  s_hr = S_OK;

  // Assume reasonable default values, if not supplied
  m_hinst          = hinst;
  m_hwnd           = hwnd;
  m_hwndStat       = hwndStat;
  m_hwndPS         = hwndPS;
  m_scoreFirst1Up  = scoreFirst1Up  ? scoreFirst1Up : 0x7fffffff;
  m_scoreSecond1Up = scoreSecond1Up ? scoreFirst1Up : 0x7fffffff;
  m_dscoreNext1Up  = dscoreNext1Up  ? dscoreNext1Up : 0x7fffffff;
  m_cshipStart     = cshipStart;

  // Init rest of class
  m_hbmpShip  = NULL;
  m_hbmpPlus  = NULL;
  m_cyMax     = 0;
  m_cxShip    = 0;
  m_cyShip    = 0;
  m_cxPlus    = 0;
  m_cyPlus    = 0;
  // m_scoreNext1Up = m_scoreFirst1Up;    // Handled by NewGame()
  // m_score        = 0;                  // Handled by NewGame()
  // m_cship        = m_cshipStart;       // Handled by NewGame()
  // m_lvl          = 0;                  // Handled by NewGame()
  // m_rectScore    = 0;                  // Handled below
  // m_rectShip     = 0;                  // Handled below
  // m_rectLvl      = 0;                  // Handled below

  // Finish initing the score object
  this->NewGame();
  ASSERT(m_scoreNext1Up == m_scoreFirst1Up, "NewGame() not initing all");
  ASSERT(!m_score, "NewGame() not initing all");
  ASSERT(m_cship == cshipStart, "NewGame() not initing all");
  ASSERT(!m_lvl, "NewGame() not initing all");

  // Load the bmp for the display of lives
  m_hbmpShip = LoadBitmap(hinst, MAKEINTRESOURCE(idbmpShip));
  if (!m_hbmpShip)
    {
    s_hr = E_FAIL;
    return;
    }
  GetObject(m_hbmpShip, sizeof(bmp), (HGDIOBJ)&bmp);
  m_cxPlus = m_cxShip = bmp.bmWidth;
  m_cyPlus = m_cyShip = m_cyMax = bmp.bmHeight;

  // Load the bmp of the "+" for the display of lives
  if (idbmpPlus)
    {
    m_hbmpPlus = LoadBitmap(hinst, MAKEINTRESOURCE(idbmpPlus));
    if (!m_hbmpPlus)
      {
      s_hr = E_FAIL;
      return;
      }
    GetObject(m_hbmpPlus, sizeof(bmp), (HGDIOBJ)&bmp);
    m_cxPlus = bmp.bmWidth;
    m_cyPlus = bmp.bmHeight;
    if (m_cyPlus > m_cyMax)
      m_cyMax = m_cyPlus;
    }

  // Calc height of Text.
  // HACK: Cheat and use DrawText() with a "|" instead of getting TextMetrics...
  rectStat.top = 0;
  DrawText(GetDC(hwnd), "|", 1, &rectStat, DT_NOCLIP|DT_NOPREFIX|DT_SINGLELINE|DT_CALCRECT);
  m_cyStat = rectStat.bottom;
  if (rectStat.bottom > m_cyMax)
    m_cyMax = rectStat.bottom;

  // Place PlaySurface correctly
  this->Size(372, 282);
}

//---------------------------------------------------------------------------
// Given the size of the PlaySurface, calculate the size of the Dlg & other
// components.
//---------------------------------------------------------------------------
void CScore::Size
(
  int cx,
  int cy
)
{
  RECT rect, rectCli;

#define cBRD  (6)
  GetWindowRect(m_hwnd, &rect);
  GetClientRect(m_hwnd, &rectCli);
  ClientToScreen(m_hwnd, (LPPOINT)&rectCli);
  ClientToScreen(m_hwnd, (LPPOINT)&rectCli.right);
  MoveWindow(m_hwnd,     rect.left,
                         rect.top,
                         cx + (2 * cBRD) + (rect.right - rect.left) - (rectCli.right - rectCli.left),
                         cy + (3 * cBRD + m_cyMax + m_cyStat) + (rect.bottom - rect.top) - (rectCli.bottom - rectCli.top),
                         TRUE);
  GetClientRect(m_hwnd, &rect);
  MoveWindow(m_hwndStat, cBRD,
                         rect.bottom - (m_cyStat + cBRD),
                         rect.right  - (2 * cBRD),
                         m_cyStat, TRUE);
  MoveWindow(m_hwndPS,   cBRD,
                         cBRD + m_cyMax,
                         rect.right  - (2 * cBRD),
                         rect.bottom - (3 * cBRD + m_cyMax + m_cyStat), TRUE);
  GetWindowRect(m_hwndPS, &rect);
  ScreenToClient(m_hwnd, (LPPOINT)&rect);
  ScreenToClient(m_hwnd, (LPPOINT)&rect.right);
  rect.top    = 1;
  rect.bottom = m_cyMax + 4;
  m_rectScore = rect;
  m_rectShip  = rect;
  m_rectLvl   = rect;
  m_rectShip.right = m_rectLvl.left   = rect.left +  (rect.right - rect.left) / 3;
  m_rectLvl.right  = m_rectScore.left = rect.left + ((rect.right - rect.left) / 3 * 2);
  GetClientRect(m_hwndPS, &rect);
  g_pgame->m_pdisp->m_cx = rect.right  - rect.left;
  g_pgame->m_pdisp->m_cy = rect.bottom - rect.top;
  InvalidateRect(m_hwnd,   NULL, TRUE);
  InvalidateRect(m_hwndPS, NULL, TRUE);
}

//---------------------------------------------------------------------------
// Terminate score stuff
//---------------------------------------------------------------------------
CScore::~CScore
(
  void
)
{
  CHECK_SIGNATURE(SIG_Score);

  if (m_hbmpShip)
    DeleteObject((HGDIOBJ)m_hbmpShip);
  if (m_hbmpPlus)
    DeleteObject((HGDIOBJ)m_hbmpPlus);

  DESTROY_SIGNATURE(SIG_Score);
}

//---------------------------------------------------------------------------
// Re-initialize score stuff for new game
//---------------------------------------------------------------------------
void CScore::NewGame
(
  void
)
{
  m_scoreNext1Up = m_scoreFirst1Up;
  m_score        = 0;
  m_cship        = m_cshipStart;
  m_lvl          = 0;

  InvalidateRect(m_hwnd, NULL, TRUE);
}

//---------------------------------------------------------------------------
// Paint score stuff: # 1Ups, current level, score
//---------------------------------------------------------------------------
void CScore::Paint
(
  HDC hdc
)
{
  char rgch[100];
  int x, y;
  int i;
  extern HDC     g_hdcMem;
  extern HBITMAP g_hbmpStock;

  SelectObject(g_hdcMem, m_hbmpShip);
  x = m_rectShip.left;
  y = m_rectShip.top + ((m_rectShip.bottom - m_rectShip.top - m_cyShip) >> 1);

  // Draw the 1Ups w/a "+", if necessary
  for (i=1; i<m_cship; i++)
    {
    BitBlt(hdc, x, y, m_cxShip, m_cyShip, g_hdcMem, 0, 0, SRCCOPY);
    x += m_cxShip + (m_cxShip >> 2);

    // If we only have more 1Ups, but only room for the "+", draw the "+"
    if (x + m_cxShip + m_cxPlus > m_rectShip.right && m_hbmpPlus && i+1<m_cship)
      {
      y = m_rectShip.top + ((m_rectShip.bottom - m_rectShip.top - m_cyPlus) >> 1);
      SelectObject(g_hdcMem, m_hbmpPlus);
      BitBlt(hdc, x, y, m_cxPlus, m_cyPlus, g_hdcMem, 0, 0, SRCCOPY);
      break;
      }
    }
  SelectObject(g_hdcMem, g_hbmpStock);

  SetBkMode(hdc, TRANSPARENT);
  wsprintf(rgch, "%ld", m_score);
  DrawText(hdc, rgch, -1, &m_rectScore, DT_RIGHT|DT_NOCLIP|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER);

  wsprintf(rgch, "%d", m_lvl);
  DrawText(hdc, rgch, -1, &m_rectLvl,   DT_CENTER|DT_NOCLIP|DT_NOPREFIX|DT_SINGLELINE|DT_VCENTER);
}

//---------------------------------------------------------------------------
// Change the score by the delta d.
//---------------------------------------------------------------------------
void CScore::Add
(
  long d      // Change the score by this amount (signed)
)
{
  m_score += d;
  if (m_score < 0)
    m_score = 0;
  if (m_score >= m_scoreNext1Up)
    {
    if (m_score > m_scoreSecond1Up)
      m_scoreNext1Up += m_dscoreNext1Up;
    else
      m_scoreNext1Up = m_scoreSecond1Up;
    m_cship++;
    InvalidateRect(m_hwnd, &m_rectShip, TRUE);
    g_pgame->m_pgameoa->FireNewShip();
    }

  InvalidateRect(m_hwnd, &m_rectScore, TRUE);
}

//---------------------------------------------------------------------------
// Called when pausing or unpausing
//---------------------------------------------------------------------------
void CScore::SetStatusText
(
  const char *pszText
)
{
  SetWindowText(m_hwndStat, pszText);
}

//--- EOF -------------------------------------------------------------------


SCORE.H

//---------------------------------------------------------------------------
// Score.h
//---------------------------------------------------------------------------
// Score object to track score, # lives, level.
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Score class
//---------------------------------------------------------------------------
class CScore
  {
public:
  // *** Constructor / Destructor ***
  static HRESULT CreateScore(HINSTANCE hinst, HWND hwnd, HWND hwndPS, HWND hwndStat,
                             long scoreFirst1Up, long scoreSecond1Up, long dscoreNext1Up,
                             int cship, WORD idbmpShip, WORD idbmpPlus,
                             CScore **ppscoreOut);
  CScore(HINSTANCE hinst, HWND hwnd, HWND hwndPS, HWND hwndStat, long scoreSecond1Up, long scoreFirst1Up, long dscoreNext1Up, int cship, WORD idbmpShip, WORD idbmpPlus);
  ~CScore();

  // *** Public methods ***
  void NewGame(void);
  void Add(long d);
  void Paint(HDC hdc);
  void SetStatusText(const char *pszText);
  void Size(int cx, int cy);

  // *** Public accessor methods ***
  inline long GetScore(void)      {return m_score;};
  inline void SetScore(int s)     {m_score = s;   InvalidateRect(m_hwnd, &m_rectScore, TRUE);};
  inline long GetLevel(void)      {return m_lvl;};
  inline long GetCShip(void)      {return m_cship;};
  inline void SetLevel(int lvl)   {m_lvl = lvl;   InvalidateRect(m_hwnd, &m_rectLvl,  TRUE);};
  inline void NextLevel(void)     {m_lvl++;       InvalidateRect(m_hwnd, &m_rectLvl,  TRUE);};
  inline void SetCShip(int cship) {m_cship=cship; InvalidateRect(m_hwnd, &m_rectShip, TRUE);};
  inline void ShipKilled(void)    {m_cship--;     InvalidateRect(m_hwnd, &m_rectShip, TRUE);};
  inline int  GetSize(void)       {return m_cyMax;};

  // Public members
  long      m_scoreFirst1Up;
  long      m_scoreSecond1Up;
  long      m_dscoreNext1Up;
  short     m_cshipStart;
  long      m_scoreNext1Up; // Inits to m_scoreFirst1Up

private:
  // *** Private member variables ***
  // Reset at NewGame()
  long      m_score;        // Inits to 0
  short     m_cship;        // Inits to m_cshipStart
  short     m_lvl;          // Inits to 1

  // Static state passed into contructor
  HINSTANCE m_hinst;
  HWND      m_hwnd;
  HWND      m_hwndPS;
  HWND      m_hwndStat;

  // State calculated from static state
  RECT      m_rectScore;
  RECT      m_rectShip;
  RECT      m_rectLvl;
  HBITMAP   m_hbmpShip;
  HBITMAP   m_hbmpPlus;
  int       m_cyMax,  m_cyStat;
  int       m_cxShip, m_cyShip;
  int       m_cxPlus, m_cyPlus;

  // Set if constructor fails
  static HRESULT s_hr;

  // DEBUG info
  #define SIG_Score 'Scor'
  DECLARE_SIGNATURE(SIG_Score);
  };

//--- EOF -------------------------------------------------------------------


SPR.CPP

//---------------------------------------------------------------------------
// Spr.cpp
//---------------------------------------------------------------------------
// Sprite handler
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop
#include "Spr.h"
#include <math.h>

//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE

//---------------------------------------------------------------------------
// Global Variables
//---------------------------------------------------------------------------
ITypeInfo *g_ptinfoClsSpriteClass = NULL;
ITypeInfo *g_ptinfoIntSpriteClass = NULL;
ITypeInfo *g_ptinfoClsSprite      = NULL;
ITypeInfo *g_ptinfoIntSprite      = NULL;
HBITMAP    g_hbmpStock            = NULL;
HBRUSH     g_hbrushStock          = NULL;
HDC        g_hdcMem               = NULL;

//***************************************************************************
//***************************************************************************
//**
//**  Display Class
//**
//***************************************************************************
//***************************************************************************

//---------------------------------------------------------------------------
// Initialize display surface for sprites
//---------------------------------------------------------------------------
CDisplay::CDisplay
(
  HINSTANCE  hinst,
  HWND       hwnd,
  HDC        hdc,
  IUnknown  *punkParent,
  PFNCOLLIDE pfnCollide,
  int        cimg,
  PIMG       prgimg,
  UINT       idbmp
)
{
  RECT rect;

  INIT_SIGNATURE(SIG_Display);

  // Create an HDC and get the stock bitmap
  if (!g_hdcMem)
    {
    HBITMAP hbmp;

    g_hdcMem = CreateCompatibleDC(NULL);
    hbmp = CreateCompatibleBitmap(g_hdcMem, 1, 1);
    g_hbmpStock = (HBITMAP)SelectObject(g_hdcMem, hbmp);
    SelectObject(g_hdcMem, g_hbmpStock);
    DeleteObject(hbmp);
    g_hbrushStock = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
    }

  // Stash away values & init member variables
  m_hinst      = hinst;
  m_hdc        = hdc;
  GetClientRect(hwnd, &rect);
  m_cx         = rect.right;
  m_cy         = rect.bottom;
  m_pfnCollide = pfnCollide;
  m_csc        = 0;
  m_cimg       = cimg;
  m_prgimg     = prgimg;
  m_idbmp      = idbmp;
  m_psprFirst  = NULL;
  m_psprLast   = NULL;
  m_punkParent = punkParent;
  m_hbmp       = NULL;
  m_hbrushBack = NULL;
  this->SetBackColor(0x00c0c0c0);
  m_hwnd       = hwnd;

  // Compute animated images
  for (int i=0; i<cimg; i++)
    if (prgimg[i].iimgNext != iimgNONE &&
        prgimg[i].iimgNext != iimgREMOVE)
      prgimg[i].pimgNext = &prgimg[prgimg[i].iimgNext];

  // Load in all hbmp
  m_hbmp = LoadBitmap(m_hinst, MAKEINTRESOURCE(m_idbmp));
}

//---------------------------------------------------------------------------
// Terminate Sprite Sytem
//---------------------------------------------------------------------------
CDisplay::~CDisplay
(
  void
)
{
  CHECK_SIGNATURE(SIG_Display);

  if (m_hbrushBack)
    {
    SelectObject(g_hdcMem, g_hbrushStock);
    DeleteObject(m_hbrushBack);
    m_hbrushBack = NULL;
    }

  if (g_hdcMem)
    {
    SelectObject(g_hdcMem, g_hbmpStock);
    DeleteDC(g_hdcMem);
    g_hdcMem = NULL;
    }

  if (m_hbmp)
    {
    // Delete all loaded bitmaps:
    DeleteObject((HGDIOBJ)m_hbmp);
    m_hbmp = (HBITMAP)NULL;
    }

  DESTROY_SIGNATURE(SIG_Display);
}

//---------------------------------------------------------------------------
// Free all sprites on Display.
//---------------------------------------------------------------------------
void CDisplay::DestroyAll
(
  void
)
{
  CSprite *pspr;

  while (m_psprFirst)
    {
    pspr = m_psprFirst;
    pspr->_Unlink(FALSE);
    pspr->Release();
    }
}

//---------------------------------------------------------------------------
// Move all sprites on Display.
//---------------------------------------------------------------------------
void CDisplay::Timer
(
  void
)
{
  CSprite *pspr;
  CSprite *psprPrev;

  for (pspr=m_psprLast; pspr; pspr=psprPrev)
    {
    psprPrev = pspr->m_psprPrev;

    // Fire Tick event, if sprite exists
    if (!pspr->m_fFree)
      {
      if (--pspr->m_tickEvt <= 0)
        {
        pspr->m_psc->FireTick(pspr);
        pspr->m_tickEvt = pspr->m_tickEvtMax;
        }
      }

    // Move sprite, if it still exits (FireTick might have killed it.)
    if (!pspr->m_fFree)
      {
      if (--pspr->m_tickMove <= 0)
        {
        long xOldFull;
        long yOldFull;

        pspr->m_tickMove = pspr->m_tickMoveMax;

        // Does this sprite have a "MoveAt" sprite attached?
        if (pspr->m_psprAt)
          pspr->DoMoveAt(pspr->m_psprAt, pspr->m_dMaxAt, pspr->m_scaleAt, pspr->m_flagsAt, TRUE);

        xOldFull = pspr->m_xFull;
        yOldFull = pspr->m_yFull;
        if (HIWORD(xOldFull + pspr->m_vxFull) == HIWORD(xOldFull) &&
            HIWORD(yOldFull + pspr->m_vyFull) == HIWORD(yOldFull))
          {
          // Movement was only fractional, so don't need to redraw sprite.
          pspr->m_xFull += pspr->m_vxFull;
          pspr->m_yFull += pspr->m_vyFull;
          }
        else
          {
          pspr->_Erase();
          pspr->m_xFull += pspr->m_vxFull;
          pspr->m_yFull += pspr->m_vyFull;
          if (pspr->m_pimg->iimgNext == iimgREMOVE)
            pspr->Remove();
          else if (pspr->m_pimg->iimgNext != iimgNONE)
            {
            pspr->m_iimg = pspr->m_pimg->iimgNext;
            pspr->m_pimg = pspr->m_pimg->pimgNext;
            }
          if (!pspr->_FDraw(TRUE))
            {
            pspr->m_xFull = xOldFull;
            pspr->m_yFull = yOldFull;
            pspr->_FDraw(FALSE);
            }
          }

        // Calculate Friction
        if (pspr->m_fFriction)
          {
          pspr->m_vxFull = (long)((FLOAT)pspr->m_vxFull * pspr->m_fx);
          pspr->m_vyFull = (long)((FLOAT)pspr->m_vyFull * pspr->m_fy);
          }

        // Calculate AccelerationFriction
        if (pspr->m_psc->m_fFrictionAccel)
          {
          pspr->m_axFull = (long)((FLOAT)pspr->m_axFull * pspr->m_psc->m_frictionAccel);
          pspr->m_ayFull = (long)((FLOAT)pspr->m_ayFull * pspr->m_psc->m_frictionAccel);
          }

        // Calculate Acceleration
        pspr->m_vxFull += pspr->m_axFull;
        pspr->m_vyFull += pspr->m_ayFull;

        // Ensure Vel is not over max allowed for SpriteClass
        if (pspr->m_vxFull > pspr->m_psc->m_vFullMax)
          pspr->m_vxFull = pspr->m_psc->m_vFullMax;
        else if (pspr->m_vxFull < -pspr->m_psc->m_vFullMax)
          pspr->m_vxFull = -pspr->m_psc->m_vFullMax;
        if (pspr->m_vyFull > pspr->m_psc->m_vFullMax)
          pspr->m_vyFull = pspr->m_psc->m_vFullMax;
        else if (pspr->m_vyFull < -pspr->m_psc->m_vFullMax)
          pspr->m_vyFull = -pspr->m_psc->m_vFullMax;
        }
      }

    if (pspr->m_fFree)
      {
      pspr->_Unlink(TRUE);
      pspr->Release();
      }
    }
}

//---------------------------------------------------------------------------
// Paint all sprites.
//---------------------------------------------------------------------------
void CDisplay::Paint
(
  HDC hdc
)
{
  CSprite *pspr;

  if (hdc == NULL)
    hdc = m_hdc;

  for (pspr=m_psprLast; pspr; pspr=pspr->m_psprPrev)
    {
    if (!pspr->m_fFree && !pspr->m_fHidden)
      {
      SelectObject(g_hdcMem, m_hbmp);
      BitBlt(hdc, pspr->m_x, pspr->m_y, pspr->m_pimg->cx, pspr->m_pimg->cy, g_hdcMem, pspr->m_pimg->x, pspr->m_pimg->y, SRCCOPY);
      }
    }
  SelectObject(g_hdcMem, g_hbmpStock);
}

//---------------------------------------------------------------------------
// Paint any sprites with fNeedRefresh set.
//---------------------------------------------------------------------------
void CDisplay::Refresh
(
  void
)
{
  CSprite *pspr;
  HDC   hdc = m_hdc;

  for (pspr=m_psprLast; pspr; pspr=pspr->m_psprPrev)
    {
    if (pspr->m_fNeedRefresh && !pspr->m_fHidden)
      {
      pspr->m_fNeedRefresh = FALSE;
      SelectObject(g_hdcMem, m_hbmp);
      BitBlt(hdc, pspr->m_x, pspr->m_y, pspr->m_pimg->cx, pspr->m_pimg->cy, g_hdcMem, pspr->m_pimg->x, pspr->m_pimg->y, SRCCOPY);
      }
    }
  SelectObject(g_hdcMem, g_hbmpStock);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CDisplay::SetBackColor
(
  COLORREF c
)
{
  static LOGBRUSH lb = {BS_SOLID, 0x00c0c0c0, 0};

  if (m_colorBack == c)
    return;
  m_colorBack = c;
  lb.lbColor = c;
  if (m_hbrushBack)
    DeleteObject(m_hbrushBack);
  m_hbrushBack = CreateBrushIndirect(&lb);
}

//---------------------------------------------------------------------------
// Send Collide event back to the fn passed into the constructor.
//---------------------------------------------------------------------------
void CDisplay::FireCollide
(
  CSprite *psprLowId,
  CSprite *psprHighId,
  int      maskCollide
)
{
  (*m_pfnCollide)(psprLowId, psprHighId, maskCollide);
}

//***************************************************************************
//***************************************************************************
//**
//**  SpriteClass Class
//**
//***************************************************************************
//***************************************************************************

HRESULT s_hr = S_OK;

//---------------------------------------------------------------------------
// Create function for SpriteClass.  Use instead of "new CSpriteClass".
//---------------------------------------------------------------------------
HRESULT CSpriteClass::Create
(
  CDisplay      *pdisp,
  CSpriteClass **ppscOut
)
{
  if (!ppscOut)
    return E_INVALIDARG;

  *ppscOut = new CSpriteClass(pdisp);
  if (!*ppscOut)
    return E_OUTOFMEMORY;

  return S_OK;
}

//---------------------------------------------------------------------------
// Constructor for SpriteClass
//---------------------------------------------------------------------------
CSpriteClass::CSpriteClass
(
  CDisplay *pdisp
)
{
  INIT_SIGNATURE(SIG_SpriteClass);

  m_id              = ++(pdisp->m_csc);
  m_pdisp           = pdisp;
  m_brd             = SPR_brdALL;
  m_maskCollide     = 0;
  m_iimg            = 0;
  m_cspr            = 0;
  m_cref            = 1;
  m_pdispBaseObject = NULL;
  VariantInit(&m_varTag);
  m_friction        = (FLOAT)1.0;
  m_frictionAccel   = (FLOAT)1.0;
  m_fFrictionAccel  = FALSE;
  m_vFullMax    = 0x000a0000;
  m_aFullMax    = 0x00010000;
}

//---------------------------------------------------------------------------
// Destructor for Sprite
//---------------------------------------------------------------------------
void CSpriteClass::Close
(
  void
)
{
  CHECK_SIGNATURE(SIG_SpriteClass);

  CSprite *pspr;
  CSprite *psprPrev;

  // Destroy all sprites of this SpriteClass
  for (pspr=m_pdisp->m_psprLast; pspr; pspr=psprPrev)
    {
    psprPrev = pspr->m_psprPrev;

    if (pspr->m_psc == this)
      {
      pspr->_Unlink(FALSE);
      pspr->m_psc = NULL;
      pspr->Release();
      }
    }

  // Unadvise all event sinks
  m_cp.Close();

  // Clear incase this is an object
  VariantClear(&m_varTag);
}

//---------------------------------------------------------------------------
// Destructor for Sprite
//---------------------------------------------------------------------------
CSpriteClass::~CSpriteClass
(
  void
)
{
  CHECK_SIGNATURE(SIG_SpriteClass);
  this->Close();
  DESTROY_SIGNATURE(SIG_SpriteClass);
}

//***************************************************************************
// IUnknown Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::QueryInterface
(
  REFIID  iid,
  LPVOID* ppvObjOut
)
{
  if (!ppvObjOut)
    return E_INVALIDARG;

  *ppvObjOut = NULL;

  if (iid == IID_IUnknown)
    *ppvObjOut = this->GetUnknown();
  else if (iid == IID_IDispatch)
    *ppvObjOut = this->GetDispatch();
  else if (iid == IID_ISpriteClass)
    *ppvObjOut = (ISpriteClass *)this;
  else if (iid == IID_IConnectionPointContainer)
    *ppvObjOut = (IConnectionPointContainer *)this;
  else if (iid == IID_IProvideClassInfo)
    *ppvObjOut = (IProvideClassInfo *)this;

  if (*ppvObjOut)
    {
    this->AddRef();
    return S_OK;
    }

  return E_NOINTERFACE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSpriteClass::AddRef
(
  void
)
{
  return ++m_cref;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSpriteClass::Release
(
  void
)
{
  ASSERT(m_cref, "bad m_cref");
  m_cref--;
  if (!m_cref)
    {
    this->Close();
    if (!m_cref && !m_cp.m_cref)
      {
      delete this;
      return 0;
      }
    }
  return m_cref;
}

//***************************************************************************
// IDispatch Interface
//***************************************************************************

//---------------------------------------------------------------------------
// Method needed by COleAuto, so it can implement IDispatch for us.
//---------------------------------------------------------------------------
HRESULT CSpriteClass::GetTypeLibInfo
(
  HINSTANCE    *phinstOut,
  const GUID  **pplibidOut,
  SHORT        *pwMajLib,
  SHORT        *pwMinLib,
  const CLSID **ppclsidOut,
  const IID   **ppiidOut,
  ITypeLib   ***ppptlOut
)
{
  *phinstOut  = g_hinst;
  *pplibidOut = &LIBID_SPRUUIDS;
  *pwMajLib   = 1;
  *pwMinLib   = 0;
  *ppclsidOut = &CLSID_SpriteClass;
  *ppiidOut   = &IID_ISpriteClass;
  *ppptlOut   = &g_ptlMain;
  return S_OK;
}

//***************************************************************************
// IProvideClassInfo Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::GetClassInfo
(
  ITypeInfo** pptinfoOut
)
{
  HRESULT hr = ((COleAuto *)this)->CheckTypeInfo(0, 0x0409);
  if (hr)
    return hr;
  *pptinfoOut = g_ptinfoClsSpriteClass;
  (*pptinfoOut)->AddRef();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::GetGUID
(
  DWORD dwGuidKind,
  GUID* pGUID
)
{
  if (pGUID)
    return E_INVALIDARG;

  if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
    return E_INVALIDARG;

  *pGUID = DIID_ISpriteClassEvents;
  return S_OK;
}

//***************************************************************************
// IConnectionPointContainer Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::EnumConnectionPoints
(
  LPENUMCONNECTIONPOINTS* ppEnum
)
{
  return E_NOTIMPL;   // UNDONE: Implement this method
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::FindConnectionPoint
(
  REFIID             iid,
  LPCONNECTIONPOINT* ppCpOut
)
{
  if (!ppCpOut)
    return E_INVALIDARG;

  if (iid == DIID_ISpriteClassEvents || iid == IID_IDispatch)
    {
    *ppCpOut = &m_cp;
    (*ppCpOut)->AddRef();
    return S_OK;
    }

  return E_NOINTERFACE;
}

//***************************************************************************
// Embedded IConnectionPoint Class
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
CSpriteClass::XCP::XCP
(
  void
)
{
  m_cref = 1;
  for (int i=0; i<SC_cADVISE; i++)
    m_rgpdisp[i] = NULL;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CSpriteClass::XCP::Close
(
  void
)
{
  for (int i=0; i<SC_cADVISE; i++)
    if (m_rgpdisp[i])
      {
      m_rgpdisp[i]->Release();
      m_rgpdisp[i] = NULL;
      }
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::QueryInterface
(
  REFIID  iid,
  LPVOID* ppvObjOut
)
{
  if (!ppvObjOut)
    return E_INVALIDARG;

  *ppvObjOut = NULL;

  if (iid == IID_IUnknown)
    *ppvObjOut = this->GetUnknown();
  else if (iid == IID_IConnectionPoint)
    *ppvObjOut = (IConnectionPoint *)this;

  if (*ppvObjOut)
    {
    this->AddRef();
    return S_OK;
    }

  return E_NOINTERFACE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSpriteClass::XCP::AddRef
(
  void
)
{
  return ++m_cref;
}

#define SCOFCP(pcp)  ((CSpriteClass *)((char *)pcp - (char *)&(((CSpriteClass *)0)->m_cp)))
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSpriteClass::XCP::Release
(
  void
)
{
  CSpriteClass *psc = SCOFCP(this);

  ASSERT(m_cref, "bad m_cref");
  m_cref--;
  if (!m_cref && !psc->m_cref)
    {
    delete psc;
    return 0;
    }
  return m_cref;
}

//***************************************************************************
// IConnectionPoint Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::GetConnectionInterface
(
  IID* piid
)
{
  if (!piid)
    return E_INVALIDARG;

  memcpy(piid, &DIID_ISpriteClassEvents, sizeof(IID));
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::GetConnectionPointContainer
(
  IConnectionPointContainer** ppCpcOut
)
{
  if (!ppCpcOut)
    return E_INVALIDARG;

  *ppCpcOut = SCOFCP(this);
  (*ppCpcOut)->AddRef();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::Advise
(
  IUnknown* punkSink,
  DWORD*    pdwCookie
)
{
  IDispatch  *psce = NULL;
  IDispatch **ppdisp;
  HRESULT     hr;

  if (!punkSink || !pdwCookie)
    return E_INVALIDARG;
  *pdwCookie = 0;

  // Look for empty slot
  for (ppdisp=m_rgpdisp; *ppdisp && ppdisp<&m_rgpdisp[SC_cADVISE]; ppdisp++)
    ;
  // Did we find one?
  if (ppdisp >= &m_rgpdisp[SC_cADVISE])
    return E_FAIL;          // UNDONE: Error?

  // Now see if sink supports correct interface
  hr = punkSink->QueryInterface(DIID_ISpriteClassEvents, (void **)&psce);
  if (hr == E_NOINTERFACE)
    {
    hr = punkSink->QueryInterface(IID_IDispatch, (void **)&psce);
    if (hr)
      return hr;
    }
  ASSERT(psce, "QI but no ptr");

  // Finish advise by stashing punkSink QI'd to our interface
  *ppdisp = psce;       // AddRef'd from QI
  *pdwCookie = (ppdisp - m_rgpdisp) + 1;

  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::Unadvise
(
  DWORD dwCookie
)
{
  if (!dwCookie || dwCookie > SC_cADVISE)
    return E_INVALIDARG;

  m_rgpdisp[dwCookie-1]->Release();
  m_rgpdisp[dwCookie-1] = NULL;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::XCP::EnumConnections
(
  LPENUMCONNECTIONS* ppEnum
)
{
  return E_NOTIMPL;   // UNDONE: Implement this method
}

//***************************************************************************
//***************************************************************************
//**
//**  Sprite Class
//**
//***************************************************************************
//***************************************************************************

//---------------------------------------------------------------------------
// Constructor for Sprite
//---------------------------------------------------------------------------
CSprite::CSprite
(
  CSpriteClass *psc,
  int           x,
  int           y,
  CSprite      *psprPrev,  // NULL==1st, -1==Last
  VARIANT      *pvarUser
)
{
  INIT_SIGNATURE(SIG_Sprite);

  m_pdisp        = psc->m_pdisp;
  m_psprNext     = NULL;
  m_psprPrev     = psprPrev;
  m_xFull        = 0;
  m_x            = x;
  m_yFull        = 0;
  m_y            = y;
  m_vxFull       = 0;
  m_vyFull       = 0;
  m_axFull       = 0;
  m_ayFull       = 0;
  m_fx           = psc->m_friction;
  m_fy           = psc->m_friction;
  m_fFriction    = (psc->m_friction != 1.0);
  m_tickEvt      = 0x7fffffff;
  m_tickEvtMax   = 0x7fffffff;
  m_tickMove     = 1;
  m_tickMoveMax  = 1;
  m_iimg         = psc->m_iimg;
  m_pimg         = &m_pdisp->m_prgimg[m_iimg];
  m_psc          = psc;
  m_fNeedRefresh = TRUE;
  m_fFree        = FALSE;
  m_fIgnoreMove  = FALSE;
  m_fLinked      = FALSE;
  m_fHidden      = 2;     // Set to 2 instead of TRUE to detect if user explicitly hides it during _Init event
  m_cref         = 1;
  m_psprAt       = NULL;
  m_pdispBaseObject = NULL;
  VariantInit(&m_varTag);
  this->_Link();

  m_psc->FireInit(this, pvarUser);
  if (m_fHidden == 2)
    m_fHidden = FALSE;    // If not explicitly hidden in event, show it
}

//---------------------------------------------------------------------------
// Destructor for Sprite
//---------------------------------------------------------------------------
CSprite::~CSprite
(
  void
)
{
  CHECK_SIGNATURE(SIG_Sprite);
  VariantClear(&m_varTag);
  if (m_psprAt)
    {
    m_psprAt->Release();
    m_psprAt = NULL;
    }
  this->_Unlink(TRUE);
  DESTROY_SIGNATURE(SIG_Sprite);
}

//---------------------------------------------------------------------------
// Change the class of an existing pspr
//---------------------------------------------------------------------------
CSprite *CSprite::PsprMutate
(
  CSpriteClass *psc,
  VARIANT      *pvarUser
)
{
  m_x          += m_pimg->cx >> 1;
  m_y          += m_pimg->cy >> 1;
  m_iimg        = psc->m_iimg;
  m_pimg        = &m_pdisp->m_prgimg[m_iimg];
  m_x          -= m_pimg->cx >> 1;
  m_y          -= m_pimg->cy >> 1;
  m_psc->m_cspr--;
  m_psc         = psc;
  m_psc->m_cspr++;
  m_tickEvt     = 0x7fffffff;
  m_tickEvtMax  = 0x7fffffff;
  m_tickMove    = 0;
  m_tickMoveMax = 1;
  m_fHidden     = 2;     // Set to 2 instead of TRUE to detect if user explicitly hides it during _Init event

  m_psc->FireInit(this, pvarUser);
  if (m_fHidden == 2)
    m_fHidden = FALSE;    // If not explicitly hidden in event, show it
  return this;
}

//---------------------------------------------------------------------------
// Free a SPR structure
//---------------------------------------------------------------------------
void CSprite::MarkFree
(
  void
)
{
  if (m_psprAt)
    {
    m_psprAt->Release();
    m_psprAt = NULL;
    }
  m_psc->FireTerm(this);
  m_fFree = TRUE;
}

//---------------------------------------------------------------------------
// Link a sprite into the display list
//---------------------------------------------------------------------------
void CSprite::_Link
(
  void
)
{
  if (!m_psprPrev)
    {
    m_psprNext = m_pdisp->m_psprFirst;
    m_pdisp->m_psprFirst = this;
    if (m_psprNext)
      m_psprNext->m_psprPrev = this;
    if (!m_pdisp->m_psprLast)
      m_pdisp->m_psprLast = this;
    }
  else if (m_psprPrev == (CSprite *)-1)
    {
    m_psprNext = NULL;
    m_psprPrev = m_pdisp->m_psprLast;
    if (m_pdisp->m_psprLast)
      m_pdisp->m_psprLast->m_psprNext = this;
    else
      m_pdisp->m_psprFirst = this;
    m_pdisp->m_psprLast = this;
    }
  else
    {
    // UNDONE: Fix this up.
    m_psprNext = m_pdisp->m_psprLast->m_psprNext;
    m_pdisp->m_psprLast->m_psprNext = this;
    m_psprNext->m_psprPrev = this;
    if (m_pdisp->m_psprLast == m_pdisp->m_psprLast)
      m_pdisp->m_psprLast = this;
    }

  m_fLinked = TRUE;
  m_psc->m_cspr++;
  //this->_FDraw(FALSE);      // Require "s.Refresh" to avoid spurious paint before img set
}

//---------------------------------------------------------------------------
// Unlink a sprite into the display list
//---------------------------------------------------------------------------
void CSprite::_Unlink
(
  BOOL fFireEvt
)
{
  if (!m_fLinked)
    return;

  this->_Erase();

  if (m_pdisp->m_psprFirst == this)
    m_pdisp->m_psprFirst = m_psprNext;
  if (m_pdisp->m_psprLast  == this)
    m_pdisp->m_psprLast  = m_psprPrev;

  if (m_psprPrev)
    m_psprPrev->m_psprNext = m_psprNext;
  if (m_psprNext)
    m_psprNext->m_psprPrev = m_psprPrev;

  m_fLinked = FALSE;
  m_fHidden = TRUE;

  // Track # of sprites of this kind.  If there are no more, then
  // fire the _LastTerm event.
  m_psc->m_cspr--;
  if (fFireEvt && !m_psc->m_cspr)
    m_psc->FireLastTerm();

  if (m_psprAt)
    {
    m_psprAt->Release();
    m_psprAt = NULL;
    }
}

//---------------------------------------------------------------------------
// Draw the given sprite
//---------------------------------------------------------------------------
BOOL CSprite::_FDraw
(
  BOOLfCollide
)
{
  CSprite *psprT;
  CSprite *psprNext;
  int x   = m_x;
  int y   = m_y;
  int cx  = m_pimg->cx;
  int cy  = m_pimg->cy;
  HDC hdc = m_pdisp->m_hdc;

  if (fCollide && !m_fFree)
    {
    int maskCollide = m_psc->m_maskCollide;

    if (m_fHidden)
      goto DoneWithColl;

    for (psprT=m_pdisp->m_psprFirst; psprT; psprT=psprNext)
      {
      psprNext = psprT->m_psprNext;
      if (psprT != this &&                                    // Never collide w/self
          ((psprT->m_psc != m_psc) || (maskCollide & 1)) &&   // Coll w/like kind only if bit 1 set

(maskCollide & psprT->m_psc->m_maskCollide & ~1) && // Coll w/other kinds only if masks overlap
          x <= psprT->m_x+psprT->m_pimg->cx && x+cx >= psprT->m_x && // Coll only if actually touch
          y <= psprT->m_y+psprT->m_pimg->cy && y+cy >= psprT->m_y)
        {
        m_fIgnoreMove = FALSE;
        if (m_psc->m_id <= psprT->m_psc->m_id)
          m_pdisp->FireCollide(this, psprT, maskCollide & psprT->m_psc->m_maskCollide);
        else
          m_pdisp->FireCollide(psprT, this, maskCollide & psprT->m_psc->m_maskCollide);
        if (m_fFree)
          return TRUE;
        if (m_fIgnoreMove)
          return FALSE;
        if (psprT->m_fFree)
          goto DoneWithColl;
        }
      }
DoneWithColl:;
    if ((m_psc->m_brd & SPR_brdALL))
      {
      BRD brd = 0;
      if (m_psc->m_brd & SPR_brdIN)
        {
        if (x < 0) brd= SPR_brdINLEFT;
        if (y < 0) brd |= SPR_brdINTOP;
        if (x+cx > m_pdisp->m_cx) brd |= SPR_brdINRIGHT;
        if (y+cy > m_pdisp->m_cy) brd |= SPR_brdINBOTTOM;
        }
      if (m_psc->m_brd & SPR_brdOUT)
        {
        if (x+cx < 0) brd  = SPR_brdOUTLEFT;
        if (y+cy < 0) brd |= SPR_brdOUTTOP;
        if (x > m_pdisp->m_cx) brd |= SPR_brdOUTRIGHT;
        if (y > m_pdisp->m_cy) brd |= SPR_brdOUTBOTTOM;
        }
      if (brd)
        {
        m_fIgnoreMove = FALSE;
        m_psc->FireBorder(this, brd);

        if (m_fFree)
          return TRUE;
        if (m_fIgnoreMove)
          return FALSE;
        }
      }
    }

  // Don't draw if not visible
  if (m_fHidden)
    return TRUE;

  SelectObject(g_hdcMem, m_pdisp->m_hbmp);
  BitBlt(hdc, m_x, m_y, cx, cy, g_hdcMem, m_pimg->x, m_pimg->y, SRCCOPY);
  m_fNeedRefresh = FALSE;
  for (psprT=m_psprPrev; psprT; psprT=psprT->m_psprPrev)
    {
    if (!psprT->m_fNeedRefresh &&
        x <= psprT->m_x+psprT->m_pimg->cx && x+cx >= psprT->m_x &&
        y <= psprT->m_y+psprT->m_pimg->cy && y+cy >= psprT->m_y)
      {
      psprT->m_fNeedRefresh = TRUE;
      }
    }
  SelectObject(g_hdcMem, g_hbmpStock);
  return TRUE;
}

//---------------------------------------------------------------------------
// Erase the given sprite
//---------------------------------------------------------------------------
void CSprite::_Erase
(
  void
)
{
  int      x   = m_x;
  int      y   = m_y;
  int      cx  = m_pimg->cx;
  int      cy  = m_pimg->cy;
  HDC      hdc = m_pdisp->m_hdc;
  CSprite *psprT;

  SelectObject(hdc, (HGDIOBJ)(m_pdisp->m_hbrushBack ? m_pdisp->m_hbrushBack : g_hbrushStock));
  PatBlt(hdc, m_x, m_y, cx, cy, PATCOPY);
  for (psprT=m_pdisp->m_psprLast; psprT; psprT=psprT->m_psprPrev)
    {
    if (psprT != this && !psprT->m_fNeedRefresh &&
        x <= psprT->m_x+psprT->m_pimg->cx && x+cx >= psprT->m_x &&
        y <= psprT->m_y+psprT->m_pimg->cy && y+cy >= psprT->m_y)
      psprT->m_fNeedRefresh = TRUE;
    }
  SelectObject(g_hdcMem, g_hbrushStock);
}

//---------------------------------------------------------------------------
// Move the given sprite to new location
//---------------------------------------------------------------------------
BOOL CSprite::MoveToFull
(
  longxFull,
  longyFull,
  BOOLfCollide
)
{
  long xOldFull = m_xFull;
  long yOldFull = m_yFull;

  this->_Erase();
  m_xFull = xFull;
  m_yFull = yFull;
  if (m_pimg->iimgNext == iimgREMOVE)
    this->Remove();
  else if (m_pimg->iimgNext != iimgNONE)
    {
    m_iimg = m_pimg->iimgNext;
    m_pimg = m_pimg->pimgNext;
    }
  if (!this->_FDraw(fCollide))
    {
    m_xFull = xOldFull;
    m_yFull = yOldFull;
    this->_FDraw(FALSE);
    return FALSE;
    }
  return TRUE;
}

//---------------------------------------------------------------------------
// Move the given sprite to new location
//---------------------------------------------------------------------------
BOOL CSprite::MoveByFull
(
  longdxFull,
  longdyFull,
  BOOLfCollide
)
{
  long xOldFull = m_xFull;
  long yOldFull = m_yFull;

  if (HIWORD(xOldFull + dxFull) == HIWORD(xOldFull) &&
      HIWORD(yOldFull + dyFull) == HIWORD(yOldFull))
    {
    m_xFull += dxFull;
    m_yFull += dyFull;
    return TRUE;
    }

  this->_Erase();
  m_xFull += dxFull;
  m_yFull += dyFull;
  if (m_pimg->iimgNext == iimgREMOVE)
    this->Remove();
  else if (m_pimg->iimgNext != iimgNONE)
    {
    m_iimg = m_pimg->iimgNext;
    m_pimg = m_pimg->pimgNext;
    }

  if (!this->_FDraw(fCollide))
    {
    m_xFull = xOldFull;
    m_yFull = yOldFull;
    this->_FDraw(FALSE);
    return FALSE;
    }
  return TRUE;
}

//---------------------------------------------------------------------------
// flags: 1=X, 2=Y;
//        4=Pos, 8=Vel, 16=Accel;
//        32=Less With Distance (default=More);
//        128=Remember
//---------------------------------------------------------------------------
BOOL CSprite::DoMoveAt
(
  CSprite *psprAt,
  float    scale,
  float    dMax,
  long     flags,
  BOOL     fCollide
)
  {
  long dx = psprAt->m_xFull - m_xFull;
  long dy = psprAt->m_yFull - m_yFull;

  if (flags & MAS_RECIPDIST)
    {
    dx = FULLOFFLOAT((FLOAT)1.0 / FLOATOFFULL(dx | 0xffff));
    dy = FULLOFFLOAT((FLOAT)1.0 / FLOATOFFULL(dy | 0xffff));
    }

  float d = FLOATOFFULL(dx) * scale;
  if (d > dMax)
    d = dMax;
  else if (d < -dMax)
    d = -dMax;
  dx = FULLOFFLOAT(d);
  d = FLOATOFFULL(dy) * scale;
  if (d > dMax)
    d = dMax;
  else if (d < -dMax)
    d = -dMax;
  dy = FULLOFFLOAT(d);

  if (flags & MAS_X)
    {
    if (flags & MAS_VEL)
      {
      m_vxFull += dx;
      if (m_vxFull > m_psc->m_vFullMax)
        m_vxFull = m_psc->m_vFullMax;
      else if (m_vxFull < -m_psc->m_vFullMax)
        m_vxFull = -m_psc->m_vFullMax;
      }
    if (flags & MAS_ACCEL)
      {
      m_axFull += dx;
      if (m_axFull > m_psc->m_aFullMax)
        m_axFull = m_psc->m_aFullMax;
      else if (m_axFull < -m_psc->m_aFullMax)
        m_axFull = -m_psc->m_aFullMax;
      }
    if (!(flags & MAS_POS))
      dx = 0;
    }
  else
    dx = 0;

  if (flags & MAS_Y)
    {
    if (flags & MAS_VEL)
      {
      m_vyFull += dy;
      if (m_vyFull > m_psc->m_vFullMax)
        m_vyFull = m_psc->m_vFullMax;
      else if (m_vyFull < -m_psc->m_vFullMax)
        m_vyFull = -m_psc->m_vFullMax;
      }
    if (flags & MAS_ACCEL)
      {
      m_ayFull += dy;
      if (m_ayFull > m_psc->m_aFullMax)
        m_ayFull = m_psc->m_aFullMax;
      else if (m_ayFull < -m_psc->m_aFullMax)
        m_ayFull = -m_psc->m_aFullMax;
      }
    if (!(flags & MAS_POS))
      dy = 0;
    }
  else
    dy = 0;

  if (dx || dy)
    return this->MoveByFull(dx, dy, fCollide);

  return TRUE;
  }

//***************************************************************************
// IUnknown Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::QueryInterface
(
  REFIID  iid,
  LPVOID* ppvObjOut
)
{
  if (!ppvObjOut)
    return E_INVALIDARG;

  *ppvObjOut = NULL;

  if (iid == IID_IUnknown)
    *ppvObjOut = this->GetUnknown();
  else if (iid == IID_IDispatch)
    *ppvObjOut = this->GetDispatch();
  else if (iid == IID_ISprite)
    *ppvObjOut = (ISprite *)this;

  if (*ppvObjOut)
    {
    this->AddRef();
    return S_OK;
    }

  return E_NOINTERFACE;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSprite::AddRef
(
  void
)
{
  return ++m_cref;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CSprite::Release
(
  void
)
{
  ASSERT(m_cref, "bad m_cref");
  m_cref--;
  if (!m_cref)
    {
    delete this;
    return 0;
    }
  return m_cref;
}

//***************************************************************************
// IDispatch Interface
//***************************************************************************

//---------------------------------------------------------------------------
// Method needed by COleAuto, so it can implement IDispatch for us.
//---------------------------------------------------------------------------
HRESULT CSprite::GetTypeLibInfo
(
  HINSTANCE    *phinstOut,
  const GUID  **pplibidOut,
  SHORT        *pwMajLib,
  SHORT        *pwMinLib,
  const CLSID **ppclsidOut,
  const IID   **ppiidOut,
  ITypeLib   ***ppptlOut
)
{
  *phinstOut  = g_hinst;
  *pplibidOut = &LIBID_SPRUUIDS;
  *pwMajLib   = 1;
  *pwMinLib   = 0;
  *ppclsidOut = &CLSID_Sprite;
  *ppiidOut   = &IID_ISprite;
  *ppptlOut   = &g_ptlMain;
  return S_OK;
}

//--- EOF -------------------------------------------------------------------


SPR.H

//---------------------------------------------------------------------------
// Spr.h
//---------------------------------------------------------------------------
// Sprite handler
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Constants
//---------------------------------------------------------------------------
#define SPR_brdINLEFT       0x0001
#define SPR_brdINTOP        0x0002
#define SPR_brdINRIGHT      0x0004
#define SPR_brdINBOTTOM     0x0008
#define SPR_brdIN           0x000F
#define SPR_brdOUTLEFT      0x0010
#define SPR_brdOUTTOP       0x0020
#define SPR_brdOUTRIGHT     0x0040
#define SPR_brdOUTBOTTOM    0x0080
#define SPR_brdOUT          0x00F0
#define SPR_brdALL          0x00FF
#define SPR_brdLEFT         0x0011
#define SPR_brdTOP          0x0022
#define SPR_brdRIGHT        0x0044
#define SPR_brdBOTTOM       0x0088

#define MAS_X               0x0001
#define MAS_Y               0x0002
#define MAS_POS             0x0004
#define MAS_VEL             0x0008
#define MAS_ACCEL           0x0010
#define MAS_RECIPDIST       0x0020
#define MAS_REMEMBER        0x0080

//---------------------------------------------------------------------------
// Macros
//---------------------------------------------------------------------------
#define tickOFSEC(sec)              ((int)(20*(sec)))
#define PuserDEREF(type, pspr)      ((type)((pspr)->m_bUser))
#define SPRITEOFI(psprite)          ((CSprite *)(void *)(psprite))

//---------------------------------------------------------------------------
// Typedefs
//---------------------------------------------------------------------------
class CDisplay;
class CSpriteClass;
class CSprite;
typedef UINT BRD;
typedef WORD IDBMP;
typedef struct tagIMG *PIMG;
typedef void (*PFNCOLLIDE)(CSprite *, CSprite *, int);
#define FLOAT float
#define FULLOFFLOAT(r8)   ((long)((r8) * (FLOAT)0x10000))
#define FLOATOFFULL(i4)   ((FLOAT)(i4) / (FLOAT)0x10000)
#define FIXED(full, sub, super) \
  union                         \
    {                           \
    long full;                  \
    struct                      \
      {                         \
      USHORT sub;               \
      SHORT super;              \
      };                        \
    }

//---------------------------------------------------------------------------
// Image description
//---------------------------------------------------------------------------
typedef struct tagIMG
  {
  int   iimgNext;
  int   x,  y;
  int   cx, cy;
  PIMG  pimgNext;
  } IMG;
#define iimgNONE      (-1)
#define iimgREMOVE    (-2)

//---------------------------------------------------------------------------
// Display surface for sprites
//---------------------------------------------------------------------------
class CDisplay
  {
  friend class CSprite;
  friend class CSpriteClass;

public:
  CDisplay(HINSTANCE hinst, HWND hwnd, HDC hdc, IUnknown *pparent, PFNCOLLIDE pfncollide, int cimg, PIMG prgimg, UINT idbmp);
  ~CDisplay(void);

  void DestroyAll(void);
  void Timer(void);
  void Paint(HDC hdc);
  void Refresh(void);
  void SetBackColor(COLORREF c);
  void FireCollide(CSprite *psprLowId, CSprite *psprHighId, int);

  HINSTANCE  m_hinst;
  HWND       m_hwnd;
  HDC        m_hdc;
  int        m_cx, m_cy;
  PFNCOLLIDE m_pfnCollide;    // Routine to handle spr collisions
  CSprite   *m_psprFirst;
  CSprite   *m_psprLast;
  IUnknown  *m_punkParent;    // For get_Parent() on SpriteClass
  COLORREF   m_colorBack;
  HBRUSH    m_hbrushBack;

protected:
  int       m_cimg;
  int       m_csc;
  PIMG      m_prgimg;
  IDBMP     m_idbmp;
  HBITMAP   m_hbmp;

  // DEBUG info
  #define SIG_Display 'Disp'
  DECLARE_SIGNATURE(SIG_Display);
  };

//---------------------------------------------------------------------------
// Class description of different types of sprites
//---------------------------------------------------------------------------
class CSpriteClass : public ISpriteClass, public COleAuto,
                     public IProvideClassInfo, public IConnectionPointContainer
  {
  friend class CDisplay;
  friend class CSprite;

public:
  // *** Constructor/Destructor methods ***
  static HRESULT Create(CDisplay *pdisp, CSpriteClass **ppscOut);
  CSpriteClass(CDisplay *pdisp);
  ~CSpriteClass(void);
  void Close(void);

  // *** Event methods ***
  void FireInit(  CSprite *pspr, VARIANT *pvarUser);
  void FireTick(  CSprite *pspr);
  void FireBorder(CSprite *pspr, BRD brd);
  void FireTerm(  CSprite *pspr);
  void FireLastTerm(void);

  // *** IUnknown methods ***
  STDMETHOD(QueryInterface)(REFIID iid, void** ppvObjOut);
  STDMETHOD_(ULONG, AddRef)(void);
  STDMETHOD_(ULONG, Release)(void);
  inline IUnknown  *GetUnknown(void)  {return (ISpriteClass *)this;};
  inline IDispatch *GetDispatch(void) {return (ISpriteClass *)this;};

  // *** IDispatch methods ***
  DECLARE_STANDARD_DISPATCH();
  // Override required by COleAuto:
  virtual ITypeInfo **GetTinfoClsAddr(void) { return &g_ptinfoClsSpriteClass; };
  virtual ITypeInfo **GetTinfoIntAddr(void) { return &g_ptinfoIntSpriteClass; };
  virtual HRESULT GetTypeLibInfo(HINSTANCE *phinstOut, const GUID **pplibidOut,
                                 SHORT *pwMajLibOut, SHORT *pwMinLibOut,
                                 const CLSID **ppclsidOut, const IID **ppiidOut,
                                 ITypeLib ***ppptlOut);
  virtual IDispatch *GetPrimary() {return this->GetDispatch(); };

  // *** ISpriteClass methods ***
  STDMETHOD(get_Application)(ISpruuidsApp** ppRet);
  STDMETHOD(get_Parent)(IGame** ppRet);
  STDMETHOD(CreateSprite)(int x, int y, VARIANT arg, ISprite** ppRet);
  STDMETHOD(get_Tag)(VARIANT* pRet);
  STDMETHOD(put_Tag)(VARIANT val);
  STDMETHOD(get_Image)(int* pRet);
  STDMETHOD(put_Image)(int val);
  STDMETHOD(get_Border)(int* pRet);
  STDMETHOD(put_Border)(int val);
  STDMETHOD(get_Collide)(int* pRet);
  STDMETHOD(put_Collide)(int val);
  STDMETHOD(get_MaximumVelocity)(float* pRet);
  STDMETHOD(put_MaximumVelocity)(float val);
  STDMETHOD(get_Friction)(float* pRet);
  STDMETHOD(put_Friction)(float val);
  STDMETHOD(get_MaximumAcceleration)(float* pRet);
  STDMETHOD(put_MaximumAcceleration)(float val);
  STDMETHOD(get_AccelerationFriction)(float* pRet);
  STDMETHOD(put_AccelerationFriction)(float val);
  STDMETHOD(get_SpriteCount)(int* pRet);

  // *** IProvideClassInfo methods ***
  STDMETHOD(GetClassInfo)(ITypeInfo** pptinfoOut);
  // *** IProvideClassInfo2 methods ***
  STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID* pGUID);

  // *** IConnectionPointContainer methods ***
  STDMETHOD(EnumConnectionPoints)(LPENUMCONNECTIONPOINTS* ppEnum);
  STDMETHOD(FindConnectionPoint)(REFIID iid, LPCONNECTIONPOINT* ppCP);

  class XCP : public IConnectionPoint
    {
    public:
    XCP();
    void Close(void);

    // *** IUnknown methods ***
    STDMETHOD(QueryInterface)(REFIID iid, void** ppvObjOut);
    STDMETHOD_(ULONG, AddRef)(void);
    STDMETHOD_(ULONG, Release)(void);
    inline IUnknown *GetUnknown(void) {return (IConnectionPoint *)this;};

    // *** IConnectionPoint methods ***
    STDMETHOD(GetConnectionInterface)(IID* piid);
    STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer** ppCPC);
    STDMETHOD(Advise)(IUnknown* punkSink, DWORD* pdwCookie);
    STDMETHOD(Unadvise)(DWORD dwCookie);
    STDMETHOD(EnumConnections)(LPENUMCONNECTIONS* ppEnum);

    // *** Member Variables ***
    ULONG     m_cref;
#define SC_cADVISE 5
    IDispatch *m_rgpdisp[SC_cADVISE];     // Array of pdisps to call.
    } m_cp;

  // *** Member Variables ***
  CDisplay *m_pdisp;
  int       m_id;
  int       m_brd;         // Mask indicates which borders to check collision with
  int       m_maskCollide; // Mask indicates which other sprites can collide with
  int       m_iimg;
  int       m_cspr;
  VARIANT   m_varTag;
  FLOAT     m_friction;
  FLOAT     m_frictionAccel;
  BOOL      m_fFrictionAccel;
  ULONG     m_cref;
  FIXED(    m_vFullMax, m_vSubMax, m_vMax);
  FIXED(    m_aFullMax, m_aSubMax, m_aMax);

private:
  // DEBUG info
  static HRESULT s_hr;
  #define SIG_SpriteClass 'SCls'
  DECLARE_SIGNATURE(SIG_SpriteClass);
  };

//---------------------------------------------------------------------------
// Definition of sprite
//---------------------------------------------------------------------------
class CSprite : ISprite, COleAuto
  {
  friend class CDisplay;
  friend class CSpriteClass;

public:
  // *** Constructor/Destructor methods ***
  CSprite(CSpriteClass *psc, int x, int y, CSprite *psprPrev, VARIANT *pvarUser);
  ~CSprite();

  // *** General Routines ***
  CSprite *PsprMutate(CSpriteClass *psc, VARIANT *pvarUser);
  void MarkFree();
  BOOL MoveToFull(long xFull,  long yFull,  BOOL fCollide);
  BOOL MoveByFull(long dxFull, long dyFull, BOOL fCollide);
  BOOL DoMoveAt(CSprite *psprAt, float dMax, float scale, long flags, BOOL fCollide);

  // *** IUnknown methods ***
  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj);
  STDMETHOD_(ULONG, AddRef)(void);
  STDMETHOD_(ULONG, Release)(void);
  inline IUnknown  *GetUnknown(void)  {return (ISprite *)this;};
  inline IDispatch *GetDispatch(void) {return (ISprite *)this;};

  // *** IDispatch methods ***
  DECLARE_STANDARD_DISPATCH();
  // Override required by COleAuto:
  virtual ITypeInfo **GetTinfoClsAddr(void) { return &g_ptinfoClsSprite; };
  virtual ITypeInfo **GetTinfoIntAddr(void) { return &g_ptinfoIntSprite; };
  virtual HRESULT GetTypeLibInfo(HINSTANCE *phinstOut, const GUID **pplibidOut,
                                 SHORT *pwMajLibOut, SHORT *pwMinLibOut,
                                 const CLSID **ppclsidOut, const IID **ppiidOut,
                                 ITypeLib ***ppptlOut);
  virtual IDispatch *GetPrimary() {return this->GetDispatch(); };

  // *** ISprite methods ***
  STDMETHOD(get_Application)(ISpruuidsApp** ppRet);
  STDMETHOD(get_Parent)(ISpriteClass** ppRet);
  STDMETHOD(MoveTo)(FLOAT x, FLOAT y);
  STDMETHOD(MoveBy)(FLOAT dx, FLOAT dy);
  STDMETHOD(MoveAtSprite)(ISprite *sprite, FLOAT dMax, FLOAT scale, long flags);
  STDMETHOD(Remove)(void);
  STDMETHOD(Refresh)(void);
  STDMETHOD(IgnoreMove)(void);
  STDMETHOD(get_Left)(FLOAT* pRet);
  STDMETHOD(put_Left)(FLOAT val);
  STDMETHOD(get_Top)(FLOAT* pRet);
  STDMETHOD(put_Top)(FLOAT val);
  STDMETHOD(get_Vx)(FLOAT* pRet);
  STDMETHOD(put_Vx)(FLOAT val);
  STDMETHOD(get_Vy)(FLOAT* pRet);
  STDMETHOD(put_Vy)(FLOAT val);
  STDMETHOD(get_Ax)(FLOAT* pRet);
  STDMETHOD(put_Ax)(FLOAT val);
  STDMETHOD(get_Ay)(FLOAT* pRet);
  STDMETHOD(put_Ay)(FLOAT val);
  STDMETHOD(get_FrictionX)(FLOAT* pRet);
  STDMETHOD(put_FrictionX)(FLOAT val);
  STDMETHOD(get_FrictionY)(FLOAT* pRet);
  STDMETHOD(put_FrictionY)(FLOAT val);
  STDMETHOD(get_Image)(int* pRet);
  STDMETHOD(put_Image)(int val);
  STDMETHOD(get_TickMove)(int* pRet);
  STDMETHOD(put_TickMove)(int val);
  STDMETHOD(get_TickEvent)(int* pRet);
  STDMETHOD(put_TickEvent)(int val);
  STDMETHOD(get_Visible)(VARIANT_BOOL* pRet);
  STDMETHOD(put_Visible)(VARIANT_BOOL val);
  STDMETHOD(get_Tag)(VARIANT* pRet);
  STDMETHOD(put_Tag)(VARIANT val);
  STDMETHOD(get_Width)(int* pRet);
  STDMETHOD(get_Height)(int* pRet);

  // *** Member Variables ***
  CDisplay     *m_pdisp;
  CSpriteClass *m_psc;
  CSprite      *m_psprNext;
  CSprite      *m_psprPrev;
  FIXED(       m_xFull,  m_xSub,  m_x);
  FIXED(       m_yFull,  m_ySub,  m_y);
  FIXED(       m_vxFull, m_vxSub, m_vx);
  FIXED(       m_vyFull, m_vySub, m_vy);
  FIXED(       m_axFull, m_axSub, m_ax);
  FIXED(       m_ayFull, m_aySub, m_ay);
  BOOL         m_fFriction;
  FLOAT        m_fx;    // Friction
  FLOAT        m_fy;
  long         m_tickEvt, m_tickEvtMax, m_tickMove, m_tickMoveMax;
  int          m_iimg;
  PIMG         m_pimg;
  VARIANT      m_varTag;

  // Support for MoveAtSprite()
  CSprite     *m_psprAt;
  float        m_dMaxAt;
  float        m_scaleAt;
  long         m_flagsAt;

protected:
  // *** Internal Routines ***
  BOOL _FDraw(BOOL fCollide);
  void _Erase();
  void _Link();
  void _Unlink(BOOL fFireEvt);

  // *** Member Variables ***
  BOOL    m_fNeedRefresh:1;
  BOOL    m_fFree:1;
  BOOL    m_fIgnoreMove:1;
  BOOL    m_fLinked:1;
  BOOL    m_fHidden;            // 2 = Implicitly hidden for _Init event
  ULONG   m_cref;

  // DEBUG info
  #define SIG_Sprite 'Spr '
  DECLARE_SIGNATURE(SIG_Sprite);
  };

//--- EOF -------------------------------------------------------------------


SPROA.CPP

//---------------------------------------------------------------------------
// SprOA.cpp
//---------------------------------------------------------------------------
// Sprite handler, OLE Automation support
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop
#include "Spr.h"
#include "DispIDs.h"
#include <math.h>

//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE

//***************************************************************************
//***************************************************************************
//**
//**  SpriteClass Class
//**
//***************************************************************************
//***************************************************************************

//***************************************************************************
// Fire ISpriteClassEvents Events
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CSpriteClass::FireInit
(
  CSprite *pspr,
  VARIANT *pvarUser
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[2];

  VariantInit(&var[1]);
  var[1].vt = VT_DISPATCH;
  var[1].pdispVal = pspr->GetDispatch();
  pspr->AddRef();
  VariantInit(&var[0]);
  VariantCopy(&var[0], pvarUser);

  for (; ppdisp < &m_cp.m_rgpdisp[SC_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_SpriteClassEvents_Init, var, 2);

  pspr->Release();    // For var[1].pdispVal
  VariantClear(&var[0]);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CSpriteClass::FireTick
(
  CSprite *pspr
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[1];

  VariantInit(&var[0]);
  var[0].vt = VT_DISPATCH;
  var[0].pdispVal = pspr->GetDispatch();
  pspr->AddRef();

  for (; ppdisp < &m_cp.m_rgpdisp[SC_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_SpriteClassEvents_Tick, var, 1);

  pspr->Release();    // For var[0].pdispVal
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CSpriteClass::FireBorder
(
  CSprite *pspr,
  BRD      brd
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[2];

  VariantInit(&var[1]);
  var[1].vt = VT_DISPATCH;
  var[1].pdispVal = pspr->GetDispatch();
  pspr->AddRef();
  VariantInit(&var[0]);
  var[0].vt = VT_I4;
  var[0].lVal = brd;

  for (; ppdisp < &m_cp.m_rgpdisp[SC_cADVISE]; ppdisp++)
    if (*ppdisp)
      {
      BOOL f;
      HRESULT hr = InvokeEvent(*ppdisp, DISPID_SpriteClassEvents_Border, var, 2);
      if (hr)
        f = TRUE;
      }

  pspr->Release();    // For var[1].pdispVal
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CSpriteClass::FireTerm
(
  CSprite *pspr
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;
  VARIANTARG  var[1];

  VariantInit(&var[0]);
  var[0].vt = VT_DISPATCH;
  var[0].pdispVal = pspr->GetDispatch();
  pspr->AddRef();

  for (; ppdisp < &m_cp.m_rgpdisp[SC_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_SpriteClassEvents_Term, var, 1);

  pspr->Release();    // For var[0].pdispVal
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CSpriteClass::FireLastTerm
(
  void
)
{
  IDispatch **ppdisp = m_cp.m_rgpdisp;

  for (; ppdisp < &m_cp.m_rgpdisp[SC_cADVISE]; ppdisp++)
    if (*ppdisp)
      InvokeEvent(*ppdisp, DISPID_SpriteClassEvents_LastTerm, NULL, 0);
}

//***************************************************************************
// ISpriteClass Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_Application
(
  ISpruuidsApp** ppRet
)
{
  if (!ppRet)
    return E_INVALIDARG;
  return g_punkApplicationNA->QueryInterface(IID_ISpruuidsApp, (void **)ppRet);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_Parent
(
  IGame** ppRet
)
{
  if (!ppRet)
    return E_INVALIDARG;
  return m_pdisp->m_punkParent->QueryInterface(IID_IGame, (void **)ppRet);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::CreateSprite
(
  int       x,
  int       y,
  VARIANT   arg,
  ISprite **ppRet
)
{
  CSprite *pspr = new CSprite(this, x, y, NULL, &arg);
  if (!pspr)
    return E_OUTOFMEMORY;
  *ppRet = pspr;
  (*ppRet)->AddRef();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_Tag
(
  VARIANT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  VariantInit(pRet);
  return VariantCopy(pRet, &m_varTag);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::put_Tag
(
  VARIANT val
)
{
  return VariantCopy(&m_varTag, &val);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_Image
(
  int* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_iimg;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::put_Image
(
  int val
)
{
  if (val > 0 && val < m_pdisp->m_cimg)
    m_iimg = val;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_Border
(
  int* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_brd;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::put_Border
(
  int val
)
{
  m_brd = val;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_Collide
(
  int* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_maskCollide;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::put_Collide
(
  int val
)
{
  m_maskCollide = val;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_MaximumVelocity
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = FLOATOFFULL(m_vFullMax);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::put_MaximumVelocity
(
  FLOAT val
)
{
  m_vFullMax = FULLOFFLOAT(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_Friction
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_friction;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::put_Friction
(
  FLOAT val
)
{
  m_friction = val;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_MaximumAcceleration
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = FLOATOFFULL(m_aFullMax);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::put_MaximumAcceleration
(
  FLOAT val
)
{
  m_aFullMax = FULLOFFLOAT(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_AccelerationFriction
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_frictionAccel;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::put_AccelerationFriction
(
  FLOAT val
)
{
  m_frictionAccel = val;
  m_fFrictionAccel = (val != (FLOAT)1.0);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSpriteClass::get_SpriteCount
(
  int* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_cspr;
  return S_OK;
}

//***************************************************************************
//***************************************************************************
//**
//**  Sprite Class
//**
//***************************************************************************
//***************************************************************************

//***************************************************************************
// ISprite Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Application
(
  ISpruuidsApp** ppRet
)
{
  if (!ppRet)
    return E_INVALIDARG;
  return g_punkApplicationNA->QueryInterface(IID_ISpruuidsApp, (void **)ppRet);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Parent
(
  ISpriteClass** ppRet
)
{
  if (!ppRet)
    return E_INVALIDARG;
  return m_psc->QueryInterface(IID_ISpriteClass, (void **)ppRet);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::MoveTo
(
  FLOAT x,
  FLOAT y
)
{
  this->MoveToFull(FULLOFFLOAT(x), FULLOFFLOAT(y), TRUE);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::MoveBy
(
  FLOAT dx,
  FLOAT dy
)
{
  this->MoveByFull(FULLOFFLOAT(dx), FULLOFFLOAT(dy), TRUE);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::MoveAtSprite
(
  ISprite* sprite,
  float    dMax,
  float    scale,
  long     flags
)
{
  if (!sprite)
    return E_INVALIDARG;

  // Store parameters & do every tickMove ticks if MAS_REMEMBER set.
  if (flags & MAS_REMEMBER)
    {
    if (m_psprAt)
      {
      m_psprAt->Release();
      m_psprAt = NULL;
      }
    m_psprAt = (CSprite *)(void *)sprite;
    if (!m_psprAt)
      return S_OK;
    m_psprAt->AddRef();
    m_dMaxAt = dMax;
    m_scaleAt = scale;
    m_flagsAt = flags;
    return S_OK;
    }

  // Otherwise, do this right now.
  this->DoMoveAt((CSprite *)(void *)sprite, dMax, scale, flags, FALSE);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::Remove
(
  void
)
{
  this->MarkFree();
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::Refresh
(
  void
)
{
  this->_FDraw(FALSE);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::IgnoreMove
(
  void
)
{
  m_fIgnoreMove = TRUE;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Left
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = FLOATOFFULL(m_xFull);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_Left
(
  FLOAT val
)
{
  m_xFull = FULLOFFLOAT(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Top
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = FLOATOFFULL(m_yFull);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_Top
(
  FLOAT val
)
{
  m_yFull = FULLOFFLOAT(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Vx
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = FLOATOFFULL(m_vxFull);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_Vx
(
  FLOAT val
)
{
  m_vxFull = FULLOFFLOAT(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Vy
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = FLOATOFFULL(m_vyFull);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_Vy
(
  FLOAT val
)
{
  m_vyFull = FULLOFFLOAT(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Ax
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = FLOATOFFULL(m_axFull);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_Ax
(
  FLOAT val
)
{
  m_axFull = FULLOFFLOAT(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Ay
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = FLOATOFFULL(m_ayFull);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_Ay
(
  FLOAT val
)
{
  m_ayFull = FULLOFFLOAT(val);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_FrictionX
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = m_fx;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_FrictionX
(
  FLOAT val
)
{
  m_fx = val;
  m_fFriction = (m_fx != 1.0) || (m_fy != 1.0);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_FrictionY
(
  FLOAT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;

  *pRet = m_fy;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_FrictionY
(
  FLOAT val
)
{
  m_fy = val;
  m_fFriction = (m_fx != 1.0) || (m_fy != 1.0);
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Width
(
  int* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_pimg->cx;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Height
(
  int* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_pimg->cy;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Image
(
  int* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_iimg;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_Image
(
  int val
)
{
  if (val >= 0 && val < m_pdisp->m_cimg)
    {
    PIMG pimgCur = m_pimg;

    if (!m_fHidden)
      this->_Erase();
    m_iimg = val;
    m_pimg = &(m_pdisp->m_prgimg[m_iimg]);
    // Keep image centered, if Visible
    if (!m_fHidden)
      {
      m_x += (pimgCur->cx - m_pimg->cx) >> 1;
      m_y += (pimgCur->cy - m_pimg->cy) >> 1;
      this->_FDraw(FALSE);
      }
    }
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_TickMove
(
  int* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_tickMoveMax;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_TickMove
(
  int val
)
{
  m_tickMoveMax = val;
  if (m_tickMove > m_tickMoveMax)
    m_tickMove = m_tickMoveMax;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_TickEvent
(
  int* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_tickEvtMax;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_TickEvent
(
  int val
)
{
  m_tickEvtMax = val;
  if (m_tickEvt > m_tickEvtMax)
    m_tickEvt = m_tickEvtMax;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Visible
(
  VARIANT_BOOL* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  *pRet = m_fHidden ? 0 : -1;
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_Visible
(
  VARIANT_BOOL val
)
{
  if (m_fLinked)
    {
    m_fHidden = !val;
    if (!m_fHidden)
      this->_Erase();
    else
      this->_FDraw(FALSE);
    }
  return S_OK;
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::get_Tag
(
  VARIANT* pRet
)
{
  if (!pRet)
    return E_INVALIDARG;
  VariantInit(pRet);
  return VariantCopy(pRet, &m_varTag);
}

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CSprite::put_Tag
(
  VARIANT val
)
{
  return VariantCopy(&m_varTag, &val);
}

//--- EOF -------------------------------------------------------------------


SPRUUIDP.H

//---------------------------------------------------------------------------
// Spruuids.h
//---------------------------------------------------------------------------
// Sample ActiveX Scripting Program
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation.  All rights reserved.
//
// 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.
//---------------------------------------------------------------------------

#include "SpruuidP.hh"

//--- EOF -------------------------------------------------------------------


SPRUUIDS.H

/* This header file machine-generated by mktyplib.exe */
/* Interface to type library: SPRUUIDS */

#ifndef _SPRUUIDS_H_
#define _SPRUUIDS_H_

DEFINE_GUID(LIBID_SPRUUIDS,0x8703FF61L,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);
#ifndef BEGIN_INTERFACE
#define BEGIN_INTERFACE
#endif

#ifdef __cplusplus
class SpruuidsApp;
#endif

interface ISpruuidsApp;

#ifdef __cplusplus
class Game;
#endif

interface IGame;

interface IGameEvents;

#ifdef __cplusplus
class SpriteClass;
#endif

interface ISpriteClass;

interface ISpriteClassEvents;

#ifdef __cplusplus
class Sprite;
#endif

interface ISprite;

DEFINE_GUID(IID_ISpruuidsApp,0x8703FF62L,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

/* Definition of interface: ISpruuidsApp */
#undef INTERFACE
#define INTERFACE ISpruuidsApp

DECLARE_INTERFACE_(ISpruuidsApp, IDispatch)
{
BEGIN_INTERFACE
#ifndef NO_BASEINTERFACE_FUNCS

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
    STDMETHOD_(ULONG, Release)(THIS) PURE;

    /* IDispatch methods */
    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;

    STDMETHOD(GetTypeInfo)(
      THIS_
      UINT itinfo,
      LCID lcid,
      ITypeInfo FAR* FAR* pptinfo) PURE;

    STDMETHOD(GetIDsOfNames)(
      THIS_
      REFIID riid,
      OLECHAR FAR* FAR* rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID FAR* rgdispid) PURE;

    STDMETHOD(Invoke)(
      THIS_
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS FAR* pdispparams,
      VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo,
      UINT FAR* puArgErr) PURE;
#endif

    /* ISpruuidsApp methods */
    STDMETHOD(get_Application)(THIS_ ISpruuidsApp FAR* FAR* ppRet) PURE;
    STDMETHOD(get_Parent)(THIS_ ISpruuidsApp FAR* FAR* ppRet) PURE;
    STDMETHOD(Quit)(THIS) PURE;
    STDMETHOD(get_Game)(THIS_ IGame FAR* FAR* ppRet) PURE;
};

DEFINE_GUID(CLSID_SpruuidsApp,0x8703FF63L,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

#ifdef __cplusplus
class SpruuidsApp;
#endif

DEFINE_GUID(IID_IGame,0x8703FF64L,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

/* Definition of interface: IGame */
#undef INTERFACE
#define INTERFACE IGame

DECLARE_INTERFACE_(IGame, IDispatch)
{
BEGIN_INTERFACE
#ifndef NO_BASEINTERFACE_FUNCS

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
    STDMETHOD_(ULONG, Release)(THIS) PURE;

    /* IDispatch methods */
    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;

    STDMETHOD(GetTypeInfo)(
      THIS_
      UINT itinfo,
      LCID lcid,
      ITypeInfo FAR* FAR* pptinfo) PURE;

    STDMETHOD(GetIDsOfNames)(
      THIS_
      REFIID riid,
      OLECHAR FAR* FAR* rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID FAR* rgdispid) PURE;

    STDMETHOD(Invoke)(
      THIS_
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS FAR* pdispparams,
      VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo,
      UINT FAR* puArgErr) PURE;
#endif

    /* IGame methods */
    STDMETHOD(get_Caption)(THIS_ BSTR FAR* pbstrReturn) PURE;
    STDMETHOD(put_Caption)(THIS_ BSTR bstr) PURE;
    STDMETHOD(get_Application)(THIS_ ISpruuidsApp FAR* FAR* ppRet) PURE;
    STDMETHOD(get_Parent)(THIS_ ISpruuidsApp FAR* FAR* ppRet) PURE;
    STDMETHOD(StartGame)(THIS) PURE;
    STDMETHOD(EndGame)(THIS) PURE;
    STDMETHOD(RemoveAllSprites)(THIS) PURE;
    STDMETHOD(NextLevel)(THIS) PURE;
    STDMETHOD(Refresh)(THIS) PURE;
    STDMETHOD(AddScore)(THIS_ int val) PURE;
    STDMETHOD(StdBorderBounce)(THIS_ ISprite FAR* pspr, int brd) PURE;
    STDMETHOD(StdBorderWrap)(THIS_ ISprite FAR* pspr, int brd) PURE;
    STDMETHOD(StdInitRand)(THIS_ ISprite FAR* pspr, VARIANT user) PURE;
    STDMETHOD(StdInitEdge)(THIS_ ISprite FAR* pspr, VARIANT user) PURE;
    STDMETHOD(get_Paused)(THIS_ VARIANT_BOOL FAR* pRet) PURE;
    STDMETHOD(put_Paused)(THIS_ VARIANT_BOOL val) PURE;
    STDMETHOD(get_Width)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_Width)(THIS_ int val) PURE;
    STDMETHOD(get_Height)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_Height)(THIS_ int val) PURE;
    STDMETHOD(get_BackColor)(THIS_ long FAR* pRet) PURE;
    STDMETHOD(put_BackColor)(THIS_ long val) PURE;
    STDMETHOD(get_Score)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_Score)(THIS_ int val) PURE;
    STDMETHOD(get_Level)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_Level)(THIS_ int val) PURE;
    STDMETHOD(get_ShipCount)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_ShipCount)(THIS_ int val) PURE;
    STDMETHOD(get_ScoreFirst1Up)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_ScoreFirst1Up)(THIS_ int val) PURE;
    STDMETHOD(get_ScoreSecond1Up)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_ScoreSecond1Up)(THIS_ int val) PURE;
    STDMETHOD(get_DScoreNext1Up)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_DScoreNext1Up)(THIS_ int val) PURE;
    STDMETHOD(get_ShipsStart)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_ShipsStart)(THIS_ int val) PURE;
    STDMETHOD(get_Tag)(THIS_ VARIANT FAR* pRet) PURE;
    STDMETHOD(put_Tag)(THIS_ VARIANT val) PURE;
    STDMETHOD(get_StatusText)(THIS_ BSTR FAR* pbstrReturn) PURE;
    STDMETHOD(put_StatusText)(THIS_ BSTR bstr) PURE;
};

DEFINE_GUID(DIID_IGameEvents,0x8703FF65L,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

/* Definition of dispatch interface: IGameEvents */
#undef INTERFACE
#define INTERFACE IGameEvents

DECLARE_INTERFACE_(IGameEvents, IDispatch)
{
BEGIN_INTERFACE
#ifndef NO_BASEINTERFACE_FUNCS

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
    STDMETHOD_(ULONG, Release)(THIS) PURE;

    /* IDispatch methods */
    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;

    STDMETHOD(GetTypeInfo)(
      THIS_
      UINT itinfo,
      LCID lcid,
      ITypeInfo FAR* FAR* pptinfo) PURE;

    STDMETHOD(GetIDsOfNames)(
      THIS_
      REFIID riid,
      OLECHAR FAR* FAR* rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID FAR* rgdispid) PURE;

    STDMETHOD(Invoke)(
      THIS_
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS FAR* pdispparams,
      VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo,
      UINT FAR* puArgErr) PURE;
#endif

    /* IGameEvents methods:
    void NewGame(void);
    void NewLevel(void);
    void NewShip(void);
    void Collide(IDispatch * pspr1, IDispatch * pspr2, long collide);
    void Tick(void);
    void KeyDown(long vk);
    void KeyPress(long ascii);
    void KeyUp(long vk);
    void MouseMove(long x, long y, long keys, long buttons);
    void MouseDown(long x, long y, long keys, long buttons);
    void MouseOver(long x, long y, long keys, long buttons);
    */
};

DEFINE_GUID(CLSID_Game,0x8703FF66L,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

#ifdef __cplusplus
class Game;
#endif

DEFINE_GUID(IID_ISpriteClass,0x8703FF67L,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

/* Definition of interface: ISpriteClass */
#undef INTERFACE
#define INTERFACE ISpriteClass

DECLARE_INTERFACE_(ISpriteClass, IDispatch)
{
BEGIN_INTERFACE
#ifndef NO_BASEINTERFACE_FUNCS

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
    STDMETHOD_(ULONG, Release)(THIS) PURE;

    /* IDispatch methods */
    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;

    STDMETHOD(GetTypeInfo)(
      THIS_
      UINT itinfo,
      LCID lcid,
      ITypeInfo FAR* FAR* pptinfo) PURE;

    STDMETHOD(GetIDsOfNames)(
      THIS_
      REFIID riid,
      OLECHAR FAR* FAR* rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID FAR* rgdispid) PURE;

    STDMETHOD(Invoke)(
      THIS_
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS FAR* pdispparams,
      VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo,
      UINT FAR* puArgErr) PURE;
#endif

    /* ISpriteClass methods */
    STDMETHOD(get_Application)(THIS_ ISpruuidsApp FAR* FAR* ppRet) PURE;
    STDMETHOD(get_Parent)(THIS_ IGame FAR* FAR* ppRet) PURE;
    STDMETHOD(CreateSprite)(THIS_ int x, int y, VARIANT arg, ISprite FAR* FAR* ppRet) PURE;
    STDMETHOD(get_Tag)(THIS_ VARIANT FAR* pRet) PURE;
    STDMETHOD(put_Tag)(THIS_ VARIANT val) PURE;
    STDMETHOD(get_Image)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_Image)(THIS_ int val) PURE;
    STDMETHOD(get_Border)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_Border)(THIS_ int val) PURE;
    STDMETHOD(get_Collide)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_Collide)(THIS_ int val) PURE;
    STDMETHOD(get_MaximumVelocity)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_MaximumVelocity)(THIS_ float val) PURE;
    STDMETHOD(get_Friction)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_Friction)(THIS_ float val) PURE;
    STDMETHOD(get_MaximumAcceleration)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_MaximumAcceleration)(THIS_ float val) PURE;
    STDMETHOD(get_AccelerationFriction)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_AccelerationFriction)(THIS_ float val) PURE;
    STDMETHOD(get_SpriteCount)(THIS_ int FAR* pRet) PURE;
};

DEFINE_GUID(DIID_ISpriteClassEvents,0x8703FF68L,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

/* Definition of dispatch interface: ISpriteClassEvents */
#undef INTERFACE
#define INTERFACE ISpriteClassEvents

DECLARE_INTERFACE_(ISpriteClassEvents, IDispatch)
{
BEGIN_INTERFACE
#ifndef NO_BASEINTERFACE_FUNCS

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
    STDMETHOD_(ULONG, Release)(THIS) PURE;

    /* IDispatch methods */
    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;

    STDMETHOD(GetTypeInfo)(
      THIS_
      UINT itinfo,
      LCID lcid,
      ITypeInfo FAR* FAR* pptinfo) PURE;

    STDMETHOD(GetIDsOfNames)(
      THIS_
      REFIID riid,
      OLECHAR FAR* FAR* rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID FAR* rgdispid) PURE;

    STDMETHOD(Invoke)(
      THIS_
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS FAR* pdispparams,
      VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo,
      UINT FAR* puArgErr) PURE;
#endif

    /* ISpriteClassEvents methods:
    void Init(IDispatch * sprite, VARIANT arg);
    void Tick(IDispatch * sprite);
    void Border(IDispatch * sprite, long border);
    void Term(IDispatch * sprite);
    void lastTerm(void);
    */
};

DEFINE_GUID(CLSID_SpriteClass,0x8703FF69L,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

#ifdef __cplusplus
class SpriteClass;
#endif

DEFINE_GUID(IID_ISprite,0x8703FF6AL,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

/* Definition of interface: ISprite */
#undef INTERFACE
#define INTERFACE ISprite

DECLARE_INTERFACE_(ISprite, IDispatch)
{
BEGIN_INTERFACE
#ifndef NO_BASEINTERFACE_FUNCS

    /* IUnknown methods */
    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
    STDMETHOD_(ULONG, Release)(THIS) PURE;

    /* IDispatch methods */
    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;

    STDMETHOD(GetTypeInfo)(
      THIS_
      UINT itinfo,
      LCID lcid,
      ITypeInfo FAR* FAR* pptinfo) PURE;

    STDMETHOD(GetIDsOfNames)(
      THIS_
      REFIID riid,
      OLECHAR FAR* FAR* rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID FAR* rgdispid) PURE;

    STDMETHOD(Invoke)(
      THIS_
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS FAR* pdispparams,
      VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo,
      UINT FAR* puArgErr) PURE;
#endif

    /* ISprite methods */
    STDMETHOD(get_Application)(THIS_ ISpruuidsApp FAR* FAR* ppRet) PURE;
    STDMETHOD(get_Parent)(THIS_ ISpriteClass FAR* FAR* ppRet) PURE;
    STDMETHOD(MoveTo)(THIS_ float x, float y) PURE;
    STDMETHOD(MoveBy)(THIS_ float dx, float dy) PURE;
    STDMETHOD(MoveAtSprite)(THIS_ ISprite FAR* sprite, float scale, float dMax, long flags) PURE;
    STDMETHOD(Remove)(THIS) PURE;
    STDMETHOD(Refresh)(THIS) PURE;
    STDMETHOD(IgnoreMove)(THIS) PURE;
    STDMETHOD(get_Left)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_Left)(THIS_ float val) PURE;
    STDMETHOD(get_Top)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_Top)(THIS_ float val) PURE;
    STDMETHOD(get_Vx)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_Vx)(THIS_ float val) PURE;
    STDMETHOD(get_Vy)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_Vy)(THIS_ float val) PURE;
    STDMETHOD(get_Ax)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_Ax)(THIS_ float val) PURE;
    STDMETHOD(get_Ay)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_Ay)(THIS_ float val) PURE;
    STDMETHOD(get_FrictionX)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_FrictionX)(THIS_ float val) PURE;
    STDMETHOD(get_FrictionY)(THIS_ float FAR* pRet) PURE;
    STDMETHOD(put_FrictionY)(THIS_ float val) PURE;
    STDMETHOD(get_Image)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_Image)(THIS_ int val) PURE;
    STDMETHOD(get_TickMove)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_TickMove)(THIS_ int val) PURE;
    STDMETHOD(get_TickEvent)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(put_TickEvent)(THIS_ int val) PURE;
    STDMETHOD(get_Visible)(THIS_ VARIANT_BOOL FAR* pRet) PURE;
    STDMETHOD(put_Visible)(THIS_ VARIANT_BOOL val) PURE;
    STDMETHOD(get_Tag)(THIS_ VARIANT FAR* pRet) PURE;
    STDMETHOD(put_Tag)(THIS_ VARIANT val) PURE;
    STDMETHOD(get_Width)(THIS_ int FAR* pRet) PURE;
    STDMETHOD(get_Height)(THIS_ int FAR* pRet) PURE;
};

DEFINE_GUID(CLSID_Sprite,0x8703FF6BL,0x669C,0x11CF,0xA9,0x2B,0x00,0xAA,0x00,0x68,0x8A,0x2F);

#ifdef __cplusplus
class Sprite;
#endif

#endif


ACTIVSCP.H

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 2.00.0101 */
/* at Fri May 03 18:25:03 1996
 */
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __activscp_h__
#define __activscp_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __IActiveScriptSite_FWD_DEFINED__
#define __IActiveScriptSite_FWD_DEFINED__
typedef interface IActiveScriptSite IActiveScriptSite;
#endif /* __IActiveScriptSite_FWD_DEFINED__ */

#ifndef __IActiveScriptError_FWD_DEFINED__
#define __IActiveScriptError_FWD_DEFINED__
typedef interface IActiveScriptError IActiveScriptError;
#endif /* __IActiveScriptError_FWD_DEFINED__ */

#ifndef __IActiveScriptSiteWindow_FWD_DEFINED__
#define __IActiveScriptSiteWindow_FWD_DEFINED__
typedef interface IActiveScriptSiteWindow IActiveScriptSiteWindow;
#endif /* __IActiveScriptSiteWindow_FWD_DEFINED__ */

#ifndef __IActiveScript_FWD_DEFINED__
#define __IActiveScript_FWD_DEFINED__
typedef interface IActiveScript IActiveScript;
#endif /* __IActiveScript_FWD_DEFINED__ */

#ifndef __IActiveScriptParse_FWD_DEFINED__
#define __IActiveScriptParse_FWD_DEFINED__
typedef interface IActiveScriptParse IActiveScriptParse;
#endif /* __IActiveScriptParse_FWD_DEFINED__ */

/* header files for imported files */
#include "olectl.h"

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

/****************************************
 * Generated header for interface: __MIDL__intf_0000
 * at Fri May 03 18:25:03 1996
 * using MIDL 2.00.0101
 ****************************************/
/* [local] */

//=--------------------------------------------------------------------------=
// ActivScp.h
//=--------------------------------------------------------------------------=
// (C) Copyright 1997 Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// Declarations for ActiveX Scripting host applications and script engines.
//

#ifndef __ActivScp_h
#define __ActivScp_h

struct tagRemExcepInfo;
typedef struct tagRemEXCEPINFO RemEXCEPINFO;

/* GUIDs
 ********/

#ifndef _NO_SCRIPT_GUIDS
// {F0B7A1A1-9847-11cf-8F20-00805F2CD064}
DEFINE_GUID(CATID_ActiveScript, 0xf0b7a1a1, 0x9847, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);

// {F0B7A1A2-9847-11cf-8F20-00805F2CD064}
DEFINE_GUID(CATID_ActiveScriptParse, 0xf0b7a1a2, 0x9847, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);

// {BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}
DEFINE_GUID(IID_IActiveScript, 0xbb1a2ae1, 0xa4f9, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);

// {BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}
DEFINE_GUID(IID_IActiveScriptParse, 0xbb1a2ae2, 0xa4f9, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);

// {DB01A1E3-A42B-11cf-8F20-00805F2CD064}
DEFINE_GUID(IID_IActiveScriptSite, 0xdb01a1e3, 0xa42b, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);

// {D10F6761-83E9-11cf-8F20-00805F2CD064}
DEFINE_GUID(IID_IActiveScriptSiteWindow, 0xd10f6761, 0x83e9, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);

// {EAE1BA61-A4ED-11cf-8F20-00805F2CD064}
DEFINE_GUID(IID_IActiveScriptError, 0xeae1ba61, 0xa4ed, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);

#endif // _NO_SCRIPT_GUIDS

// Constants used by ActiveX Scripting:
//

/* IActiveScript::AddNamedItem() input flags */

#define SCRIPTITEM_ISVISIBLE0x00000002
#define SCRIPTITEM_ISSOURCE0x00000004
#define SCRIPTITEM_GLOBALMEMBERS0x00000008
#define SCRIPTITEM_ISPERSISTENT0x00000040
#define SCRIPTITEM_CODEONLY0x00000200
#define SCRIPTITEM_NOCODE0x00000400

#define SCRIPTITEM_ALL_FLAGS(SCRIPTITEM_ISSOURCE | \
 SCRIPTITEM_ISVISIBLE | \
 SCRIPTITEM_ISPERSISTENT | \
 SCRIPTITEM_GLOBALMEMBERS | \
 SCRIPTITEM_NOCODE | \
 SCRIPTITEM_CODEONLY)

/* IActiveScript::AddTypeLib() input flags */

#define SCRIPTTYPELIB_ISCONTROL0x00000010
#define SCRIPTTYPELIB_ISPERSISTENT0x00000040
#define SCRIPTTYPELIB_ALL_FLAGS(SCRIPTTEXT_ISCONTROL | SCRIPTTYPELIB_ISPERSISTENT)

/* IActiveScriptParse::AddScriptlet() and IActiveScriptParse::ParseScriptText() input flags */

#define SCRIPTTEXT_ISVISIBLE0x00000002
#define SCRIPTTEXT_ISEXPRESSION0x00000020
#define SCRIPTTEXT_ISPERSISTENT0x00000040
#define SCRIPTTEXT_ALL_FLAGS(SCRIPTTEXT_ISVISIBLE | \
 SCRIPTTEXT_ISEXPRESSION | \
 SCRIPTTEXT_ISPERSISTENT)

/* IActiveScriptSite::GetItemInfo() input flags */

#define SCRIPTINFO_IUNKNOWN0x00000001
#define SCRIPTINFO_ITYPEINFO0x00000002
#define SCRIPTINFO_ALL_FLAGS(SCRIPTINFO_IUNKNOWN | \
 SCRIPTINFO_ITYPEINFO)

/* IActiveScript::Interrupt() Flags */

#define SCRIPTINTERRUPT_DEBUG0x00000001
#define SCRIPTINTERRUPT_RAISEEXCEPTION0x00000002
#define SCRIPTINTERRUPT_ALL_FLAGS(SCRIPTINTERRUPT_DEBUG | \
 SCRIPTINTERRUPT_RAISEEXCEPTION)

/* script state values */

/* size is 4 */
typedef
enum tagSCRIPTSTATE
    {SCRIPTSTATE_UNINITIALIZED= 0,
SCRIPTSTATE_INITIALIZED= 5,
SCRIPTSTATE_STARTED= 1,
SCRIPTSTATE_CONNECTED= 2,
SCRIPTSTATE_DISCONNECTED= 3,
SCRIPTSTATE_CLOSED= 4
    }SCRIPTSTATE;

/* script thread state values */

/* size is 4 */
typedef
enum tagSCRIPTTHREADSTATE
    {SCRIPTTHREADSTATE_NOTINSCRIPT= 0,
SCRIPTTHREADSTATE_RUNNING= 1
    }SCRIPTTHREADSTATE;

/* Thread IDs */

/* size is 4 */
typedef DWORD SCRIPTTHREADID;

#define SCRIPTTHREADID_CURRENT((SCRIPTTHREADID)-1)
#define SCRIPTTHREADID_BASE((SCRIPTTHREADID)-2)
#define SCRIPTTHREADID_ALL((SCRIPTTHREADID)-3)

/* Structures */

/* Interfaces
 *************/

/* size is 0 */

/* size is 0 */

/* size is 0 */

/* size is 0 */

/* size is 0 */

extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_s_ifspec;

#ifndef __IActiveScriptSite_INTERFACE_DEFINED__
#define __IActiveScriptSite_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IActiveScriptSite
 * at Fri May 03 18:25:03 1996
 * using MIDL 2.00.0101
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IActiveScriptSite;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface IActiveScriptSite : public IUnknown
    {
    public:
        virtual HRESULT __stdcall GetLCID(
            /* [out] */ LCID __RPC_FAR *plcid) = 0;

        virtual HRESULT __stdcall GetItemInfo(
            /* [in] */ LPCOLESTR pstrName,
            /* [in] */ DWORD dwReturnMask,
            /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppiunkItem,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppti) = 0;

        virtual HRESULT __stdcall GetDocVersionString(
            /* [out] */ BSTR __RPC_FAR *pbstrVersion) = 0;

        virtual HRESULT __stdcall OnScriptTerminate(
            /* [in] */ const VARIANT __RPC_FAR *pvarResult,
            /* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo) = 0;

        virtual HRESULT __stdcall OnStateChange(
            /* [in] */ SCRIPTSTATE ssScriptState) = 0;

        virtual HRESULT __stdcall OnScriptError(
            /* [in] */ IActiveScriptError __RPC_FAR *pscripterror) = 0;

        virtual HRESULT __stdcall OnEnterScript( void) = 0;

        virtual HRESULT __stdcall OnLeaveScript( void) = 0;

    };

#else /* C style interface */

    typedef struct IActiveScriptSiteVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            IActiveScriptSite __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            IActiveScriptSite __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            IActiveScriptSite __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *GetLCID )(
            IActiveScriptSite __RPC_FAR * This,
            /* [out] */ LCID __RPC_FAR *plcid);

        HRESULT ( __stdcall __RPC_FAR *GetItemInfo )(
            IActiveScriptSite __RPC_FAR * This,
            /* [in] */ LPCOLESTR pstrName,
            /* [in] */ DWORD dwReturnMask,
            /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppiunkItem,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppti);

        HRESULT ( __stdcall __RPC_FAR *GetDocVersionString )(
            IActiveScriptSite __RPC_FAR * This,
            /* [out] */ BSTR __RPC_FAR *pbstrVersion);

        HRESULT ( __stdcall __RPC_FAR *OnScriptTerminate )(
            IActiveScriptSite __RPC_FAR * This,
            /* [in] */ const VARIANT __RPC_FAR *pvarResult,
            /* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo);

        HRESULT ( __stdcall __RPC_FAR *OnStateChange )(
            IActiveScriptSite __RPC_FAR * This,
            /* [in] */ SCRIPTSTATE ssScriptState);

        HRESULT ( __stdcall __RPC_FAR *OnScriptError )(
            IActiveScriptSite __RPC_FAR * This,
            /* [in] */ IActiveScriptError __RPC_FAR *pscripterror);

        HRESULT ( __stdcall __RPC_FAR *OnEnterScript )(
            IActiveScriptSite __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *OnLeaveScript )(
            IActiveScriptSite __RPC_FAR * This);

    } IActiveScriptSiteVtbl;

    interface IActiveScriptSite
    {
        CONST_VTBL struct IActiveScriptSiteVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IActiveScriptSite_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IActiveScriptSite_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IActiveScriptSite_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IActiveScriptSite_GetLCID(This,plcid)\
    (This)->lpVtbl -> GetLCID(This,plcid)

#define IActiveScriptSite_GetItemInfo(This,pstrName,dwReturnMask,ppiunkItem,ppti)\
    (This)->lpVtbl -> GetItemInfo(This,pstrName,dwReturnMask,ppiunkItem,ppti)

#define IActiveScriptSite_GetDocVersionString(This,pbstrVersion)\
    (This)->lpVtbl -> GetDocVersionString(This,pbstrVersion)

#define IActiveScriptSite_OnScriptTerminate(This,pvarResult,pexcepinfo)\
    (This)->lpVtbl -> OnScriptTerminate(This,pvarResult,pexcepinfo)

#define IActiveScriptSite_OnStateChange(This,ssScriptState)\
    (This)->lpVtbl -> OnStateChange(This,ssScriptState)

#define IActiveScriptSite_OnScriptError(This,pscripterror)\
    (This)->lpVtbl -> OnScriptError(This,pscripterror)

#define IActiveScriptSite_OnEnterScript(This)\
    (This)->lpVtbl -> OnEnterScript(This)

#define IActiveScriptSite_OnLeaveScript(This)\
    (This)->lpVtbl -> OnLeaveScript(This)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall IActiveScriptSite_GetLCID_Proxy(
    IActiveScriptSite __RPC_FAR * This,
    /* [out] */ LCID __RPC_FAR *plcid);

void __RPC_STUB IActiveScriptSite_GetLCID_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptSite_GetItemInfo_Proxy(
    IActiveScriptSite __RPC_FAR * This,
    /* [in] */ LPCOLESTR pstrName,
    /* [in] */ DWORD dwReturnMask,
    /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppiunkItem,
    /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppti);

void __RPC_STUB IActiveScriptSite_GetItemInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptSite_GetDocVersionString_Proxy(
    IActiveScriptSite __RPC_FAR * This,
    /* [out] */ BSTR __RPC_FAR *pbstrVersion);

void __RPC_STUB IActiveScriptSite_GetDocVersionString_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptSite_OnScriptTerminate_Proxy(
    IActiveScriptSite __RPC_FAR * This,
    /* [in] */ const VARIANT __RPC_FAR *pvarResult,
    /* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo);

void __RPC_STUB IActiveScriptSite_OnScriptTerminate_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptSite_OnStateChange_Proxy(
    IActiveScriptSite __RPC_FAR * This,
    /* [in] */ SCRIPTSTATE ssScriptState);

void __RPC_STUB IActiveScriptSite_OnStateChange_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptSite_OnScriptError_Proxy(
    IActiveScriptSite __RPC_FAR * This,
    /* [in] */ IActiveScriptError __RPC_FAR *pscripterror);

void __RPC_STUB IActiveScriptSite_OnScriptError_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptSite_OnEnterScript_Proxy(
    IActiveScriptSite __RPC_FAR * This);

void __RPC_STUB IActiveScriptSite_OnEnterScript_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptSite_OnLeaveScript_Proxy(
    IActiveScriptSite __RPC_FAR * This);

void __RPC_STUB IActiveScriptSite_OnLeaveScript_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IActiveScriptSite_INTERFACE_DEFINED__ */

#ifndef __IActiveScriptError_INTERFACE_DEFINED__
#define __IActiveScriptError_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IActiveScriptError
 * at Fri May 03 18:25:03 1996
 * using MIDL 2.00.0101
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IActiveScriptError;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface IActiveScriptError : public IUnknown
    {
    public:
        virtual HRESULT __stdcall GetExceptionInfo(
            /* [out] */ EXCEPINFO __RPC_FAR *pexcepinfo) = 0;

        virtual HRESULT __stdcall GetSourcePosition(
            /* [out] */ DWORD __RPC_FAR *pdwSourceContext,
            /* [out] */ ULONG __RPC_FAR *pulLineNumber,
            /* [out] */ LONG __RPC_FAR *plCharacterPosition) = 0;

        virtual HRESULT __stdcall GetSourceLineText(
            /* [out] */ BSTR __RPC_FAR *pbstrSourceLine) = 0;

    };

#else /* C style interface */

    typedef struct IActiveScriptErrorVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            IActiveScriptError __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            IActiveScriptError __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            IActiveScriptError __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *GetExceptionInfo )(
            IActiveScriptError __RPC_FAR * This,
            /* [out] */ EXCEPINFO __RPC_FAR *pexcepinfo);

        HRESULT ( __stdcall __RPC_FAR *GetSourcePosition )(
            IActiveScriptError __RPC_FAR * This,
            /* [out] */ DWORD __RPC_FAR *pdwSourceContext,
            /* [out] */ ULONG __RPC_FAR *pulLineNumber,
            /* [out] */ LONG __RPC_FAR *plCharacterPosition);

        HRESULT ( __stdcall __RPC_FAR *GetSourceLineText )(
            IActiveScriptError __RPC_FAR * This,
            /* [out] */ BSTR __RPC_FAR *pbstrSourceLine);

    } IActiveScriptErrorVtbl;

    interface IActiveScriptError
    {
        CONST_VTBL struct IActiveScriptErrorVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IActiveScriptError_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IActiveScriptError_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IActiveScriptError_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IActiveScriptError_GetExceptionInfo(This,pexcepinfo)\
    (This)->lpVtbl -> GetExceptionInfo(This,pexcepinfo)

#define IActiveScriptError_GetSourcePosition(This,pdwSourceContext,pulLineNumber,plCharacterPosition)\
    (This)->lpVtbl -> GetSourcePosition(This,pdwSourceContext,pulLineNumber,plCharacterPosition)

#define IActiveScriptError_GetSourceLineText(This,pbstrSourceLine)\
    (This)->lpVtbl -> GetSourceLineText(This,pbstrSourceLine)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall IActiveScriptError_GetExceptionInfo_Proxy(
    IActiveScriptError __RPC_FAR * This,
    /* [out] */ EXCEPINFO __RPC_FAR *pexcepinfo);

void __RPC_STUB IActiveScriptError_GetExceptionInfo_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptError_GetSourcePosition_Proxy(
    IActiveScriptError __RPC_FAR * This,
    /* [out] */ DWORD __RPC_FAR *pdwSourceContext,
    /* [out] */ ULONG __RPC_FAR *pulLineNumber,
    /* [out] */ LONG __RPC_FAR *plCharacterPosition);

void __RPC_STUB IActiveScriptError_GetSourcePosition_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptError_GetSourceLineText_Proxy(
    IActiveScriptError __RPC_FAR * This,
    /* [out] */ BSTR __RPC_FAR *pbstrSourceLine);

void __RPC_STUB IActiveScriptError_GetSourceLineText_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IActiveScriptError_INTERFACE_DEFINED__ */

#ifndef __IActiveScriptSiteWindow_INTERFACE_DEFINED__
#define __IActiveScriptSiteWindow_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IActiveScriptSiteWindow
 * at Fri May 03 18:25:03 1996
 * using MIDL 2.00.0101
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IActiveScriptSiteWindow;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface IActiveScriptSiteWindow : public IUnknown
    {
    public:
        virtual HRESULT __stdcall GetWindow(
            /* [out] */ HWND __RPC_FAR *phwnd) = 0;

        virtual HRESULT __stdcall EnableModeless(
            /* [in] */ BOOL fEnable) = 0;

    };

#else /* C style interface */

    typedef struct IActiveScriptSiteWindowVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            IActiveScriptSiteWindow __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            IActiveScriptSiteWindow __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            IActiveScriptSiteWindow __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *GetWindow )(
            IActiveScriptSiteWindow __RPC_FAR * This,
            /* [out] */ HWND __RPC_FAR *phwnd);

        HRESULT ( __stdcall __RPC_FAR *EnableModeless )(
            IActiveScriptSiteWindow __RPC_FAR * This,
            /* [in] */ BOOL fEnable);

    } IActiveScriptSiteWindowVtbl;

    interface IActiveScriptSiteWindow
    {
        CONST_VTBL struct IActiveScriptSiteWindowVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IActiveScriptSiteWindow_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IActiveScriptSiteWindow_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IActiveScriptSiteWindow_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IActiveScriptSiteWindow_GetWindow(This,phwnd)\
    (This)->lpVtbl -> GetWindow(This,phwnd)

#define IActiveScriptSiteWindow_EnableModeless(This,fEnable)\
    (This)->lpVtbl -> EnableModeless(This,fEnable)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall IActiveScriptSiteWindow_GetWindow_Proxy(
    IActiveScriptSiteWindow __RPC_FAR * This,
    /* [out] */ HWND __RPC_FAR *phwnd);

void __RPC_STUB IActiveScriptSiteWindow_GetWindow_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptSiteWindow_EnableModeless_Proxy(
    IActiveScriptSiteWindow __RPC_FAR * This,
    /* [in] */ BOOL fEnable);

void __RPC_STUB IActiveScriptSiteWindow_EnableModeless_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IActiveScriptSiteWindow_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL__intf_0058
 * at Fri May 03 18:25:03 1996
 * using MIDL 2.00.0101
 ****************************************/
/* [local] */

typedef IActiveScriptSite *PIActiveScriptSite;

extern RPC_IF_HANDLE __MIDL__intf_0058_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0058_v0_0_s_ifspec;

#ifndef __IActiveScript_INTERFACE_DEFINED__
#define __IActiveScript_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IActiveScript
 * at Fri May 03 18:25:03 1996
 * using MIDL 2.00.0101
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IActiveScript;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface IActiveScript : public IUnknown
    {
    public:
        virtual HRESULT __stdcall SetScriptSite(
            /* [in] */ IActiveScriptSite __RPC_FAR *pass) = 0;

        virtual HRESULT __stdcall GetScriptSite(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) = 0;

        virtual HRESULT __stdcall SetScriptState(
            /* [in] */ SCRIPTSTATE ss) = 0;

        virtual HRESULT __stdcall GetScriptState(
            /* [out] */ SCRIPTSTATE __RPC_FAR *pssState) = 0;

        virtual HRESULT __stdcall Close( void) = 0;

        virtual HRESULT __stdcall AddNamedItem(
            /* [in] */ LPCOLESTR pstrName,
            /* [in] */ DWORD dwFlags) = 0;

        virtual HRESULT __stdcall AddTypeLib(
            /* [in] */ REFGUID rguidTypeLib,
            /* [in] */ DWORD dwMajor,
            /* [in] */ DWORD dwMinor,
            /* [in] */ DWORD dwFlags) = 0;

        virtual HRESULT __stdcall GetScriptDispatch(
            /* [in] */ LPCOLESTR pstrItemName,
            /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdisp) = 0;

        virtual HRESULT __stdcall GetCurrentScriptThreadID(
            /* [out] */ SCRIPTTHREADID __RPC_FAR *pstidThread) = 0;

        virtual HRESULT __stdcall GetScriptThreadID(
            /* [in] */ DWORD dwWin32ThreadId,
            /* [out] */ SCRIPTTHREADID __RPC_FAR *pstidThread) = 0;

        virtual HRESULT __stdcall GetScriptThreadState(
            /* [in] */ SCRIPTTHREADID stidThread,
            /* [out] */ SCRIPTTHREADSTATE __RPC_FAR *pstsState) = 0;

        virtual HRESULT __stdcall InterruptScriptThread(
            /* [in] */ SCRIPTTHREADID stidThread,
            /* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo,
            /* [in] */ DWORD dwFlags) = 0;

        virtual HRESULT __stdcall Clone(
            /* [out] */ IActiveScript __RPC_FAR *__RPC_FAR *ppscript) = 0;

    };

#else /* C style interface */

    typedef struct IActiveScriptVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            IActiveScript __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            IActiveScript __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *SetScriptSite )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ IActiveScriptSite __RPC_FAR *pass);

        HRESULT ( __stdcall __RPC_FAR *GetScriptSite )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        HRESULT ( __stdcall __RPC_FAR *SetScriptState )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ SCRIPTSTATE ss);

        HRESULT ( __stdcall __RPC_FAR *GetScriptState )(
            IActiveScript __RPC_FAR * This,
            /* [out] */ SCRIPTSTATE __RPC_FAR *pssState);

        HRESULT ( __stdcall __RPC_FAR *Close )(
            IActiveScript __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *AddNamedItem )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ LPCOLESTR pstrName,
            /* [in] */ DWORD dwFlags);

        HRESULT ( __stdcall __RPC_FAR *AddTypeLib )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ REFGUID rguidTypeLib,
            /* [in] */ DWORD dwMajor,
            /* [in] */ DWORD dwMinor,
            /* [in] */ DWORD dwFlags);

        HRESULT ( __stdcall __RPC_FAR *GetScriptDispatch )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ LPCOLESTR pstrItemName,
            /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdisp);

        HRESULT ( __stdcall __RPC_FAR *GetCurrentScriptThreadID )(
            IActiveScript __RPC_FAR * This,
            /* [out] */ SCRIPTTHREADID __RPC_FAR *pstidThread);

        HRESULT ( __stdcall __RPC_FAR *GetScriptThreadID )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ DWORD dwWin32ThreadId,
            /* [out] */ SCRIPTTHREADID __RPC_FAR *pstidThread);

        HRESULT ( __stdcall __RPC_FAR *GetScriptThreadState )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ SCRIPTTHREADID stidThread,
            /* [out] */ SCRIPTTHREADSTATE __RPC_FAR *pstsState);

        HRESULT ( __stdcall __RPC_FAR *InterruptScriptThread )(
            IActiveScript __RPC_FAR * This,
            /* [in] */ SCRIPTTHREADID stidThread,
            /* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo,
            /* [in] */ DWORD dwFlags);

        HRESULT ( __stdcall __RPC_FAR *Clone )(
            IActiveScript __RPC_FAR * This,
            /* [out] */ IActiveScript __RPC_FAR *__RPC_FAR *ppscript);

    } IActiveScriptVtbl;

    interface IActiveScript
    {
        CONST_VTBL struct IActiveScriptVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IActiveScript_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IActiveScript_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IActiveScript_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IActiveScript_SetScriptSite(This,pass)\
    (This)->lpVtbl -> SetScriptSite(This,pass)

#define IActiveScript_GetScriptSite(This,riid,ppvObject)\
    (This)->lpVtbl -> GetScriptSite(This,riid,ppvObject)

#define IActiveScript_SetScriptState(This,ss)\
    (This)->lpVtbl -> SetScriptState(This,ss)

#define IActiveScript_GetScriptState(This,pssState)\
    (This)->lpVtbl -> GetScriptState(This,pssState)

#define IActiveScript_Close(This)\
    (This)->lpVtbl -> Close(This)

#define IActiveScript_AddNamedItem(This,pstrName,dwFlags)\
    (This)->lpVtbl -> AddNamedItem(This,pstrName,dwFlags)

#define IActiveScript_AddTypeLib(This,rguidTypeLib,dwMajor,dwMinor,dwFlags)\
    (This)->lpVtbl -> AddTypeLib(This,rguidTypeLib,dwMajor,dwMinor,dwFlags)

#define IActiveScript_GetScriptDispatch(This,pstrItemName,ppdisp)\
    (This)->lpVtbl -> GetScriptDispatch(This,pstrItemName,ppdisp)

#define IActiveScript_GetCurrentScriptThreadID(This,pstidThread)\
    (This)->lpVtbl -> GetCurrentScriptThreadID(This,pstidThread)

#define IActiveScript_GetScriptThreadID(This,dwWin32ThreadId,pstidThread)\
    (This)->lpVtbl -> GetScriptThreadID(This,dwWin32ThreadId,pstidThread)

#define IActiveScript_GetScriptThreadState(This,stidThread,pstsState)\
    (This)->lpVtbl -> GetScriptThreadState(This,stidThread,pstsState)

#define IActiveScript_InterruptScriptThread(This,stidThread,pexcepinfo,dwFlags)\
    (This)->lpVtbl -> InterruptScriptThread(This,stidThread,pexcepinfo,dwFlags)

#define IActiveScript_Clone(This,ppscript)\
    (This)->lpVtbl -> Clone(This,ppscript)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall IActiveScript_SetScriptSite_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [in] */ IActiveScriptSite __RPC_FAR *pass);

void __RPC_STUB IActiveScript_SetScriptSite_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_GetScriptSite_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [in] */ REFIID riid,
    /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

void __RPC_STUB IActiveScript_GetScriptSite_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_SetScriptState_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [in] */ SCRIPTSTATE ss);

void __RPC_STUB IActiveScript_SetScriptState_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_GetScriptState_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [out] */ SCRIPTSTATE __RPC_FAR *pssState);

void __RPC_STUB IActiveScript_GetScriptState_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_Close_Proxy(
    IActiveScript __RPC_FAR * This);

void __RPC_STUB IActiveScript_Close_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_AddNamedItem_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [in] */ LPCOLESTR pstrName,
    /* [in] */ DWORD dwFlags);

void __RPC_STUB IActiveScript_AddNamedItem_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_AddTypeLib_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [in] */ REFGUID rguidTypeLib,
    /* [in] */ DWORD dwMajor,
    /* [in] */ DWORD dwMinor,
    /* [in] */ DWORD dwFlags);

void __RPC_STUB IActiveScript_AddTypeLib_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_GetScriptDispatch_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [in] */ LPCOLESTR pstrItemName,
    /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdisp);

void __RPC_STUB IActiveScript_GetScriptDispatch_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_GetCurrentScriptThreadID_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [out] */ SCRIPTTHREADID __RPC_FAR *pstidThread);

void __RPC_STUB IActiveScript_GetCurrentScriptThreadID_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_GetScriptThreadID_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [in] */ DWORD dwWin32ThreadId,
    /* [out] */ SCRIPTTHREADID __RPC_FAR *pstidThread);

void __RPC_STUB IActiveScript_GetScriptThreadID_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_GetScriptThreadState_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [in] */ SCRIPTTHREADID stidThread,
    /* [out] */ SCRIPTTHREADSTATE __RPC_FAR *pstsState);

void __RPC_STUB IActiveScript_GetScriptThreadState_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_InterruptScriptThread_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [in] */ SCRIPTTHREADID stidThread,
    /* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo,
    /* [in] */ DWORD dwFlags);

void __RPC_STUB IActiveScript_InterruptScriptThread_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScript_Clone_Proxy(
    IActiveScript __RPC_FAR * This,
    /* [out] */ IActiveScript __RPC_FAR *__RPC_FAR *ppscript);

void __RPC_STUB IActiveScript_Clone_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IActiveScript_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL__intf_0059
 * at Fri May 03 18:25:03 1996
 * using MIDL 2.00.0101
 ****************************************/
/* [local] */

typedef IActiveScript *PIActiveScript;

extern RPC_IF_HANDLE __MIDL__intf_0059_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0059_v0_0_s_ifspec;

#ifndef __IActiveScriptParse_INTERFACE_DEFINED__
#define __IActiveScriptParse_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IActiveScriptParse
 * at Fri May 03 18:25:03 1996
 * using MIDL 2.00.0101
 ****************************************/
/* [unique][uuid][object] */

EXTERN_C const IID IID_IActiveScriptParse;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface IActiveScriptParse : public IUnknown
    {
    public:
        virtual HRESULT __stdcall InitNew( void) = 0;

        virtual HRESULT __stdcall AddScriptlet(
            /* [in] */ LPCOLESTR pstrDefaultName,
            /* [in] */ LPCOLESTR pstrCode,
            /* [in] */ LPCOLESTR pstrItemName,
            /* [in] */ LPCOLESTR pstrSubItemName,
            /* [in] */ LPCOLESTR pstrEventName,
            /* [in] */ LPCOLESTR pstrDelimiter,
            /* [in] */ DWORD dwSourceContextCookie,
            /* [in] */ ULONG ulStartingLineNumber,
            /* [in] */ DWORD dwFlags,
            /* [out] */ BSTR __RPC_FAR *pbstrName,
            /* [out] */ EXCEPINFO __RPC_FAR *pexcepinfo) = 0;

        virtual HRESULT __stdcall ParseScriptText(
            /* [in] */ LPCOLESTR pstrCode,
            /* [in] */ LPCOLESTR pstrItemName,
            /* [in] */ IUnknown __RPC_FAR *punkContext,
            /* [in] */ LPCOLESTR pstrDelimiter,
            /* [in] */ DWORD dwSourceContextCookie,
            /* [in] */ ULONG ulStartingLineNumber,
            /* [in] */ DWORD dwFlags,
            /* [out] */ VARIANT __RPC_FAR *pvarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pexcepinfo) = 0;

    };

#else /* C style interface */

    typedef struct IActiveScriptParseVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            IActiveScriptParse __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            IActiveScriptParse __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            IActiveScriptParse __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *InitNew )(
            IActiveScriptParse __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *AddScriptlet )(
            IActiveScriptParse __RPC_FAR * This,
            /* [in] */ LPCOLESTR pstrDefaultName,
            /* [in] */ LPCOLESTR pstrCode,
            /* [in] */ LPCOLESTR pstrItemName,
            /* [in] */ LPCOLESTR pstrSubItemName,
            /* [in] */ LPCOLESTR pstrEventName,
            /* [in] */ LPCOLESTR pstrDelimiter,
            /* [in] */ DWORD dwSourceContextCookie,
            /* [in] */ ULONG ulStartingLineNumber,
            /* [in] */ DWORD dwFlags,
            /* [out] */ BSTR __RPC_FAR *pbstrName,
            /* [out] */ EXCEPINFO __RPC_FAR *pexcepinfo);

        HRESULT ( __stdcall __RPC_FAR *ParseScriptText )(
            IActiveScriptParse __RPC_FAR * This,
            /* [in] */ LPCOLESTR pstrCode,
            /* [in] */ LPCOLESTR pstrItemName,
            /* [in] */ IUnknown __RPC_FAR *punkContext,
            /* [in] */ LPCOLESTR pstrDelimiter,
            /* [in] */ DWORD dwSourceContextCookie,
            /* [in] */ ULONG ulStartingLineNumber,
            /* [in] */ DWORD dwFlags,
            /* [out] */ VARIANT __RPC_FAR *pvarResult,
            /* [out] */ EXCEPINFO __RPC_FAR *pexcepinfo);

    } IActiveScriptParseVtbl;

    interface IActiveScriptParse
    {
        CONST_VTBL struct IActiveScriptParseVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IActiveScriptParse_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IActiveScriptParse_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IActiveScriptParse_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IActiveScriptParse_InitNew(This)\
    (This)->lpVtbl -> InitNew(This)

#define IActiveScriptParse_AddScriptlet(This,pstrDefaultName,pstrCode,pstrItemName,pstrSubItemName,pstrEventName,pstrDelimiter,dwSourceContextCookie,ulStartingLineNumber,dwFlags,pbstrName,pexcepinfo)\
    (This)->lpVtbl -> AddScriptlet(This,pstrDefaultName,pstrCode,pstrItemName,pstrSubItemName,pstrEventName,pstrDelimiter,dwSourceContextCookie,ulStartingLineNumber,dwFlags,pbstrName,pexcepinfo)

#define IActiveScriptParse_ParseScriptText(This,pstrCode,pstrItemName,punkContext,pstrDelimiter,dwSourceContextCookie,ulStartingLineNumber,dwFlags,pvarResult,pexcepinfo)\
    (This)->lpVtbl -> ParseScriptText(This,pstrCode,pstrItemName,punkContext,pstrDelimiter,dwSourceContextCookie,ulStartingLineNumber,dwFlags,pvarResult,pexcepinfo)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall IActiveScriptParse_InitNew_Proxy(
    IActiveScriptParse __RPC_FAR * This);

void __RPC_STUB IActiveScriptParse_InitNew_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptParse_AddScriptlet_Proxy(
    IActiveScriptParse __RPC_FAR * This,
    /* [in] */ LPCOLESTR pstrDefaultName,
    /* [in] */ LPCOLESTR pstrCode,
    /* [in] */ LPCOLESTR pstrItemName,
    /* [in] */ LPCOLESTR pstrSubItemName,
    /* [in] */ LPCOLESTR pstrEventName,
    /* [in] */ LPCOLESTR pstrDelimiter,
    /* [in] */ DWORD dwSourceContextCookie,
    /* [in] */ ULONG ulStartingLineNumber,
    /* [in] */ DWORD dwFlags,
    /* [out] */ BSTR __RPC_FAR *pbstrName,
    /* [out] */ EXCEPINFO __RPC_FAR *pexcepinfo);

void __RPC_STUB IActiveScriptParse_AddScriptlet_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IActiveScriptParse_ParseScriptText_Proxy(
    IActiveScriptParse __RPC_FAR * This,
    /* [in] */ LPCOLESTR pstrCode,
    /* [in] */ LPCOLESTR pstrItemName,
    /* [in] */ IUnknown __RPC_FAR *punkContext,
    /* [in] */ LPCOLESTR pstrDelimiter,
    /* [in] */ DWORD dwSourceContextCookie,
    /* [in] */ ULONG ulStartingLineNumber,
    /* [in] */ DWORD dwFlags,
    /* [out] */ VARIANT __RPC_FAR *pvarResult,
    /* [out] */ EXCEPINFO __RPC_FAR *pexcepinfo);

void __RPC_STUB IActiveScriptParse_ParseScriptText_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IActiveScriptParse_INTERFACE_DEFINED__ */

/****************************************
 * Generated header for interface: __MIDL__intf_0060
 * at Fri May 03 18:25:03 1996
 * using MIDL 2.00.0101
 ****************************************/
/* [local] */

typedef IActiveScriptParse *PIActiveScriptParse;

#endif  // __ActivScp_h

extern RPC_IF_HANDLE __MIDL__intf_0060_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0060_v0_0_s_ifspec;

/* Additional Prototypes for ALL interfaces */

void __RPC_USER HWND_to_xmit( HWND __RPC_FAR *, long  __RPC_FAR * __RPC_FAR * );
void __RPC_USER HWND_from_xmit( long  __RPC_FAR *, HWND __RPC_FAR * );
void __RPC_USER HWND_free_inst( HWND __RPC_FAR * );
void __RPC_USER HWND_free_xmit( long  __RPC_FAR * );

void __RPC_USER EXCEPINFO_to_xmit( EXCEPINFO __RPC_FAR *, RemEXCEPINFO  __RPC_FAR * __RPC_FAR * );
void __RPC_USER EXCEPINFO_from_xmit( RemEXCEPINFO  __RPC_FAR *, EXCEPINFO __RPC_FAR * );
void __RPC_USER EXCEPINFO_free_inst( EXCEPINFO __RPC_FAR * );
void __RPC_USER EXCEPINFO_free_xmit( RemEXCEPINFO  __RPC_FAR * );

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif


COMPMGR.H

//---------------------------------------------------------------------------
// CompMgr.h
//---------------------------------------------------------------------------
// This file contains the exported interfaces and declarations for
// Component Manager Interfaces.
//---------------------------------------------------------------------------
// Copyright (c) 1995-1997 Microsoft Corporation
//---------------------------------------------------------------------------

#ifdef INITGUID
    #define DEFINE_GUID2(name, bCategory, bIndex) \
        EXTERN_C const GUID name = {0x000C0000 | MAKEWORD(bIndex, bCategory),}
#else
    #define DEFINE_GUID2(name, bCategory, bIndex) \
        EXTERN_C const GUID name
#endif

DEFINE_GUID2(IID_IOleComponent,        0x06, 0x00);
DEFINE_GUID2(IID_IOleComponentManager, 0x06, 0x01);

//***************************************************************************
// Component integration structures and constants
//***************************************************************************

//---------------------------------------------------------------------------
// Component registration flags
//---------------------------------------------------------------------------
enum
    {
    cmgrrfNeedIdleTime         = 1,  // needs idle time
    cmgrrfNeedPeriodicIdleTime = 2,  // needs idle time every N milliseconds
    cmgrrfPreTranslateKeys     = 4,  // must process keyboard msgs
                             // before translation
    cmgrrfPreTranslateAll      = 8,  // must process all msgs
                             // before translation
    };

//---------------------------------------------------------------------------
// Component registration advise flags (see cmgrstate enumeration)
//---------------------------------------------------------------------------
enum
    {
    cmgradvfModal              = 1,  // needs modal state change notification
                             // (must be registered by components
                             // managing a toplevel window)
    cmgradvfRedrawOff          = 2,  // needs redrawOff state change notif
    cmgradvfWarningsOff        = 4,  // needs warningsOff state change notif
    cmgradvfRecording          = 8,  // needs Recording state change notif
    cmgradvfExclusiveActive    = 16, // needs ExclusiveActive change notif
                             // (must be registered by components
                             //  managing a window with UI).
    };

//---------------------------------------------------------------------------
// Component registration information
//---------------------------------------------------------------------------
typedef struct _CMGRRINFO
    {
    ULONG cbSize;             // size of CMGRRINFO structure in bytes.
    ULONG uIdleTimeInterval;  // If cmgrrfNeedPeriodicIdleTime is registered
                      // in grfcrf, component needs to perform
                      // periodic idle time tasks during an idle phase
                      // every uIdleTimeInterval milliseconds.
    DWORD grfcrf;             // bit flags taken from cmgrrf values (above)
    DWORD grfcadvf;           // bit flags taken from cmgradvf values (above)
    } CMGRRINFO;

//---------------------------------------------------------------------------
// Idle flags, passed to IOleComponent::FDoIdle and
// IOleStdComponentMgr::FDoIdle.
//---------------------------------------------------------------------------
enum
    {
    cmgridlefPeriodic    = 1,  // periodic idle tasks
    cmgridlefNonPeriodic = 2,  // any nonperiodic idle task
    cmgridlefPriority    = 4,  // high priority, nonperiodic idle tasks
    cmgridlefAll         = -1  // all idle tasks
    };

//---------------------------------------------------------------------------
// Reasons for pushing a message loop, passed to
// IOleComponentHost::FPushMessageLoop.  The host should remain in message
// loop until IOleComponent::FContinueMessageLoop returns FALSE.
//---------------------------------------------------------------------------
enum
    {
    cmgrloopFocusWait = 1,  // component is activating host
    cmgrloopDoEvents  = 2,  // component is asking host to process messages
    cmgrloopDebug     = 3,  // component has entered debug mode
    cmgrloopModalForm = 4   // component is displaying a modal form
    };

//---------------------------------------------------------------------------
// cmgrstate values: state IDs passed to
//  IOleComponent::OnEnterState,
//  IOleComponentManager::OnComponentEnterState/FOnComponentExitState/FInState,
//
//  When the host or a component is notified through one of these methods that
//  another entity (component or host) is entering or exiting a state
//  identified by one of these state IDs, the host/component should take
//  appropriate action:
//      cmgrstateModal (modal state):
//    If app is entering modal state, host/component should disable
//    its toplevel windows, and reenable them when app exits this
//    state.  Also, when this state is entered or exited, host/component
//    should notify approprate inplace objects via
//    IOleInPlaceActiveObject::EnableModeless.
//cmgrstateRedrawOff (redrawOff state):
//    If app is entering redrawOff state, host/component should disable
//    repainting of its windows, and reenable repainting when app exits
//    this state.
//cmgrstateWarningsOff (warningsOff state):
//    If app is entering warningsOff state, host/component should disable
//    the presentation of any user warnings, and reenable this when
//    app exits this state.
//cmgrstateRecording (Recording state):
//    Used to notify host/component when Recording is turned on or off.
//cmgrstateExclusiveActive (Exclusive Active state)
//    TODO: add comments on what host/component should do.
//---------------------------------------------------------------------------
enum
    {
    cmgrstateModal       = 1,     // Modal state; disable toplevel windows
    cmgrstateRedrawOff   = 2,     // RedrawOff state; disable window repainting
    cmgrstateWarningsOff = 3,     // WarningsOff state; disable user warnings
    cmgrstateRecording   = 4,     // Recording state
    cmgrstateExclusiveActive = 5, // ExclusiveActive state; TODO: add brief
                          // comment on what should be done here
    };

//---------------------------------------------------------------------------
// ** Comments on State Contexts **
//---------------------------------------------------------------------------
// IOleComponentManager::FCreateSubComponentManager allows one to create a
// hierarchical tree of component managers.  This tree is used to maintain
// multiple contexts with regard to cmgrstateXXX states.  These contexts are
// referred to as 'state contexts'.
// Each component manager in the tree defines a state context.  The
// components registered with a particular component manager or any of its
// descendents live within that component manager's state context.  Calls
// to IOleComponentManager::OnComponentEnterState/FOnComponentExitState
// can be used to affect all components, only components within the component
// manager's state context, or only those components that are outside of the
// component manager's state context.  IOleComponentManager::FInState is used
// to query the state of the component manager's state context at its root.
//
// cmgrcontext values: context indicators passed to
// IOleComponentManager::OnComponentEnterState/FOnComponentExitState.
// These values indicate the state context that is to be affected by the
// state change.
// In IOleComponentManager::OnComponentEnterState/FOnComponentExitState,
// the comp mgr informs only those components/host that are within the
// specified state context.
//---------------------------------------------------------------------------
enum
    {
    cmgrcontextAll    = 0, // all state contexts in state context tree
    cmgrcontextMine   = 1, // component manager's state context
    cmgrcontextOthers = 2, // all other state contexts outside of comp mgr's
    };

//---------------------------------------------------------------------------
// Defines the IOleComponent interface
//---------------------------------------------------------------------------
// Any component that needs idle time, the ability to process messages before
// they are translated (for example, to call TranslateAccelerator() or
// IsDialogMessage), notification about modal states, or the ability push
// message loops must implement this interface and register with the
// Component Manager.
//---------------------------------------------------------------------------
#undef  INTERFACE
#define INTERFACE  IOleComponent

DECLARE_INTERFACE_(IOleComponent, IUnknown)
{
// *** IUnknown methods ***
STDMETHOD(QueryInterface) (THIS_ REFIID riid, void **ppvObj) PURE;
STDMETHOD_(ULONG, AddRef) (THIS) PURE;
STDMETHOD_(ULONG, Release) (THIS) PURE;

// Give component a chance to process the message pMsg before it is
// translated and dispatched. Component can do TranslateAccelerator,
// do IsDialogMessage, modify pMsg, or take some other action.
// Return TRUE if the message is consumed, FALSE otherwise.
STDMETHOD_(BOOL, FPreTranslateMessage) (THIS_ MSG *pMsg) PURE;

#if MAC
// Give Macintosh component a chance to process the event pEvt
// before it is processed by the host.
// Return TRUE if the event is consumed, FALSE otherwise.
// (WLM components can simply return FALSE.)
STDMETHOD_(BOOL, FPreProcessMacEvent) (THIS_ EventRecord *pEvt) PURE;
#endif // MAC

// Notify component when app enters or exits (as indicated by fEnter)
// the state identified by uStateID (a value from cmgrstate enumeration).
// Component should take action depending on value of uStateID
// (see cmgrstate comments, above).
//
// Note: If n calls are made with TRUE fEnter, component should consider
// the state to be in effect until n calls are made with FALSE fEnter.
//
// Note: Components should be aware that it is possible for this method to
// be called with FALSE fEnter more times than it was called with TRUE
// fEnter (so, for example, if component is maintaining a state counter
// (incremented when this method is called with TRUE fEnter, decremented
// when called with FALSE fEnter), the counter should not be decremented
// for FALSE fEnter if it is already at zero.)
STDMETHOD_(void, OnEnterState) (THIS_ ULONG uStateID, BOOL fEnter) PURE;

// Notify component when the host application gains or loses activation.
// If fActive is TRUE, the host app is being activated and dwOtherThreadID
// is the ID of the thread owning the window being deactivated.
// If fActive is FALSE, the host app is being deactivated and
// dwOtherThreadID is the ID of the thread owning the window being
// activated.
// Note: this method is not called when both the window being activated
// and the one being deactivated belong to the host app.
STDMETHOD_(void, OnAppActivate) (THIS_
BOOL fActive, DWORD dwOtherThreadID) PURE;

// Notify the active component that it has lost its active status because
// the host or another component has become active.
STDMETHOD_(void, OnLoseActivation) (THIS) PURE;

// Give component a chance to do idle time tasks.  grfidlef is a group of
// bit flags taken from the enumeration of cmgridlef values (above),
// indicating the type of idle tasks to perform.
// Component may periodically call IOleComponentManager::FContinueIdle;
// if this method returns FALSE, component should terminate its idle
// time processing and return.
// Return TRUE if more time is needed to perform the idle time tasks,
// FALSE otherwise.
// Note: If a component reaches a point where it has no idle tasks
// and does not need FDoIdle calls, it should remove its idle task
// registration via IOleComponentManager::FUpdateComponentRegistration.
// Note: If this method is called on while component is performing a
// tracking operation, component should only perform idle time tasks that
// it deems are appropriate to perform during tracking.
STDMETHOD_(BOOL, FDoIdle) (THIS_ DWORD grfidlef) PURE;

// Called once per iteration of a message loop that the component pushed.
// uReason and pvLoopData are the reason and the component private data
// that were passed to IOleComponentManager::FPushMessageLoop.
// Return TRUE if the message loop should continue, FALSE otherwise.
STDMETHOD_(BOOL, FContinueMessageLoop) (THIS_
ULONG uReason, void *pvLoopData) PURE;

// Called when component manager wishes to know if the component is in a
// state in which it can terminate.  If fPromptUser is FALSE, component
// should simply return TRUE if it can terminate, FALSE otherwise.
// If fPromptUser is TRUE, component should return TRUE if it can
// terminate without prompting the user; otherwise it should prompt the
// user, either 1.) asking user if it can terminate and returning TRUE
// or FALSE appropriately, or 2.) giving an indication as to why it
// cannot terminate and returning FALSE.
STDMETHOD_(BOOL, FQueryTerminate) (THIS_ BOOL fPromptUser) PURE;

// Called when component manager wishes to terminate the component's
// registration.  Component should revoke its registration with component
// manager, release references to component manager and perform any
// necessary cleanup.
STDMETHOD_(void, Terminate) (THIS) PURE;
};

//---------------------------------------------------------------------------
// Defines the IOleComponentManager interface
//---------------------------------------------------------------------------
// A component manager is an object implementing the IOleComponentManager
// interface.  The component manager coordinates components with its message
// loop for proper distribution of idle time and pre-translation message
// processing.
// It also coordinates modalities and the pushing of message loops.
//---------------------------------------------------------------------------
#undef  INTERFACE
#define INTERFACE  IOleComponentManager

DECLARE_INTERFACE_(IOleComponentManager, IUnknown)
{
// *** IUnknown methods ***
STDMETHOD(QueryInterface) (THIS_ REFIID riid, void **ppvObj) PURE;
STDMETHOD_(ULONG, AddRef) (THIS) PURE;
STDMETHOD_(ULONG, Release) (THIS) PURE;

// Return in *ppvObj an implementation of interface iid for service
// guidService (same as IServiceProvider::QueryService).
// Return NOERROR if the requested service is supported, otherwise return
// NULL in *ppvObj and an appropriate error (eg E_FAIL, E_NOINTERFACE).
STDMETHOD(QueryService) (THIS_
REFGUID guidService, REFIID iid, void **ppvObj) PURE;

// Register component piComponent and its registration info pcrinfo with
// this component manager.  Return in *pdwComponentID a cookie which will
// identify the component when it calls other IOleComponentManager
// methods.
// Return TRUE if successful, FALSE otherwise.
STDMETHOD_(BOOL, FRegisterComponent) (THIS_
IOleComponent *piComponent, const CMGRRINFO *pcrinfo,
DWORD *pdwComponentID) PURE;

// Undo the registration of the component identified by dwComponentID
// (the cookie returned from the FRegisterComponent method).
// Return TRUE if successful, FALSE otherwise.
STDMETHOD_(BOOL, FRevokeComponent) (THIS_ DWORD dwComponentID) PURE;

// Update the registration info of the component identified by
// dwComponentID (the cookie returned from FRegisterComponent) with the
// new registration information pcrinfo.
// Typically this is used to update the idle time registration data, but
// can be used to update other registration data as well.
// Return TRUE if successful, FALSE otherwise.
STDMETHOD_(BOOL, FUpdateComponentRegistration) (THIS_
DWORD dwComponentID, const CMGRRINFO *pcrinfo) PURE;

// Notify component manager that component identified by dwComponentID
// (cookie returned from FRegisterComponent) has been activated.
// The active component gets the chance to process messages before they
// are dispatched (via IOleComponent::FPreTranslateMessage) and typically
// gets first crack at idle time after the host.
STDMETHOD_(void, OnComponentActivate) (THIS_ DWORD dwComponentID) PURE;

// Called to inform component manager that  component identified by
// dwComponentID (cookie returned from FRegisterComponent) wishes
// to perform a tracking operation (such as mouse tracking).
// The component calls this method with fTrack == TRUE to begin the
// tracking operation and with fTrack == FALSE to end the operation.
// During the tracking operation the component manager routes messages
// to the tracking component (via IOleComponent::FPreTranslateMessage)
// rather than to the active component.  When the tracking operation ends,
// the component manager should resume routing messages to the active
// component.
// Note: component manager should perform no idle time processing during a
// tracking operation other than give the tracking component idle
// time via IOleComponent::FDoIdle.
// Note: there can only be one tracking component at a time.
// Return TRUE if successful, FALSE otherwise.
STDMETHOD_(BOOL, FSetTrackingComponent) (THIS_
DWORD dwComponentID, BOOL fTrack) PURE;

// Notify component manager that component identified by dwComponentID
// (cookie returned from FRegisterComponent) is entering the state
// identified by uStateID (cmgrstateXXX value).  (For convenience when
// dealing with sub CompMgrs, the host can call this method passing 0 for
// dwComponentID.)
// Component manager should notify all other interested components within
// the state context indicated by uContext (a cmgrcontextXXX value),
// excluding those within the state context of a CompMgr in rgpicmExclude,
// via IOleComponent::OnEnterState (see "Comments on State Contexts",
// above).
// Component Manager should also take appropriate action depending on the
// value of uStateID (see cmgrstate comments, above).
// dwReserved is reserved for future use and should be zero.
        //
// rgpicmExclude (can be NULL) is an array of cpicmExclude CompMgrs (can
// include root CompMgr and/or sub CompMgrs); components within the state
// context of a CompMgr appearing in thisarray should NOT be notified of
// the state change (note: if uContextis cmgrcontextMine, the only
// CompMgrs in rgpicmExclude that are checked for exclusion are those that
// are sub CompMgrs of this Component Manager, since all other CompMgrs
// are outside of this Component Manager's state context anyway.)
        //
// Note: Calls to this method are symmetric with calls to
// FOnComponentExitState.
// That is, if n OnComponentEnterState calls are made, the component is
// considered to be in the state until n FOnComponentExitState calls are
// made.  Before revoking its registration a component must make a
// sufficient number of FOnComponentExitState calls to offset any
// outstanding OnComponentEnterState calls it has made.
        //
// Note: inplace objects should not call this method with
// uStateID == cmgrstateModal when entering modal state. Such objects
// should call IOleInPlaceFrame::EnableModeless instead.
STDMETHOD_(void, OnComponentEnterState) (THIS_
DWORD dwComponentID,
ULONG uStateID,
ULONG uContext,
ULONG cpicmExclude,
IOleComponentManager **rgpicmExclude,
DWORD dwReserved) PURE;

// Notify component manager that component identified by dwComponentID
// (cookie returned from FRegisterComponent) is exiting the state
// identified by uStateID (a cmgrstateXXX value).  (For convenience when
// dealing with sub CompMgrs, the host can call this method passing 0 for
// dwComponentID.)
// uContext, cpicmExclude, and rgpicmExclude are as they are in
// OnComponentEnterState.
// Component managershould notify all appropriate interested components
// (taking into account uContext, cpicmExclude, rgpicmExclude) via
// IOleComponent::OnEnterState (see "Comments on State Contexts", above).
// Component Manager should also take appropriate action depending on
// the value of uStateID (see cmgrstate comments, above).
// Return TRUE if, at the end of this call, the state is still in effect
// at the root of this component manager's state context
// (because the host or some other component is still in the state),
// otherwise return FALSE (ie. return what FInState would return).
// Caller can normally ignore the return value.
//
// Note: n calls to this method are symmetric with n calls to
// OnComponentEnterState (see OnComponentEnterState comments, above).
STDMETHOD_(BOOL, FOnComponentExitState) (THIS_
DWORD dwComponentID,
ULONG uStateID,
ULONG uContext,
ULONG cpicmExclude,
IOleComponentManager **rgpicmExclude) PURE;

// Return TRUE if the state identified by uStateID (a cmgrstateXXX value)
// is in effect at the root of this component manager's state context,
// FALSE otherwise (see "Comments on State Contexts", above).
// pvoid is reserved for future use and should be NULL.
STDMETHOD_(BOOL, FInState) (THIS_ ULONG uStateID, void *pvoid) PURE;

// Called periodically by a component during IOleComponent::FDoIdle.
// Return TRUE if component can continue its idle time processing,
// FALSE if not (in which case component returns from FDoIdle.)
STDMETHOD_(BOOL, FContinueIdle) (THIS) PURE;

// Component identified by dwComponentID (cookie returned from
// FRegisterComponent) wishes to push a message loop for reason uReason.
// uReason is one the values from the cmgrloop enumeration (above).
// pvLoopData is data private to the component.
// The host should push its message loop,
// calling IOleComponent::FContinueMessageLoop(uReason, pvLoopData)
// once per loop iteration.  When IOleComponent::FContinueMessageLoop
// returns FALSE, the host should terminate the loop.
STDMETHOD_(BOOL, FPushMessageLoop) (THIS_
DWORD dwComponentID, ULONG uReason, void *pvLoopData) PURE;

// Cause the component manager to create a "sub" component manager, which
// will be one of its children in the hierarchical tree of component
// managers used to maintiain state contexts (see "Comments on State
// Contexts", above).
// piunkOuter is the controlling unknown (can be NULL), riid is the
// desired IID, and *ppvObj returns the created sub component manager.
// piunkServProv (can be NULL) is a ptr to an object supporting
// IServiceProvider interface to which the created sub component manager
// will delegate its IOleComponentManager::QueryService calls.
// (see objext.h or docobj.h for definition of IServiceProvider).
// Returns TRUE if successful.
STDMETHOD_(BOOL, FCreateSubComponentManager) (THIS_
IUnknown *piunkOuter,
IUnknown *piunkServProv,
REFIID riid,
void **ppvObj) PURE;

// Return in *ppicm an AddRef'ed ptr to this component manager's parent
// in the hierarchical tree of component managers used to maintain state
// contexts (see "Comments on State Contexts", above).
// Returns TRUE if the parent is returned, FALSE if no parent exists or
// some error occurred.
STDMETHOD_(BOOL, FGetParentComponentManager) (THIS_
IOleComponentManager **ppicm) PURE;

// Return in *ppic an AddRef'ed ptr to the current active component.
// Returns TRUE if the active component is returned, FALSE if no component
// is active or some error occurred.
// dwReserved is reserved for future use and should be zero.
STDMETHOD_(BOOL, FGetActiveComponent) (THIS_
DWORD dwReserved, IOleComponent **ppic) PURE;
};

//--- EOF -------------------------------------------------------------------


MULTINFO.H

/* this ALWAYS GENERATED file contains the definitions for the interfaces */

/* File created by MIDL compiler version 2.00.0102 */
/* at Fri Mar 29 16:59:57 1996
 */
//@@MIDL_FILE_HEADING(  )
#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __multinfo_h__
#define __multinfo_h__

#ifdef __cplusplus
extern "C"{
#endif

/* Forward Declarations */

#ifndef __IProvideClassInfo_FWD_DEFINED__
#define __IProvideClassInfo_FWD_DEFINED__
typedef interface IProvideClassInfo IProvideClassInfo;
#endif /* __IProvideClassInfo_FWD_DEFINED__ */

#ifndef __IProvideClassInfo2_FWD_DEFINED__
#define __IProvideClassInfo2_FWD_DEFINED__
typedef interface IProvideClassInfo2 IProvideClassInfo2;
#endif /* __IProvideClassInfo2_FWD_DEFINED__ */

#ifndef __IProvideMultipleClassInfo_FWD_DEFINED__
#define __IProvideMultipleClassInfo_FWD_DEFINED__
typedef interface IProvideMultipleClassInfo IProvideMultipleClassInfo;
#endif /* __IProvideMultipleClassInfo_FWD_DEFINED__ */

/* header files for imported files */
#include "oaidl.h"

void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void __RPC_FAR * );

/****************************************
 * Generated header for interface: __MIDL__intf_0000
 * at Fri Mar 29 16:59:57 1996
 * using MIDL 2.00.0102
 ****************************************/
/* [local] */

#ifndef _OLECTL_H_
#include <olectl.h>
#endif

// {A7ABA9C1-8983-11cf-8F20-00805F2CD064}
DEFINE_GUID(IID_IProvideMultipleClassInfo,
0xa7aba9c1, 0x8983, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);

extern RPC_IF_HANDLE __MIDL__intf_0053_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL__intf_0053_v0_0_s_ifspec;

#ifndef __IProvideMultipleClassInfo_INTERFACE_DEFINED__
#define __IProvideMultipleClassInfo_INTERFACE_DEFINED__

/****************************************
 * Generated header for interface: IProvideMultipleClassInfo
 * at Fri Mar 29 16:59:57 1996
 * using MIDL 2.00.0102
 ****************************************/
/* [unique][uuid][object] */

#define MULTICLASSINFO_GETTYPEINFO           0x00000001
#define MULTICLASSINFO_GETNUMRESERVEDDISPIDS 0x00000002
#define MULTICLASSINFO_GETIIDPRIMARY         0x00000004
#define MULTICLASSINFO_GETIIDSOURCE          0x00000008
#define TIFLAGS_EXTENDDISPATCHONLY           0x00000001

EXTERN_C const IID IID_IProvideMultipleClassInfo;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface IProvideMultipleClassInfo : public IProvideClassInfo2
    {
    public:
        virtual HRESULT __stdcall GetMultiTypeInfoCount(
            /* [out] */ ULONG __RPC_FAR *pcti) = 0;

        virtual HRESULT __stdcall GetInfoOfIndex(
            /* [in] */ ULONG iti,
            /* [in] */ DWORD dwFlags,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *pptiCoClass,
            /* [out] */ DWORD __RPC_FAR *pdwTIFlags,
            /* [out] */ ULONG __RPC_FAR *pcdispidReserved,
            /* [out] */ IID __RPC_FAR *piidPrimary,
            /* [out] */ IID __RPC_FAR *piidSource) = 0;

    };

#else /* C style interface */

    typedef struct IProvideMultipleClassInfoVtbl
    {

        HRESULT ( __stdcall __RPC_FAR *QueryInterface )(
            IProvideMultipleClassInfo __RPC_FAR * This,
            /* [in] */ REFIID riid,
            /* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);

        ULONG ( __stdcall __RPC_FAR *AddRef )(
            IProvideMultipleClassInfo __RPC_FAR * This);

        ULONG ( __stdcall __RPC_FAR *Release )(
            IProvideMultipleClassInfo __RPC_FAR * This);

        HRESULT ( __stdcall __RPC_FAR *GetClassInfo )(
            IProvideMultipleClassInfo __RPC_FAR * This,
            /* [out] */ LPTYPEINFO __RPC_FAR *ppTI);

        HRESULT ( __stdcall __RPC_FAR *GetGUID )(
            IProvideMultipleClassInfo __RPC_FAR * This,
            /* [in] */ DWORD dwGuidKind,
            /* [out] */ GUID __RPC_FAR *pGUID);

        HRESULT ( __stdcall __RPC_FAR *GetMultiTypeInfoCount )(
            IProvideMultipleClassInfo __RPC_FAR * This,
            /* [out] */ ULONG __RPC_FAR *pcti);

        HRESULT ( __stdcall __RPC_FAR *GetInfoOfIndex )(
            IProvideMultipleClassInfo __RPC_FAR * This,
            /* [in] */ ULONG iti,
            /* [in] */ DWORD dwFlags,
            /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *pptiCoClass,
            /* [out] */ DWORD __RPC_FAR *pdwTIFlags,
            /* [out] */ ULONG __RPC_FAR *pcdispidReserved,
            /* [out] */ IID __RPC_FAR *piidPrimary,
            /* [out] */ IID __RPC_FAR *piidSource);

    } IProvideMultipleClassInfoVtbl;

    interface IProvideMultipleClassInfo
    {
        CONST_VTBL struct IProvideMultipleClassInfoVtbl __RPC_FAR *lpVtbl;
    };

#ifdef COBJMACROS

#define IProvideMultipleClassInfo_QueryInterface(This,riid,ppvObject)\
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IProvideMultipleClassInfo_AddRef(This)\
    (This)->lpVtbl -> AddRef(This)

#define IProvideMultipleClassInfo_Release(This)\
    (This)->lpVtbl -> Release(This)

#define IProvideMultipleClassInfo_GetClassInfo(This,ppTI)\
    (This)->lpVtbl -> GetClassInfo(This,ppTI)

#define IProvideMultipleClassInfo_GetGUID(This,dwGuidKind,pGUID)\
    (This)->lpVtbl -> GetGUID(This,dwGuidKind,pGUID)

#define IProvideMultipleClassInfo_GetMultiTypeInfoCount(This,pcti)\
    (This)->lpVtbl -> GetMultiTypeInfoCount(This,pcti)

#define IProvideMultipleClassInfo_GetInfoOfIndex(This,iti,dwFlags,pptiCoClass,pdwTIFlags,pcdispidReserved,piidPrimary,piidSource)\
    (This)->lpVtbl -> GetInfoOfIndex(This,iti,dwFlags,pptiCoClass,pdwTIFlags,pcdispidReserved,piidPrimary,piidSource)

#endif /* COBJMACROS */

#endif /* C style interface */

HRESULT __stdcall IProvideMultipleClassInfo_GetMultiTypeInfoCount_Proxy(
    IProvideMultipleClassInfo __RPC_FAR * This,
    /* [out] */ ULONG __RPC_FAR *pcti);

void __RPC_STUB IProvideMultipleClassInfo_GetMultiTypeInfoCount_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

HRESULT __stdcall IProvideMultipleClassInfo_GetInfoOfIndex_Proxy(
    IProvideMultipleClassInfo __RPC_FAR * This,
    /* [in] */ ULONG iti,
    /* [in] */ DWORD dwFlags,
    /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *pptiCoClass,
    /* [out] */ DWORD __RPC_FAR *pdwTIFlags,
    /* [out] */ ULONG __RPC_FAR *pcdispidReserved,
    /* [out] */ IID __RPC_FAR *piidPrimary,
    /* [out] */ IID __RPC_FAR *piidSource);

void __RPC_STUB IProvideMultipleClassInfo_GetInfoOfIndex_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);

#endif /* __IProvideMultipleClassInfo_INTERFACE_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif


VBSGUIDS.H

//=--------------------------------------------------------------------------=
// VBSGuids.H
//=--------------------------------------------------------------------------=
// Copyright  1997  Microsoft Corporation.  All Rights Reserved.
//
// 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.
//=--------------------------------------------------------------------------=
//
// guids that we define locally
//
#ifndef _VBSGUIDS_H_
#define _VBSGUIDS_H_

// The GUID used to identify the coclass of the VB Script engine
// {B54F3741-5B07-11cf-A4B0-00AA004A55E8}
DEFINE_GUID(CLSID_VBScript,
0xb54f3741, 0x5b07, 0x11cf, 0xa4, 0xb0, 0x0, 0xaa, 0x0, 0x4a, 0x55, 0xe8);
#define szCLSID_VBScript "{B54F3741-5B07-11cf-A4B0-00AA004A55E8}"
#endif // _VBSGUIDS_H_

//--- EOF -------------------------------------------------------------------


README.TXT

********************************************************************
*                                                                  *
*    Writing Applications with the Microsoft Web Publishing SDK    *
*              Copyright (C) 1997 Microsoft Corporation            *
*                                                                  *
********************************************************************

If you are authoring an application which will use the Web Publishing
API or SPI, then go to the BIN directory of the Internet Client SDK and run
WPSDK.EXE to install the necessary DLLs on your system. Then consult
the API-usage sample in SAMPLES\WEBPOST\WBPOST.

If you are authoring a Web Publishing Service Provider for use with
the Web Publishing API, you should install the complete Web Publishing Wizard
so that you can test your provider (this SDK only contains the DLLs required
by authors writing to the Web Publishing API). The Wizard can be downloaded from
http://www.microsoft.com/windows/software/webpost.

This SDK includes the source code for a stubbed Service Provider
(in SAMPLES\WEBPOST\STUBWPP). This code is ready to be compiled, and, when
registered with REGSVR32, will install an "empty" provider on your system
called "Web Publishing Service Provider Stub". All you need to do to implement
your own provider is fill in the implementations of the Service Provider
methods. More details are given in SAMPLES\WEBPOST\STUBWPP\README.TXT.


README.TXT

***************************************************************
*                                                             *
*    Authoring a Web Publishing Service Provider using the    *
*        Microsoft Web Publishing Service Provider Stub       *
*           Copyright (C) 1997 Microsoft Corporation          *
*                                                             *
***************************************************************

0.First verify that your development environment can compile the stubbed
provider. You should be able to successfully compile the stubbed provider
DLL (STUBWPP.DLL) with the command "make stubwpp" ("nmake stubwpp" in the
case of Microsoft's Developer's Studio) in the SAMPLES\WEBPOST\STUBWPP
directory. If this does not succeed verify that the proper environment
variables have been set for your development environment to work properly
(Dev Studio users should try running \DevStudio\VC\bin\VCVARS32.BAT).

Once you have verified that your compiler can compile the stubbed provider
as-is, you can begin authoring.

1.Select a class name, DLL name, and friendly name for your Service
Provider. If you wish to be consistent with extant Service Providers,
use the following model:

Class Name:C...Wpp
DLL Name:...WPP.DLL
Friendly Name:"... Provider"

where the elipses are replaced with the name of your protocol.

For example, the FTP provider is named as follows:

Class Name:CFtpWpp
DLL Name:FTPWPP.DLL
Friendly Name:"FTP Provider"

2.Everywhere in the source code files, replace "CWPProvider" with
the class name you have selected.

3.In WPPROV.IDL, replace the TypeLibID and CLSID with new UUIDs.
(Note that most of the changes described in this list are
 indicated in the source code itself with a "TO DO" comment.)

4.In WPPROV.RC, replace the IDS_WPPROVIDER_DESC string with the
Friendly name you have selected.

5.In UTIL.H and the compiler makefile, change the name of the
generated executable from STUBWPP.DLL to the DLL name you have
selected.

6.To double-check that all the replacements have been done correctly,
compile the DLL, run REGSVR32 on it (it should succeed), and then
run the Wizard. Click "New" on the destination site page, enter a
Web server Friendly name, and then click "Advanced". You should
see your provider's Friendly name in the Provider listbox.
Furthermore you should be able to step through the rest of the
Wizard successfully. Nothing will actually be posted, but this
will verify that all the renaming has gone correctly.

7.Finally write the primary implementation code. The following order
is suggested:

A.Fill in PAGES.H, RESOURCE.H, and WPPROV.RC with the dialog
templates and #defines for the property sheet pages your
provider will add to the Web Publishing Wizard. Two example
pages are already present.

B.Write the Dialog procedures for each property sheet page in
PAGES.CPP. Again, a sample procedure is already available for
each of the two example pages.

C.Write the implementation of the SPI and SPI helper functions
in WPPOBJ.CPP and WPSPIHLP.CPP, respectively. Note that in many
cases WPSPIHLP.CPP may not require any modification. If your
provider does not *need* to do site management itself, it is
strongly recommended that the helper functions in WPSPIHLP.CPP be
left unmodified, which will allow the Web Publishing API to do all
site management for you.

8.Test using the wizard.


ATLCOM.H

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLCOM_H__
#define __ATLCOM_H__

#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLBASE_H__
#error atlcom.h requires atlbase.h to be included first
#endif

#pragma pack(push, _ATL_PACKING)

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

#define CComConnectionPointContainerImpl IConnectionPointContainerImpl
#define CComISupportErrorInfoImpl ISupportErrorInfoImpl
#define CComProvideClassInfo2Impl IProvideClassInfoImpl
#define CComDualImpl IDispatchImpl

#ifdef _ATL_DEBUG_QI
HRESULT WINAPI AtlDumpIID(REFIID iid, LPCTSTR pszClassName, HRESULT hr);
#define _ATLDUMPIID(iid, name, hr) AtlDumpIID(iid, name, hr)
#else
#define _ATLDUMPIID(iid, name, hr) hr
#endif

#ifdef _ATL_DEBUG_REFCOUNT
//////////////////////////////////////////////////////////////////////////////
// CComDebugRefCount for interface level ref counting
class CComDebugRefCount
{
public:
CComDebugRefCount()
{
m_nRef = 0;
}
~CComDebugRefCount()
{
_ASSERTE(m_nRef == 0);
}
long m_nRef;
};
#define _ATL_DEBUG_ADDREF_RELEASE_IMPL(className) \
public:\
CComDebugRefCount _ref;\
virtual ULONG STDMETHODCALLTYPE _DebugAddRef(void) \
{return ((T*)this)->DebugAddRef(_ref.m_nRef, _T(#className));} \
virtual ULONG STDMETHODCALLTYPE _DebugRelease(void) \
{return ((T*)this)->DebugRelease(_ref.m_nRef, _T(#className));}
#else
#define _ATL_DEBUG_ADDREF_RELEASE_IMPL(className)\
virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;\
virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
#endif // _ATL_DEBUG_REFCOUNT

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc,
const IID& iid = GUID_NULL, HRESULT hRes = 0);

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc,
DWORD dwHelpID, LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL,
HRESULT hRes = 0);

HRESULT WINAPI AtlReportError(const CLSID& clsid, UINT nID,
const IID& iid = GUID_NULL, HRESULT hRes = 0,
HINSTANCE hInst = _Module.GetResourceInstance());

HRESULT WINAPI AtlReportError(const CLSID& clsid, UINT nID,
DWORD dwHelpID, LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL,
HRESULT hRes = 0, HINSTANCE hInst = _Module.GetResourceInstance());

#ifndef OLE2ANSI
HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCSTR lpszDesc,
const IID& iid = GUID_NULL, HRESULT hRes = 0);

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCSTR lpszDesc,
DWORD dwHelpID, LPCSTR lpszHelpFile, const IID& iid = GUID_NULL,
HRESULT hRes = 0);
#endif

#ifndef _ATL_NO_SECURITY

/////////////////////////////////////////////////////////////////////////////
// CSecurityDescriptor

class CSecurityDescriptor
{
public:
CSecurityDescriptor();
~CSecurityDescriptor();

public:
HRESULT Attach(PSECURITY_DESCRIPTOR pSelfRelativeSD);
HRESULT AttachObject(HANDLE hObject);
HRESULT Initialize();
HRESULT InitializeFromProcessToken(BOOL bDefaulted = FALSE);
HRESULT InitializeFromThreadToken(BOOL bDefaulted = FALSE, BOOL bRevertToProcessToken = TRUE);
HRESULT SetOwner(PSID pOwnerSid, BOOL bDefaulted = FALSE);
HRESULT SetGroup(PSID pGroupSid, BOOL bDefaulted = FALSE);
HRESULT Allow(LPCTSTR pszPrincipal, DWORD dwAccessMask);
HRESULT Deny(LPCTSTR pszPrincipal, DWORD dwAccessMask);
HRESULT Revoke(LPCTSTR pszPrincipal);

// utility functions
// Any PSID you get from these functions should be free()ed
static HRESULT SetPrivilege(LPCTSTR Privilege, BOOL bEnable = TRUE, HANDLE hToken = NULL);
static HRESULT GetTokenSids(HANDLE hToken, PSID* ppUserSid, PSID* ppGroupSid);
static HRESULT GetProcessSids(PSID* ppUserSid, PSID* ppGroupSid = NULL);
static HRESULT GetThreadSids(PSID* ppUserSid, PSID* ppGroupSid = NULL, BOOL bOpenAsSelf = FALSE);
static HRESULT CopyACL(PACL pDest, PACL pSrc);
static HRESULT GetCurrentUserSID(PSID *ppSid);
static HRESULT GetPrincipalSID(LPCTSTR pszPrincipal, PSID *ppSid);
static HRESULT AddAccessAllowedACEToACL(PACL *Acl, LPCTSTR pszPrincipal, DWORD dwAccessMask);
static HRESULT AddAccessDeniedACEToACL(PACL *Acl, LPCTSTR pszPrincipal, DWORD dwAccessMask);
static HRESULT RemovePrincipalFromACL(PACL Acl, LPCTSTR pszPrincipal);

operator PSECURITY_DESCRIPTOR()
{
return m_pSD;
}

public:
PSECURITY_DESCRIPTOR m_pSD;
PSID m_pOwner;
PSID m_pGroup;
PACL m_pDACL;
PACL m_pSACL;
};

#endif // _ATL_NO_SECURITY

/////////////////////////////////////////////////////////////////////////////
// COM Objects

#define DECLARE_PROTECT_FINAL_CONSTRUCT()\
void InternalFinalConstructAddRef() {InternalAddRef();}\
void InternalFinalConstructRelease() {InternalRelease();}

template <class T1>
class CComCreator
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
{
_ASSERTE(*ppv == NULL);
HRESULT hRes = E_OUTOFMEMORY;
T1* p = NULL;
ATLTRY(p = new T1(pv))
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes == S_OK)
hRes = p->QueryInterface(riid, ppv);
if (hRes != S_OK)
delete p;
}
return hRes;
}
};

template <class T1>
class CComInternalCreator
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
{
_ASSERTE(*ppv == NULL);
HRESULT hRes = E_OUTOFMEMORY;
T1* p = NULL;
ATLTRY(p = new T1(pv))
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes == S_OK)
hRes = p->_InternalQueryInterface(riid, ppv);
if (hRes != S_OK)
delete p;
}
return hRes;
}
};

template <HRESULT hr>
class CComFailCreator
{
public:
static HRESULT WINAPI CreateInstance(void*, REFIID, LPVOID*)
{
return hr;
}
};

template <class T1, class T2>
class CComCreator2
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
{
_ASSERTE(*ppv == NULL);
HRESULT hRes = E_OUTOFMEMORY;
if (pv == NULL)
hRes = T1::CreateInstance(NULL, riid, ppv);
else
hRes = T2::CreateInstance(pv, riid, ppv);
return hRes;
}
};

#define DECLARE_NOT_AGGREGATABLE(x) public:\
typedef CComCreator2< CComCreator< CComObject< x > >, CComFailCreator<CLASS_E_NOAGGREGATION> > _CreatorClass;
#define DECLARE_AGGREGATABLE(x) public:\
typedef CComCreator2< CComCreator< CComObject< x > >, CComCreator< CComAggObject< x > > > _CreatorClass;
#define DECLARE_ONLY_AGGREGATABLE(x) public:\
typedef CComCreator2< CComFailCreator<E_FAIL>, CComCreator< CComAggObject< x > > > _CreatorClass;
#define DECLARE_POLY_AGGREGATABLE(x) public:\
typedef CComCreator< CComPolyObject< x > > _CreatorClass;

struct _ATL_CREATORDATA
{
_ATL_CREATORFUNC* pFunc;
};

template <class Creator>
class _CComCreatorData
{
public:
static _ATL_CREATORDATA data;
};

template <class Creator>
_ATL_CREATORDATA _CComCreatorData<Creator>::data = {Creator::CreateInstance};

struct _ATL_CACHEDATA
{
DWORD dwOffsetVar;
_ATL_CREATORFUNC* pFunc;
};

template <class Creator, DWORD dwVar>
class _CComCacheData
{
public:
static _ATL_CACHEDATA data;
};

template <class Creator, DWORD dwVar>
_ATL_CACHEDATA _CComCacheData<Creator, dwVar>::data = {dwVar, Creator::CreateInstance};

struct _ATL_CHAINDATA
{
DWORD dwOffset;
const _ATL_INTMAP_ENTRY* (WINAPI *pFunc)();
};

template <class base, class derived>
class _CComChainData
{
public:
static _ATL_CHAINDATA data;
};

template <class base, class derived>
_ATL_CHAINDATA _CComChainData<base, derived>::data =
{offsetofclass(base, derived), base::_GetEntries};

template <class T, const CLSID* pclsid>
class CComAggregateCreator
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID/*riid*/, LPVOID* ppv)
{
_ASSERTE(*ppv == NULL);
_ASSERTE(pv != NULL);
T* p = (T*) pv;
// Add the following line to your object if you get a message about
// GetControllingUnknown() being undefined
// DECLARE_GET_CONTROLLING_UNKNOWN()
return CoCreateInstance(*pclsid, p->GetControllingUnknown(), CLSCTX_INPROC, IID_IUnknown, ppv);
}
};

#ifdef _ATL_DEBUG_QI
#define DEBUG_QI_ENTRY(x) \
{NULL, \
(DWORD)_T(#x), \
(_ATL_CREATORARGFUNC*)0},
#else
#define DEBUG_QI_ENTRY(x)
#endif //_ATL_DEBUG_QI

//If you get a message that FinalConstruct is ambiguous then you need to
// override it in your class and call each base class' version of this
#define BEGIN_COM_MAP(x) public: \
typedef x _ComMapClass; \
static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject, DWORD dw)\
{\
_ComMapClass* p = (_ComMapClass*)pv;\
p->Lock();\
HRESULT hRes = CComObjectRootBase::_Cache(pv, iid, ppvObject, dw);\
p->Unlock();\
return hRes;\
}\
IUnknown* GetUnknown() \
{ _ASSERTE(_GetEntries()[0].pFunc == _ATL_SIMPLEMAPENTRY); return (IUnknown*)((int)this+_GetEntries()->dw); } \
HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject) \
{ return InternalQueryInterface(this, _GetEntries(), iid, ppvObject); } \
const static _ATL_INTMAP_ENTRY* WINAPI _GetEntries() { \
static const _ATL_INTMAP_ENTRY _entries[] = { DEBUG_QI_ENTRY(x)

#define DECLARE_GET_CONTROLLING_UNKNOWN() public:\
virtual IUnknown* GetControllingUnknown() {return GetUnknown();}

#define COM_INTERFACE_ENTRY_BREAK(x)\
{&IID_##x, \
NULL, \
_Break},

#define COM_INTERFACE_ENTRY_NOINTERFACE(x)\
{&IID_##x, \
NULL, \
_NoInterface},

#define COM_INTERFACE_ENTRY(x)\
{&IID_##x, \
offsetofclass(x, _ComMapClass), \
_ATL_SIMPLEMAPENTRY},

#define COM_INTERFACE_ENTRY_IID(iid, x)\
{&iid,\
offsetofclass(x, _ComMapClass),\
_ATL_SIMPLEMAPENTRY},

#define COM_INTERFACE_ENTRY_IMPL(x)\
COM_INTERFACE_ENTRY_IID(IID_##x, x##Impl<_ComMapClass>)

#define COM_INTERFACE_ENTRY_IMPL_IID(iid, x)\
COM_INTERFACE_ENTRY_IID(iid, x##Impl<_ComMapClass>)

#define COM_INTERFACE_ENTRY2(x, x2)\
{&IID_##x,\
(DWORD)((x*)(x2*)((_ComMapClass*)8))-8,\
_ATL_SIMPLEMAPENTRY},

#define COM_INTERFACE_ENTRY2_IID(iid, x, x2)\
{&iid,\
(DWORD)((x*)(x2*)((_ComMapClass*)8))-8,\
_ATL_SIMPLEMAPENTRY},

#define COM_INTERFACE_ENTRY_FUNC(iid, dw, func)\
{&iid, \
dw, \
func},

#define COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func)\
{NULL, \
dw, \
func},

#define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x)\
{&iid,\
(DWORD)&_CComCreatorData<\
CComInternalCreator< CComTearOffObject< x > >\
>::data,\
_Creator},

#define COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(iid, x, punk)\
{&iid,\
(DWORD)&_CComCacheData<\
CComCreator< CComCachedTearOffObject< x > >,\
(DWORD)offsetof(_ComMapClass, punk)\
>::data,\
_Cache},

#define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)\
{&iid,\
(DWORD)offsetof(_ComMapClass, punk),\
_Delegate},

#define COM_INTERFACE_ENTRY_AGGREGATE_BLIND(punk)\
{NULL,\
(DWORD)offsetof(_ComMapClass, punk),\
_Delegate},

#define COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid)\
{&iid,\
(DWORD)&_CComCacheData<\
CComAggregateCreator<_ComMapClass, &clsid>,\
(DWORD)offsetof(_ComMapClass, punk)\
>::data,\
_Cache},

#define COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(punk, clsid)\
{NULL,\
(DWORD)&_CComCacheData<\
CComAggregateCreator<_ComMapClass, &clsid>,\
(DWORD)offsetof(_ComMapClass, punk)\
>::data,\
_Cache},

#define COM_INTERFACE_ENTRY_CHAIN(classname)\
{NULL,\
(DWORD)&_CComChainData<classname, _ComMapClass>::data,\
_Chain},

#ifdef _ATL_DEBUG_QI
#define END_COM_MAP()   {NULL, 0, 0}};\
return &_entries[1];}
#else
#define END_COM_MAP()   {NULL, 0, 0}};\
return _entries;}
#endif // _ATL_DEBUG_QI

#define BEGIN_OBJECT_MAP(x) static _ATL_OBJMAP_ENTRY x[] = {
#define END_OBJECT_MAP()   {NULL, NULL, NULL, NULL}};
#define OBJECT_ENTRY(clsid, class) {&clsid, &class::UpdateRegistry, &class::_ClassFactoryCreatorClass::CreateInstance, &class::_CreatorClass::CreateInstance, NULL, 0, &class::GetObjectDescription },

#ifdef _ATL_DEBUG_QI
extern HRESULT WINAPI AtlDumpIID(REFIID iid, LPCTSTR pszClassName, HRESULT hr);
#endif // _ATL_DEBUG_QI

// the functions in this class don't need to be virtual because
// they are called from CComObject
class CComObjectRootBase
{
public:
CComObjectRootBase()
{
m_dwRef = 0L;
}
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease() {}

static HRESULT WINAPI InternalQueryInterface(void* pThis,
const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
{
_ASSERTE(pThis != NULL);
// First entry in the com map should be a simple map entry
_ASSERTE(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
#ifdef _ATL_DEBUG_QI
LPCTSTR pszClassName = (LPCTSTR) pEntries[-1].dw;
#endif // _ATL_DEBUG_QI
HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);
return _ATLDUMPIID(iid, pszClassName, hRes);
}

//Outer funcs
ULONG OuterAddRef()
{
return m_pOuterUnknown->AddRef();
}
ULONG OuterRelease()
{
return m_pOuterUnknown->Release();
}
HRESULT OuterQueryInterface(REFIID iid, void ** ppvObject)
{
return m_pOuterUnknown->QueryInterface(iid, ppvObject);
}

void SetVoid(void*) {}
void InternalFinalConstructAddRef() {}
void InternalFinalConstructRelease()
{
_ASSERTE(m_dwRef == 0);
}
// If this assert occurs, your object has probably been deleted
// Try using DECLARE_PROTECT_FINAL_CONSTRUCT()

static HRESULT WINAPI _Break(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _NoInterface(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _Creator(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _Delegate(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _Chain(void* pv, REFIID iid, void** ppvObject, DWORD dw);
static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject, DWORD dw);

union
{
long m_dwRef;
IUnknown* m_pOuterUnknown;
};
};

template <class ThreadModel>
class CComObjectRootEx : public CComObjectRootBase
{
public:
typedef ThreadModel _ThreadModel;
typedef _ThreadModel::AutoCriticalSection _CritSec;

ULONG InternalAddRef()
{
_ASSERTE(m_dwRef != -1L);
return _ThreadModel::Increment(&m_dwRef);
}
ULONG InternalRelease()
{
return _ThreadModel::Decrement(&m_dwRef);
}

#ifdef _ATL_DEBUG_REFCOUNT
ULONG DebugAddRef(long& dw, LPCTSTR lpszClassName)
{
_ThreadModel::Increment(&dw);
ATLTRACE(_T("%s %d>\n"), lpszClassName, dw);
return AddRef();
}
ULONG DebugRelease(long& dw, LPCTSTR lpszClassName)
{
_ThreadModel::Decrement(&dw);
ATLTRACE(_T("%s %d<\n"), lpszClassName, dw);
return Release();
}
virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
#endif // _ATL_DEBUG_REFCOUNT

void Lock() {m_critsec.Lock();}
void Unlock() {m_critsec.Unlock();}
private:
_CritSec m_critsec;
};

#if _MSC_VER>1020
template <>
#endif
class CComObjectRootEx<CComSingleThreadModel> : public CComObjectRootBase
{
public:
typedef CComSingleThreadModel _ThreadModel;
typedef _ThreadModel::AutoCriticalSection _CritSec;

ULONG InternalAddRef()
{
_ASSERTE(m_dwRef != -1L);
return _ThreadModel::Increment(&m_dwRef);
}
ULONG InternalRelease()
{
return _ThreadModel::Decrement(&m_dwRef);
}

#ifdef _ATL_DEBUG_REFCOUNT
ULONG DebugAddRef(long& dw, LPCTSTR lpszClassName)
{
_ThreadModel::Increment(&dw);
ATLTRACE(_T("%s %d>\n"), lpszClassName, dw);
return AddRef();
}
ULONG DebugRelease(long& dw, LPCTSTR lpszClassName)
{
_ThreadModel::Decrement(&dw);
ATLTRACE(_T("%s %d<\n"), lpszClassName, dw);
return Release();
}
virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
#endif // _ATL_DEBUG_REFCOUNT

void Lock() {}
void Unlock() {}
};

typedef CComObjectRootEx<CComObjectThreadModel> CComObjectRoot;

#if defined(_WINDLL) | defined(_USRDLL)
#define DECLARE_CLASSFACTORY_EX(cf) typedef CComCreator< CComObjectCached< cf > > _ClassFactoryCreatorClass;
#else
// don't let class factory refcount influence lock count
#define DECLARE_CLASSFACTORY_EX(cf) typedef CComCreator< CComObjectNoLock< cf > > _ClassFactoryCreatorClass;
#endif
#define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(CComClassFactory)
#define DECLARE_CLASSFACTORY2(lic) DECLARE_CLASSFACTORY_EX(CComClassFactory2<lic>)
#define DECLARE_CLASSFACTORY_AUTO_THREAD() DECLARE_CLASSFACTORY_EX(CComClassFactoryAutoThread)
#define DECLARE_CLASSFACTORY_SINGLETON(obj) DECLARE_CLASSFACTORY_EX(CComClassFactorySingleton<obj>)

#define DECLARE_OBJECT_DESCRIPTION(x)\
static LPCTSTR WINAPI GetObjectDescription()\
{\
return _T(x);\
}

#define DECLARE_NO_REGISTRY()\
static HRESULT WINAPI UpdateRegistry(BOOL /*bRegister*/)\
{return S_OK;}

#define DECLARE_REGISTRY(class, pid, vpid, nid, flags)\
static HRESULT WINAPI UpdateRegistry(BOOL bRegister)\
{\
return _Module.UpdateRegistryClass(GetObjectCLSID(), pid, vpid, nid,\
flags, bRegister);\
}

#define DECLARE_REGISTRY_RESOURCE(x)\
static HRESULT WINAPI UpdateRegistry(BOOL bRegister)\
{\
return _Module.UpdateRegistryFromResource(_T(#x), bRegister);\
}

#define DECLARE_REGISTRY_RESOURCEID(x)\
static HRESULT WINAPI UpdateRegistry(BOOL bRegister)\
{\
return _Module.UpdateRegistryFromResource(x, bRegister);\
}

//DECLARE_STATIC_* provided for backward compatibility
#ifdef _ATL_STATIC_REGISTRY
#define DECLARE_STATIC_REGISTRY_RESOURCE(x) DECLARE_REGISTRY_RESOURCE(x)
#define DECLARE_STATIC_REGISTRY_RESOURCEID(x) DECLARE_REGISTRY_RESOURCEID(x)
#endif //_ATL_STATIC_REGISTRY

template<class Base> class CComObject; // fwd decl

template <class Owner, class ThreadModel = CComObjectThreadModel>
class CComTearOffObjectBase : public CComObjectRootEx<ThreadModel>
{
public:
typedef Owner _OwnerClass;
CComObject<Owner>* m_pOwner;
CComTearOffObjectBase() {m_pOwner = NULL;}
};

//Base is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
template <class Base>
class CComObject : public Base
{
public:
typedef Base _BaseClass;
CComObject(void* = NULL)
{
_Module.Lock();
}
// Set refcount to 1 to protect destruction
~CComObject()
{
m_dwRef = 1L;
FinalRelease();
_Module.Unlock();
}
//If InternalAddRef or InteralRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
static HRESULT WINAPI CreateInstance(CComObject<Base>** pp);
};

template <class Base>
HRESULT WINAPI CComObject<Base>::CreateInstance(CComObject<Base>** pp)
{
_ASSERTE(pp != NULL);
HRESULT hRes = E_OUTOFMEMORY;
CComObject<Base>* p = NULL;
ATLTRY(p = new CComObject<Base>())
if (p != NULL)
{
p->SetVoid(NULL);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes != S_OK)
{
delete p;
p = NULL;
}
}
*pp = p;
return hRes;
}

//Base is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
// CComObjectCached is used primarily for class factories in DLL's
// but it is useful anytime you want to cache an object
template <class Base>
class CComObjectCached : public Base
{
public:
typedef Base _BaseClass;
CComObjectCached(void* = NULL){}
// Set refcount to 1 to protect destruction
~CComObjectCached(){m_dwRef = 1L; FinalRelease();}
//If InternalAddRef or InteralRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)()
{
m_csCached.Lock();
ULONG l = InternalAddRef();
if (m_dwRef == 2)
_Module.Lock();
m_csCached.Unlock();
return l;
}
STDMETHOD_(ULONG, Release)()
{
m_csCached.Lock();
InternalRelease();
ULONG l = m_dwRef;
m_csCached.Unlock();
if (l == 0)
delete this;
else if (l == 1)
_Module.Unlock();
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
CComGlobalsThreadModel::AutoCriticalSection m_csCached;
};

//Base is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
template <class Base>
class CComObjectNoLock : public Base
{
public:
typedef Base _BaseClass;
CComObjectNoLock(void* = NULL){}
// Set refcount to 1 to protect destruction
~CComObjectNoLock() {m_dwRef = 1L; FinalRelease();}

//If InternalAddRef or InteralRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
};

// It is possible for Base not to derive from CComObjectRoot
// However, you will need to provide FinalConstruct and InternalQueryInterface
template <class Base>
class CComObjectGlobal : public Base
{
public:
typedef Base _BaseClass;
CComObjectGlobal(void* = NULL){m_hResFinalConstruct = FinalConstruct();}
~CComObjectGlobal() {FinalRelease();}

STDMETHOD_(ULONG, AddRef)() {return _Module.Lock();}
STDMETHOD_(ULONG, Release)(){return _Module.Unlock();}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
HRESULT m_hResFinalConstruct;
};

// It is possible for Base not to derive from CComObjectRoot
// However, you will need to provide FinalConstruct and InternalQueryInterface
template <class Base>
class CComObjectStack : public Base
{
public:
typedef Base _BaseClass;
CComObjectStack(void* = NULL){m_hResFinalConstruct = FinalConstruct();}
~CComObjectStack() {FinalRelease();}

STDMETHOD_(ULONG, AddRef)() {_ASSERTE(FALSE);return 0;}
STDMETHOD_(ULONG, Release)(){_ASSERTE(FALSE);return 0;}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{_ASSERTE(FALSE);return E_NOINTERFACE;}
HRESULT m_hResFinalConstruct;
};

template <class Base> //Base must be derived from CComObjectRoot
class CComContainedObject : public Base
{
public:
typedef Base _BaseClass;
CComContainedObject(void* pv) {m_pOuterUnknown = (IUnknown*)pv;}

STDMETHOD_(ULONG, AddRef)() {return OuterAddRef();}
STDMETHOD_(ULONG, Release)() {return OuterRelease();}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return OuterQueryInterface(iid, ppvObject);}
//GetControllingUnknown may be virtual if the Base class has declared
//DECLARE_GET_CONTROLLING_UNKNOWN()
IUnknown* GetControllingUnknown() {return m_pOuterUnknown;}
};

//contained is the user's class that derives from CComObjectRoot and whatever
//interfaces the user wants to support on the object
template <class contained>
class CComAggObject :
public IUnknown,
public CComObjectRootEx< contained::_ThreadModel::ThreadModelNoCS >
{
public:
typedef contained _BaseClass;
CComAggObject(void* pv) : m_contained(pv)
{
_Module.Lock();
}
//If you get a message that this call is ambiguous then you need to
// override it in your class and call each base class' version of this
HRESULT FinalConstruct()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalConstruct();
return m_contained.FinalConstruct();
}
void FinalRelease()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalRelease();
m_contained.FinalRelease();
}
// Set refcount to 1 to protect destruction
~CComAggObject()
{
m_dwRef = 1L;
FinalRelease();
_Module.Unlock();
}

STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
HRESULT hRes = S_OK;
if (InlineIsEqualUnknown(iid))
{
*ppvObject = (void*)(IUnknown*)this;
AddRef();
}
else
hRes = m_contained._InternalQueryInterface(iid, ppvObject);
return hRes;
}
CComContainedObject<contained> m_contained;
};

///////////////////////////////////////////////////////////////////////////////
// CComPolyObject can be either aggregated or not aggregated

template <class contained>
class CComPolyObject :
public IUnknown,
public CComObjectRootEx< contained::_ThreadModel::ThreadModelNoCS >
{
public:
typedef contained _BaseClass;
CComPolyObject(void* pv) : m_contained(pv ? pv : this)
{
_Module.Lock();
}
//If you get a message that this call is ambiguous then you need to
// override it in your class and call each base class' version of this
HRESULT FinalConstruct()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalConstruct();
return m_contained.FinalConstruct();
}
void FinalRelease()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalRelease();
m_contained.FinalRelease();
}
// Set refcount to 1 to protect destruction
~CComPolyObject()
{
m_dwRef = 1L;
FinalRelease();
_Module.Unlock();
}

STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
HRESULT hRes = S_OK;
if (InlineIsEqualUnknown(iid))
{
*ppvObject = (void*)(IUnknown*)this;
AddRef();
}
else
hRes = m_contained._InternalQueryInterface(iid, ppvObject);
return hRes;
}
CComContainedObject<contained> m_contained;
};

template <class Base>
class CComTearOffObject : public Base
{
public:
CComTearOffObject(void* pv)
{
_ASSERTE(m_pOwner == NULL);
m_pOwner = reinterpret_cast<CComObject<Base::_OwnerClass>*>(pv);
m_pOwner->AddRef();
}
// Set refcount to 1 to protect destruction
~CComTearOffObject()
{
m_dwRef = 1L;
FinalRelease();
m_pOwner->Release();
}

STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
return m_pOwner->QueryInterface(iid, ppvObject);
}
};

template <class contained>
class CComCachedTearOffObject :
public IUnknown,
public CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>
{
public:
typedef contained _BaseClass;
CComCachedTearOffObject(void* pv) :
m_contained(((contained::_OwnerClass*)pv)->GetControllingUnknown())
{
_ASSERTE(m_contained.m_pOwner == NULL);
m_contained.m_pOwner = reinterpret_cast<CComObject<contained::_OwnerClass>*>(pv);
}
//If you get a message that this call is ambiguous then you need to
// override it in your class and call each base class' version of this
HRESULT FinalConstruct()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalConstruct();
return m_contained.FinalConstruct();
}
void FinalRelease()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalRelease();
m_contained.FinalRelease();
}
// Set refcount to 1 to protect destruction
~CComCachedTearOffObject(){m_dwRef = 1L; FinalRelease();}

STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();

if (l == 0)
delete this;
return l;
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
HRESULT hRes = S_OK;
if (InlineIsEqualUnknown(iid))
{
*ppvObject = (void*)(IUnknown*)this;
AddRef();
}
else
hRes = m_contained._InternalQueryInterface(iid, ppvObject);
return hRes;
}
CComContainedObject<contained> m_contained;
};

class CComClassFactory :
public IClassFactory,
public CComObjectRootEx<CComGlobalsThreadModel>
{
public:
BEGIN_COM_MAP(CComClassFactory)
COM_INTERFACE_ENTRY(IClassFactory)
END_COM_MAP()

// IClassFactory
STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj);
STDMETHOD(LockServer)(BOOL fLock);
// helper
void SetVoid(void* pv)
{
m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv;
}
_ATL_CREATORFUNC* m_pfnCreateInstance;
};

class CComClassFactory2Base :
public IClassFactory2,
public CComObjectRootEx<CComGlobalsThreadModel>
{
public:
BEGIN_COM_MAP(CComClassFactory2Base)
COM_INTERFACE_ENTRY(IClassFactory)
COM_INTERFACE_ENTRY(IClassFactory2)
END_COM_MAP()
// IClassFactory
STDMETHOD(LockServer)(BOOL fLock);
// helper
void SetVoid(void* pv)
{
m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv;
}
_ATL_CREATORFUNC* m_pfnCreateInstance;
};

template <class license>
class CComClassFactory2 : public CComClassFactory2Base, license
{
public:
typedef license _LicenseClass;
typedef CComClassFactory2<license> _ComMapClass;
// IClassFactory
STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter,
REFIID riid, void** ppvObj)
{
_ASSERTE(m_pfnCreateInstance != NULL);
if (ppvObj == NULL)
return E_POINTER;
*ppvObj = NULL;
if (!IsLicenseValid())
return CLASS_E_NOTLICENSED;

if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
return CLASS_E_NOAGGREGATION;
else
return m_pfnCreateInstance(pUnkOuter, riid, ppvObj);
}
// IClassFactory2
STDMETHOD(CreateInstanceLic)(IUnknown* pUnkOuter, IUnknown* pUnkReserved,
REFIID riid, BSTR bstrKey, void** ppvObject)
{
_ASSERTE(m_pfnCreateInstance != NULL);
if (ppvObject == NULL)
return E_POINTER;
*ppvObject = NULL;
if ( ((bstrKey != NULL) && !VerifyLicenseKey(bstrKey)) ||
 ((bstrKey == NULL) && !IsLicenseValid()) )
return CLASS_E_NOTLICENSED;
return m_pfnCreateInstance(pUnkOuter, riid, ppvObject);
}
STDMETHOD(RequestLicKey)(DWORD dwReserved, BSTR* pbstrKey)
{
if (pbstrKey == NULL)
return E_POINTER;
*pbstrKey = NULL;

if (!IsLicenseValid())
return CLASS_E_NOTLICENSED;
return GetLicenseKey(dwReserved,pbstrKey) ? S_OK : E_FAIL;
}
STDMETHOD(GetLicInfo)(LICINFO* pLicInfo)
{
if (pLicInfo == NULL)
return E_POINTER;
pLicInfo->cbLicInfo = sizeof(LICINFO);
pLicInfo->fLicVerified = IsLicenseValid();
BSTR bstr = NULL;
pLicInfo->fRuntimeKeyAvail = GetLicenseKey(0,&bstr);
::SysFreeString(bstr);
return S_OK;
}
};

/////////////////////////////////////////////////////////////////////////////////////////////
// Thread Pooling class factory

class CComClassFactoryAutoThread :
public IClassFactory,
public CComObjectRootEx<CComGlobalsThreadModel>
{
public:
BEGIN_COM_MAP(CComClassFactoryAutoThread)
COM_INTERFACE_ENTRY(IClassFactory)
END_COM_MAP()

// helper
void SetVoid(void* pv)
{
m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv;
}
STDMETHODIMP CComClassFactoryAutoThread::CreateInstance(LPUNKNOWN pUnkOuter,
REFIID riid, void** ppvObj)
{
_ASSERTE(m_pfnCreateInstance != NULL);
HRESULT hRes = E_POINTER;
if (ppvObj != NULL)
{
*ppvObj = NULL;
// cannot aggregate across apartments
_ASSERTE(pUnkOuter == NULL);
if (pUnkOuter != NULL)
hRes = CLASS_E_NOAGGREGATION;
else
hRes = _Module.CreateInstance(m_pfnCreateInstance, riid, ppvObj);
}
return hRes;
}
STDMETHODIMP CComClassFactoryAutoThread::LockServer(BOOL fLock)
{
if (fLock)
_Module.Lock();
else
_Module.Unlock();
return S_OK;
}
_ATL_CREATORFUNC* m_pfnCreateInstance;
};

/////////////////////////////////////////////////////////////////////////////////////////////
// Singleton Class Factory
template <class T>
class CComClassFactorySingleton : public CComClassFactory
{
public:
// IClassFactory
STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
HRESULT hRes = E_POINTER;
if (ppvObj != NULL)
{
// can't ask for anything other than IUnknown when aggregating
_ASSERTE((pUnkOuter == NULL) || InlineIsEqualUnknown(riid));
if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
hRes = CLASS_E_NOAGGREGATION;
else
hRes = m_Obj.QueryInterface(riid, ppvObj);
}
return hRes;
}
CComObjectGlobal<T> m_Obj;
};

template <class T, const CLSID* pclsid>
class CComCoClass
{
public:
DECLARE_CLASSFACTORY()
DECLARE_AGGREGATABLE(T)
typedef T _CoClass;
static const CLSID& WINAPI GetObjectCLSID() {return *pclsid;}
static LPCTSTR WINAPI GetObjectDescription() {return NULL;}
static HRESULT WINAPI Error(LPCOLESTR lpszDesc,
const IID& iid = GUID_NULL, HRESULT hRes = 0)
{
return AtlReportError(GetObjectCLSID(), lpszDesc, iid, hRes);
}
static HRESULT WINAPI Error(LPCOLESTR lpszDesc, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
{
return AtlReportError(GetObjectCLSID(), lpszDesc, dwHelpID, lpszHelpFile,
iid, hRes);
}
static HRESULT WINAPI Error(UINT nID, const IID& iid = GUID_NULL,
HRESULT hRes = 0, HINSTANCE hInst = _Module.GetResourceInstance())
{
return AtlReportError(GetObjectCLSID(), nID, iid, hRes, hInst);
}
static HRESULT WINAPI Error(UINT nID, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid = GUID_NULL,
HRESULT hRes = 0, HINSTANCE hInst = _Module.GetResourceInstance())
{
return AtlReportError(GetObjectCLSID(), nID, dwHelpID, lpszHelpFile,
iid, hRes, hInst);
}
#ifndef OLE2ANSI
static HRESULT WINAPI Error(LPCSTR lpszDesc,
const IID& iid = GUID_NULL, HRESULT hRes = 0)
{
return AtlReportError(GetObjectCLSID(), lpszDesc, iid, hRes);
}
static HRESULT WINAPI Error(LPCSTR lpszDesc, DWORD dwHelpID,
LPCSTR lpszHelpFile, const IID& iid = GUID_NULL, HRESULT hRes = 0)
{
return AtlReportError(GetObjectCLSID(), lpszDesc, dwHelpID,
lpszHelpFile, iid, hRes);
}
#endif
};

// ATL doesn't support multiple LCID's at the same time
// Whatever LCID is queried for first is the one that is used.
class CComTypeInfoHolder
{
// Should be 'protected' but can cause compiler to generate fat code.
public:
const GUID* m_pguid;
const GUID* m_plibid;
WORD m_wMajor;
WORD m_wMinor;

ITypeInfo* m_pInfo;
long m_dwRef;

public:
HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo);

void AddRef();
void Release();
HRESULT GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo);
HRESULT GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
LCID lcid, DISPID* rgdispid);
HRESULT Invoke(IDispatch* p, DISPID dispidMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
EXCEPINFO* pexcepinfo, UINT* puArgErr);
};

//////////////////////////////////////////////////////////////////////////////
// IObjectWithSite
//
template <class T>
class ATL_NO_VTABLE IObjectWithSiteImpl
{
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IObjectWithSiteImpl)

// IObjectWithSite
//
STDMETHOD(SetSite)(IUnknown *pUnkSite)
{
ATLTRACE(_T("IObjectWithSiteImpl::SetSite\n"));
T* pT = static_cast<T*>(this);
pT->m_spUnkSite = pUnkSite;
return S_OK;
}
STDMETHOD(GetSite)(REFIID riid, void **ppvSite)
{
ATLTRACE(_T("IObjectWithSiteImpl::GetSite\n"));
T* pT = static_cast<T*>(this);
_ASSERTE(ppvSite);
HRESULT hRes = E_POINTER;
if (ppvSite != NULL)
{
if (pT->m_spUnkSite)
hRes = pT->m_spUnkSite->QueryInterface(riid, ppvSite);
else
{
*ppvSite = NULL;
hRes = E_FAIL;
}
}
return hRes;
}

CComPtr<IUnknown> m_spUnkSite;
};

/////////////////////////////////////////////////////////////////////////////
// IDispatchImpl

template <class T, const IID* piid, const GUID* plibid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE IDispatchImpl : public T
{
public:
typedef tihclass _tihclass;
IDispatchImpl() {_tih.AddRef();}
~IDispatchImpl() {_tih.Release();}

STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
{*pctinfo = 1; return S_OK;}

STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
{return _tih.GetTypeInfo(itinfo, lcid, pptinfo);}

STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
LCID lcid, DISPID* rgdispid)
{return _tih.GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);}

STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
EXCEPINFO* pexcepinfo, UINT* puArgErr)
{return _tih.Invoke((IDispatch*)this, dispidMember, riid, lcid,
wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);}
protected:
static _tihclass _tih;
static HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo)
{return _tih.GetTI(lcid, ppInfo);}
};

template <class T, const IID* piid, const GUID* plibid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
IDispatchImpl<T, piid, plibid, wMajor, wMinor, tihclass>::_tihclass
IDispatchImpl<T, piid, plibid, wMajor, wMinor, tihclass>::_tih =
{piid, plibid, wMajor, wMinor, NULL, 0};

/////////////////////////////////////////////////////////////////////////////
// IProvideClassInfoImpl

template <const CLSID* pcoclsid, const IID* psrcid, const GUID* plibid,
WORD wMajor = 1, WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE IProvideClassInfo2Impl : public IProvideClassInfo2
{
public:
typedef tihclass _tihclass;
IProvideClassInfo2Impl() {_tih.AddRef();}
~IProvideClassInfo2Impl() {_tih.Release();}

STDMETHOD(GetClassInfo)(ITypeInfo** pptinfo)
{
return _tih.GetTypeInfo(0, LANG_NEUTRAL, pptinfo);
}
STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID* pGUID)
{
if (pGUID == NULL)
return E_POINTER;

if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID && psrcid)
{
*pGUID = *psrcid;
return S_OK;
}
*pGUID = GUID_NULL;
return E_FAIL;
}

protected:
static _tihclass _tih;
};

template <const CLSID* pcoclsid, const IID* psrcid, const GUID* plibid,
WORD wMajor = 1, WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
IProvideClassInfo2Impl<pcoclsid, psrcid, plibid, wMajor, wMinor, tihclass>::_tihclass
IProvideClassInfo2Impl<pcoclsid, psrcid, plibid, wMajor, wMinor, tihclass>::_tih =
{pcoclsid,plibid, wMajor, wMinor, NULL, 0};

/////////////////////////////////////////////////////////////////////////////
// ISupportErrorInfoImpl

template <const IID* piid>
class ATL_NO_VTABLE ISupportErrorInfoImpl : public ISupportErrorInfo
{
public:
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid)\
{return (InlineIsEqualGUID(riid,*piid)) ? S_OK : S_FALSE;}
};

/////////////////////////////////////////////////////////////////////////////
// CComEnumImpl

// These _CopyXXX classes are used with enumerators in order to control
// how enumerated items are initialized, copied, and deleted

// Default is shallow copy with no special init or cleanup
template <class T>
class _Copy
{
public:
static void copy(T* p1, T* p2) {memcpy(p1, p2, sizeof(T));}
static void init(T*) {}
static void destroy(T*) {}
};

#if _MSC_VER>1020
template<>
#endif
class _Copy<VARIANT>
{
public:
static void copy(VARIANT* p1, VARIANT* p2) {VariantCopy(p1, p2);}
static void init(VARIANT* p) {VariantInit(p);}
static void destroy(VARIANT* p) {VariantClear(p);}
};

#if _MSC_VER>1020
template<>
#endif
class _Copy<LPOLESTR>
{
public:
static void copy(LPOLESTR* p1, LPOLESTR* p2)
{
(*p1) = (LPOLESTR)CoTaskMemAlloc(sizeof(OLECHAR)*(ocslen(*p2)+1));
ocscpy(*p1,*p2);
}
static void init(LPOLESTR* p) {*p = NULL;}
static void destroy(LPOLESTR* p) { CoTaskMemFree(*p);}
};

#if _MSC_VER>1020
template<>
#endif
class _Copy<OLEVERB>
{
public:
static void copy(OLEVERB* p1, OLEVERB* p2)
{
*p1 = *p2;
if (p1->lpszVerbName == NULL)
return;
p1->lpszVerbName = (LPOLESTR)CoTaskMemAlloc(sizeof(OLECHAR)*(ocslen(p2->lpszVerbName)+1));
ocscpy(p1->lpszVerbName,p2->lpszVerbName);
}
static void init(OLEVERB* p) { p->lpszVerbName = NULL;}
static void destroy(OLEVERB* p) { if (p->lpszVerbName) CoTaskMemFree(p->lpszVerbName);}
};

#if _MSC_VER>1020
template<>
#endif
class _Copy<CONNECTDATA>
{
public:
static void copy(CONNECTDATA* p1, CONNECTDATA* p2)
{
*p1 = *p2;
if (p1->pUnk)
p1->pUnk->AddRef();
}
static void init(CONNECTDATA* ) {}
static void destroy(CONNECTDATA* p) {if (p->pUnk) p->pUnk->Release();}
};

template <class T>
class _CopyInterface
{
public:
static void copy(T** p1, T** p2)
{*p1 = *p2;if (*p1) (*p1)->AddRef();}
static void init(T** ) {}
static void destroy(T** p) {if (*p) (*p)->Release();}
};

template<class T>
class ATL_NO_VTABLE CComIEnum : public IUnknown
{
public:
STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched) = 0;
STDMETHOD(Skip)(ULONG celt) = 0;
STDMETHOD(Reset)(void) = 0;
STDMETHOD(Clone)(CComIEnum<T>** ppEnum) = 0;
};

enum CComEnumFlags
{
//see FlagBits in CComEnumImpl
AtlFlagNoCopy = 0,
AtlFlagTakeOwnership = 2,
AtlFlagCopy = 3 // copy implies ownership
};

template <class Base, const IID* piid, class T, class Copy>
class ATL_NO_VTABLE CComEnumImpl : public Base
{
public:
CComEnumImpl() {m_begin = m_end = m_iter = NULL; m_dwFlags = 0; m_pUnk = NULL;}
~CComEnumImpl();
STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched);
STDMETHOD(Skip)(ULONG celt);
STDMETHOD(Reset)(void){m_iter = m_begin;return S_OK;}
STDMETHOD(Clone)(Base** ppEnum);
HRESULT Init(T* begin, T* end, IUnknown* pUnk,
CComEnumFlags flags = AtlFlagNoCopy);
IUnknown* m_pUnk;
T* m_begin;
T* m_end;
T* m_iter;
DWORD m_dwFlags;
protected:
enum FlagBits
{
BitCopy=1,
BitOwn=2
};
};

template <class Base, const IID* piid, class T, class Copy>
CComEnumImpl<Base, piid, T, Copy>::~CComEnumImpl()
{
if (m_dwFlags & BitOwn)
{
for (T* p = m_begin; p != m_end; p++)
Copy::destroy(p);
delete [] m_begin;
}
if (m_pUnk)
m_pUnk->Release();
}

template <class Base, const IID* piid, class T, class Copy>
STDMETHODIMP CComEnumImpl<Base, piid, T, Copy>::Next(ULONG celt, T* rgelt,
ULONG* pceltFetched)
{
if (rgelt == NULL || (celt != 1 && pceltFetched == NULL))
return E_POINTER;
if (m_begin == NULL || m_end == NULL || m_iter == NULL)
return E_FAIL;
ULONG nRem = (ULONG)(m_end - m_iter);
HRESULT hRes = S_OK;
if (nRem < celt)
hRes = S_FALSE;
ULONG nMin = min(celt, nRem);
if (pceltFetched != NULL)
*pceltFetched = nMin;
while(nMin--)
Copy::copy(rgelt++, m_iter++);
return hRes;
}

template <class Base, const IID* piid, class T, class Copy>
STDMETHODIMP CComEnumImpl<Base, piid, T, Copy>::Skip(ULONG celt)
{
m_iter += celt;
if (m_iter < m_end)
return S_OK;
m_iter = m_end;
return S_FALSE;
}

template <class Base, const IID* piid, class T, class Copy>
STDMETHODIMP CComEnumImpl<Base, piid, T, Copy>::Clone(Base** ppEnum)
{
typedef CComObject<CComEnum<Base, piid, T, Copy> > _class;
HRESULT hRes = E_POINTER;
if (ppEnum != NULL)
{
_class* p = NULL;
ATLTRY(p = new _class)
if (p == NULL)
{
*ppEnum = NULL;
hRes = E_OUTOFMEMORY;
}
else
{
// If the data is a copy then we need to keep "this" object around
hRes = p->Init(m_begin, m_end, (m_dwFlags & BitCopy) ? this : m_pUnk);
if (FAILED(hRes))
delete p;
else
{
p->m_iter = m_iter;
hRes = p->_InternalQueryInterface(*piid, (void**)ppEnum);
if (FAILED(hRes))
delete p;
}
}
}
return hRes;
}

template <class Base, const IID* piid, class T, class Copy>
HRESULT CComEnumImpl<Base, piid, T, Copy>::Init(T* begin, T* end, IUnknown* pUnk,
CComEnumFlags flags)
{
if (flags == AtlFlagCopy)
{
_ASSERTE(m_begin == NULL); //Init called twice?
ATLTRY(m_begin = new T[end-begin])
m_iter = m_begin;
if (m_begin == NULL)
return E_OUTOFMEMORY;
for (T* i=begin; i != end; i++)
{
Copy::init(m_iter);
Copy::copy(m_iter++, i);
}
m_end = m_begin + (end-begin);
}
else
{
m_begin = begin;
m_end = end;
}
m_pUnk = pUnk;
if (m_pUnk)
m_pUnk->AddRef();
m_iter = m_begin;
m_dwFlags = flags;
return S_OK;
}

template <class Base, const IID* piid, class T, class Copy, class ThreadModel = CComObjectThreadModel>
class ATL_NO_VTABLE CComEnum :
public CComEnumImpl<Base, piid, T, Copy>,
public CComObjectRootEx< ThreadModel >
{
public:
typedef CComEnum<Base, piid, T, Copy > _CComEnum;
typedef CComEnumImpl<Base, piid, T, Copy > _CComEnumBase;
BEGIN_COM_MAP(_CComEnum)
COM_INTERFACE_ENTRY_IID(*piid, _CComEnumBase)
END_COM_MAP()
};

#ifndef _ATL_NO_CONNECTION_POINTS
/////////////////////////////////////////////////////////////////////////////
// Connection Points

struct _ATL_CONNMAP_ENTRY
{
DWORD dwOffset;
};

// We want the offset of the connection point relative to the connection
// point container base class
#define BEGIN_CONNECTION_POINT_MAP(x)\
typedef x _atl_conn_classtype;\
static const _ATL_CONNMAP_ENTRY* GetConnMap(int* pnEntries) {\
static const _ATL_CONNMAP_ENTRY _entries[] = {
// CONNECTION_POINT_ENTRY computes the offset of the connection point to the
// IConnectionPointContainer interface
#define CONNECTION_POINT_ENTRY(iid){offsetofclass(_ICPLocator<&iid>, _atl_conn_classtype)-\
offsetofclass(IConnectionPointContainerImpl<_atl_conn_classtype>, _atl_conn_classtype)},
#define END_CONNECTION_POINT_MAP() {(DWORD)-1} }; \
if (pnEntries) *pnEntries = sizeof(_entries)/sizeof(_ATL_CONNMAP_ENTRY) - 1; \
return _entries;}

#ifndef _DEFAULT_VECTORLENGTH
#define _DEFAULT_VECTORLENGTH 4
#endif

template <unsigned int nMaxSize>
class CComUnkArray
{
public:
CComUnkArray()
{
memset(m_arr, 0, sizeof(IUnknown*)*nMaxSize);
}
DWORD Add(IUnknown* pUnk);
BOOL Remove(DWORD dwCookie);
static DWORD WINAPI GetCookie(IUnknown** pp)
{
return (DWORD)pp;
}
static IUnknown* WINAPI GetUnknown(DWORD dwCookie)
{
return dwCookie ? *(IUnknown**)dwCookie : 0;
}
IUnknown** begin()
{
return &m_arr[0];
}
IUnknown** end()
{
return &m_arr[nMaxSize];
}
protected:
IUnknown* m_arr[nMaxSize];
};

template <unsigned int nMaxSize>
inline DWORD CComUnkArray<nMaxSize>::Add(IUnknown* pUnk)
{
for (IUnknown** pp = begin();pp<end();pp++)
{
if (*pp == NULL)
{
*pp = pUnk;
return (DWORD)pp; // return cookie
}
}
// If this fires then you need a larger array
_ASSERTE(0);
return 0;
}

template <unsigned int nMaxSize>
inline BOOL CComUnkArray<nMaxSize>::Remove(DWORD dwCookie)
{
IUnknown** pp = (IUnknown**)dwCookie;
BOOL b = ((pp >= begin()) && (pp < end()));
if (b)
*pp = NULL;
return b;
}

#if _MSC_VER>1020
template<>
#endif
class CComUnkArray<1>
{
public:
CComUnkArray()
{
m_arr[0] = NULL;
}
DWORD Add(IUnknown* pUnk)
{
if (m_arr[0] != NULL)
{
// If this fires then you need a larger array
_ASSERTE(0);
return 0;
}
m_arr[0] = pUnk;
return (DWORD)&m_arr[0];
}
BOOL Remove(DWORD dwCookie)
{
if (dwCookie != (DWORD)&m_arr[0])
return FALSE;
m_arr[0] = NULL;
return TRUE;
}
static DWORD WINAPI GetCookie(IUnknown** pp)
{
return (DWORD)pp;
}
static IUnknown* WINAPI GetUnknown(DWORD dwCookie)
{
return dwCookie ? *(IUnknown**)dwCookie : 0;
}
IUnknown** begin()
{
return &m_arr[0];
}
IUnknown** end()
{
return (&m_arr[0])+1;
}
protected:
IUnknown* m_arr[1];
};

class CComDynamicUnkArray
{
public:
CComDynamicUnkArray()
{
m_nSize = 0;
m_ppUnk = NULL;
}

~CComDynamicUnkArray()
{
if (m_nSize > 1)
free(m_ppUnk);
}
DWORD Add(IUnknown* pUnk);
BOOL Remove(DWORD dwCookie);
static DWORD WINAPI GetCookie(IUnknown** pp)
{
return (DWORD)*pp;
}
static IUnknown* WINAPI GetUnknown(DWORD dwCookie)
{
return (IUnknown*)dwCookie;
}
IUnknown** begin()
{
return (m_nSize < 2) ? &m_pUnk : m_ppUnk;
}
IUnknown** end()
{
return (m_nSize < 2) ? (&m_pUnk)+m_nSize : &m_ppUnk[m_nSize];
}
protected:
union
{
IUnknown** m_ppUnk;
IUnknown* m_pUnk;
};
int m_nSize;
};

template <const IID* piid>
class ATL_NO_VTABLE _ICPLocator
{
//this method needs a different name than QueryInterface
STDMETHOD(_LocCPQueryInterface)(REFIID riid, void ** ppvObject) = 0;
};

template <class T, const IID* piid, class CDV = CComDynamicUnkArray >
class ATL_NO_VTABLE IConnectionPointImpl : public _ICPLocator<piid>
{
typedef CComEnum<IEnumConnections, &IID_IEnumConnections, CONNECTDATA,
_Copy<CONNECTDATA> > CComEnumConnections;
typedef CDV _CDV;
public:
STDMETHOD(_LocCPQueryInterface)(REFIID riid, void ** ppvObject)
{
if (InlineIsEqualGUID(riid, IID_IConnectionPoint) || InlineIsEqualGUID(riid, IID_IUnknown))
{
*ppvObject = this;
#ifdef _ATL_DEBUG_REFCOUNT
_DebugAddRef();
#else
AddRef();
#endif
return S_OK;
}
else
return E_NOINTERFACE;
}
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IConnectionPointImpl)

STDMETHOD(GetConnectionInterface)(IID* piid2)
{
if (piid2 == NULL)
return E_POINTER;
*piid2 = *piid;
return S_OK;
}
STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer** ppCPC)
{
if (ppCPC == NULL)
return E_POINTER;
*ppCPC = reinterpret_cast<IConnectionPointContainer*>(
(IConnectionPointContainerImpl<T>*)(T*)this);
return S_OK;
}
STDMETHOD(Advise)(IUnknown* pUnkSink, DWORD* pdwCookie);
STDMETHOD(Unadvise)(DWORD dwCookie);
STDMETHOD(EnumConnections)(IEnumConnections** ppEnum);
CDV m_vec;
};

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImpl<T, piid, CDV>::Advise(IUnknown* pUnkSink,
DWORD* pdwCookie)
{
T* pT = (T*)this;
IUnknown* p;
HRESULT hRes = S_OK;
if (pUnkSink == NULL || pdwCookie == NULL)
return E_POINTER;
IID iid;
GetConnectionInterface(&iid);
hRes = pUnkSink->QueryInterface(iid, (void**)&p);
if (SUCCEEDED(hRes))
{
pT->Lock();
*pdwCookie = m_vec.Add(p);
hRes = (*pdwCookie != NULL) ? S_OK : CONNECT_E_ADVISELIMIT;
pT->Unlock();
if (hRes != S_OK)
{
*pdwCookie = 0;
p->Release();
}
}
else if (hRes == E_NOINTERFACE)
hRes = CONNECT_E_CANNOTCONNECT;
return hRes;
}

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImpl<T, piid, CDV>::Unadvise(DWORD dwCookie)
{
T* pT = (T*)this;
pT->Lock();
IUnknown* p = _CDV::GetUnknown(dwCookie);
HRESULT hRes = m_vec.Remove(dwCookie) ? S_OK : CONNECT_E_NOCONNECTION;
pT->Unlock();
if (hRes == S_OK && p != NULL)
p->Release();
return hRes;
}

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImpl<T, piid, CDV>::EnumConnections(
IEnumConnections** ppEnum)
{
if (ppEnum == NULL)
return E_POINTER;
*ppEnum = NULL;
CComObject<CComEnumConnections>* pEnum = NULL;
ATLTRY(pEnum = new CComObject<CComEnumConnections>)
if (pEnum == NULL)
return E_OUTOFMEMORY;
T* pT = (T*)this;
pT->Lock();
CONNECTDATA* pcd = NULL;
ATLTRY(pcd = new CONNECTDATA[m_vec.end()-m_vec.begin()])
if (pcd == NULL)
{
delete pEnum;
pT->Unlock();
return E_OUTOFMEMORY;
}
CONNECTDATA* pend = pcd;
// Copy the valid CONNECTDATA's
for (IUnknown** pp = m_vec.begin();pp<m_vec.end();pp++)
{
if (*pp != NULL)
{
(*pp)->AddRef();
pend->pUnk = *pp;
pend->dwCookie = _CDV::GetCookie(pp);
pend++;
}
}
// don't copy the data, but transfer ownership to it
pEnum->Init(pcd, pend, NULL, AtlFlagTakeOwnership);
pT->Unlock();
HRESULT hRes = pEnum->_InternalQueryInterface(IID_IEnumConnections, (void**)ppEnum);
if (FAILED(hRes))
delete pEnum;
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// IConnectionPointContainerImpl

template <class T>
class ATL_NO_VTABLE IConnectionPointContainerImpl
{
typedef CComEnum<IEnumConnectionPoints,
&IID_IEnumConnectionPoints, IConnectionPoint*,
_CopyInterface<IConnectionPoint> >
CComEnumConnectionPoints;
public:
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(IConnectionPointContainerImpl)

STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints** ppEnum)
{
if (ppEnum == NULL)
return E_POINTER;
*ppEnum = NULL;
CComEnumConnectionPoints* pEnum = NULL;
ATLTRY(pEnum = new CComObject<CComEnumConnectionPoints>)
if (pEnum == NULL)
return E_OUTOFMEMORY;

int nCPCount;
const _ATL_CONNMAP_ENTRY* pEntry = T::GetConnMap(&nCPCount);
_ASSERTE(nCPCount > 0);

// allocate an initialize a vector of connection point object pointers
IConnectionPoint** ppCP = (IConnectionPoint**)alloca(sizeof(IConnectionPoint*)*nCPCount);

int i = 0;
while (pEntry->dwOffset != (DWORD)-1)
{
ppCP[i++] = (IConnectionPoint*)((int)this+pEntry->dwOffset);
pEntry++;
}

// copy the pointers: they will AddRef this object
HRESULT hRes = pEnum->Init((IConnectionPoint**)&ppCP[0],
(IConnectionPoint**)&ppCP[nCPCount],
reinterpret_cast<IConnectionPointContainer*>(this), AtlFlagCopy);
if (FAILED(hRes))
{
delete pEnum;
return hRes;
}
hRes = pEnum->QueryInterface(IID_IEnumConnectionPoints, (void**)ppEnum);
if (FAILED(hRes))
delete pEnum;
return hRes;
}
STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint** ppCP)
{
if (ppCP == NULL)
return E_POINTER;
*ppCP = NULL;
HRESULT hRes = CONNECT_E_NOCONNECTION;
const _ATL_CONNMAP_ENTRY* pEntry = T::GetConnMap(NULL);
IID iid;
while (pEntry->dwOffset != (DWORD)-1)
{
IConnectionPoint* pCP =
(IConnectionPoint*)((int)this+pEntry->dwOffset);
if (SUCCEEDED(pCP->GetConnectionInterface(&iid)) &&
InlineIsEqualGUID(riid, iid))
{
*ppCP = pCP;
pCP->AddRef();
hRes = S_OK;
break;
}
pEntry++;
}
return hRes;
}
};

#endif //!_ATL_NO_CONNECTION_POINTS

#pragma pack(pop)

/////////////////////////////////////////////////////////////////////////////
// CComAutoThreadModule

template <class ThreadAllocator>
inline void CComAutoThreadModule<ThreadAllocator>::Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, int nThreads)
{
m_nThreads = nThreads;
m_pApartments = new CComApartment[m_nThreads];
for (int i = 0; i < nThreads; i++)
m_pApartments[i].m_hThread = CreateThread(NULL, 0, CComApartment::_Apartment, (void*)&m_pApartments[i], 0, &m_pApartments[i].m_dwThreadID);
CComApartment::ATL_CREATE_OBJECT = RegisterWindowMessage(_T("ATL_CREATE_OBJECT"));
CComModule::Init(p, h);
}

template <class ThreadAllocator>
inline LONG CComAutoThreadModule<ThreadAllocator>::Lock()
{
LONG l = CComModule::Lock();
DWORD dwThreadID = GetCurrentThreadId();
for (int i=0; i < m_nThreads; i++)
{
if (m_pApartments[i].m_dwThreadID == dwThreadID)
{
m_pApartments[i].Lock();
break;
}
}
return l;
}

template <class ThreadAllocator>
inline LONG CComAutoThreadModule<ThreadAllocator>::Unlock()
{
LONG l = CComModule::Unlock();
DWORD dwThreadID = GetCurrentThreadId();
for (int i=0; i < m_nThreads; i++)
{
if (m_pApartments[i].m_dwThreadID == dwThreadID)
{
m_pApartments[i].Unlock();
break;
}
}
return l;
}

template <class ThreadAllocator>
HRESULT CComAutoThreadModule<ThreadAllocator>::CreateInstance(void* pfnCreateInstance, REFIID riid, void** ppvObj)
{
_ATL_CREATORFUNC* pFunc = (_ATL_CREATORFUNC*) pfnCreateInstance;
_AtlAptCreateObjData data;
data.pfnCreateInstance = pFunc;
data.piid = &riid;
data.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
data.hRes = S_OK;
int nThread = m_Allocator.GetThread(m_pApartments, m_nThreads);

::PostThreadMessage(m_pApartments[nThread].m_dwThreadID, CComApartment::ATL_CREATE_OBJECT, 0, (LPARAM)&data);
AtlWaitWithMessageLoop(data.hEvent);
CloseHandle(data.hEvent);
if (SUCCEEDED(data.hRes))
data.hRes = CoGetInterfaceAndReleaseStream(data.pStream, riid, ppvObj);
return data.hRes;
}

template <class ThreadAllocator>
CComAutoThreadModule<ThreadAllocator>::~CComAutoThreadModule()
{
for (int i=0; i < m_nThreads; i++)
{
::PostThreadMessage(m_pApartments[i].m_dwThreadID, WM_QUIT, 0, 0);
::WaitForSingleObject(m_pApartments[i].m_hThread, INFINITE);
}
delete[] m_pApartments;
}

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

#endif // __ATLCOM_H__

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


ATLIMPL.CPP

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLBASE_H__
#error atlimpl.cpp requires atlbase.h to be included first
#endif

extern "C" const IID IID_IRegistrar = {0x44EC053B,0x400F,0x11D0,{0x9D,0xCD,0x00,0xA0,0xC9,0x03,0x91,0xD3}};
#ifndef _ATL_DLL_IMPL
extern "C" const CLSID CLSID_Registrar = {0x44EC053A,0x400F,0x11D0,{0x9D,0xCD,0x00,0xA0,0xC9,0x03,0x91,0xD3}};
#endif

#include <atlconv.cpp>
#ifdef _DEBUG
#include <stdio.h>
#include <stdarg.h>
#endif

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

// used in thread pooling
UINT CComApartment::ATL_CREATE_OBJECT = 0;

#ifdef __ATLCOM_H__

/////////////////////////////////////////////////////////////////////////////
// AtlReportError

HRESULT WINAPI AtlReportError(const CLSID& clsid, UINT nID, const IID& iid,
HRESULT hRes, HINSTANCE hInst)
{
return AtlSetErrorInfo(clsid, (LPCOLESTR)MAKEINTRESOURCE(nID), 0, NULL, iid, hRes, hInst);
}

HRESULT WINAPI AtlReportError(const CLSID& clsid, UINT nID, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes, HINSTANCE hInst)
{
return AtlSetErrorInfo(clsid, (LPCOLESTR)MAKEINTRESOURCE(nID), dwHelpID,
lpszHelpFile, iid, hRes, hInst);
}

#ifndef OLE2ANSI
HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCSTR lpszDesc,
DWORD dwHelpID, LPCSTR lpszHelpFile, const IID& iid, HRESULT hRes)
{
_ASSERTE(lpszDesc != NULL);
USES_CONVERSION;
return AtlSetErrorInfo(clsid, A2COLE(lpszDesc), dwHelpID, A2CW(lpszHelpFile),
iid, hRes, NULL);
}

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCSTR lpszDesc,
const IID& iid, HRESULT hRes)
{
_ASSERTE(lpszDesc != NULL);
USES_CONVERSION;
return AtlSetErrorInfo(clsid, A2COLE(lpszDesc), 0, NULL, iid, hRes, NULL);
}
#endif

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc,
const IID& iid, HRESULT hRes)
{
return AtlSetErrorInfo(clsid, lpszDesc, 0, NULL, iid, hRes, NULL);
}

HRESULT WINAPI AtlReportError(const CLSID& clsid, LPCOLESTR lpszDesc, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes)
{
return AtlSetErrorInfo(clsid, lpszDesc, dwHelpID, lpszHelpFile, iid, hRes, NULL);
}

#endif //__ATLCOM_H__

/////////////////////////////////////////////////////////////////////////////
// CComBSTR

CComBSTR& CComBSTR::operator=(const CComBSTR& src)
{
if (m_str != src.m_str)
{
if (m_str)
::SysFreeString(m_str);
m_str = src.Copy();
}
return *this;
}

CComBSTR& CComBSTR::operator=(LPCOLESTR pSrc)
{
::SysFreeString(m_str);
m_str = ::SysAllocString(pSrc);
return *this;
}

void CComBSTR::Append(LPCOLESTR lpsz, int nLen)
{
int n1 = Length();
BSTR b = SysAllocStringLen(NULL, n1+nLen);
memcpy(b, m_str, n1*sizeof(OLECHAR));
memcpy(b+n1, lpsz, nLen*sizeof(OLECHAR));
b[n1+nLen] = NULL;
SysFreeString(m_str);
m_str = b;
}

#ifndef OLE2ANSI
void CComBSTR::Append(LPCSTR lpsz)
{
USES_CONVERSION;
LPCOLESTR lpo = A2COLE(lpsz);
Append(lpo, ocslen(lpo));
}

CComBSTR::CComBSTR(LPCSTR pSrc)
{
USES_CONVERSION;
m_str = ::SysAllocString(A2COLE(pSrc));
}

CComBSTR::CComBSTR(int nSize, LPCSTR sz)
{
USES_CONVERSION;
m_str = ::SysAllocStringLen(A2COLE(sz), nSize);
}

CComBSTR& CComBSTR::operator=(LPCSTR pSrc)
{
USES_CONVERSION;
::SysFreeString(m_str);
m_str = ::SysAllocString(A2COLE(pSrc));
return *this;
}
#endif

HRESULT CComBSTR::ReadFromStream(IStream* pStream)
{
_ASSERTE(pStream != NULL);
_ASSERTE(m_str == NULL); // should be empty
ULONG cb;
ULONG cbStrLen;
HRESULT hr = pStream->Read((void*) &cbStrLen, sizeof(cbStrLen), &cb);
if (FAILED(hr))
return hr;
m_str = SysAllocStringByteLen(NULL, cbStrLen+sizeof(OLECHAR));
if (m_str == NULL)
hr = E_OUTOFMEMORY;
else
{
hr = pStream->Read((void*) m_str, cbStrLen, &cb);
*(OLECHAR*)&((BYTE*)m_str)[cb] = (OLECHAR)NULL;
}
return hr;
}

HRESULT CComBSTR::WriteToStream(IStream* pStream)
{
_ASSERTE(pStream != NULL);
ULONG cb;
ULONG cbStrLen = SysStringByteLen(m_str);
HRESULT hr = pStream->Write((void*) &cbStrLen, sizeof(cbStrLen), &cb);
if (FAILED(hr))
return hr;
return pStream->Write((void*) m_str, cbStrLen, &cb);
}

/////////////////////////////////////////////////////////////////////////////
// CComVariant

CComVariant& CComVariant::operator=(BSTR bstrSrc)
{
InternalClear();
vt = VT_BSTR;
bstrVal = ::SysAllocString(bstrSrc);
if (bstrVal == NULL)
{
vt = VT_ERROR;
scode = E_OUTOFMEMORY;
}
return *this;
}

CComVariant& CComVariant::operator=(LPCOLESTR lpszSrc)
{
InternalClear();
vt = VT_BSTR;
bstrVal = ::SysAllocString(lpszSrc);

if (bstrVal == NULL)
{
vt = VT_ERROR;
scode = E_OUTOFMEMORY;
}
return *this;
}

#ifndef OLE2ANSI
CComVariant& CComVariant::operator=(LPCSTR lpszSrc)
{
USES_CONVERSION;
InternalClear();
vt = VT_BSTR;
bstrVal = ::SysAllocString(A2COLE(lpszSrc));

if (bstrVal == NULL)
{
vt = VT_ERROR;
scode = E_OUTOFMEMORY;
}
return *this;
}
#endif

#if _MSC_VER>1020
CComVariant& CComVariant::operator=(bool bSrc)
{
if (vt != VT_BOOL)
{
InternalClear();
vt = VT_BOOL;
}
#pragma warning(disable: 4310) // cast truncates constant value
boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE;
#pragma warning(default: 4310) // cast truncates constant value
return *this;
}
#endif

CComVariant& CComVariant::operator=(int nSrc)
{
if (vt != VT_I4)
{
InternalClear();
vt = VT_I4;
}
lVal = nSrc;

return *this;
}

CComVariant& CComVariant::operator=(BYTE nSrc)
{
if (vt != VT_UI1)
{
InternalClear();
vt = VT_UI1;
}
bVal = nSrc;
return *this;
}

CComVariant& CComVariant::operator=(short nSrc)
{
if (vt != VT_I2)
{
InternalClear();
vt = VT_I2;
}
iVal = nSrc;
return *this;
}

CComVariant& CComVariant::operator=(long nSrc)
{
if (vt != VT_I4)
{
InternalClear();
vt = VT_I4;
}
lVal = nSrc;
return *this;
}

CComVariant& CComVariant::operator=(float fltSrc)
{
if (vt != VT_R4)
{
InternalClear();
vt = VT_R4;
}
fltVal = fltSrc;
return *this;
}

CComVariant& CComVariant::operator=(double dblSrc)
{
if (vt != VT_R8)
{
InternalClear();
vt = VT_R8;
}
dblVal = dblSrc;
return *this;
}

CComVariant& CComVariant::operator=(CY cySrc)
{
if (vt != VT_CY)
{
InternalClear();
vt = VT_CY;
}
cyVal.Hi = cySrc.Hi;
cyVal.Lo = cySrc.Lo;
return *this;
}

CComVariant& CComVariant::operator=(IDispatch* pSrc)
{
InternalClear();
vt = VT_DISPATCH;
pdispVal = pSrc;
// Need to AddRef as VariantClear will Release
if (pdispVal != NULL)
pdispVal->AddRef();
return *this;
}

CComVariant& CComVariant::operator=(IUnknown* pSrc)
{
InternalClear();
vt = VT_UNKNOWN;
punkVal = pSrc;

// Need to AddRef as VariantClear will Release
if (punkVal != NULL)
punkVal->AddRef();
return *this;
}

#if _MSC_VER>1020
bool CComVariant::operator==(const VARIANT& varSrc)
{
if (this == &varSrc)
return true;

// Variants not equal if types don't match
if (vt != varSrc.vt)
return false;

// Check type specific values
switch (vt)
{
case VT_EMPTY:
case VT_NULL:
return true;

case VT_BOOL:
return boolVal == varSrc.boolVal;

case VT_UI1:
return bVal == varSrc.bVal;

case VT_I2:
return iVal == varSrc.iVal;

case VT_I4:
return lVal == varSrc.lVal;

case VT_R4:
return fltVal == varSrc.fltVal;

case VT_R8:
return dblVal == varSrc.dblVal;

case VT_BSTR:
return (::SysStringByteLen(bstrVal) == ::SysStringByteLen(varSrc.bstrVal)) &&
(::memcmp(bstrVal, varSrc.bstrVal, ::SysStringByteLen(bstrVal)) == 0);

case VT_ERROR:
return scode == varSrc.scode;

case VT_DISPATCH:
return pdispVal == varSrc.pdispVal;

case VT_UNKNOWN:
return punkVal == varSrc.punkVal;

default:
_ASSERTE(false);
// fall through
}

return false;
}
#else
BOOL CComVariant::operator==(const VARIANT& varSrc)
{
if (this == &varSrc)
return TRUE;

// Variants not equal if types don't match
if (vt != varSrc.vt)
return FALSE;

// Check type specific values
switch (vt)
{
case VT_EMPTY:
case VT_NULL:
return TRUE;

case VT_BOOL:
return boolVal == varSrc.boolVal;

case VT_UI1:
return bVal == varSrc.bVal;

case VT_I2:
return iVal == varSrc.iVal;

case VT_I4:
return lVal == varSrc.lVal;

case VT_R4:
return fltVal == varSrc.fltVal;

case VT_R8:
return dblVal == varSrc.dblVal;

case VT_BSTR:
return (::SysStringByteLen(bstrVal) == ::SysStringByteLen(varSrc.bstrVal)) &&
(::memcmp(bstrVal, varSrc.bstrVal, ::SysStringByteLen(bstrVal)) == 0);

case VT_ERROR:
return scode == varSrc.scode;

case VT_DISPATCH:
return pdispVal == varSrc.pdispVal;

case VT_UNKNOWN:
return punkVal == varSrc.punkVal;

default:
_ASSERTE(FALSE);
// fall through
}

return FALSE;
}
#endif

HRESULT CComVariant::Attach(VARIANT* pSrc)
{
// Clear out the variant
HRESULT hr = Clear();
if (!FAILED(hr))
{
// Copy the contents and give control to CComVariant
memcpy(this, pSrc, sizeof(VARIANT));
VariantInit(pSrc);
hr = S_OK;
}
return hr;
}

HRESULT CComVariant::Detach(VARIANT* pDest)
{
// Clear out the variant
HRESULT hr = ::VariantClear(pDest);
if (!FAILED(hr))
{
// Copy the contents and remove control from CComVariant
memcpy(pDest, this, sizeof(VARIANT));
vt = VT_EMPTY;
hr = S_OK;
}
return hr;
}

HRESULT CComVariant::ChangeType(VARTYPE vtNew, const VARIANT* pSrc)
{
VARIANT* pVar = const_cast<VARIANT*>(pSrc);
// Convert in place if pSrc is NULL
if (pVar == NULL)
pVar = this;
// Do nothing if doing in place convert and vts not different
return ::VariantChangeType(this, pVar, 0, vtNew);
}

HRESULT CComVariant::InternalClear()
{
HRESULT hr = Clear();
_ASSERTE(SUCCEEDED(hr));
if (FAILED(hr))
{
vt = VT_ERROR;
scode = hr;
}
return hr;
}

void CComVariant::InternalCopy(const VARIANT* pSrc)
{
HRESULT hr = Copy(pSrc);
if (FAILED(hr))
{
vt = VT_ERROR;
scode = hr;
}
}

HRESULT CComVariant::WriteToStream(IStream* pStream)
{
HRESULT hr = pStream->Write(&vt, sizeof(VARTYPE), NULL);
if (FAILED(hr))
return hr;

if (vt == VT_UNKNOWN || vt == VT_DISPATCH)
{
CComPtr<IPersistStream> spStream;
hr = punkVal->QueryInterface(IID_IPersistStream, (void**)&spStream);
if (FAILED(hr))
return hr;
CLSID       clsid;
LPOLESTR    szClsid;

if (FAILED(spStream->GetClassID(&clsid)))
return E_FAIL;
hr = StringFromCLSID(clsid, &szClsid);
if (FAILED(hr))
return hr;

hr = pStream->Write(szClsid, ocslen(szClsid), NULL);
if (FAILED(hr))
return hr;

return spStream->Save(pStream, TRUE);
}

CComBSTR bstrWrite;
CComVariant varBSTR;
if (vt != VT_BSTR)
{
hr = VariantChangeType(&varBSTR, this, VARIANT_NOVALUEPROP, VT_BSTR);
if (FAILED(hr))
return hr;
bstrWrite = varBSTR.bstrVal;
}
else
bstrWrite = bstrVal;

_ASSERTE(bstrWrite != NULL);
return bstrWrite.WriteToStream(pStream);
}

HRESULT CComVariant::ReadFromStream(IStream* pStream)
{
_ASSERTE(pStream != NULL);
HRESULT hr;
hr = VariantClear(this);
if (FAILED(hr))
return hr;
VARTYPE vt;
hr = pStream->Read(&vt, sizeof(VARTYPE), NULL);
if (FAILED(hr))
return hr;

if (vt == VT_UNKNOWN || vt == VT_DISPATCH)
{
CLSID clsid;
CComBSTR bstrClsid;
hr = bstrClsid.ReadFromStream(pStream);
if (FAILED(hr))
return hr;
if (FAILED(CLSIDFromString(bstrClsid, &clsid)))
return E_FAIL;
CComPtr<IPersistStream> spPStream;
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
  IID_IPersistStream, (void**)&spPStream);
if (FAILED(hr))
return E_FAIL;
hr = spPStream->Load(pStream);
if (FAILED(hr))
return hr;
if (vt == VT_UNKNOWN)
return spPStream->QueryInterface(IID_IUnknown, (void**)&punkVal);
return spPStream->QueryInterface(IID_IDispatch, (void**)&punkVal);
}

CComBSTR bstrRead;

hr = bstrRead.ReadFromStream(pStream);
if (FAILED(hr))
return hr;
CComVariant var = bstrRead;
return VariantChangeType(this, &var, VARIANT_NOVALUEPROP, vt);
}

#ifdef __ATLCOM_H__

/////////////////////////////////////////////////////////////////////////////
// CComTypeInfoHolder

void CComTypeInfoHolder::AddRef()
{
EnterCriticalSection(&_Module.m_csTypeInfoHolder);
m_dwRef++;
LeaveCriticalSection(&_Module.m_csTypeInfoHolder);
}

void CComTypeInfoHolder::Release()
{
EnterCriticalSection(&_Module.m_csTypeInfoHolder);
if (--m_dwRef == 0)
{
if (m_pInfo != NULL)
m_pInfo->Release();
m_pInfo = NULL;
}
LeaveCriticalSection(&_Module.m_csTypeInfoHolder);
}

HRESULT CComTypeInfoHolder::GetTI(LCID lcid, ITypeInfo** ppInfo)
{
//If this assert occurs then most likely didn't initialize properly
_ASSERTE(m_plibid != NULL && m_pguid != NULL);
_ASSERTE(ppInfo != NULL);
*ppInfo = NULL;

HRESULT hRes = E_FAIL;
EnterCriticalSection(&_Module.m_csTypeInfoHolder);
if (m_pInfo == NULL)
{
ITypeLib* pTypeLib;
hRes = LoadRegTypeLib(*m_plibid, m_wMajor, m_wMinor, lcid, &pTypeLib);
if (SUCCEEDED(hRes))
{
ITypeInfo* pTypeInfo;
hRes = pTypeLib->GetTypeInfoOfGuid(*m_pguid, &pTypeInfo);
if (SUCCEEDED(hRes))
m_pInfo = pTypeInfo;
pTypeLib->Release();
}
}
*ppInfo = m_pInfo;
if (m_pInfo != NULL)
{
m_pInfo->AddRef();
hRes = S_OK;
}
LeaveCriticalSection(&_Module.m_csTypeInfoHolder);
return hRes;
}

HRESULT CComTypeInfoHolder::GetTypeInfo(UINT /*itinfo*/, LCID lcid,
ITypeInfo** pptinfo)
{
HRESULT hRes = E_POINTER;
if (pptinfo != NULL)
hRes = GetTI(lcid, pptinfo);
return hRes;
}

HRESULT CComTypeInfoHolder::GetIDsOfNames(REFIID /*riid*/, LPOLESTR* rgszNames,
UINT cNames, LCID lcid, DISPID* rgdispid)
{
ITypeInfo* pInfo;
HRESULT hRes = GetTI(lcid, &pInfo);
if (pInfo != NULL)
{
hRes = pInfo->GetIDsOfNames(rgszNames, cNames, rgdispid);
pInfo->Release();
}
return hRes;
}

HRESULT CComTypeInfoHolder::Invoke(IDispatch* p, DISPID dispidMember, REFIID /*riid*/,
LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
SetErrorInfo(0, NULL);
ITypeInfo* pInfo;
HRESULT hRes = GetTI(lcid, &pInfo);
if (pInfo != NULL)
{
hRes = pInfo->Invoke(p, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
pInfo->Release();
}
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// QI implementation

#ifdef _ATL_DEBUG_QI
HRESULT WINAPI AtlDumpIID(REFIID iid, LPCTSTR pszClassName, HRESULT hr)
{
USES_CONVERSION;
CRegKey key;
TCHAR szName[100];
DWORD dwType,dw = sizeof(szName);

LPOLESTR pszGUID = NULL;
StringFromCLSID(iid, &pszGUID);
OutputDebugString(pszClassName);
OutputDebugString(_T(" - "));

// Attempt to find it in the interfaces section
key.Open(HKEY_CLASSES_ROOT, _T("Interface"));
if (key.Open(key, OLE2T(pszGUID)) == S_OK)
{
*szName = 0;
RegQueryValueEx(key.m_hKey, (LPTSTR)NULL, NULL, &dwType, (LPBYTE)szName, &dw);
OutputDebugString(szName);
goto cleanup;
}
// Attempt to find it in the clsid section
key.Open(HKEY_CLASSES_ROOT, _T("CLSID"));
if (key.Open(key, OLE2T(pszGUID)) == S_OK)
{
*szName = 0;
RegQueryValueEx(key.m_hKey, (LPTSTR)NULL, NULL, &dwType, (LPBYTE)szName, &dw);
OutputDebugString(_T("(CLSID\?\?\?) "));
OutputDebugString(szName);
goto cleanup;
}
OutputDebugString(OLE2T(pszGUID));
cleanup:
if (hr != S_OK)
OutputDebugString(_T(" - failed"));
OutputDebugString(_T("\n"));
CoTaskMemFree(pszGUID);
return hr;
}
#endif

HRESULT WINAPI CComObjectRootBase::_Break(void* /* pv */, REFIID iid, void** /* ppvObject */, DWORD /* dw */)
{
iid;
_ATLDUMPIID(iid, _T("Break due to QI for interface "), S_OK);
DebugBreak();
return S_FALSE;
}

HRESULT WINAPI CComObjectRootBase::_NoInterface(void* /* pv */, REFIID /* iid */, void** /* ppvObject */, DWORD /* dw */)
{
return E_NOINTERFACE;
}

HRESULT WINAPI CComObjectRootBase::_Creator(void* pv, REFIID iid, void** ppvObject, DWORD dw)
{
_ATL_CREATORDATA* pcd = (_ATL_CREATORDATA*)dw;
return pcd->pFunc(pv, iid, ppvObject);
}

HRESULT WINAPI CComObjectRootBase::_Delegate(void* pv, REFIID iid, void** ppvObject, DWORD dw)
{
HRESULT hRes = E_NOINTERFACE;
IUnknown* p = *(IUnknown**)((DWORD)pv + dw);
if (p != NULL)
hRes = p->QueryInterface(iid, ppvObject);
return hRes;
}

HRESULT WINAPI CComObjectRootBase::_Chain(void* pv, REFIID iid, void** ppvObject, DWORD dw)
{
_ATL_CHAINDATA* pcd = (_ATL_CHAINDATA*)dw;
void* p = (void*)((DWORD)pv + pcd->dwOffset);
return InternalQueryInterface(p, pcd->pFunc(), iid, ppvObject);
}

HRESULT WINAPI CComObjectRootBase::_Cache(void* pv, REFIID iid, void** ppvObject, DWORD dw)
{
HRESULT hRes = E_NOINTERFACE;
_ATL_CACHEDATA* pcd = (_ATL_CACHEDATA*)dw;
IUnknown** pp = (IUnknown**)((DWORD)pv + pcd->dwOffsetVar);
if (*pp == NULL)
hRes = pcd->pFunc(pv, IID_IUnknown, (void**)pp);
if (*pp != NULL)
hRes = (*pp)->QueryInterface(iid, ppvObject);
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// CComClassFactory

STDMETHODIMP CComClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
REFIID riid, void** ppvObj)
{
_ASSERTE(m_pfnCreateInstance != NULL);
HRESULT hRes = E_POINTER;
if (ppvObj != NULL)
{
*ppvObj = NULL;
// can't ask for anything other than IUnknown when aggregating
_ASSERTE((pUnkOuter == NULL) || InlineIsEqualUnknown(riid));
if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
hRes = CLASS_E_NOAGGREGATION;
else
hRes = m_pfnCreateInstance(pUnkOuter, riid, ppvObj);
}
return hRes;
}

STDMETHODIMP CComClassFactory::LockServer(BOOL fLock)
{
if (fLock)
_Module.Lock();
else
_Module.Unlock();
return S_OK;
}

STDMETHODIMP CComClassFactory2Base::LockServer(BOOL fLock)
{
if (fLock)
_Module.Lock();
else
_Module.Unlock();
return S_OK;
}

#ifndef _ATL_NO_CONNECTION_POINTS
/////////////////////////////////////////////////////////////////////////////
// Connection Points

DWORD CComDynamicUnkArray::Add(IUnknown* pUnk)
{
IUnknown** pp = NULL;
if (m_nSize == 0) // no connections
{
m_pUnk = pUnk;
m_nSize = 1;
return (DWORD)m_pUnk;
}
else if (m_nSize == 1)
{
//create array
pp = (IUnknown**)malloc(sizeof(IUnknown*)*_DEFAULT_VECTORLENGTH);
if (pp == NULL)
return 0;
memset(pp, 0, sizeof(IUnknown*)*_DEFAULT_VECTORLENGTH);
*pp = m_pUnk;
m_ppUnk = pp;
m_nSize = _DEFAULT_VECTORLENGTH;
}
for (pp = begin();pp<end();pp++)
{
if (*pp == NULL)
{
*pp = pUnk;
return (DWORD)pUnk;
}
}
int nAlloc = m_nSize*2;
pp = (IUnknown**)realloc(m_ppUnk, sizeof(IUnknown*)*nAlloc);
if (pp == NULL)
return 0;
m_ppUnk = pp;
memset(&m_ppUnk[m_nSize], 0, sizeof(IUnknown*)*m_nSize);
m_ppUnk[m_nSize] = pUnk;
m_nSize = nAlloc;
return (DWORD)pUnk;
}

BOOL CComDynamicUnkArray::Remove(DWORD dwCookie)
{
IUnknown** pp;
if (dwCookie == NULL)
return FALSE;
if (m_nSize == 0)
return FALSE;
if (m_nSize == 1)
{
if ((DWORD)m_pUnk == dwCookie)
{
m_nSize = 0;
return TRUE;
}
return FALSE;
}
for (pp=begin();pp<end();pp++)
{
if ((DWORD)*pp == dwCookie)
{
*pp = NULL;
return TRUE;
}
}
return FALSE;
}

#endif //!_ATL_NO_CONNECTION_POINTS

#endif //__ATLCOM_H__

/////////////////////////////////////////////////////////////////////////////
// Object Registry Support

static HRESULT WINAPI AtlRegisterProgID(LPCTSTR lpszCLSID, LPCTSTR lpszProgID, LPCTSTR lpszUserDesc)
{
CRegKey keyProgID;
LONG lRes = keyProgID.Create(HKEY_CLASSES_ROOT, lpszProgID);
if (lRes == ERROR_SUCCESS)
{
keyProgID.SetValue(lpszUserDesc);
keyProgID.SetKeyValue(_T("CLSID"), lpszCLSID);
return S_OK;
}
return HRESULT_FROM_WIN32(lRes);
}

void CComModule::AddCreateWndData(_AtlCreateWndData* pData, void* pObject)
{
pData->m_pThis = pObject;
pData->m_dwThreadID = ::GetCurrentThreadId();
::EnterCriticalSection(&m_csWindowCreate);
pData->m_pNext = m_pCreateWndList;
m_pCreateWndList = pData;
::LeaveCriticalSection(&m_csWindowCreate);
}

void* CComModule::ExtractCreateWndData()
{
::EnterCriticalSection(&m_csWindowCreate);
_AtlCreateWndData* pEntry = m_pCreateWndList;
if(pEntry == NULL)
{
::LeaveCriticalSection(&m_csWindowCreate);
return NULL;
}

DWORD dwThreadID = ::GetCurrentThreadId();
_AtlCreateWndData* pPrev = NULL;
while(pEntry != NULL)
{
if(pEntry->m_dwThreadID == dwThreadID)
{
if(pPrev == NULL)
m_pCreateWndList = pEntry->m_pNext;
else
pPrev->m_pNext = pEntry->m_pNext;
::LeaveCriticalSection(&m_csWindowCreate);
return pEntry->m_pThis;
}
pPrev = pEntry;
pEntry = pEntry->m_pNext;
}

::LeaveCriticalSection(&m_csWindowCreate);
return NULL;
}

#ifdef _ATL_STATIC_REGISTRY
// Statically linking to Registry Ponent
HRESULT WINAPI CComModule::UpdateRegistryFromResourceS(UINT nResID, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries)
{
USES_CONVERSION;
CRegObject ro;
TCHAR szModule[_MAX_PATH];
GetModuleFileName(_Module.GetModuleInstance(), szModule, _MAX_PATH);
LPOLESTR pszModule = T2OLE(szModule);
ro.AddReplacement(OLESTR("Module"), pszModule);
if (NULL != pMapEntries)
{
while (NULL != pMapEntries->szKey)
{
_ASSERTE(NULL != pMapEntries->szData);
ro.AddReplacement(pMapEntries->szKey, pMapEntries->szData);
pMapEntries++;
}
}

LPCOLESTR szType = OLESTR("REGISTRY");
return (bRegister) ? ro.ResourceRegister(pszModule, nResID, szType) :
ro.ResourceUnregister(pszModule, nResID, szType);
}

HRESULT WINAPI CComModule::UpdateRegistryFromResourceS(LPCTSTR lpszRes, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries)
{
USES_CONVERSION;
CRegObject ro;
TCHAR szModule[_MAX_PATH];
GetModuleFileName(_Module.GetModuleInstance(), szModule, _MAX_PATH);
LPOLESTR pszModule = T2OLE(szModule);
ro.AddReplacement(OLESTR("Module"), pszModule);
if (NULL != pMapEntries)
{
while (NULL != pMapEntries->szKey)
{
_ASSERTE(NULL != pMapEntries->szData);
ro.AddReplacement(pMapEntries->szKey, pMapEntries->szData);
pMapEntries++;
}
}

LPCOLESTR szType = OLESTR("REGISTRY");
LPCOLESTR pszRes = T2COLE(lpszRes);
return (bRegister) ? ro.ResourceRegisterSz(pszModule, pszRes, szType) :
ro.ResourceUnregisterSz(pszModule, pszRes, szType);
}
#endif // _ATL_STATIC_REGISTRY

HRESULT WINAPI CComModule::UpdateRegistryClass(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID, UINT nDescID, DWORD dwFlags, BOOL bRegister)
{
if (bRegister)
{
return RegisterClassHelper(clsid, lpszProgID, lpszVerIndProgID, nDescID,
dwFlags);
}
else
return UnregisterClassHelper(clsid, lpszProgID, lpszVerIndProgID);
}

HRESULT WINAPI CComModule::RegisterClassHelper(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID, UINT nDescID, DWORD dwFlags)
{
static const TCHAR szProgID[] = _T("ProgID");
static const TCHAR szVIProgID[] = _T("VersionIndependentProgID");
static const TCHAR szLS32[] = _T("LocalServer32");
static const TCHAR szIPS32[] = _T("InprocServer32");
static const TCHAR szThreadingModel[] = _T("ThreadingModel");
static const TCHAR szAUTPRX32[] = _T("AUTPRX32.DLL");
static const TCHAR szApartment[] = _T("Apartment");
static const TCHAR szBoth[] = _T("both");
USES_CONVERSION;
HRESULT hRes = S_OK;
TCHAR szDesc[256];
LoadString(m_hInst, nDescID, szDesc, 256);
TCHAR szModule[_MAX_PATH];
GetModuleFileName(m_hInst, szModule, _MAX_PATH);

LPOLESTR lpOleStr;
StringFromCLSID(clsid, &lpOleStr);
LPTSTR lpsz = OLE2T(lpOleStr);

hRes = AtlRegisterProgID(lpsz, lpszProgID, szDesc);
if (hRes == S_OK)
hRes = AtlRegisterProgID(lpsz, lpszVerIndProgID, szDesc);
LONG lRes = ERROR_SUCCESS;
if (hRes == S_OK)
{
CRegKey key;
LONG lRes = key.Open(HKEY_CLASSES_ROOT, _T("CLSID"));
if (lRes == ERROR_SUCCESS)
{
lRes = key.Create(key, lpsz);
if (lRes == ERROR_SUCCESS)
{
key.SetValue(szDesc);
key.SetKeyValue(szProgID, lpszProgID);
key.SetKeyValue(szVIProgID, lpszVerIndProgID);

if ((m_hInst == NULL) || (m_hInst == GetModuleHandle(NULL))) // register as EXE
key.SetKeyValue(szLS32, szModule);
else
{
key.SetKeyValue(szIPS32, (dwFlags & AUTPRXFLAG) ? szAUTPRX32 : szModule);
LPCTSTR lpszModel = (dwFlags & THREADFLAGS_BOTH) ? szBoth :
(dwFlags & THREADFLAGS_APARTMENT) ? szApartment : NULL;
if (lpszModel != NULL)
key.SetKeyValue(szIPS32, lpszModel, szThreadingModel);
}
}
}
}
CoTaskMemFree(lpOleStr);
if (lRes != ERROR_SUCCESS)
hRes = HRESULT_FROM_WIN32(lRes);
return hRes;
}

HRESULT WINAPI CComModule::UnregisterClassHelper(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID)
{
USES_CONVERSION;
CRegKey key;

key.Attach(HKEY_CLASSES_ROOT);
if (lpszProgID != NULL && lstrcmpi(lpszProgID, _T("")))
key.RecurseDeleteKey(lpszProgID);
if (lpszVerIndProgID != NULL && lstrcmpi(lpszVerIndProgID, _T("")))
key.RecurseDeleteKey(lpszVerIndProgID);
LPOLESTR lpOleStr;
StringFromCLSID(clsid, &lpOleStr);
LPTSTR lpsz = OLE2T(lpOleStr);
if (key.Open(key, _T("CLSID")) == ERROR_SUCCESS)
key.RecurseDeleteKey(lpsz);
CoTaskMemFree(lpOleStr);
return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// CRegKey

LONG CRegKey::Close()
{
LONG lRes = ERROR_SUCCESS;
if (m_hKey != NULL)
{
lRes = RegCloseKey(m_hKey);
m_hKey = NULL;
}
return lRes;
}

LONG CRegKey::Create(HKEY hKeyParent, LPCTSTR lpszKeyName,
LPTSTR lpszClass, DWORD dwOptions, REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecAttr, LPDWORD lpdwDisposition)
{
_ASSERTE(hKeyParent != NULL);
DWORD dw;
HKEY hKey = NULL;
LONG lRes = RegCreateKeyEx(hKeyParent, lpszKeyName, 0,
lpszClass, dwOptions, samDesired, lpSecAttr, &hKey, &dw);
if (lpdwDisposition != NULL)
*lpdwDisposition = dw;
if (lRes == ERROR_SUCCESS)
{
lRes = Close();
m_hKey = hKey;
}
return lRes;
}

LONG CRegKey::Open(HKEY hKeyParent, LPCTSTR lpszKeyName, REGSAM samDesired)
{
_ASSERTE(hKeyParent != NULL);
HKEY hKey = NULL;
LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyName, 0, samDesired, &hKey);
if (lRes == ERROR_SUCCESS)
{
lRes = Close();
_ASSERTE(lRes == ERROR_SUCCESS);
m_hKey = hKey;
}
return lRes;
}

LONG CRegKey::QueryValue(DWORD& dwValue, LPCTSTR lpszValueName)
{
DWORD dwType = NULL;
DWORD dwCount = sizeof(DWORD);
LONG lRes = RegQueryValueEx(m_hKey, (LPTSTR)lpszValueName, NULL, &dwType,
(LPBYTE)&dwValue, &dwCount);
_ASSERTE((lRes!=ERROR_SUCCESS) || (dwType == REG_DWORD));
_ASSERTE((lRes!=ERROR_SUCCESS) || (dwCount == sizeof(DWORD)));
return lRes;
}

LONG CRegKey::QueryValue(LPTSTR szValue, LPCTSTR lpszValueName, DWORD* pdwCount)
{
_ASSERTE(pdwCount != NULL);
DWORD dwType = NULL;
LONG lRes = RegQueryValueEx(m_hKey, (LPTSTR)lpszValueName, NULL, &dwType,
(LPBYTE)szValue, pdwCount);
_ASSERTE((lRes!=ERROR_SUCCESS) || (dwType == REG_SZ) ||
 (dwType == REG_MULTI_SZ) || (dwType == REG_EXPAND_SZ));
return lRes;
}

LONG WINAPI CRegKey::SetValue(HKEY hKeyParent, LPCTSTR lpszKeyName, LPCTSTR lpszValue, LPCTSTR lpszValueName)
{
_ASSERTE(lpszValue != NULL);
CRegKey key;
LONG lRes = key.Create(hKeyParent, lpszKeyName);
if (lRes == ERROR_SUCCESS)

lRes = key.SetValue(lpszValue, lpszValueName);
return lRes;
}

LONG CRegKey::SetKeyValue(LPCTSTR lpszKeyName, LPCTSTR lpszValue, LPCTSTR lpszValueName)
{
_ASSERTE(lpszValue != NULL);
CRegKey key;
LONG lRes = key.Create(m_hKey, lpszKeyName);
if (lRes == ERROR_SUCCESS)
lRes = key.SetValue(lpszValue, lpszValueName);
return lRes;
}

LONG CRegKey::SetValue(DWORD dwValue, LPCTSTR lpszValueName)
{
_ASSERTE(m_hKey != NULL);
return RegSetValueEx(m_hKey, lpszValueName, NULL, REG_DWORD,
(BYTE * const)&dwValue, sizeof(DWORD));
}

HRESULT CRegKey::SetValue(LPCTSTR lpszValue, LPCTSTR lpszValueName)
{
_ASSERTE(lpszValue != NULL);
_ASSERTE(m_hKey != NULL);
return RegSetValueEx(m_hKey, lpszValueName, NULL, REG_SZ,
(BYTE * const)lpszValue, (lstrlen(lpszValue)+1)*sizeof(TCHAR));
}

//RecurseDeleteKey is necessary because on NT RegDeleteKey doesn't work if the
//specified key has subkeys
LONG CRegKey::RecurseDeleteKey(LPCTSTR lpszKey)
{
CRegKey key;
LONG lRes = key.Open(m_hKey, lpszKey);
if (lRes != ERROR_SUCCESS)
return lRes;
FILETIME time;
TCHAR szBuffer[256];
DWORD dwSize = 256;
while (RegEnumKeyEx(key.m_hKey, 0, szBuffer, &dwSize, NULL, NULL, NULL,
&time)==ERROR_SUCCESS)
{
lRes = key.RecurseDeleteKey(szBuffer);
if (lRes != ERROR_SUCCESS)
return lRes;
dwSize = 256;
}
key.Close();
return DeleteSubKey(lpszKey);
}

#ifdef __ATLCOM_H__
#ifndef _ATL_NO_SECURITY

CSecurityDescriptor::CSecurityDescriptor()
{
m_pSD = NULL;
m_pOwner = NULL;
m_pGroup = NULL;
m_pDACL = NULL;
m_pSACL= NULL;
}

CSecurityDescriptor::~CSecurityDescriptor()
{
if (m_pSD)
delete m_pSD;
if (m_pOwner)
free(m_pOwner);
if (m_pGroup)
free(m_pGroup);
if (m_pDACL)
free(m_pDACL);
if (m_pSACL)
free(m_pSACL);
}

HRESULT CSecurityDescriptor::Initialize()
{
if (m_pSD)
{
delete m_pSD;
m_pSD = NULL;
}
if (m_pOwner)
{
free(m_pOwner);
m_pOwner = NULL;
}
if (m_pGroup)
{
free(m_pGroup);
m_pGroup = NULL;
}
if (m_pDACL)
{
free(m_pDACL);
m_pDACL = NULL;
}
if (m_pSACL)
{
free(m_pSACL);
m_pSACL = NULL;
}

ATLTRY(m_pSD = new SECURITY_DESCRIPTOR);
if (!m_pSD)
return E_OUTOFMEMORY;
if (!InitializeSecurityDescriptor(m_pSD, SECURITY_DESCRIPTOR_REVISION))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
delete m_pSD;
m_pSD = NULL;
_ASSERTE(FALSE);
return hr;
}
// Set the DACL to allow EVERYONE
SetSecurityDescriptorDacl(m_pSD, TRUE, NULL, FALSE);
return S_OK;
}

HRESULT CSecurityDescriptor::InitializeFromProcessToken(BOOL bDefaulted)
{
PSID pUserSid;
PSID pGroupSid;
HRESULT hr;

Initialize();
hr = GetProcessSids(&pUserSid, &pGroupSid);
if (FAILED(hr))
return hr;
hr = SetOwner(pUserSid, bDefaulted);
if (FAILED(hr))
return hr;
hr = SetGroup(pGroupSid, bDefaulted);
if (FAILED(hr))
return hr;
return S_OK;
}

HRESULT CSecurityDescriptor::InitializeFromThreadToken(BOOL bDefaulted, BOOL bRevertToProcessToken)
{
PSID pUserSid;
PSID pGroupSid;
HRESULT hr;

Initialize();
hr = GetThreadSids(&pUserSid, &pGroupSid);
if (HRESULT_CODE(hr) == ERROR_NO_TOKEN && bRevertToProcessToken)
hr = GetProcessSids(&pUserSid, &pGroupSid);
if (FAILED(hr))
return hr;
hr = SetOwner(pUserSid, bDefaulted);
if (FAILED(hr))
return hr;
hr = SetGroup(pGroupSid, bDefaulted);
if (FAILED(hr))
return hr;
return S_OK;
}

HRESULT CSecurityDescriptor::SetOwner(PSID pOwnerSid, BOOL bDefaulted)
{
_ASSERTE(m_pSD);

// Mark the SD as having no owner
if (!SetSecurityDescriptorOwner(m_pSD, NULL, bDefaulted))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}

if (m_pOwner)
{
free(m_pOwner);
m_pOwner = NULL;
}

// If they asked for no owner don't do the copy
if (pOwnerSid == NULL)
return S_OK;

// Make a copy of the Sid for the return value
DWORD dwSize = GetLengthSid(pOwnerSid);

m_pOwner = (PSID) malloc(dwSize);
if (!m_pOwner)
{
// Insufficient memory to allocate Sid
_ASSERTE(FALSE);
return E_OUTOFMEMORY;
}
if (!CopySid(dwSize, m_pOwner, pOwnerSid))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
free(m_pOwner);
m_pOwner = NULL;
return hr;
}

_ASSERTE(IsValidSid(m_pOwner));

if (!SetSecurityDescriptorOwner(m_pSD, m_pOwner, bDefaulted))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
free(m_pOwner);
m_pOwner = NULL;
return hr;
}

return S_OK;
}

HRESULT CSecurityDescriptor::SetGroup(PSID pGroupSid, BOOL bDefaulted)
{
_ASSERTE(m_pSD);

// Mark the SD as having no Group
if (!SetSecurityDescriptorGroup(m_pSD, NULL, bDefaulted))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}

if (m_pGroup)
{
free(m_pGroup);
m_pGroup = NULL;
}

// If they asked for no Group don't do the copy
if (pGroupSid == NULL)
return S_OK;

// Make a copy of the Sid for the return value
DWORD dwSize = GetLengthSid(pGroupSid);

m_pGroup = (PSID) malloc(dwSize);
if (!m_pGroup)
{
// Insufficient memory to allocate Sid
_ASSERTE(FALSE);
return E_OUTOFMEMORY;
}
if (!CopySid(dwSize, m_pGroup, pGroupSid))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
free(m_pGroup);
m_pGroup = NULL;
return hr;
}

_ASSERTE(IsValidSid(m_pGroup));

if (!SetSecurityDescriptorGroup(m_pSD, m_pGroup, bDefaulted))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
free(m_pGroup);
m_pGroup = NULL;
return hr;
}

return S_OK;
}

HRESULT CSecurityDescriptor::Allow(LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
HRESULT hr = AddAccessAllowedACEToACL(&m_pDACL, pszPrincipal, dwAccessMask);
if (SUCCEEDED(hr))
SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE);
return hr;
}

HRESULT CSecurityDescriptor::Deny(LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
HRESULT hr = AddAccessDeniedACEToACL(&m_pDACL, pszPrincipal, dwAccessMask);
if (SUCCEEDED(hr))
SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE);
return hr;
}

HRESULT CSecurityDescriptor::Revoke(LPCTSTR pszPrincipal)
{
HRESULT hr = RemovePrincipalFromACL(m_pDACL, pszPrincipal);
if (SUCCEEDED(hr))
SetSecurityDescriptorDacl(m_pSD, TRUE, m_pDACL, FALSE);
return hr;
}

HRESULT CSecurityDescriptor::GetProcessSids(PSID* ppUserSid, PSID* ppGroupSid)
{
BOOL bRes;
HRESULT hr;
HANDLE hToken = NULL;
if (ppUserSid)
*ppUserSid = NULL;
if (ppGroupSid)
*ppGroupSid = NULL;
bRes = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
if (!bRes)
{
// Couldn't open process token
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}
hr = GetTokenSids(hToken, ppUserSid, ppGroupSid);
return hr;
}

HRESULT CSecurityDescriptor::GetThreadSids(PSID* ppUserSid, PSID* ppGroupSid, BOOL bOpenAsSelf)
{
BOOL bRes;
HRESULT hr;
HANDLE hToken = NULL;
if (ppUserSid)
*ppUserSid = NULL;
if (ppGroupSid)
*ppGroupSid = NULL;
bRes = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, bOpenAsSelf, &hToken);
if (!bRes)
{
// Couldn't open thread token
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
hr = GetTokenSids(hToken, ppUserSid, ppGroupSid);
return hr;
}

HRESULT CSecurityDescriptor::GetTokenSids(HANDLE hToken, PSID* ppUserSid, PSID* ppGroupSid)
{
DWORD dwSize;
HRESULT hr;
PTOKEN_USER ptkUser = NULL;
PTOKEN_PRIMARY_GROUP ptkGroup = NULL;

if (ppUserSid)
*ppUserSid = NULL;
if (ppGroupSid)
*ppGroupSid = NULL;

if (ppUserSid)
{
// Get length required for TokenUser by specifying buffer length of 0
GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
hr = GetLastError();
if (hr != ERROR_INSUFFICIENT_BUFFER)
{
// Expected ERROR_INSUFFICIENT_BUFFER
_ASSERTE(FALSE);
hr = HRESULT_FROM_WIN32(hr);
goto failed;
}

ptkUser = (TOKEN_USER*) malloc(dwSize);
if (!ptkUser)
{
// Insufficient memory to allocate TOKEN_USER
_ASSERTE(FALSE);
hr = E_OUTOFMEMORY;
goto failed;
}
// Get Sid of process token.
if (!GetTokenInformation(hToken, TokenUser, ptkUser, dwSize, &dwSize))
{
// Couldn't get user info
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
goto failed;
}

// Make a copy of the Sid for the return value
dwSize = GetLengthSid(ptkUser->User.Sid);

PSID pSid = (PSID) malloc(dwSize);
if (!pSid)
{
// Insufficient memory to allocate Sid
_ASSERTE(FALSE);
hr = E_OUTOFMEMORY;
goto failed;
}
if (!CopySid(dwSize, pSid, ptkUser->User.Sid))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
goto failed;
}

_ASSERTE(IsValidSid(pSid));
*ppUserSid = pSid;
free(ptkUser);
}
if (ppGroupSid)
{
// Get length required for TokenPrimaryGroup by specifying buffer length of 0
GetTokenInformation(hToken, TokenPrimaryGroup, NULL, 0, &dwSize);
hr = GetLastError();
if (hr != ERROR_INSUFFICIENT_BUFFER)
{
// Expected ERROR_INSUFFICIENT_BUFFER
_ASSERTE(FALSE);
hr = HRESULT_FROM_WIN32(hr);
goto failed;
}

ptkGroup = (TOKEN_PRIMARY_GROUP*) malloc(dwSize);
if (!ptkGroup)
{
// Insufficient memory to allocate TOKEN_USER
_ASSERTE(FALSE);
hr = E_OUTOFMEMORY;
goto failed;
}
// Get Sid of process token.
if (!GetTokenInformation(hToken, TokenPrimaryGroup, ptkGroup, dwSize, &dwSize))
{
// Couldn't get user info
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
goto failed;
}

// Make a copy of the Sid for the return value
dwSize = GetLengthSid(ptkGroup->PrimaryGroup);

PSID pSid = (PSID) malloc(dwSize);
if (!pSid)
{
// Insufficient memory to allocate Sid
_ASSERTE(FALSE);
hr = E_OUTOFMEMORY;
goto failed;
}
if (!CopySid(dwSize, pSid, ptkGroup->PrimaryGroup))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
goto failed;
}

_ASSERTE(IsValidSid(pSid));

*ppGroupSid = pSid;
free(ptkGroup);
}

return S_OK;

failed:
if (ptkUser)
free(ptkUser);
if (ptkGroup)
free (ptkGroup);
return hr;
}

HRESULT CSecurityDescriptor::GetCurrentUserSID(PSID *ppSid)
{
HANDLE tkHandle;

if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tkHandle))
{
TOKEN_USER *tkUser;
DWORD tkSize;
DWORD sidLength;

// Call to get size information for alloc
GetTokenInformation(tkHandle, TokenUser, NULL, 0, &tkSize);
tkUser = (TOKEN_USER *) malloc(tkSize);

// Now make the real call
if (GetTokenInformation(tkHandle, TokenUser, tkUser, tkSize, &tkSize))
{
sidLength = GetLengthSid(tkUser->User.Sid);
*ppSid = (PSID) malloc(sidLength);

memcpy(*ppSid, tkUser->User.Sid, sidLength);
CloseHandle(tkHandle);

free(tkUser);
return S_OK;
}
else
{
free(tkUser);
return HRESULT_FROM_WIN32(GetLastError());
}
}
return HRESULT_FROM_WIN32(GetLastError());
}

HRESULT CSecurityDescriptor::GetPrincipalSID(LPCTSTR pszPrincipal, PSID *ppSid)
{
HRESULT hr;
LPTSTR pszRefDomain = NULL;
DWORD dwDomainSize = 0;
DWORD dwSidSize = 0;
SID_NAME_USE snu;

// Call to get size info for alloc
LookupAccountName(NULL, pszPrincipal, *ppSid, &dwSidSize, pszRefDomain, &dwDomainSize, &snu);

hr = GetLastError();
if (hr != ERROR_INSUFFICIENT_BUFFER)
return HRESULT_FROM_WIN32(hr);

ATLTRY(pszRefDomain = new TCHAR[dwDomainSize]);
if (pszRefDomain == NULL)
return E_OUTOFMEMORY;

*ppSid = (PSID) malloc(dwSidSize);
if (*ppSid != NULL)
{
if (!LookupAccountName(NULL, pszPrincipal, *ppSid, &dwSidSize, pszRefDomain, &dwDomainSize, &snu))
{
free(*ppSid);
*ppSid = NULL;
delete[] pszRefDomain;
return HRESULT_FROM_WIN32(GetLastError());
}
delete[] pszRefDomain;
return S_OK;
}
delete[] pszRefDomain;
return E_OUTOFMEMORY;
}

HRESULT CSecurityDescriptor::Attach(PSECURITY_DESCRIPTOR pSelfRelativeSD)
{
PACL    pDACL = NULL;
PACL    pSACL = NULL;
BOOL    bDACLPresent, bSACLPresent;
BOOL    bDefaulted;
PACL    m_pDACL = NULL;
ACCESS_ALLOWED_ACE* pACE;
HRESULT hr;
PSID    pUserSid;
PSID    pGroupSid;

hr = Initialize();
if(FAILED(hr))
return hr;

// get the existing DACL.
if (!GetSecurityDescriptorDacl(pSelfRelativeSD, &bDACLPresent, &pDACL, &bDefaulted))
goto failed;

if (bDACLPresent)
{
if (pDACL)
{
// allocate new DACL.
m_pDACL = (PACL) malloc(pDACL->AclSize);
if (!m_pDACL)
goto failed;

// initialize the DACL
if (!InitializeAcl(m_pDACL, pDACL->AclSize, ACL_REVISION))
goto failed;

// copy the ACES
for (int i = 0; i < pDACL->AceCount; i++)
{
if (!GetAce(pDACL, i, (void **)&pACE))
goto failed;

if (!AddAccessAllowedAce(m_pDACL, ACL_REVISION, pACE->Mask, (PSID)&(pACE->SidStart)))
goto failed;
}

if (!IsValidAcl(m_pDACL))
goto failed;
}

// set the DACL
if (!SetSecurityDescriptorDacl(m_pSD, m_pDACL ? TRUE : FALSE, m_pDACL, bDefaulted))
goto failed;
}

// get the existing SACL.
if (!GetSecurityDescriptorSacl(pSelfRelativeSD, &bSACLPresent, &pSACL, &bDefaulted))
goto failed;

if (bSACLPresent)
{
if (pSACL)
{
// allocate new SACL.
m_pSACL = (PACL) malloc(pSACL->AclSize);
if (!m_pSACL)
goto failed;

// initialize the SACL
if (!InitializeAcl(m_pSACL, pSACL->AclSize, ACL_REVISION))
goto failed;

// copy the ACES
for (int i = 0; i < pSACL->AceCount; i++)
{
if (!GetAce(pSACL, i, (void **)&pACE))
goto failed;

if (!AddAccessAllowedAce(m_pSACL, ACL_REVISION, pACE->Mask, (PSID)&(pACE->SidStart)))
goto failed;
}

if (!IsValidAcl(m_pSACL))
goto failed;
}

// set the SACL
if (!SetSecurityDescriptorSacl(m_pSD, m_pSACL ? TRUE : FALSE, m_pSACL, bDefaulted))
goto failed;
}

if (!GetSecurityDescriptorOwner(m_pSD, &pUserSid, &bDefaulted))
goto failed;

if (FAILED(SetOwner(pUserSid, bDefaulted)))
goto failed;

if (!GetSecurityDescriptorGroup(m_pSD, &pGroupSid, &bDefaulted))
goto failed;

if (FAILED(SetGroup(pGroupSid, bDefaulted)))
goto failed;

if (!IsValidSecurityDescriptor(m_pSD))
goto failed;

return hr;

failed:
if (m_pDACL)
free(m_pDACL);
if (m_pSD)
free(m_pSD);
return E_UNEXPECTED;
}

HRESULT CSecurityDescriptor::AttachObject(HANDLE hObject)
{
HRESULT hr;
DWORD dwSize = 0;
PSECURITY_DESCRIPTOR pSD = NULL;

GetKernelObjectSecurity(hObject, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION, pSD, 0, &dwSize);

hr = GetLastError();
if (hr != ERROR_INSUFFICIENT_BUFFER)
return HRESULT_FROM_WIN32(hr);

pSD = (PSECURITY_DESCRIPTOR) malloc(dwSize);

if (!GetKernelObjectSecurity(hObject, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION, pSD, dwSize, &dwSize))
{
hr = HRESULT_FROM_WIN32(GetLastError());
free(pSD);
return hr;
}

hr = Attach(pSD);
free(pSD);
return hr;
}

HRESULT CSecurityDescriptor::CopyACL(PACL pDest, PACL pSrc)
{
ACL_SIZE_INFORMATION aclSizeInfo;
LPVOID pAce;
ACE_HEADER *aceHeader;

if (pSrc == NULL)
return S_OK;

if (!GetAclInformation(pSrc, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
return HRESULT_FROM_WIN32(GetLastError());

// Copy all of the ACEs to the new ACL
for (UINT i = 0; i < aclSizeInfo.AceCount; i++)
{
if (!GetAce(pSrc, i, &pAce))
return HRESULT_FROM_WIN32(GetLastError());

aceHeader = (ACE_HEADER *) pAce;

if (!AddAce(pDest, ACL_REVISION, 0xffffffff, pAce, aceHeader->AceSize))
return HRESULT_FROM_WIN32(GetLastError());
}

return S_OK;
}

HRESULT CSecurityDescriptor::AddAccessDeniedACEToACL(PACL *ppAcl, LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
ACL_SIZE_INFORMATION aclSizeInfo;
int aclSize;
DWORD returnValue;
PSID principalSID;
PACL oldACL, newACL;

oldACL = *ppAcl;

returnValue = GetPrincipalSID(pszPrincipal, &principalSID);
if (FAILED(returnValue))
return returnValue;

aclSizeInfo.AclBytesInUse = 0;
if (*ppAcl != NULL)
GetAclInformation(oldACL, (LPVOID) &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_DENIED_ACE) + GetLengthSid(principalSID) - sizeof(DWORD);

ATLTRY(newACL = (PACL) new BYTE[aclSize]);

if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

if (!AddAccessDeniedAce(newACL, ACL_REVISION2, dwAccessMask, principalSID))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

returnValue = CopyACL(newACL, oldACL);
if (FAILED(returnValue))
{
free(principalSID);
return returnValue;
}

*ppAcl = newACL;

if (oldACL != NULL)
free(oldACL);
free(principalSID);
return S_OK;
}

HRESULT CSecurityDescriptor::AddAccessAllowedACEToACL(PACL *ppAcl, LPCTSTR pszPrincipal, DWORD dwAccessMask)
{
ACL_SIZE_INFORMATION aclSizeInfo;
int aclSize;
DWORD returnValue;
PSID principalSID;
PACL oldACL, newACL;

oldACL = *ppAcl;

returnValue = GetPrincipalSID(pszPrincipal, &principalSID);
if (FAILED(returnValue))
return returnValue;

aclSizeInfo.AclBytesInUse = 0;
if (*ppAcl != NULL)
GetAclInformation(oldACL, (LPVOID) &aclSizeInfo, (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

aclSize = aclSizeInfo.AclBytesInUse + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(principalSID) - sizeof(DWORD);

ATLTRY(newACL = (PACL) new BYTE[aclSize]);

if (!InitializeAcl(newACL, aclSize, ACL_REVISION))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

returnValue = CopyACL(newACL, oldACL);
if (FAILED(returnValue))
{
free(principalSID);
return returnValue;
}

if (!AddAccessAllowedAce(newACL, ACL_REVISION2, dwAccessMask, principalSID))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

*ppAcl = newACL;

if (oldACL != NULL)
free(oldACL);
free(principalSID);
return S_OK;
}

HRESULT CSecurityDescriptor::RemovePrincipalFromACL(PACL pAcl, LPCTSTR pszPrincipal)
{
ACL_SIZE_INFORMATION aclSizeInfo;
ULONG i;
LPVOID ace;
ACCESS_ALLOWED_ACE *accessAllowedAce;
ACCESS_DENIED_ACE *accessDeniedAce;
SYSTEM_AUDIT_ACE *systemAuditAce;
PSID principalSID;
DWORD returnValue;
ACE_HEADER *aceHeader;

returnValue = GetPrincipalSID(pszPrincipal, &principalSID);
if (FAILED(returnValue))
return returnValue;

GetAclInformation(pAcl, (LPVOID) &aclSizeInfo, (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

for (i = 0; i < aclSizeInfo.AceCount; i++)
{
if (!GetAce(pAcl, i, &ace))
{
free(principalSID);
return HRESULT_FROM_WIN32(GetLastError());
}

aceHeader = (ACE_HEADER *) ace;

if (aceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
{
accessAllowedAce = (ACCESS_ALLOWED_ACE *) ace;

if (EqualSid(principalSID, (PSID) &accessAllowedAce->SidStart))
{
DeleteAce(pAcl, i);
free(principalSID);
return S_OK;
}
} else

if (aceHeader->AceType == ACCESS_DENIED_ACE_TYPE)
{
accessDeniedAce = (ACCESS_DENIED_ACE *) ace;

if (EqualSid(principalSID, (PSID) &accessDeniedAce->SidStart))
{
DeleteAce(pAcl, i);
free(principalSID);
return S_OK;
}
} else

if (aceHeader->AceType == SYSTEM_AUDIT_ACE_TYPE)
{
systemAuditAce = (SYSTEM_AUDIT_ACE *) ace;

if (EqualSid(principalSID, (PSID) &systemAuditAce->SidStart))
{
DeleteAce(pAcl, i);
free(principalSID);
return S_OK;
}
}
}
free(principalSID);
return S_OK;
}

HRESULT CSecurityDescriptor::SetPrivilege(LPCTSTR privilege, BOOL bEnable, HANDLE hToken)
{
HRESULT hr;
TOKEN_PRIVILEGES tpPrevious;
TOKEN_PRIVILEGES tp;
DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);
LUID luid;

// if no token specified open process token
if (hToken == 0)
{
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}
}

if (!LookupPrivilegeValue(NULL, privilege, &luid ))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;

if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}

tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;

if (bEnable)
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
else
tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes);

if (!AdjustTokenPrivileges(hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
_ASSERTE(FALSE);
return hr;
}
return S_OK;
}

#endif //_ATL_NO_SECURITY
#endif //__ATLCOM_H__

#ifdef _DEBUG

void _cdecl AtlTrace(LPCTSTR lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);

int nBuf;
TCHAR szBuffer[512];

nBuf = _vstprintf(szBuffer, lpszFormat, args);
_ASSERTE(nBuf < sizeof(szBuffer));

OutputDebugString(szBuffer);
va_end(args);
}
#endif

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL
#endif

///////////////////////////////////////////////////////////////////////////////
//All Global stuff goes below this line
///////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// Minimize CRT
// Specify DllMain as EntryPoint
// Turn off exception handling
// Define _ATL_MIN_CRT

#ifdef _ATL_MIN_CRT
/////////////////////////////////////////////////////////////////////////////
// Heap Allocation

#ifndef _DEBUG

#ifndef _MERGE_PROXYSTUB
//rpcproxy.h does the same thing as this
int __cdecl _purecall()
{
DebugBreak();
return 0;
}
#endif

extern "C" const int _fltused = 0;

void* __cdecl malloc(size_t n)
{
if (_Module.m_hHeap == NULL)
{
_Module.m_hHeap = HeapCreate(0, 0, 0);
if (_Module.m_hHeap == NULL)
return NULL;
}
_ASSERTE(_Module.m_hHeap != NULL);

#ifdef _MALLOC_ZEROINIT
return HeapAlloc(_Module.m_hHeap, HEAP_ZERO_MEMORY, n);
#else
return HeapAlloc(_Module.m_hHeap, 0, n);
#endif
}

void* __cdecl calloc(size_t n, size_t s)
{
return malloc(n * s);
}

void* __cdecl realloc(void* p, size_t n)
{
_ASSERTE(_Module.m_hHeap != NULL);
#ifdef _MALLOC_ZEROINIT
return (p == NULL) ? malloc(n) : HeapReAlloc(_Module.m_hHeap, HEAP_ZERO_MEMORY, p, n);
#else
return (p == NULL) ? malloc(n) : HeapReAlloc(_Module.m_hHeap, 0, p, n);
#endif
}

void __cdecl free(void* p)
{
_ASSERTE(_Module.m_hHeap != NULL);
HeapFree(_Module.m_hHeap, 0, p);
}

void* __cdecl operator new(size_t n)
{
return malloc(n);
}

void __cdecl operator delete(void* p)
{
free(p);
}

#endif  //_DEBUG

#endif //_ATL_MIN_CRT

#ifndef _ATL_DLL

#ifndef ATL_NO_NAMESPACE
#ifndef _ATL_DLL_IMPL
namespace ATL
{
#endif
#endif

/////////////////////////////////////////////////////////////////////////////
// statics

static UINT WINAPI AtlGetDirLen(LPCOLESTR lpszPathName)
{
_ASSERTE(lpszPathName != NULL);

// always capture the complete file name including extension (if present)
LPCOLESTR lpszTemp = lpszPathName;
for (LPCOLESTR lpsz = lpszPathName; *lpsz != NULL; )
{
LPCOLESTR lp = CharNextO(lpsz);
// remember last directory/drive separator
if (*lpsz == OLESTR('\\') || *lpsz == OLESTR('/') || *lpsz == OLESTR(':'))
lpszTemp = lp;
lpsz = lp;
}

return lpszTemp-lpszPathName;
}

/////////////////////////////////////////////////////////////////////////////
// QI support

ATLAPI AtlInternalQueryInterface(void* pThis,
const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
{
_ASSERTE(pThis != NULL);
// First entry in the com map should be a simple map entry
_ASSERTE(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
if (ppvObject == NULL)
return E_POINTER;
*ppvObject = NULL;
if (InlineIsEqualUnknown(iid)) // use first interface
{
IUnknown* pUnk = (IUnknown*)((int)pThis+pEntries->dw);
pUnk->AddRef();
*ppvObject = pUnk;
return S_OK;
}
while (pEntries->pFunc != NULL)
{
BOOL bBlind = (pEntries->piid == NULL);
if (bBlind || InlineIsEqualGUID(*(pEntries->piid), iid))
{
if (pEntries->pFunc == _ATL_SIMPLEMAPENTRY) //offset
{
_ASSERTE(!bBlind);
IUnknown* pUnk = (IUnknown*)((int)pThis+pEntries->dw);
pUnk->AddRef();
*ppvObject = pUnk;
return S_OK;
}
else //actual function call
{
HRESULT hRes = pEntries->pFunc(pThis,
iid, ppvObject, pEntries->dw);
if (hRes == S_OK || (!bBlind && FAILED(hRes)))
return hRes;
}
}
pEntries++;
}
return E_NOINTERFACE;
}

/////////////////////////////////////////////////////////////////////////////
// Smart Pointer helpers

ATLAPI_(IUnknown*) AtlComPtrAssign(IUnknown** pp, IUnknown* lp)
{
if (lp != NULL)
lp->AddRef();
if (*pp)
(*pp)->Release();
*pp = lp;
return lp;
}

ATLAPI_(IUnknown*) AtlComQIPtrAssign(IUnknown** pp, IUnknown* lp, REFIID riid)
{
IUnknown* pTemp = *pp;
lp->QueryInterface(riid, (void**)pp);
if (pTemp)
pTemp->Release();
return *pp;
}

/////////////////////////////////////////////////////////////////////////////
// Inproc Marshaling helpers

ATLAPI AtlFreeMarshalStream(IStream* pStream)
{
if (pStream != NULL)
{
CoReleaseMarshalData(pStream);
pStream->Release();
}
return S_OK;
}

ATLAPI AtlMarshalPtrInProc(IUnknown* pUnk, const IID& iid, IStream** ppStream)
{
HRESULT hRes = CreateStreamOnHGlobal(NULL, TRUE, ppStream);
if (SUCCEEDED(hRes))
{
hRes = CoMarshalInterface(*ppStream, iid,
pUnk, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLESTRONG);
if (FAILED(hRes))
{
(*ppStream)->Release();
*ppStream = NULL;
}
}
return hRes;
}

ATLAPI AtlUnmarshalPtr(IStream* pStream, const IID& iid, IUnknown** ppUnk)
{
*ppUnk = NULL;
HRESULT hRes = E_INVALIDARG;
if (pStream != NULL)
{
LARGE_INTEGER l;
l.QuadPart = 0;
pStream->Seek(l, STREAM_SEEK_SET, NULL);
hRes = CoUnmarshalInterface(pStream, iid, (void**)ppUnk);
}
return hRes;
}

ATLAPI_(BOOL) AtlWaitWithMessageLoop(HANDLE hEvent)
{
DWORD dwRet;
MSG msg;

while(1)
{
dwRet = MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT);

if (dwRet == WAIT_OBJECT_0)
return TRUE;    // The event was signaled

if (dwRet != WAIT_OBJECT_0 + 1)
break;          // Something else happened

// There is one or more window message available. Dispatch them
while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
return TRUE; // Event is now signaled.
}
}
return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Connection Point Helpers

ATLAPI AtlAdvise(IUnknown* pUnkCP, IUnknown* pUnk, const IID& iid, LPDWORD pdw)
{
CComPtr<IConnectionPointContainer> pCPC;
CComPtr<IConnectionPoint> pCP;
HRESULT hRes = pUnkCP->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
if (SUCCEEDED(hRes))
hRes = pCPC->FindConnectionPoint(iid, &pCP);
if (SUCCEEDED(hRes))
hRes = pCP->Advise(pUnk, pdw);
return hRes;
}

ATLAPI AtlUnadvise(IUnknown* pUnkCP, const IID& iid, DWORD dw)

{
CComPtr<IConnectionPointContainer> pCPC;
CComPtr<IConnectionPoint> pCP;
HRESULT hRes = pUnkCP->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
if (SUCCEEDED(hRes))
hRes = pCPC->FindConnectionPoint(iid, &pCP);
if (SUCCEEDED(hRes))
hRes = pCP->Unadvise(dw);
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// IDispatch Error handling

ATLAPI AtlSetErrorInfo(const CLSID& clsid, LPCOLESTR lpszDesc, DWORD dwHelpID,
LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes, HINSTANCE hInst)
{
USES_CONVERSION;
TCHAR szDesc[1024];
szDesc[0] = NULL;
// For a valid HRESULT the id should be in the range [0x0200, 0xffff]
if (HIWORD(lpszDesc) == 0) //id
{
UINT nID = LOWORD((DWORD)lpszDesc);
_ASSERTE((nID >= 0x0200 && nID <= 0xffff) || hRes != 0);
if (LoadString(hInst, nID, szDesc, 1024) == 0)
{
_ASSERTE(FALSE);
lstrcpy(szDesc, _T("Unknown Error"));
}
lpszDesc = T2OLE(szDesc);
if (hRes == 0)
hRes = MAKE_HRESULT(3, FACILITY_ITF, nID);
}

CComPtr<ICreateErrorInfo> pICEI;
if (SUCCEEDED(CreateErrorInfo(&pICEI)))
{
CComPtr<IErrorInfo> pErrorInfo;
pICEI->SetGUID(iid);
LPOLESTR lpsz;
ProgIDFromCLSID(clsid, &lpsz);
if (lpsz != NULL)
pICEI->SetSource(lpsz);
if (dwHelpID != 0 && lpszHelpFile != NULL)
{
pICEI->SetHelpContext(dwHelpID);
pICEI->SetHelpFile(const_cast<LPOLESTR>(lpszHelpFile));
}
CoTaskMemFree(lpsz);
pICEI->SetDescription((LPOLESTR)lpszDesc);
if (SUCCEEDED(pICEI->QueryInterface(IID_IErrorInfo, (void**)&pErrorInfo)))
SetErrorInfo(0, pErrorInfo);
}
//#ifdef _DEBUG
//  USES_CONVERSION;
//  ATLTRACE(_T("AtlReportError: Description=\"%s\" returning %x\n"), OLE2CT(lpszDesc), hRes);
//#endif
return (hRes == 0) ? DISP_E_EXCEPTION : hRes;
}

/////////////////////////////////////////////////////////////////////////////
// Module

//Although these functions are big, they are only used once in a module
//so we should make them inline.

ATLAPI AtlModuleInit(_ATL_MODULE* pM, _ATL_OBJMAP_ENTRY* p, HINSTANCE h)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
if (pM->cbSize < sizeof(_ATL_MODULE))
return E_INVALIDARG;
pM->m_pObjMap = p;
pM->m_hInst = pM->m_hInstTypeLib = pM->m_hInstResource = h;
pM->m_nLockCnt=0L;
pM->m_hHeap = NULL;
InitializeCriticalSection(&pM->m_csTypeInfoHolder);
InitializeCriticalSection(&pM->m_csWindowCreate);
InitializeCriticalSection(&pM->m_csObjMap);
return S_OK;
}

ATLAPI AtlModuleRegisterClassObjects(_ATL_MODULE* pM, DWORD dwClsContext, DWORD dwFlags)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
HRESULT hRes = S_OK;
while (pEntry->pclsid != NULL && hRes == S_OK)
{
hRes = pEntry->RegisterClassObject(dwClsContext, dwFlags);
pEntry++;
}
return hRes;
}

ATLAPI AtlModuleRevokeClassObjects(_ATL_MODULE* pM)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
HRESULT hRes = S_OK;
while (pEntry->pclsid != NULL && hRes == S_OK)
{
hRes = pEntry->RevokeClassObject();
pEntry++;
}
return hRes;
}

ATLAPI AtlModuleGetClassObject(_ATL_MODULE* pM, REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
HRESULT hRes = S_OK;
if (ppv == NULL)
return E_POINTER;
while (pEntry->pclsid != NULL)
{
if (InlineIsEqualGUID(rclsid, *pEntry->pclsid))
{
if (pEntry->pCF == NULL)
{
EnterCriticalSection(&pM->m_csObjMap);
if (pEntry->pCF == NULL)
hRes = pEntry->pfnGetClassObject(pEntry->pfnCreateInstance, IID_IUnknown, (LPVOID*)&pEntry->pCF);
LeaveCriticalSection(&pM->m_csObjMap);
}
if (pEntry->pCF != NULL)
hRes = pEntry->pCF->QueryInterface(riid, ppv);
break;
}
pEntry++;
}
if (*ppv == NULL && hRes == S_OK)
hRes = CLASS_E_CLASSNOTAVAILABLE;
return hRes;
}

ATLAPI AtlModuleTerm(_ATL_MODULE* pM)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_hInst != NULL);
if (pM->m_pObjMap != NULL)
{
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
while (pEntry->pclsid != NULL)
{
if (pEntry->pCF != NULL)
pEntry->pCF->Release();
pEntry->pCF = NULL;
pEntry++;
}
}
DeleteCriticalSection(&pM->m_csTypeInfoHolder);
DeleteCriticalSection(&pM->m_csWindowCreate);
DeleteCriticalSection(&pM->m_csObjMap);
if (pM->m_hHeap != NULL)
HeapDestroy(pM->m_hHeap);
return S_OK;
}

ATLAPI AtlModuleRegisterServer(_ATL_MODULE* pM, BOOL bRegTypeLib, CLSID* pCLSID)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_hInst != NULL);
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
HRESULT hRes = S_OK;
for (;pEntry->pclsid != NULL; pEntry++)
{
if (pCLSID == NULL)
{
if (pEntry->pfnGetObjectDescription() != NULL)
continue;
}
else
{
if (!IsEqualGUID(*pCLSID, *pEntry->pclsid))
continue;
}
hRes = pEntry->pfnUpdateRegistry(TRUE);
if (FAILED(hRes))
break;
}
if (SUCCEEDED(hRes) && bRegTypeLib)
hRes = AtlModuleRegisterTypeLib(pM, 0);
return hRes;
}

ATLAPI AtlModuleUnregisterServer(_ATL_MODULE* pM, CLSID* pCLSID)
{
_ASSERTE(pM != NULL);
if (pM == NULL)
return E_INVALIDARG;
_ASSERTE(pM->m_hInst != NULL);
_ASSERTE(pM->m_pObjMap != NULL);
_ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap;
for (;pEntry->pclsid != NULL; pEntry++)
{
if (pCLSID == NULL)
{
if (pEntry->pfnGetObjectDescription() != NULL)
continue;
}
else
{
if (!IsEqualGUID(*pCLSID, *pEntry->pclsid))
continue;
}
pEntry->pfnUpdateRegistry(FALSE); //unregister
}
return S_OK;
}

ATLAPI AtlModuleUpdateRegistryFromResourceD(_ATL_MODULE* pM, LPCOLESTR lpszRes,
BOOL bRegister, struct _ATL_REGMAP_ENTRY* pMapEntries, IRegistrar* pReg)
{
USES_CONVERSION;
_ASSERTE(pM != NULL);
HRESULT hRes = S_OK;
CComPtr<IRegistrar> p;
if (pReg != NULL)
p = pReg;
else
{
hRes = CoCreateInstance(CLSID_Registrar, NULL,
CLSCTX_INPROC_SERVER, IID_IRegistrar, (void**)&p);
}
if (SUCCEEDED(hRes))
{
TCHAR szModule[_MAX_PATH];
GetModuleFileName(pM->m_hInst, szModule, _MAX_PATH);
p->AddReplacement(OLESTR("Module"), T2OLE(szModule));

if (NULL != pMapEntries)
{
while (NULL != pMapEntries->szKey)
{
_ASSERTE(NULL != pMapEntries->szData);
p->AddReplacement((LPOLESTR)pMapEntries->szKey, (LPOLESTR)pMapEntries->szData);
pMapEntries++;
}
}
LPCOLESTR szType = OLESTR("REGISTRY");
GetModuleFileName(pM->m_hInstResource, szModule, _MAX_PATH);
LPOLESTR pszModule = T2OLE(szModule);
if (HIWORD(lpszRes)==0)
{
if (bRegister)
hRes = p->ResourceRegister(pszModule, ((UINT)LOWORD((DWORD)lpszRes)), szType);
else
hRes = p->ResourceUnregister(pszModule, ((UINT)LOWORD((DWORD)lpszRes)), szType);
}
else
{
if (bRegister)
hRes = p->ResourceRegisterSz(pszModule, lpszRes, szType);
else
hRes = p->ResourceUnregisterSz(pszModule, lpszRes, szType);
}

}
return hRes;
}

/////////////////////////////////////////////////////////////////////////////
// TypeLib Support

ATLAPI AtlModuleRegisterTypeLib(_ATL_MODULE* pM, LPCOLESTR lpszIndex)
{
_ASSERTE(pM != NULL);
USES_CONVERSION;
_ASSERTE(pM->m_hInstTypeLib != NULL);
TCHAR szModule[_MAX_PATH+10];
OLECHAR szDir[_MAX_PATH];
GetModuleFileName(pM->m_hInstTypeLib, szModule, _MAX_PATH);
if (lpszIndex != NULL)
lstrcat(szModule, OLE2CT(lpszIndex));
ITypeLib* pTypeLib;
LPOLESTR lpszModule = T2OLE(szModule);
HRESULT hr = LoadTypeLib(lpszModule, &pTypeLib);
if (!SUCCEEDED(hr))
{
// typelib not in module, try <module>.tlb instead
LPTSTR lpszExt = NULL;
LPTSTR lpsz;
for (lpsz = szModule; *lpsz != NULL; lpsz = CharNext(lpsz))
{
if (*lpsz == _T('.'))
lpszExt = lpsz;
}
if (lpszExt == NULL)
lpszExt = lpsz;
lstrcpy(lpszExt, _T(".tlb"));
lpszModule = T2OLE(szModule);
hr = LoadTypeLib(lpszModule, &pTypeLib);
}
if (SUCCEEDED(hr))
{
ocscpy(szDir, lpszModule);
szDir[AtlGetDirLen(szDir)] = 0;
hr = ::RegisterTypeLib(pTypeLib, lpszModule, szDir);
}
if (pTypeLib != NULL)
pTypeLib->Release();
return hr;
}

#ifndef ATL_NO_NAMESPACE
#ifndef _ATL_DLL_IMPL
}; //namespace ATL
#endif
#endif

#endif //!_ATL_DLL


PAGES.CPP

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *pages.cpp
 *
 *Abstract:
 *
 *Property sheet page routines
 *
\*------------------------------------------------*/

#include "stdafx.h"
#include "wpprov.h"
#include "wppobj.h"
#include "pages.h"
#include "resource.h"
#include "util.h"

const PAGEINFO g_pPageInfo[NUM_PAGES] =
{
{ Page0DlgProc, IDD_PAGE_0 },
{ Page1DlgProc, IDD_PAGE_1 }
};

HRESULT MakeBold(HWND hwnd, BOOL fSize, LONG lfWeight)
{
HRESULThr = ERROR_SUCCESS;
HFONThfont = NULL;
HFONThnewfont = NULL;
LOGFONT*plogfont = NULL;

if (!hwnd) goto MakeBoldExit;

hfont = (HFONT)SendMessage(hwnd,WM_GETFONT,0,0);
if (!hfont)
{
hr = ERROR_GEN_FAILURE;
goto MakeBoldExit;
}

plogfont = (LOGFONT*)malloc(sizeof(LOGFONT));
if (!plogfont)
{
hr = ERROR_NOT_ENOUGH_MEMORY;
goto MakeBoldExit;
}

if (!GetObject(hfont,sizeof(LOGFONT),(LPVOID)plogfont))
{
hr = ERROR_GEN_FAILURE;
goto MakeBoldExit;
}

if (abs(plogfont->lfHeight) < 24 && fSize)
{
plogfont->lfHeight = plogfont->lfHeight + (plogfont->lfHeight / 4);
}

plogfont->lfWeight = (int) lfWeight;

if (!(hnewfont = CreateFontIndirect(plogfont)))
{
hr = ERROR_GEN_FAILURE;
goto MakeBoldExit;
}

SendMessage(hwnd,WM_SETFONT,(WPARAM)hnewfont,MAKELPARAM(TRUE,0));

free(plogfont);

MakeBoldExit:

return (HRESULT_FROM_WIN32(hr));
}

HRESULT ReleaseBold(HWND hwnd)
{
HFONT hfont = NULL;

hfont = (HFONT)SendMessage(hwnd,WM_GETFONT,0,0);
if (hfont) DeleteObject(hfont);
return ERROR_SUCCESS;
}

BOOL CALLBACK Page0DlgProc(HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CComObject<CWPProvider>*pWPP;

switch (uMsg) {

case WM_INITDIALOG:
{
// get propsheet page struct passed in
LPPROPSHEETPAGE lpsp = (LPPROPSHEETPAGE) lParam;

// fetch our private page info from propsheet struct
CComObject<CWPProvider> *pWPP = (CComObject<CWPProvider> *) lpsp->lParam;

// store pointer to private page info in window data for later
SetWindowLong(hDlg, DWL_USER, (LPARAM) pWPP);

// Enable Back and Next buttons
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT | PSWIZB_BACK);

// Make the title text bold
MakeBold(GetDlgItem(hDlg, IDC_PAGE_TITLE), TRUE, FW_BOLD);

return(TRUE);
}

break;// WM_INITDIALOG

case WM_NOTIFY:
{
// get pointer to private page data out of window data
NMHDR*lpnm = (NMHDR *) lParam;

pWPP = (CComObject<CWPProvider> *) GetWindowLong(hDlg,DWL_USER);

switch (lpnm->code)
{
case PSN_SETACTIVE:

// Enable Back and Next buttons
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT | PSWIZB_BACK);
SetWindowLong(hDlg, DWL_MSGRESULT, 0);

break;

case PSN_WIZNEXT:

SetWindowLong(hDlg, DWL_MSGRESULT, g_pPageInfo[1].uDlgID);
break;

case PSN_WIZBACK:

SetWindowLong(hDlg, DWL_MSGRESULT, pWPP->m_dwPageBeforeMe);
break;

case PSN_WIZFINISH:
case PSN_HELP:

break;

case PSN_QUERYCANCEL:

{
charsTitle[MAX_PATH];
charsMsg[MAX_MSG_LEN];
BOOLfQuit;

LoadString(GetModuleHandle(DLL_FILENAME_A), IDS_APPNAME, sTitle, sizeof(sTitle));
LoadString(GetModuleHandle(DLL_FILENAME_A), IDS_WARN_CANCEL, sMsg, sizeof(sMsg));
fQuit = (MessageBox(hDlg,
sMsg,
sTitle,
MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2)
== IDYES);

SetWindowLong(hDlg, DWL_MSGRESULT, !fQuit);
return(TRUE);
}

break;
}

return(TRUE); // return value is ignored and can be anything
}

break;

case WM_COMMAND:
{
// get pointer to private page data out of window data
pWPP = (CComObject<CWPProvider> *) GetWindowLong(hDlg, DWL_USER);

WORD wNotifyCode, wId;
HWND hwndCtl;

wNotifyCode = HIWORD(wParam); // notification code
wId = LOWORD(wParam);         // item, control, or accelerator identifier
hwndCtl = (HWND) lParam;      // handle of control
}

break;

    case WM_DESTROY:

ReleaseBold(GetDlgItem(hDlg, IDC_PAGE_TITLE));
break;

}

return(FALSE);
}

BOOL CALLBACK Page1DlgProc(HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CComObject<CWPProvider>*pWPP;

switch (uMsg) {

case WM_INITDIALOG:
{
// get propsheet page struct passed in
LPPROPSHEETPAGE lpsp = (LPPROPSHEETPAGE) lParam;

// fetch our private page info from propsheet struct
CComObject<CWPProvider> *pWPP = (CComObject<CWPProvider> *) lpsp->lParam;

// store pointer to private page info in window data for later
SetWindowLong(hDlg, DWL_USER, (LPARAM) pWPP);

// Enable Back and Next buttons
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT | PSWIZB_BACK);

// Make the title text bold
MakeBold(GetDlgItem(hDlg, IDC_PAGE_TITLE), TRUE, FW_BOLD);

return(TRUE);
}

break;// WM_INITDIALOG

case WM_NOTIFY:
{
// get pointer to private page data out of window data
NMHDR*lpnm = (NMHDR *) lParam;

pWPP = (CComObject<CWPProvider> *) GetWindowLong(hDlg,DWL_USER);

switch (lpnm->code)
{
case PSN_SETACTIVE:

// Enable Back and Next buttons
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT | PSWIZB_BACK);
SetWindowLong(hDlg, DWL_MSGRESULT, 0);

break;

case PSN_WIZNEXT:

SetWindowLong(hDlg, DWL_MSGRESULT, pWPP->m_dwPageAfterMe);
break;

case PSN_WIZBACK:

SetWindowLong(hDlg, DWL_MSGRESULT, g_pPageInfo[0].uDlgID);
break;

case PSN_WIZFINISH:
case PSN_HELP:

break;

case PSN_QUERYCANCEL:

{
charsTitle[MAX_PATH];
charsMsg[MAX_MSG_LEN];
BOOLfQuit;

LoadString(GetModuleHandle(DLL_FILENAME_A), IDS_APPNAME, sTitle, sizeof(sTitle));
LoadString(GetModuleHandle(DLL_FILENAME_A), IDS_WARN_CANCEL, sMsg, sizeof(sMsg));
fQuit = (MessageBox(hDlg,
sMsg,
sTitle,
MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2)
== IDYES);

SetWindowLong(hDlg, DWL_MSGRESULT, !fQuit);
return(TRUE);
}

break;
}

return(TRUE); // return value is ignored and can be anything
}

break;

case WM_COMMAND:
{
// get pointer to private page data out of window data
pWPP = (CComObject<CWPProvider> *) GetWindowLong(hDlg, DWL_USER);

WORD wNotifyCode, wId;
HWND hwndCtl;

wNotifyCode = HIWORD(wParam); // notification code
wId = LOWORD(wParam);         // item, control, or accelerator identifier
hwndCtl = (HWND) lParam;      // handle of control
}

break;

    case WM_DESTROY:

ReleaseBold(GetDlgItem(hDlg, IDC_PAGE_TITLE));
break;

}

return(FALSE);
}


PAGES.H

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *pages.h
 *
 *Abstract:
 *
 *Declaration of property-sheet page variables
 *and routines for Service Provider wizard pages
 *
\*------------------------------------------------*/

#ifndef __PAGES_H__
#define __PAGES_H__

#include <windows.h>
#include "resource.h"

//
//TO DO:Set this to the number of pages for your provider
//

#defineNUM_PAGES2

//
//TO DO:Set these to the dialog IDs of your first and last pages
//

#define IDD_FIRST_PAGEIDD_PAGE_0
#define IDD_LAST_PAGEIDD_PAGE_1

//
//TO DO:Declare the dialog routines for each of your pages here
//

BOOL CALLBACK Page0DlgProc(HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);

BOOL CALLBACK Page1DlgProc(HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);

typedef struct tagPAGEINFO
{
BOOL (CALLBACK *DlgProc)(HWND, UINT, WPARAM, LPARAM);
UINT uDlgID;

} PAGEINFO;

extern const PAGEINFO g_pPageInfo[NUM_PAGES];

#endif // __PAGES_H__


PRIORITY.H

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *priority.h
 *
 *Abstract:
 *
 *Specifies Provider priorities
 *
\*------------------------------------------------*/

#ifndef __WPP_PRIORITY_H__
#define __WPP_PRIORITY_H__

//Set the priority for this provider

#define WPP_PRIORITY0x00000100

//Microsoft supplied providers priorities start from
//0x00010000 and are 0x00000100 apart

#define CRS_PRIORITY0x00010000
#define POST_PRIORITY0x00010100
#define UNC_PRIORITY0x00010200
#define FTP_PRIORITY0x00010300

#endif // __WPP_PRIORITY_H__


ATLBASE.H

// This is a part of the Active Template Library.
// Copyright (C) 1996-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLBASE_H__
#define __ATLBASE_H__

#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifdef _UNICODE
#ifndef UNICODE
#define UNICODE         // UNICODE is used by Windows headers
#endif
#endif

#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE        // _UNICODE is used by C-runtime/MFC headers
#endif
#endif

#ifdef _DEBUG
#ifndef DEBUG
#define DEBUG
#endif
#endif

// namespace and our templates don't quite work with earlier compiler
// Can turn off namespace support directly, also
#if _MSC_VER<1100
#ifndef ATL_NO_NAMESPACE
#define ATL_NO_NAMESPACE
#endif
#endif

///////////////////////////////////////////////////////////////////////////////
// __declspec(novtable) is used on a class declaration to prevent the vtable
// pointer from being initialized in the constructor and destructor for the
// class.  This has many benefits because the linker can now eliminate the
// vtable and all the functions pointed to by the vtable.  Also, the actual
// constructor and destructor code are now smaller.
///////////////////////////////////////////////////////////////////////////////
// This should only be used on a class that is not directly createable but is
// rather only used as a base class.  Additionally, the constructor and
// destructor (if provided by the user) should not call anything that may cause
// a virtual function call to occur back on the object.
///////////////////////////////////////////////////////////////////////////////
// By default, the wizards will generate new ATL object classes with this
// attribute (through the ATL_NO_VTABLE macro).  This is normally safe as long
// the restriction mentioned above is followed.  It is always safe to remove
// this macro from your class, so if in doubt, remove it.
///////////////////////////////////////////////////////////////////////////////

#if _MSC_VER<1100
#define ATL_NO_VTABLE
#else
#ifdef _ATL_DISABLE_NO_VTABLE
#define ATL_NO_VTABLE
#else
#define ATL_NO_VTABLE __declspec(novtable)
#endif
#endif

#ifndef _ATL_NO_PRAGMA_WARNINGS
#pragma warning(disable: 4201) // nameless unions are part of C++
#pragma warning(disable: 4127) // constant expression
#pragma warning(disable: 4512) // can't generate assignment operator (so what?)
#pragma warning(disable: 4514) // unreferenced inlines are common
#pragma warning(disable: 4103) // pragma pack
#pragma warning(disable: 4702) // unreachable code
#pragma warning(disable: 4237) // bool
#pragma warning(disable: 4710) // function couldn't be inlined
#pragma warning(disable: 4355) // 'this' : used in base member initializer list
#pragma warning(disable: 4097) // typedef name used as synonym for class-name
#endif //!_ATL_NO_PRAGMA_WARNINGS

#include <windows.h>
#include <winnls.h>
#include <ole2.h>

#include <stddef.h>
#include <tchar.h>
#include <malloc.h>
#ifndef _ATL_NO_DEBUG_CRT
// Warning: if you define the above symbol, you will have
// to provide your own definition of the _ASSERTE(x) macro
// in order to compile ATL
#include <crtdbg.h>
#endif

#include <olectl.h>
#include <winreg.h>
#include <atliface.h>

#ifndef _ATL_PACKING
#define _ATL_PACKING 8
#endif
#pragma pack(push, _ATL_PACKING)

#include <atlconv.h>

#if defined(_ATL_DLL)
#pragma comment(lib, "atl.lib")
#define ATLAPI extern "C" HRESULT __stdcall
#define ATLAPI_(x) extern "C" x __stdcall
#elif defined(_ATL_DLL_IMPL)
#define ATLAPI extern "C" HRESULT __declspec(dllexport) __stdcall
#define ATLAPI_(x) extern "C" x __declspec(dllexport) __stdcall
#else
#define ATLAPI HRESULT __stdcall
#define ATLAPI_(x) x __stdcall
#endif

#ifndef ATL_NO_NAMESPACE
#ifndef _ATL_DLL_IMPL
namespace ATL
{
#endif
#endif

typedef HRESULT (WINAPI _ATL_CREATORFUNC)(void* pv, REFIID riid, LPVOID* ppv);
typedef HRESULT (WINAPI _ATL_CREATORARGFUNC)(void* pv, REFIID riid, LPVOID* ppv, DWORD dw);
typedef HRESULT (WINAPI _ATL_MODULEFUNC)(DWORD dw);
typedef LPCTSTR (WINAPI _ATL_DESCRIPTIONFUNC)();

struct _ATL_OBJMAP_ENTRY
{
const CLSID* pclsid;
HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister);
_ATL_CREATORFUNC* pfnGetClassObject;
_ATL_CREATORFUNC* pfnCreateInstance;
IUnknown* pCF;
DWORD dwRegister;
_ATL_DESCRIPTIONFUNC* pfnGetObjectDescription;
HRESULT WINAPI RevokeClassObject()
{
return CoRevokeClassObject(dwRegister);
}
HRESULT WINAPI RegisterClassObject(DWORD dwClsContext, DWORD dwFlags)
{
IUnknown* p = NULL;
HRESULT hRes = pfnGetClassObject(pfnCreateInstance, IID_IUnknown, (LPVOID*) &p);
if (SUCCEEDED(hRes))
hRes = CoRegisterClassObject(*pclsid, p, dwClsContext, dwFlags, &dwRegister);
if (p != NULL)
p->Release();
return hRes;
}
};

struct _ATL_REGMAP_ENTRY
{
LPCOLESTR     szKey;
LPCOLESTR     szData;
};

struct _ATL_MODULE
{
// Attributes
public:
UINT cbSize;
HINSTANCE m_hInst;
HINSTANCE m_hInstResource;
HINSTANCE m_hInstTypeLib;
_ATL_OBJMAP_ENTRY* m_pObjMap;
LONG m_nLockCnt;
HANDLE m_hHeap;
CRITICAL_SECTION m_csTypeInfoHolder;
CRITICAL_SECTION m_csWindowCreate;
CRITICAL_SECTION m_csObjMap;
};

//This define makes debugging asserts easier.
#define _ATL_SIMPLEMAPENTRY ((_ATL_CREATORARGFUNC*)1)

struct _ATL_INTMAP_ENTRY
{
const IID* piid;       // the interface id (IID)
DWORD dw;
_ATL_CREATORARGFUNC* pFunc; //NULL:end, 1:offset, n:ptr
};

/////////////////////////////////////////////////////////////////////////////
// QI Support

ATLAPI AtlInternalQueryInterface(void* pThis,
const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject);

/////////////////////////////////////////////////////////////////////////////
// Smart Pointer helpers

ATLAPI_(IUnknown*) AtlComPtrAssign(IUnknown** pp, IUnknown* lp);
ATLAPI_(IUnknown*) AtlComQIPtrAssign(IUnknown** pp, IUnknown* lp, REFIID riid);

/////////////////////////////////////////////////////////////////////////////
// Inproc Marshaling helpers
ATLAPI AtlFreeMarshalStream(IStream* pStream);
ATLAPI AtlMarshalPtrInProc(IUnknown* pUnk, const IID& iid, IStream** ppStream);
ATLAPI AtlUnmarshalPtr(IStream* pStream, const IID& iid, IUnknown** ppUnk);

ATLAPI_(BOOL) AtlWaitWithMessageLoop(HANDLE hEvent);

/////////////////////////////////////////////////////////////////////////////
// Connection Point Helpers

ATLAPI AtlAdvise(IUnknown* pUnkCP, IUnknown* pUnk, const IID& iid, LPDWORD pdw);
ATLAPI AtlUnadvise(IUnknown* pUnkCP, const IID& iid, DWORD dw);

/////////////////////////////////////////////////////////////////////////////
// IDispatch Error handling

ATLAPI AtlSetErrorInfo(const CLSID& clsid, LPCOLESTR lpszDesc,
DWORD dwHelpID, LPCOLESTR lpszHelpFile, const IID& iid, HRESULT hRes,
HINSTANCE hInst);

/////////////////////////////////////////////////////////////////////////////
// Module

ATLAPI AtlModuleInit(_ATL_MODULE* pM, _ATL_OBJMAP_ENTRY* p, HINSTANCE h);
ATLAPI AtlModuleRegisterClassObjects(_ATL_MODULE* pM, DWORD dwClsContext, DWORD dwFlags);
ATLAPI AtlModuleRevokeClassObjects(_ATL_MODULE* pM);
ATLAPI AtlModuleGetClassObject(_ATL_MODULE* pM, REFCLSID rclsid, REFIID riid, LPVOID* ppv);
ATLAPI AtlModuleTerm(_ATL_MODULE* pM);
ATLAPI AtlModuleRegisterServer(_ATL_MODULE* pM, BOOL bRegTypeLib, CLSID* pCLSID = NULL);
ATLAPI AtlModuleUnregisterServer(_ATL_MODULE* pM, CLSID* pCLSID = NULL);
ATLAPI AtlModuleUpdateRegistryFromResourceD(_ATL_MODULE*pM, LPCOLESTR lpszRes,
BOOL bRegister, struct _ATL_REGMAP_ENTRY* pMapEntries, IRegistrar* pReg = NULL);
ATLAPI AtlModuleRegisterTypeLib(_ATL_MODULE* pM, LPCOLESTR lpszIndex);

#ifndef ATL_NO_NAMESPACE
#ifndef _ATL_DLL_IMPL
}; //namespace ATL
#endif
#endif

#ifndef ATL_NO_NAMESPACE
namespace ATL
{
#endif

#if defined (_CPPUNWIND) & (defined(_ATL_EXCEPTIONS) | defined(_AFX))
#define ATLTRY(x) try{x;} catch(...) {}
#else
#define ATLTRY(x) x;
#endif

#ifdef _DEBUG
void _cdecl AtlTrace(LPCTSTR lpszFormat, ...);
#ifndef ATLTRACE
#define ATLTRACE            AtlTrace
#endif
#define ATLTRACENOTIMPL(funcname)   ATLTRACE(_T("%s not implemented.\n"), funcname); return E_NOTIMPL
#else
inline void _cdecl AtlTrace(LPCTSTR , ...){}
#ifndef ATLTRACE
#define ATLTRACE            1 ? (void)0 : AtlTrace
#endif
#define ATLTRACENOTIMPL(funcname)   return E_NOTIMPL
#endif //_DEBUG

#define offsetofclass(base, derived) ((DWORD)(static_cast<base*>((derived*)8))-8)

/////////////////////////////////////////////////////////////////////////////
// Master version numbers

#define _ATL     1      // Active Template Library
#define _ATL_VER 0x0200 // Active Template Library version 2.00

/////////////////////////////////////////////////////////////////////////////
// Win32 libraries

#ifndef _ATL_NO_FORCE_LIBS
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")
#pragma comment(lib, "olepro32.lib")
#pragma comment(lib, "uuid.lib")
#pragma comment(lib, "advapi32.lib")
#endif // _ATL_NO_FORCE_LIBS

template <class T>
class CComPtr
{
public:
typedef T _PtrClass;
CComPtr() {p=NULL;}
CComPtr(T* lp)
{
if ((p = lp) != NULL)
p->AddRef();
}
CComPtr(const CComPtr<T>& lp)
{
if ((p = lp.p) != NULL)
p->AddRef();
}
~CComPtr() {if (p) p->Release();}
void Release() {if (p) p->Release(); p=NULL;}
operator T*() {return (T*)p;}
T& operator*() {_ASSERTE(p!=NULL); return *p; }
//The assert on operator& usually indicates a bug.  If this is really
//what is needed, however, take the address of the p member explicitly.
T** operator&() { _ASSERTE(p==NULL); return &p; }
T* operator->() { _ASSERTE(p!=NULL); return p; }
T* operator=(T* lp){return (T*)AtlComPtrAssign((IUnknown**)&p, lp);}
T* operator=(const CComPtr<T>& lp)
{
return (T*)AtlComPtrAssign((IUnknown**)&p, lp.p);
}
#if _MSC_VER>1020
bool operator!(){return (p == NULL);}
#else
BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}
#endif
T* p;
};

//Note: CComQIPtr<IUnknown, &IID_IUnknown> is not meaningful
//      Use CComPtr<IUnknown>
template <class T, const IID* piid>
class CComQIPtr
{
public:
typedef T _PtrClass;
CComQIPtr() {p=NULL;}
CComQIPtr(T* lp)
{
if ((p = lp) != NULL)
p->AddRef();
}
CComQIPtr(const CComQIPtr<T,piid>& lp)
{
if ((p = lp.p) != NULL)
p->AddRef();
}
// If you get an error that this member is already defined, you are probably
// using a CComQIPtr<IUnknown, &IID_IUnknown>.  This is not necessary.
// Use CComPtr<IUnknown>
CComQIPtr(IUnknown* lp)
{
p=NULL;
if (lp != NULL)
lp->QueryInterface(*piid, (void **)&p);
}
~CComQIPtr() {if (p) p->Release();}
void Release() {if (p) p->Release(); p=NULL;}
operator T*() {return p;}
T& operator*() {_ASSERTE(p!=NULL); return *p; }
//The assert on operator& usually indicates a bug.  If this is really
//what is needed, however, take the address of the p member explicitly.
T** operator&() { _ASSERTE(p==NULL); return &p; }
T* operator->() {_ASSERTE(p!=NULL); return p; }
T* operator=(T* lp){return (T*)AtlComPtrAssign((IUnknown**)&p, lp);}
T* operator=(const CComQIPtr<T,piid>& lp)
{
return (T*)AtlComPtrAssign((IUnknown**)&p, lp.p);
}
T* operator=(IUnknown* lp)
{
return (T*)AtlComQIPtrAssign((IUnknown**)&p, lp, *piid);
}
#if _MSC_VER>1020
bool operator!(){return (p == NULL);}
#else
BOOL operator!(){return (p == NULL) ? TRUE : FALSE;}
#endif
T* p;
};

/////////////////////////////////////////////////////////////////////////////
// CComBSTR
class CComBSTR
{
public:
BSTR m_str;
CComBSTR()
{
m_str = NULL;
}
/*explicit*/ CComBSTR(int nSize, LPCOLESTR sz = NULL)
{
m_str = ::SysAllocStringLen(sz, nSize);
}
/*explicit*/ CComBSTR(LPCOLESTR pSrc)
{
m_str = ::SysAllocString(pSrc);
}
/*explicit*/ CComBSTR(const CComBSTR& src)
{
m_str = src.Copy();
}
CComBSTR& operator=(const CComBSTR& src);
CComBSTR& operator=(LPCOLESTR pSrc);
~CComBSTR()
{
::SysFreeString(m_str);
}
unsigned int Length() const
{
return SysStringLen(m_str);
}
operator BSTR() const
{
return m_str;
}
BSTR* operator&()
{
return &m_str;
}
BSTR Copy() const
{
return ::SysAllocStringLen(m_str, ::SysStringLen(m_str));
}
void Attach(BSTR src)
{
_ASSERTE(m_str == NULL);
m_str = src;
}
BSTR Detach()
{
BSTR s = m_str;
m_str = NULL;
return s;
}
void Empty()
{
::SysFreeString(m_str);
m_str = NULL;
}
#if _MSC_VER>1020
bool operator!()
{
return (m_str == NULL);
}
#else
BOOL operator!()
{
return (m_str == NULL) ? TRUE : FALSE;
}
#endif
void Append(const CComBSTR& bstrSrc)
{
Append(bstrSrc.m_str, SysStringLen(bstrSrc.m_str));
}
void Append(LPCOLESTR lpsz)
{
Append(lpsz, ocslen(lpsz));
}
// a BSTR is just a LPCOLESTR so we need a special version to signify
// that we are appending a BSTR
void AppendBSTR(BSTR p)
{
Append(p, SysStringLen(p));
}
void Append(LPCOLESTR lpsz, int nLen);

CComBSTR& operator+=(const CComBSTR& bstrSrc)
{
AppendBSTR(bstrSrc.m_str);
return *this;
}
#ifndef OLE2ANSI
/*explicit*/ CComBSTR(LPCSTR pSrc);
/*explicit*/ CComBSTR(int nSize, LPCSTR sz = NULL);
CComBSTR& operator=(LPCSTR pSrc);
void Append(LPCSTR);
#endif
HRESULT WriteToStream(IStream* pStream);
HRESULT ReadFromStream(IStream* pStream);
};

/////////////////////////////////////////////////////////////////////////////
// CComVariant

class CComVariant : public tagVARIANT
{
// Constructors
public:
CComVariant() {::VariantInit(this);}
~CComVariant() {Clear();}

CComVariant(const VARIANT& varSrc)
{
::VariantInit(this);
InternalCopy(&varSrc);
}

CComVariant(const CComVariant& varSrc)
{
InternalCopy(&varSrc);
}

CComVariant(BSTR bstrSrc)
{
::VariantInit(this);
*this = bstrSrc;
}
CComVariant(LPCOLESTR lpszSrc)
{
::VariantInit(this);
*this = lpszSrc;
}

#ifndef OLE2ANSI
CComVariant(LPCSTR lpszSrc)
{
::VariantInit(this);
*this = lpszSrc;}
#endif

#if _MSC_VER>1020
CComVariant(bool bSrc)
{
::VariantInit(this);
vt = VT_BOOL;
#pragma warning(disable: 4310) // cast truncates constant value
boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE;
#pragma warning(default: 4310) // cast truncates constant value
}
#endif

CComVariant(int nSrc)
{
::VariantInit(this);
vt = VT_I4;
lVal = nSrc;
}
CComVariant(BYTE nSrc)
{
::VariantInit(this);
vt = VT_UI1;
bVal = nSrc;
}
CComVariant(short nSrc)
{
::VariantInit(this);
vt = VT_I2;
iVal = nSrc;
}
CComVariant(long nSrc, VARTYPE vtSrc = VT_I4)
{
_ASSERTE(vtSrc == VT_I4 || vtSrc == VT_ERROR);
::VariantInit(this);
vt = vtSrc;
lVal = nSrc;
}
CComVariant(float fltSrc)
{
::VariantInit(this);
vt = VT_R4;
fltVal = fltSrc;
}
CComVariant(double dblSrc)
{
::VariantInit(this);
vt = VT_R8;
dblVal = dblSrc;
}
CComVariant(CY cySrc)
{
::VariantInit(this);
vt = VT_CY;
cyVal.Hi = cySrc.Hi;
cyVal.Lo = cySrc.Lo;
}
CComVariant(IDispatch* pSrc)
{
::VariantInit(this);
vt = VT_DISPATCH;
pdispVal = pSrc;
// Need to AddRef as VariantClear will Release
if (pdispVal != NULL)
pdispVal->AddRef();
}
CComVariant(IUnknown* pSrc)
{
::VariantInit(this);
vt = VT_UNKNOWN;
punkVal = pSrc;
// Need to AddRef as VariantClear will Release
if (punkVal != NULL)
punkVal->AddRef();
}

// Assignment Operators
public:
CComVariant& operator=(const CComVariant& varSrc)
{
InternalCopy(&varSrc);
return *this;
}
CComVariant& operator=(const VARIANT& varSrc)
{
InternalCopy(&varSrc);
return *this;
}

CComVariant& operator=(BSTR bstrSrc);
CComVariant& operator=(LPCOLESTR lpszSrc);

#ifndef OLE2ANSI
CComVariant& operator=(LPCSTR lpszSrc);
#endif

#if _MSC_VER>1020
CComVariant& operator=(bool bSrc);
#endif
CComVariant& operator=(int nSrc);
CComVariant& operator=(BYTE nSrc);
CComVariant& operator=(short nSrc);
CComVariant& operator=(long nSrc);
CComVariant& operator=(float fltSrc);
CComVariant& operator=(double dblSrc);
CComVariant& operator=(CY cySrc);

CComVariant& operator=(IDispatch* pSrc);
CComVariant& operator=(IUnknown* pSrc);

// Comparison Operators
public:
#if _MSC_VER>1020
bool operator==(const VARIANT& varSrc);
bool operator!=(const VARIANT& varSrc) {return !operator==(varSrc);}
#else
BOOL operator==(const VARIANT& varSrc);
BOOL operator!=(const VARIANT& varSrc) {return !operator==(varSrc);}
#endif

// Operations
public:
HRESULT Clear() { return ::VariantClear(this); }
HRESULT Copy(const VARIANT* pSrc) { return ::VariantCopy(this, const_cast<VARIANT*>(pSrc)); }
HRESULT Attach(VARIANT* pSrc);
HRESULT Detach(VARIANT* pDest);
HRESULT ChangeType(VARTYPE vtNew, const VARIANT* pSrc = NULL);
HRESULT WriteToStream(IStream* pStream);
HRESULT ReadFromStream(IStream* pStream);

// Implementation
public:
HRESULT InternalClear();
void InternalCopy(const VARIANT* pSrc);
};
/////////////////////////////////////////////////////////////////////////////
// GUID comparison

inline BOOL InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2)
{
   return (
  ((PLONG) &rguid1)[0] == ((PLONG) &rguid2)[0] &&
  ((PLONG) &rguid1)[1] == ((PLONG) &rguid2)[1] &&
  ((PLONG) &rguid1)[2] == ((PLONG) &rguid2)[2] &&
  ((PLONG) &rguid1)[3] == ((PLONG) &rguid2)[3]);
}

inline BOOL InlineIsEqualUnknown(REFGUID rguid1)
{
   return (
  ((PLONG) &rguid1)[0] == 0 &&
  ((PLONG) &rguid1)[1] == 0 &&
#ifdef _MAC
  ((PLONG) &rguid1)[2] == 0xC0000000 &&
  ((PLONG) &rguid1)[3] == 0x00000046);
#else
  ((PLONG) &rguid1)[2] == 0x000000C0 &&
  ((PLONG) &rguid1)[3] == 0x46000000);
#endif
}

/////////////////////////////////////////////////////////////////////////////
// Threading Model Support

class CComCriticalSection
{
public:
void Lock() {EnterCriticalSection(&m_sec);}
void Unlock() {LeaveCriticalSection(&m_sec);}
void Init() {InitializeCriticalSection(&m_sec);}
void Term() {DeleteCriticalSection(&m_sec);}
CRITICAL_SECTION m_sec;
};

class CComAutoCriticalSection
{
public:
void Lock() {EnterCriticalSection(&m_sec);}
void Unlock() {LeaveCriticalSection(&m_sec);}
CComAutoCriticalSection() {InitializeCriticalSection(&m_sec);}
~CComAutoCriticalSection() {DeleteCriticalSection(&m_sec);}
CRITICAL_SECTION m_sec;
};

class CComFakeCriticalSection
{
public:
void Lock() {}
void Unlock() {}
void Init() {}
void Term() {}
};

class CComMultiThreadModelNoCS
{
public:
static ULONG WINAPI Increment(LPLONG p) {return InterlockedIncrement(p);}
static ULONG WINAPI Decrement(LPLONG p) {return InterlockedDecrement(p);}
typedef CComFakeCriticalSection AutoCriticalSection;
typedef CComFakeCriticalSection CriticalSection;
typedef CComMultiThreadModelNoCS ThreadModelNoCS;
};

class CComMultiThreadModel
{
public:
static ULONG WINAPI Increment(LPLONG p) {return InterlockedIncrement(p);}
static ULONG WINAPI Decrement(LPLONG p) {return InterlockedDecrement(p);}
typedef CComAutoCriticalSection AutoCriticalSection;
typedef CComCriticalSection CriticalSection;
typedef CComMultiThreadModelNoCS ThreadModelNoCS;
};

class CComSingleThreadModel
{
public:
static ULONG WINAPI Increment(LPLONG p) {return ++(*p);}
static ULONG WINAPI Decrement(LPLONG p) {return --(*p);}
typedef CComFakeCriticalSection AutoCriticalSection;
typedef CComFakeCriticalSection CriticalSection;
typedef CComSingleThreadModel ThreadModelNoCS;
};

#ifndef _ATL_SINGLE_THREADED
#ifndef _ATL_APARTMENT_THREADED
#ifndef _ATL_FREE_THREADED
#define _ATL_FREE_THREADED
#endif
#endif
#endif

#if defined(_ATL_SINGLE_THREADED)
typedef CComSingleThreadModel CComObjectThreadModel;
typedef CComSingleThreadModel CComGlobalsThreadModel;
#elif defined(_ATL_APARTMENT_THREADED)
typedef CComSingleThreadModel CComObjectThreadModel;
typedef CComMultiThreadModel CComGlobalsThreadModel;
#else
typedef CComMultiThreadModel CComObjectThreadModel;
typedef CComMultiThreadModel CComGlobalsThreadModel;
#endif

/////////////////////////////////////////////////////////////////////////////
// CComModule

#define THREADFLAGS_APARTMENT 0x1
#define THREADFLAGS_BOTH 0x2
#define AUTPRXFLAG 0x4

struct _AtlCreateWndData
{
void* m_pThis;
DWORD m_dwThreadID;
_AtlCreateWndData* m_pNext;
};

class CComModule : public _ATL_MODULE
{
// Operations
public:
_AtlCreateWndData* m_pCreateWndList;

void AddCreateWndData(_AtlCreateWndData* pData, void* pObject);
void* ExtractCreateWndData();

void Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h)
{
cbSize = sizeof(_ATL_MODULE);
m_pCreateWndList = NULL;
AtlModuleInit(this, p, h);
}
void Term()
{
AtlModuleTerm(this);
}

LONG Lock() {return CComGlobalsThreadModel::Increment(&m_nLockCnt);}
LONG Unlock() {return CComGlobalsThreadModel::Decrement(&m_nLockCnt);}
LONG GetLockCount() {return m_nLockCnt;}

HINSTANCE GetModuleInstance() {return m_hInst;}
HINSTANCE GetResourceInstance() {return m_hInstResource;}
HINSTANCE GetTypeLibInstance() {return m_hInstTypeLib;}

// Registry support (helpers)
HRESULT RegisterTypeLib()
{
return AtlModuleRegisterTypeLib(this, NULL);
}
HRESULT RegisterTypeLib(LPCTSTR lpszIndex)
{
USES_CONVERSION;
return AtlModuleRegisterTypeLib(this, T2COLE(lpszIndex));
}
HRESULT RegisterServer(BOOL bRegTypeLib = FALSE, CLSID* pCLSID = NULL)
{
return AtlModuleRegisterServer(this, bRegTypeLib, pCLSID);
}

HRESULT UnregisterServer(CLSID* pCLSID = NULL)
{
return AtlModuleUnregisterServer(this, pCLSID);
}

// Resource-based Registration
HRESULT WINAPI UpdateRegistryFromResourceD(LPCTSTR lpszRes, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries = NULL)
{
USES_CONVERSION;
return AtlModuleUpdateRegistryFromResourceD(this, T2COLE(lpszRes), bRegister,
pMapEntries);
}
HRESULT WINAPI UpdateRegistryFromResourceD(UINT nResID, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries = NULL)
{
return AtlModuleUpdateRegistryFromResourceD(this,
(LPCOLESTR)MAKEINTRESOURCE(nResID), bRegister, pMapEntries);
}

#ifdef _ATL_STATIC_REGISTRY
// Statically linking to Registry Ponent
HRESULT WINAPI UpdateRegistryFromResourceS(LPCTSTR lpszRes, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries = NULL);
HRESULT WINAPI UpdateRegistryFromResourceS(UINT nResID, BOOL bRegister,
struct _ATL_REGMAP_ENTRY* pMapEntries = NULL);
#endif //_ATL_STATIC_REGISTRY

// Standard Registration
HRESULT WINAPI UpdateRegistryClass(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID, UINT nDescID, DWORD dwFlags, BOOL bRegister);
HRESULT WINAPI RegisterClassHelper(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID, UINT nDescID, DWORD dwFlags);
HRESULT WINAPI UnregisterClassHelper(const CLSID& clsid, LPCTSTR lpszProgID,
LPCTSTR lpszVerIndProgID);

// Register/Revoke All Class Factories with the OS (EXE only)
HRESULT RegisterClassObjects(DWORD dwClsContext, DWORD dwFlags)
{
return AtlModuleRegisterClassObjects(this, dwClsContext, dwFlags);
}
HRESULT RevokeClassObjects()
{
return AtlModuleRevokeClassObjects(this);
}

// Obtain a Class Factory (DLL only)
HRESULT GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return AtlModuleGetClassObject(this, rclsid, riid, ppv);
}

// Only used in CComAutoThreadModule
HRESULT CreateInstance(void* /*pfnCreateInstance*/, REFIID /*riid*/, void** /*ppvObj*/)
{
return S_OK;
}
};

/////////////////////////////////////////////////////////////////////////////////////////////
// Thread Pooling classes

class _AtlAptCreateObjData
{
public:
_ATL_CREATORFUNC* pfnCreateInstance;
const IID* piid;
HANDLE hEvent;
LPSTREAM pStream;
HRESULT hRes;
};

class CComApartment
{
public:
static UINT ATL_CREATE_OBJECT;
static DWORD WINAPI _Apartment(void* pv)
{
return ((CComApartment*)pv)->Apartment();
}
DWORD Apartment()
{
CoInitialize(NULL);
MSG msg;
while(GetMessage(&msg, 0, 0, 0))
{
if (msg.message == ATL_CREATE_OBJECT)
{
_AtlAptCreateObjData* pdata = (_AtlAptCreateObjData*)msg.lParam;
IUnknown* pUnk = NULL;
pdata->hRes = pdata->pfnCreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
if (SUCCEEDED(pdata->hRes))
pdata->hRes = CoMarshalInterThreadInterfaceInStream(*pdata->piid, pUnk, &pdata->pStream);
if (SUCCEEDED(pdata->hRes))
{
pUnk->Release();
ATLTRACE(_T("Object created on thread = %d\n"), GetCurrentThreadId());
}
SetEvent(pdata->hEvent);
}
DispatchMessage(&msg);
}
CoUninitialize();
return 0;
}
LONG Lock() {return CComGlobalsThreadModel::Increment(&m_nLockCnt);}
LONG Unlock(){return CComGlobalsThreadModel::Decrement(&m_nLockCnt);
}
LONG GetLockCount() {return m_nLockCnt;}

DWORD m_dwThreadID;
HANDLE m_hThread;
LONG m_nLockCnt;
};

class CComSimpleThreadAllocator
{
public:
CComSimpleThreadAllocator()
{
m_nThread = 0;
}
int GetThread(CComApartment* /*pApt*/, int nThreads)
{
if (++m_nThread == nThreads)
m_nThread = 0;
return m_nThread;
}
int m_nThread;
};

template <class ThreadAllocator>
class CComAutoThreadModule : public CComModule
{
public:
void Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, int nThreads = GetDefaultThreads());
~CComAutoThreadModule();
HRESULT CreateInstance(void* pfnCreateInstance, REFIID riid, void** ppvObj);
LONG Lock();
LONG Unlock();
DWORD dwThreadID;
int m_nThreads;
CComApartment* m_pApartments;
ThreadAllocator m_Allocator;
static int GetDefaultThreads()
{
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwNumberOfProcessors * 4;
}
};

#ifdef _ATL_STATIC_REGISTRY
#define UpdateRegistryFromResource UpdateRegistryFromResourceS
#else
#define UpdateRegistryFromResource UpdateRegistryFromResourceD
#endif

/////////////////////////////////////////////////////////////////////////////
// CRegKey

class CRegKey
{
public:
CRegKey();
~CRegKey();

// Attributes
public:
operator HKEY() const;
HKEY m_hKey;

// Operations
public:
LONG SetValue(DWORD dwValue, LPCTSTR lpszValueName);
LONG QueryValue(DWORD& dwValue, LPCTSTR lpszValueName);
LONG QueryValue(LPTSTR szValue, LPCTSTR lpszValueName, DWORD* pdwCount);
LONG SetValue(LPCTSTR lpszValue, LPCTSTR lpszValueName = NULL);

LONG SetKeyValue(LPCTSTR lpszKeyName, LPCTSTR lpszValue, LPCTSTR lpszValueName = NULL);
static LONG WINAPI SetValue(HKEY hKeyParent, LPCTSTR lpszKeyName,
LPCTSTR lpszValue, LPCTSTR lpszValueName = NULL);

LONG Create(HKEY hKeyParent, LPCTSTR lpszKeyName,
LPTSTR lpszClass = REG_NONE, DWORD dwOptions = REG_OPTION_NON_VOLATILE,
REGSAM samDesired = KEY_ALL_ACCESS,
LPSECURITY_ATTRIBUTES lpSecAttr = NULL,
LPDWORD lpdwDisposition = NULL);
LONG Open(HKEY hKeyParent, LPCTSTR lpszKeyName,
REGSAM samDesired = KEY_ALL_ACCESS);
LONG Close();
HKEY Detach();
void Attach(HKEY hKey);
LONG DeleteSubKey(LPCTSTR lpszSubKey);
LONG RecurseDeleteKey(LPCTSTR lpszKey);
LONG DeleteValue(LPCTSTR lpszValue);
};

inline CRegKey::CRegKey()
{m_hKey = NULL;}

inline CRegKey::~CRegKey()
{Close();}

inline CRegKey::operator HKEY() const
{return m_hKey;}

inline HKEY CRegKey::Detach()
{
HKEY hKey = m_hKey;
m_hKey = NULL;
return hKey;
}

inline void CRegKey::Attach(HKEY hKey)
{
_ASSERTE(m_hKey == NULL);
m_hKey = hKey;
}

inline LONG CRegKey::DeleteSubKey(LPCTSTR lpszSubKey)
{
_ASSERTE(m_hKey != NULL);
return RegDeleteKey(m_hKey, lpszSubKey);
}

inline LONG CRegKey::DeleteValue(LPCTSTR lpszValue)
{
_ASSERTE(m_hKey != NULL);
return RegDeleteValue(m_hKey, (LPTSTR)lpszValue);
}

#pragma pack(pop)

#ifndef ATL_NO_NAMESPACE
}; //namespace ATL

using namespace ATL;
#endif

#endif // __ATLBASE_H__

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


REGKEYEX.CPP

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *regkeyex.cpp
 *
 *Abstract:
 *
 *Implementation of CRegKeyEx class
 *
\*------------------------------------------------*/

#include "regkeyex.h"

LONG CRegKeyEx::QueryValue(LPTSTR tsBuffer,
DWORD *pdwBufLen,
LPCTSTR tsValueName)
{
DWORDdwType = NULL;
LONGlRes;

lRes = RegQueryValueEx(m_hKey,
(LPTSTR) tsValueName,
NULL,
&dwType,
(LPBYTE) tsBuffer,
pdwBufLen);

if (ERROR_SUCCESS == lRes)
{
if (REG_SZ != dwType)
lRes = E_FAIL;
}

return(lRes);
}


REGKEYEX.H

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *regkeyex.cpp
 *
 *Abstract:
 *
 *Declaration of CRegKeyEx class
 *
\*------------------------------------------------*/

#ifndef __REGKEYEX_H__
#define __REGKEYEX_H__

#include "atlbase.h"

class CRegKeyEx : public CRegKey
{
public:

//Add String QueryValue

LONG QueryValue(LPTSTR tsBuffer,
DWORD *pdwBufLen,
LPCTSTR tsValueName);

};

#endif __REGKEYEX_H__


RESOURCE.H

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *resource.h
 *
 *Abstract:
 *
 *Resource ID #defines
 *
\*------------------------------------------------*/

#define IDS_APPNAME1
#define IDS_WPPROVIDER_DESC2
#define IDS_WARN_CANCEL3
#define IDD_PAGE_09000
#define IDD_PAGE_19001
#define IDC_PAGE_TITLE9002
#define IDB_WEBPOST9999
#define IDC_UNUSED-1

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        201
#define _APS_NEXT_COMMAND_VALUE         32768
#define _APS_NEXT_CONTROL_VALUE         201
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


STDAFX.CPP

/*------------------------------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *stdafx.cpp
 *
 *Abstract:
 *
 *ATL-generated source file
 *stdafx.cpp : source file that includes just the standard includes
 *stdafx.pch will be the pre-compiled header
 *stdafx.obj will contain the pre-compiled type information
 *
\*------------------------------------------------------------------------*/

#include "stdafx.h"

#ifdef _ATL_STATIC_REGISTRY
#include <statreg.h>
#include <statreg.cpp>
#endif

#include <atlimpl.cpp>


STDAFX.H

/*------------------------------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *stdafx.h
 *
 *Abstract:
 *
 *ATL-generated header file
 *
\*------------------------------------------------------------------------*/

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>


UTIL.H

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *util.h
 *
 *Abstract:
 *
 *Utility functions for Service Provider
 *implementation.
 *
\*------------------------------------------------*/

#ifndef __UTIL_H__
#define __UTIL_H__

//
//TO DO:Change the file name below to suit your provider
//

#define DLL_FILENAME_A"STUBWPP.DLL"

#defineMAX_MSG_LEN1024

#define REGKEY_CLASSES_A"CLSID"
#define REGKEY_INPROCSERVER_A"InprocServer32"

#define REGKEY_CLASSES_WL REGKEY_CLASSES_A
#define REGKEY_INPROCSERVER_WL REGKEY_INPROCSERVER_A

#define g_mbstowcs(pwszDest, pszSrc, dwDestSize)MultiByteToWideChar(CP_ACP, 0, (pszSrc), -1, (pwszDest), (dwDestSize))
#define g_wcstombs(pszDest, pwszSrc, dwDestSize)WideCharToMultiByte(CP_ACP, 0, (pwszSrc), -1, (pszDest), (dwDestSize), NULL, NULL)

#define leave(x){ hRetCode = x; goto leave_routine; }

#endif  /* __UTIL_H__ */


WPPDEFS.H

/*-----------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *wppdefs.h
 *
 *Abstract:
 *
 *#defines for Web Publishing Service Provider
 *implementations
 *
\*-----------------------------------------------------*/

#ifndef __WPPDEFS_H__
#define __WPPDEFS_H__

//Miscellaneous

#define MAX_MSG_LEN1024
#define MAX_URL_LEN1024
#define WIN32_FROM_HRESULT(x)((x) & 0x0000FFFF)
#define WIZARD_HELP_FILENAME_A"WPWIZ.HLP>proc4"
#define MY_CXDLG304
#define MY_CYDLG163
#define MY_CXBMP79
#define MY_CYBMP160

//Service Provider's Resource IDs should be between the following

#define IDD_FIRST_PROV_PAGE9000
#define IDD_LAST_PROV_PAGE9999

//Registry keys & values

#define REGSUBKEY_WP_SITES_A"Software\\Microsoft\\WebPost\\Sites"
#define REGKEY_WP_SITES_A"HKCU\\" REGSUBKEY_WP_SITES_A
#define REGVAL_SITE_PROVIDER_A"Provider"
#define REGVAL_SITE_URL_A"DestURL"
#define REGVAL_SITE_BASEDIR_A"LocalBaseDir"
#define REGVAL_SITE_FLAGS_A"Flags"

#define REGSUBKEY_WP_SITES_WL""REGSUBKEY_WP_SITES_A
#define REGKEY_WP_SITES_WL""REGKEY_WP_SITES_A
#define REGVAL_SITE_PROVIDER_WL""REGVAL_SITE_PROVIDER_A
#define REGVAL_SITE_URL_WL"DestURL"
#define REGVAL_SITE_BASEDIR_WL""REGVAL_SITE_BASEDIR_A
#define REGVAL_SITE_FLAGS_WL""REGVAL_SITE_FLAGS_A

//Provider registry keys and values

#define HKEY_WPPHKEY_LOCAL_MACHINE

#define REGSUBKEY_WPP_A"System\\CurrentControlSet\\Services\\WebPost\\Providers"
#define REGKEY_WPP_A"HKLM\\"REGSUBKEY_WPP_A
#define REGVAL_WPP_NAME_A"Provider"
#define REGVAL_WPP_DLLPATH_A"Path"
#define REGVAL_WPP_PRIORITY_A"Priority"
#define REGVAL_WPP_POSTINFO_REQUIRED_A"PostInfoRequired"

#define REGSUBKEY_WPP_WL""REGSUBKEY_WPP_A
#define REGKEY_WPP_WL""REGKEY_WPP_A
#define REGVAL_WPP_NAME_WL""REGVAL_WPP_NAME_A
#define REGVAL_WPP_DLLPATH_WL""REGVAL_WPP_DLLPATH_A
#define REGVAL_WPP_PRIORITY_WL""REGVAL_WPP_PRIORITY_A
#define REGVAL_WPP_POSTINFO_REQUIRED_WL""REGVAL_WPP_POSTINFO_REQUIRED_A

#endif//__WPPDEFS_H__


WPPOBJ.CPP

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *wppobj.cpp
 *
 *Abstract:
 *
 *Implementation of Service Provider Object
 *
\*------------------------------------------------*/

#include "stdafx.h"
#include "wpprov.h"
#include "wppobj.h"
#include "util.h"
#include "pages.h"
#include "resource.h"

extern HINSTANCE g_hInstance;

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

STDMETHODIMP CWPProvider::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_IWPProvider,
};

for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}

HRESULT CWPProvider::Init(LPCWSTR wsSiteName, LPCWSTR wsDestURL)
{
HRESULThRetCode = NO_ERROR;

m_pfnProgressUpdateProc = NULL;

//
//TO DO:Add code here for initialization of Service Provider object
//

leave(NO_ERROR);

leave_routine:

return(hRetCode);
}

HRESULT CWPProvider::DoTestPost(void)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO:Add code here to implement post using Service
//Provider protocol and retrieval using HTTP and
//destination URL for site
//
//This routine should fail if the Service Provider
//can't determine all the information it needs
//in order to do a test post
//

leave(NO_ERROR);

leave_routine:

return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::SetProgressUpdateProc(
/* [in] */ LONG __RPC_FAR *lpfnProgressUpdateProc)
{
HRESULThRetCode;

m_pfnProgressUpdateProc = (PFNPROGRESSUPDATEPROC) lpfnProgressUpdateProc;

leave(NO_ERROR);

leave_routine:

return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::AddWizardPages(
    /* [in] */ LONG __RPC_FAR *lpv,
    /* [in] */ LONG __RPC_FAR *lpfnAdd,
    /* [out][in] */ LONG __RPC_FAR *plParam)
{
HRESULThRetCode = NO_ERROR;
DWORDi;
LPFNADDPROPSHEETPAGEpfnAdd = (LPFNADDPROPSHEETPAGE) lpfnAdd;

//
//Save IDs of pages immediately
//before and after ours
//

m_dwPageBeforeMe = HIWORD(*plParam);
m_dwPageAfterMe = LOWORD(*plParam);

//
//TO DO:Add code here if pages to add into wizard
//are determined dynamically
//

//
//Fill in property sheet structures
//

for (i = 0; i < NUM_PAGES; i++)
{
m_Pages[i].dwSize = sizeof(PROPSHEETPAGE);
   m_Pages[i].dwFlags = PSP_DEFAULT;
m_Pages[i].hInstance = g_hInstance;
m_Pages[i].pszTemplate = MAKEINTRESOURCE(g_pPageInfo[i].uDlgID);
m_Pages[i].pszIcon = NULL;
m_Pages[i].pfnDlgProc = (DLGPROC) g_pPageInfo[i].DlgProc;
m_Pages[i].pszTitle = NULL;
m_Pages[i].lParam = (LPARAM) this;

m_hPages[i] = CreatePropertySheetPage(&m_Pages[i]);

if (NULL == m_hPages[i])
{
hRetCode = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto leave_routine;
}

 if ( FALSE == (pfnAdd(m_hPages[i], NULL)) )
{
DestroyPropertySheetPage(m_hPages[i]);
hRetCode = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto leave_routine;
}
}

//
//Return IDs of our first and last pages
//

*plParam = (IDD_FIRST_PAGE << 16) | IDD_LAST_PAGE;

leave(NO_ERROR);

leave_routine:

return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::Commit( void)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO:Write code here
//Call m_pfnProgressUpdateProc every time a chunk of bytes
//is sent (pass in filename and number of bytes committed)
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::DeleteFile(
/* [string][in] */ LPCWSTR lpszFile)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::FindClose(
/* [in] */ LONG hSearchHandle)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::FindFirstFile(
/* [string][in] */ LPCWSTR lpszSearchFile,
/* [out] */ LPWIN32_FIND_DATAW lpFindFileData,
/* [out] */ LONG __RPC_FAR *lpSearchHandle)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::FindNextFile(
/* [in] */ LONG hSearchHandle,
/* [out] */ LPWIN32_FIND_DATAW lpFindFileData)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::GetError(
/* [out] */ LONG __RPC_FAR *lpdwErrorType,
/* [out] */ LONG __RPC_FAR *lpdwErrorCode,
/* [out][in] */ LONG __RPC_FAR *lpcbError,
/* [string][out] */ LPWSTR lpszError)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::GetParam(
/* [string][in] */ LPCWSTR lpszParameter,
/* [out][in] */ LONG __RPC_FAR *lpcbValue,
/* [string][out] */ LPWSTR lpszValue)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::GetSiteInfo(
/* [out] */ LPWPSITEINFOW lpbSite,
/* [out][in] */ LONG __RPC_FAR *lpcbSite)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::NetworkConnect(
/* [string][in] */ LPCWSTR lpszUserName,
/* [string][in] */ LPCWSTR lpszPassword)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::NetworkDisconnect( void)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::PostFiles(
/* [in] */ LONG cLocalPaths,
/* [string][in] */ LPWSTR __RPC_FAR *lppszLocalPaths,
/* [out][in] */ LONG __RPC_FAR *lpcbURL,
/* [string][out][in] */ LPWSTR lpszURL,
/* [in] */ LONG fdwFlags)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO:Write code here
//Call m_pfnProgressUpdateProc every time a chunk of bytes
//is sent (pass in filename and number of bytes posted)
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::ServerLogin(
/* [string][in] */ LPCWSTR lpszUserName,
/* [string][in] */ LPCWSTR lpszPassword)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::ServerLogout( void)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}

HRESULT STDMETHODCALLTYPE CWPProvider::SetParam(
/* [string][in] */ LPCWSTR lpszParameter,
    /* [string][in] */ LPCWSTR lpszValue)
{
HRESULThRetCode = NO_ERROR;

//
//TO DO: Write code here
//

leave(NO_ERROR);

leave_routine:
return(hRetCode);
}


WPPOBJ.H

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *wppobj.h
 *
 *Abstract:
 *
 *Declaration of Service Provider object
 *
\*------------------------------------------------*/

#ifndef __WPPOBJ_H__
#define __WPPOBJ_H__

#include "resource.h"       // main symbols
#include "pages.h"

/////////////////////////////////////////////////////////////////////////////
// wpprov

class CWPProvider :
public CComDualImpl<IWPProvider, &IID_IWPProvider, &LIBID_CWPProviderLib>,
public ISupportErrorInfo,
public CComObjectRoot,
public CComCoClass<CWPProvider,&CLSID_CWPProvider>
{
public:
CWPProvider() {}
BEGIN_COM_MAP(CWPProvider)
COM_INTERFACE_ENTRY(IWPSiteW)
COM_INTERFACE_ENTRY(IWPProvider)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()
//DECLARE_NOT_AGGREGATABLE(CWPProvider)
// Remove the comment from the line above if you don't want your object to
// support aggregation.  The default is to support it

DECLARE_REGISTRY(CWPProvider, _T("WebPost.CWPProvider.1"), _T("WebPost.CWPProvider"), IDS_WPPROVIDER_DESC, THREADFLAGS_BOTH)
// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IWPProvider
public:

    HRESULT STDMETHODCALLTYPE AddWizardPages(
        /* [in] */ LONG __RPC_FAR *lpv,
        /* [in] */ LONG __RPC_FAR *lpfnAdd,
        /* [out][in] */ LONG __RPC_FAR *plParam);

HRESULT STDMETHODCALLTYPE Commit( void);

HRESULT STDMETHODCALLTYPE DeleteFile(
/* [string][in] */ LPCWSTR lpszFile);

HRESULT STDMETHODCALLTYPE FindClose(
/* [in] */ LONG hSearchHandle);

HRESULT STDMETHODCALLTYPE FindFirstFile(
/* [string][in] */ LPCWSTR lpszSearchFile,
/* [out] */ LPWIN32_FIND_DATAW lpFindFileData,
/* [out] */ LONG __RPC_FAR *lpSearchHandle);

HRESULT STDMETHODCALLTYPE FindNextFile(
/* [in] */ LONG hSearchHandle,
/* [out] */ LPWIN32_FIND_DATAW lpFindFileData);

HRESULT STDMETHODCALLTYPE GetError(
/* [out] */ LONG __RPC_FAR *lpdwErrorType,
/* [out] */ LONG __RPC_FAR *lpdwErrorCode,
/* [out][in] */ LONG __RPC_FAR *lpcbError,
/* [string][out] */ LPWSTR lpszError);

HRESULT STDMETHODCALLTYPE GetParam(
/* [string][in] */ LPCWSTR lpszParameter,
/* [out][in] */ LONG __RPC_FAR *lpcbValue,
/* [string][out] */ LPWSTR lpszValue);

HRESULT STDMETHODCALLTYPE GetSiteInfo(
/* [out] */ LPWPSITEINFOW lpbSite,
/* [out][in] */ LONG __RPC_FAR *lpcbSite);

HRESULT STDMETHODCALLTYPE NetworkConnect(
/* [string][in] */ LPCWSTR lpszUserName,
/* [string][in] */ LPCWSTR lpszPassword);

HRESULT STDMETHODCALLTYPE NetworkDisconnect( void);

HRESULT STDMETHODCALLTYPE PostFiles(
/* [in] */ LONG cLocalPaths,
/* [string][in] */ LPWSTR __RPC_FAR *lppszLocalPaths,
/* [out][in] */ LONG __RPC_FAR *lpcbURL,
/* [string][out][in] */ LPWSTR lpszURL,
/* [in] */ LONG fdwFlags);

HRESULT STDMETHODCALLTYPE ServerLogin(
/* [string][in] */ LPCWSTR lpszUserName,
/* [string][in] */ LPCWSTR lpszPassword);

HRESULT STDMETHODCALLTYPE ServerLogout( void);

HRESULT STDMETHODCALLTYPE SetParam(
/* [string][in] */ LPCWSTR lpszParameter,
            /* [string][in] */ LPCWSTR lpszValue);

HRESULT STDMETHODCALLTYPE SetProgressUpdateProc(
/* [in] */ LONG __RPC_FAR *lpfnProgressUpdateProc);

//
//Property page related variables
//

PROPSHEETPAGEm_Pages[NUM_PAGES];
HPROPSHEETPAGEm_hPages[NUM_PAGES];
DWORDm_dwPageBeforeMe;
DWORDm_dwPageAfterMe;

//
//Non-IWPSite public methods
//

HRESULT Init(LPCWSTR wsSiteName, LPCWSTR wsDestURL);
HRESULT DoTestPost();

private:

//
//Object state
//

PFNPROGRESSUPDATEPROCm_pfnProgressUpdateProc;

};

#endif//__WPPOBJ_H__


WPPROV.CPP

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *wpprov.cpp
 *
 *Abstract:
 *
 *Implementation of DLL Exports
 *
\*------------------------------------------------*/

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"
#include "wpprov.h"
#include "wppobj.h"
#include "regkeyex.h"
#include "util.h"
#include "priority.h"
#include "wppdefs.h"

#define IID_DEFINED
#include "wpspi_i.c"
#include "wpprov_i.c"

CComModule _Module;
HINSTANCE g_hInstance;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_CWPProvider, CWPProvider)
END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
g_hInstance = hInstance;
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
CRegKeyExhKey;
WCHARwsProvCLSID[MAX_PATH];
CHARsProvCLSID[MAX_PATH];
CHARsProvKey[MAX_PATH];
CHARsPathKey[MAX_PATH];
CHARsProvDesc[MAX_PATH];
CHARsProvPath[MAX_PATH];
DWORDdwBufLen;
DWORDdwResult;
HRESULThResult;

//Registers object, typelib and all interfaces in typelib

hResult = _Module.RegisterServer(TRUE);
if (S_OK != hResult)
return(hResult);

//Get string version of Provider CLSID

if (0 == StringFromGUID2(CLSID_CWPProvider, wsProvCLSID, MAX_PATH))
return(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));

g_wcstombs(sProvCLSID, wsProvCLSID, MAX_PATH);

//construct regkey names

lstrcpyA(sPathKey, REGKEY_CLASSES_A);
lstrcatA(sPathKey, "\\");
lstrcatA(sPathKey, sProvCLSID);
lstrcatA(sPathKey, "\\");
lstrcatA(sPathKey, REGKEY_INPROCSERVER_A);

lstrcpyA(sProvKey, REGSUBKEY_WPP_A);
lstrcatA(sProvKey, "\\");
lstrcatA(sProvKey, sProvCLSID);

//Get DLL path

dwResult = hKey.Create(HKEY_CLASSES_ROOT, sPathKey);
hResult = HRESULT_FROM_WIN32(dwResult);
if (FAILED(hResult))
return(hResult);

dwBufLen = MAX_PATH;
dwResult = hKey.QueryValue(sProvPath, &dwBufLen, "");
hResult = HRESULT_FROM_WIN32(dwResult);
if (FAILED(hResult))
return(hResult);

//Create WebPost Provider key

dwResult = hKey.Create(HKEY_WPP, sProvKey);
hResult = HRESULT_FROM_WIN32(dwResult);
if (FAILED(hResult))
return(hResult);

//Load Provider description string

if (0 == LoadString(g_hInstance, IDS_WPPROVIDER_DESC, sProvDesc, MAX_PATH))
{
dwResult = GetLastError();
return(HRESULT_FROM_WIN32(dwResult));
}

//Set Provider key values

dwResult = hKey.SetValue(sProvDesc, REGVAL_WPP_NAME_A);
hResult = HRESULT_FROM_WIN32(dwResult);
if (FAILED(hResult))
return(hResult);

dwResult = hKey.SetValue(sProvPath, REGVAL_WPP_DLLPATH_A);
hResult = HRESULT_FROM_WIN32(dwResult);
if (FAILED(hResult))
return(hResult);

dwResult = hKey.SetValue(WPP_PRIORITY, REGVAL_WPP_PRIORITY_A);
hResult = HRESULT_FROM_WIN32(dwResult);
if (FAILED(hResult))
return(hResult);

dwResult = hKey.SetValue((DWORD) 0, REGVAL_WPP_POSTINFO_REQUIRED_A);
hResult = HRESULT_FROM_WIN32(dwResult);
if (FAILED(hResult))
return(hResult);

return(NO_ERROR);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
CRegKeyExhKey;
WCHARwsProvCLSID[MAX_PATH];
CHARsProvCLSID[MAX_PATH];
HRESULThResult;
DWORDdwResult;

//Get string version of Provider CLSID

if (0 == StringFromGUID2(CLSID_CWPProvider, wsProvCLSID, MAX_PATH))
return(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));

g_wcstombs(sProvCLSID, wsProvCLSID, MAX_PATH);

//Remove WebPost registry entries

dwResult = hKey.Open(HKEY_WPP, REGSUBKEY_WPP_A);
hResult = HRESULT_FROM_WIN32(dwResult);
if (FAILED(hResult))
return(hResult);

dwResult = hKey.DeleteSubKey(sProvCLSID);
hResult = HRESULT_FROM_WIN32(dwResult);
if (FAILED(hResult))
return(hResult);

//Do ATL unregistration

_Module.UnregisterServer();

return(NO_ERROR);
}


WPPROV.IDL

/*-------------------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *wpprov.idl
 *
 *Abstract:
 *
 *Declaration of stubbed WebPost Service Provider object.
 *
\*-------------------------------------------------------------*/

//Import the WebPost Service Provider interface definition

import "wpspi.idl";

//
//TO DO:Replace this UUID with the TypeLibID of your WebPost Service Provider
//

[
uuid(CB1D530F-30F6-11D0-AD03-00AA00A219AA),
version(1.0),
helpstring("CWPProvider 1.0 Type Library")
]
library CWPProviderLib
{
importlib("stdole2.tlb");

//
//TO DO:Replace this UUID with the CLSID for your WebPost Service Provider
//

[
uuid(CB1D531A-30F6-11D0-AD03-00AA00A219AA),
helpstring("CWPProvider Class")
]
coclass CWPProvider
{
[default] interface IWPProvider;
interface IWPSiteW;
};

};


WPSPIHLP.CPP

/*------------------------------------------------*\
 *
 *Copyright (c) 1997 Microsoft Corporation
 *
 *Module Name:
 *
 *wpspihlp.cpp
 *
 *Abstract:
 *
 *Implementation of WebPost Service Provider
 *Interface helper functions.
 *
\*------------------------------------------------*/

#include "stdafx.h"
#include <windows.h>
#include "util.h"
#include <wptypes.h>
#include "wpprov.h"
#include "wppobj.h"
#include "wpspi.h"
#include <wpapi.h>

#define IN_WPPDLL
#include <wpspihlp.h>

HRESULT WPPFUNC WINAPI WppBindToSiteW(
HWNDhwnd,
LPCWSTRwsSiteName,
LPCWSTRwsURL,
    REFIIDriid,
DWORDdwFlag,
DWORDdwReserved,
    PVOID *ppvUnk)
{
HRESULThRetCode = S_OK;
HRESULThResult;
CComObject<CWPProvider>*pWPP;

//
//Check arguments
//

if (!IsEqualIID(riid, IID_IUnknown) &&
!IsEqualIID(riid, IID_IWPSiteW) &&
!IsEqualIID(riid, IID_IWPProvider))
leave(E_NOINTERFACE);

if (IsBadWritePtr(ppvUnk, sizeof(IUnknown *)))
leave(E_INVALIDARG);

//
//Create new Service Provider object
//

pWPP = new CComObject<CWPProvider>;
if (NULL == pWPP)
leave(E_OUTOFMEMORY);

pWPP->AddRef();

//Initialize object state

hResult = pWPP->Init(wsSiteName, wsURL);
if (FAILED(hResult))
leave(hResult);

//If this is an autobind, try a test post

if (0 == (dwFlag & WPF_FORCE_BIND))
{
hResult = pWPP->DoTestPost();
if (FAILED(hResult))
leave(hResult);
}

//
//Set pointer provided to point to new object
//

*ppvUnk = pWPP;

leave_routine:

return(hRetCode);
}

HRESULT WPPFUNC WINAPI WppDeleteSiteW(LPCWSTR sSiteName)
{
HRESULThRetCode = S_OK;

//
//TO DO:Place implementation of site deletion code here
//

leave(S_OK);

leave_routine:

return(hRetCode);
}


README.TXT

This is sample application showing the usage of WebPost APIs.

You should run the webpost.exe to install the webpost components before
using this sample code. If you have the INetSDK, the webpost.exe can be
found in the bin directory under the directory of INetSDK.

Usage:
wbpost-?: usage
wbpost -l: list sites
wbpost -e: enumerates all available providers
wbpost : call the wizard
wbpost filename: call the wizard, with filename chosen
wbpost -s site filename: post filename to site

This example demonstrates calling WpPost() for the simple usage
as well as calling WpBindToSite() to get a pointer to the provider
interface and calling the functions in the web post provider.


WBPOST.CPP

//----------------------------------------------------------------------
//       Copyright (C) 1993-1997 Microsoft Corporation.
//       All rights reserved.
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//WebPost API usage example
//
//  This example allows the user to post files to a web site.
//
//----------------------------------------------------------------------

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <tchar.h>

#include <wpapi.h>
#include <wpspi.h>

#include "wbpost.h"

//global buffer for holding the error message
TCHARg_rgchErr[MAX_PATH];

//you can always call WpGetErrorString to get the full error string text.
VOID
GetErrorString(LONG lErr)
{
DWORDdwErr;
LONGlRet;

dwErr=MAX_PATH;

lRet=WpGetErrorString(
(UINT)lErr,//uErrCode
(LPTSTR)g_rgchErr,//sOutputBuf
&dwErr);//pdwBufLen

if(FAILED(lRet))
g_rgchErr[0]=NULL;

return;
}

LONG
SimpleWebPost( LPTSTR lpszFileName )
{
LONG lRet;

_tprintf("Calling WpPost(...,%s,...)\n", lpszFileName);

lRet = WpPost(NULL,// hWin
1,// count of files
&lpszFileName,// file list
NULL,// lpcbSiteName
NULL,// lpszSiteName
NULL,// lpcbURL
NULL,// lpszURL
0 );// flags

GetErrorString(lRet);

_tprintf("WpPost returned 0x%x which means %s\n", lRet, g_rgchErr);

return lRet;
}

LONG
EnumProviders()
{
static TCHARbuffer[8192];
LPWPPROVINFOlpbProv;
DWORDcProv, cbProv, lRet, i;

cbProv=sizeof(buffer);
cProv=0;
lpbProv=(LPWPPROVINFO)buffer;

printf("Calling WpEnumProviders()\n");

lRet=WpEnumProviders(&cbProv,//pdwProvidersBufLen
lpbProv,//pProvidersBuffer
&cProv);//pdwNumProviders

GetErrorString(lRet);

_tprintf("WpEnumProviders() returned 0x%x which means %s\n", lRet, g_rgchErr);

if(lRet)
return lRet;

printf("Total Providers: %d\n", cProv);

for(i=0; i<cProv; i++, lpbProv++)
{
_tprintf("%d, Name =%s\n", i, lpbProv->lpszProviderName);
_tprintf("   CLSID=%s\n", lpbProv->lpszProviderCLSID);
_tprintf("   Path =%s\n", lpbProv->lpszDllPath);
}

return lRet;
}

LONG
ListSites()
{
static TCHARbuffer[8192];
LPWPSITEINFOlpbSites;
DWORDcSites, cbSites, lRet, i;

cbSites = sizeof( buffer );
cSites = 0;
lpbSites = (LPWPSITEINFO) buffer;

printf("Calling WpListSites()\n");

lRet = WpListSites( &cbSites, lpbSites, &cSites );

GetErrorString(lRet);

_tprintf("WpListSites() returned 0x%x which means %s\n", lRet, g_rgchErr);

if (lRet)
return lRet;

printf("Total Sites: %d\n", cSites);

for (i = 0; i < cSites; i++, lpbSites++)
_tprintf("%d. SiteName=%s\n", i, lpbSites->lpszSiteName);

return lRet;
}

//
// The next function illustrates getting a pointer to the web post
// provider's interface and calling the functions in that interface.
//

LONG
AdvancedWebPost( LPTSTR lpszSiteName, LPTSTR lpszFileName )
{
LONGcbURL = 0;
LONGlRet;
LONGlErrType;
LONGlErrBufLen;

WCHARrgwchFileName[MAX_PATH];
WCHARrgwchError[MAX_PATH];
LPWSTRlpwFileName;

IWPProvider*lpSite = NULL;

_tprintf("Calling WpBindToSite(..., %s, ...)\n", lpszSiteName);

lRet = WpBindToSite(NULL,// hWin
lpszSiteName,
NULL,// lpszURL
0,// fdwFlags
0,// dwReserved
(PVOID *)&lpSite );// Interface pointer

GetErrorString(lRet);

_tprintf("WpBindToSite returned 0x%x which means %s\n", lRet, g_rgchErr);

if (lRet)
return lRet;

//note that both the old IWPSite interface and the IWPProvider interface
//are unicode only.

lRet = lpSite->NetworkConnect(NULL, NULL);
printf("NetworkConnect returned 0x%x\n", HRESULT_CODE(lRet));
if (lRet)
goto cleanup;

lRet = lpSite->ServerLogin(NULL, NULL);
printf("ServerLogin returned 0x%x\n", HRESULT_CODE(lRet));
if (lRet)
goto cleanup;

_tprintf("Calling PostFiles(...,%s,..)\n", lpszFileName);

#ifdef _UNICODE
lpwFileName=lpszFileName;
#else
MultiByteToWideChar(CP_ACP, 0, lpszFileName, lstrlenA(lpszFileName), rgwchFileName, MAX_PATH);
lpwFileName=rgwchFileName;
#endif

lRet = lpSite->PostFiles(1,// cLocalPaths
&lpwFileName,//this has to be unicode
&cbURL,// lpcbURL
NULL,// lpszURL
0 );// fdwFlags

lErrType=0;
lErrBufLen=MAX_PATH;
lpSite->GetError(
&lErrType,//pdwErrorType
&lRet,//pdwErrorCode
&lErrBufLen,//pdwErrorBufLen
rgwchError);//wsError

wprintf(L"PostFiles returned 0x%x which means %s\n", HRESULT_CODE(lRet), rgwchError);

lRet = lpSite->ServerLogout();
printf("ServerLogout returned 0x%x\n", HRESULT_CODE(lRet));
if (lRet)
goto cleanup;

lRet = lpSite->NetworkDisconnect();
printf("NetworkDisconnect returned 0x%x\n", HRESULT_CODE(lRet));

cleanup:
lRet = lpSite->Release();
printf("Release returned 0x%x\n", HRESULT_CODE(lRet));

return lRet;
}

void
usage(void)
{
printf("Usage: wbpost [-s sitename] [filename]\n");
printf("       wbpost -l\t: lists the web sites\n");
printf("       wbpost -e\t: numerates the available providers\n");
exit(1);
}

int __cdecl
main(int argc, char *argv[])
{
LPTSTRlpszSiteName = NULL, lpszFileName = NULL;
intfList = 0;
intfEnum = 0;
LONGlRet;
charc;

#ifdef _UNICODE
WCHARrgwch[MAX_PATH];
#endif

// parse the arguments

while (--argc > 0 && (*++argv)[0] == '-')
if (c = *++argv[0])
switch (c) {
case 'l':
fList = 1;
break;
case 'e':
fEnum = 1;
break;
case 's':
if (--argc) {
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP, 0, *++argv, lstrlenA(*argv), rgwch, MAX_PATH);
lpszSiteName=rgwch;
#else
lpszSiteName = *++argv;
#endif
}
break;
default:
printf("Error: illegal option %c\n", c);
usage();
break;
}
if (argc == 1) {
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP, 0, *argv, lstrlenA(*argv), rgwch, MAX_PATH);
lpszFileName=rgwch;
#else
lpszFileName = *argv;
#endif
argc--;
}

if (argc)
usage();

// call the apis

if (fList)
lRet = ListSites();
else if (fEnum)
lRet = EnumProviders();
else if (lpszSiteName)
lRet = AdvancedWebPost(lpszSiteName, lpszFileName);
else
lRet = SimpleWebPost(lpszFileName);

return lRet;
}


WBPOST.H

/******************************************************************************\
*       Copyright (C) 1993-1997 Microsoft Corporation.
*       All rights reserved.
\******************************************************************************/

/*
   wpcons.h

   Prototypes and definitions for Web Post console sample

*/

#define SITE_BUFFER_SIZE    16000