Share via


Figure 2 Grabbing Dynamic Content

  
Public Function GetForecast(sAirport As String) As String
    Dim oIE As New InternetExplorer
    
    On Error Resume Next
    
    '— Check for offline mode
    If (oIE.Offline = True) Then
        GetForecast = "Offline"
        Exit Function
    End If
    
    '— Don't show any dialogs
    oIE.Silent = True
    
    '— Post the data to the server
    oIE.Navigate "https://www.msnbc.com/news/wea_front.asp?faa=" & _
        sAirport & "&view=local&area=usa"

    '— Wait for the results
    Dim iCount
    Do While True
        If (oIE.ReadyState = 3) Then
            If (oIE.Document.ReadyState = "interactive") Then
                Dim iPos As Integer, iLen As Integer, sDocument As String
                Const sConditions = "strConditions="""
                Const sForecast = "strForecast="""
                sDocument = oIE.Document.Body.innerHTML
                
                '— Parse out the local conditions
                iPos = InStr(1, sDocument, sConditions) + _ 
                    Len(sConditions)
                iLen = InStr(iPos, sDocument, """;") - iPos
                If (iPos > 15 And iLen >< 0) Then
                    GetForecast = Mid(sDocument, iPos, iLen)
                    
                    '— Parse out the forecast
                    iPos = InStr(1, sDocument, sForecast) + _ 
                        Len(sForecast)
                    iLen = InStr(iPos, sDocument, """;") - iPos
                    GetForecast = GetForecast + "!" + Mid(sDocument, _ 
                                                          iPos, iLen)
                    Exit Do
                End If
            End If
        End If
                
        iCount = iCount + 1
        If iCount > 60 Then Exit Do     ' Timeout is set for 30 seconds
        Sleep 500                       ' Wait 1/2 second
    Loop
        
     '— Close and quit this copy of Internet Explorer
    oIE.Quit
    Set oIE = Nothing
    
End Function

Figure 4 Creating a Table

  
function ChangeTime()
{
    var e = window.event.srcElement;
    var group = e.parentElement.children;

    for(var i=0; i<group.length; i++)
        group[i].style.fontWeight = "normal";
 
    e.style.fontWeight = 'bold';

    DisplayHeadcount(); //causes the table to be refreshed
}

function GenerateTableHeader(s, e, oTable)
{
    var sMonths = new Array('January','February','March','April','May','June','July','August','September', 
     'October','November','December');
    var nMonths = 0;
    
    oRow = oTable.insertRow();
    oCell = oRow.insertCell();
    oCell.innerHTML = "&nbsp";
    oCell.rowSpan = 2;
    oCell.style.borderBottom = "1px solid black";
    for(var dt= new Date(s); dt<=e; dt.setMonth(dt.getMonth()+1),nMonths++)
    {
        oCell = oRow.insertCell();
        oCell.innerHTML = sMonths[dt.getMonth()];
        oCell.style.fontWeight = "bold";
        oCell.align = "center";
        oCell.colSpan = 2;
    }
    oRow = document.all.hc_headcount.insertRow();
    for(var i=0; i<nMonths; i++)
    {
        oCell = oRow.insertCell();
        oCell.style.padding = "4px";
        oCell.style.borderBottom = "1px solid black";
        oCell.innerHTML = "Budget";
        oCell = oRow.insertCell();
        oCell.innerHTML = "Actual";
        oCell.style.padding = "4px";
        oCell.style.borderBottom = "1px solid black";
    }
    
    return nMonths;
}

function BuildHeadcount(d, s, e)
{
    var oTable = document.all.hc_headcount;    
    // Remove old rows
    with(oTable)
    {
        while(rows.length > 0)
            deleteRow();
    }

    // Build the query
    var sSQL = "SELECT Month, SUM(ActualHC) AS Actual, 
        Sum(BudgetHC)  AS Budget
        FROM HeadCount h "


    // Query the database        
    var r = oConn.Execute(sSQL);

    // Generate header
    var nMonths = GenerateTableHeader(s, e, oTable);
    
    // Generate HTML
    while(!r.EOF)
    {
        // Put in the month's numbers (budget & actual)
        oCell = oRow.insertCell();
        oCell.align = "right";
        oCell.innerHTML = r("Budget").Value;
        oCell = oRow.insertCell();
        oCell.align = "right";
        oCell.innerHTML = r("Actual").Value;        
        
        // Advance to the next record
        r.moveNext();
    }
    
}

Figure 6 Start Nugget Wrapper Arguments

Argument
Description
sNugget
Unique ID of the nugget
sTitle
Title to place in the title bar of the nugget
iHeight
Default height of the nugget
fMinimized
Indicates whether the nugget should start in the minimized state

Figure 7 Nugget Wrapper

  
<style type="text/css">
.nugget { border: 1px solid black; background-color: white }
    .nuggetTitle { cursor:default; border:2px groove lightsteelblue; font: 11pt; color: white; background-color: steelblue; width:100% }
    .nuggetButtonWrapper { background-color: steelblue; border: 2px groove lightsteelblue; vertical-align:middle }
</style>

function StartNuggetWrapper(sNugget, sTitle, iHeight, fMinimized)
{
    var sStyle = "style='";
    var sHTML = "<div class='nugget' id='" + sNugget + "'>" &_
        "<table id='nuggetTable' border='0' cellpadding='0'" &_ 
        "cellspacing='0'><tr><td nowrap width='100%'>";
    sHTML += "<span id='maximize' ondblclick='toggleMaxMin();'" &_ 
        "title='Double click to maximize' valign='center' ";
    sHTML += "class='nuggetTitle'>" + sTitle + "</span></td>" &_
        "<td nowrap class='nuggetButtonWrapper'>";
    sHTML += "<img id='toggle' src='images/close.gif' width='13px'" &_ 
        "height='17px' title='Hide'>";
    sHTML += "</td></tr></table><div ";
    
    if (0 != iHeight)
        sStyle += "height:" + iHeight;
    if (true == fMinimized)
        sStyle += ";display:none";
    
    if ("style='" != sStyle)
        sHTML += sStyle + "'";
     sHTML += " valign='top' id='content' width='100%'>";
    
    return sHTML;
}

Figure 8 toggleMaxMin

  
function toggleMaxMin()
{
    var e = window.event.srcElement;
    var p = FindParentElement(e, "DIV");
    var t = FindParentElement(p, "TABLE");

    with(p.style)
    {
        if ('absolute' == position)
        {
            position = '';
            height = '';
            width = '';
            left = '';
            top = '';
            zIndex = '';
            e.title = 'Double click to maximize';
            ToggleSiblingDisplay(p);            
        }
        else
        {
            position = 'absolute';
            height = t.parentElement.clientHeight;
            width = t.clientWidth;
            left = t.offsetLeft;
            top = t.offsetTop;
            zIndex = 100;
            e.title = 'Double click to minimize';            
            ToggleSiblingDisplay(p);            
        }
    }
    p.document.recalc(true);
}

Figure 9 Building a Pivot Table

  
<html>
<body>

      <OBJECT classid=clsid:0002E520-0000-0000-C000-000000000046 
            id=cht style="HEIGHT: 100%; WIDTH: 100%">
      <PARAM NAME="ScreenUpdating" VALUE="-1" ></OBJECT>

<script defer language="javascript">

    var oTable = document.all.cht;
    oTable.ConnectionString = "provider=msolap;data source=c:\\warecube.cub";
    oTable.DataMember = "MyCube";

    var oView = oTable.ActiveView;        // Get the view
    var oConstants = oTable.Constants;    // Get the constants
    oView.AutoLayout();                   // Clear the view
                                          // For more samples see
                                          // Programming Office 2000 Web Components
        
    with(oView)
    {
        FilterAxis.InsertFieldSet(FieldSets("Product"));   
            // Allow filtering of the data by product
        RowAxis.InsertFieldSet(FieldSets("Column"));       
            // Put time on the left
        ColumnAxis.InsertFieldSet(FieldSets("Store"));     
            // and Store on the top
        DataAxis.InsertTotal(Totals("Units Ordered"));
        DataAxis.InsertTotal(Totals("Units Shipped"));     
            // here are the data fields
        DataAxis.InsertTotal(Totals("Store Invoice"));
        
        Totals("Units Ordered").NumberFormat = "#,##0";    
            // format the numbers
        Totals("Units Shipped").NumberFormat = "#,##0";        
            // format the numbers
        Totals("Store Invoice").NumberFormat = "$#,##0";    
            // format the numbers
    }
      
</script>
</body>
</html>

Figure 11 Generating an Offline Cube

  
D Dim cnCube As ADODB.Connection
Dim s As String
Dim strProvider As String
Dim strDataSource As String
Dim strSourceDSN As String
Dim strSourceDSNSuffix As String
Dim strCreateCube As String
Dim strInsertInto As String
On Error GoTo Error_cmdCreateCubeFromDatabase
'*——————————————————————————
'* Add Provider that will process the connection string.
'*——————————————————————————
strProvider = "PROVIDER=MSOLAP"
'*——————————————————————————
'* Add DataSource, the name of the cube file (.cub) 
'* that will be created.
'*——————————————————————————
strDataSource = "DATA SOURCE=c:\warecube.cub"
'*——————————————————————————
'* Add Source DSN, the connection string for where the data comes from.
'* We need to quote the value so it is parsed as one value.
'* This can either be an ODBC connection string or 
'* an OLE DB connection string.
'* (As returned by the Data Source Locator component.)
'*
'*    strSourceDSN = "SOURCE_DSN=""DRIVER=Microsoft Access Driver 
'* (*.mdb);DBQ=\\machue1\Samples\Sales.MDB"";"
'*
'*——————————————————————————
strSourceDSN = "SOURCE_DSN=FoodMart"
'*——————————————————————————
'* We may have some other parameters that we want applied
'* at run time, but not stored in the cube file,
'* or returned in the output string.
'* Example:
'* strSourceDSNSuffix = "UID=;PWD="
'*——————————————————————————
'*——————————————————————————
'* Add CREATE CUBE.  This defines the structure of the cube, 
'* but not the data in it.
'* The BNF for this statement is in the 
'* Microsoft SQL Server OLAP Services documentation.
'* Note: The names are quoted with square brackets.
'*——————————————————————————
strCreateCube = "CREATECUBE=CREATE CUBE Mycube( "
strCreateCube = strCreateCube & "DIMENSION [Product],"
        strCreateCube = strCreateCube & "LEVEL [All Products]  TYPE ALL,"
        strCreateCube = strCreateCube & "LEVEL [Product Family] ,"
        strCreateCube = strCreateCube & "LEVEL [Product Department] ,"
        strCreateCube = strCreateCube & "LEVEL [Product Category] ,"
        strCreateCube = strCreateCube & "LEVEL [Product Subcategory] ,"
strCreateCube = strCreateCube & "LEVEL [Brand Name] ,"
strCreateCube = strCreateCube & "LEVEL [Product Name] ,"
strCreateCube = strCreateCube & "DIMENSION [Store],"
strCreateCube = strCreateCube & "LEVEL [All Stores]  TYPE ALL,"
strCreateCube = strCreateCube & "LEVEL [Store Country] ,"
strCreateCube = strCreateCube & "LEVEL [Store State] ,"
strCreateCube = strCreateCube & "LEVEL [Store City] ,"
strCreateCube = strCreateCube & "LEVEL [Store Name] ,"
strCreateCube = strCreateCube & "DIMENSION [Store Type],"
strCreateCube = strCreateCube & "LEVEL [All Store Type]  TYPE ALL,"
strCreateCube = strCreateCube & "LEVEL [Store Type] ,"
strCreateCube = strCreateCube & "DIMENSION [Time] TYPE TIME,"
strCreateCube = strCreateCube & "HIERARCHY [Column],"
strCreateCube = strCreateCube & "LEVEL [All Time]  TYPE ALL,"
strCreateCube = strCreateCube & "LEVEL [Year]  TYPE YEAR,"
strCreateCube = strCreateCube & "LEVEL [Quarter]  TYPE QUARTER,"
strCreateCube = strCreateCube & "LEVEL [Month]  TYPE MONTH,"
strCreateCube = strCreateCube & "LEVEL [Week]  TYPE WEEK,"
strCreateCube = strCreateCube & "LEVEL [Day]  TYPE DAY,"
strCreateCube = strCreateCube & "HIERARCHY [Formula],"
strCreateCube = strCreateCube & "LEVEL [All Formula Time]  TYPE ALL,"
strCreateCube = strCreateCube & "LEVEL [Year]  TYPE YEAR,"
strCreateCube = strCreateCube & "LEVEL [Quarter]  TYPE QUARTER,"
strCreateCube = strCreateCube & "LEVEL [Month]  TYPE MONTH OPTIONS" &_ 
    "(SORTBYKEY) ,"
strCreateCube = strCreateCube & "DIMENSION [Warehouse],"
strCreateCube = strCreateCube & "LEVEL [All Warehouses]  TYPE ALL,"
strCreateCube = strCreateCube & "LEVEL [Country] ,"
strCreateCube = strCreateCube & "LEVEL [State Province] ,"
strCreateCube = strCreateCube & "LEVEL [City] ,"
strCreateCube = strCreateCube & "LEVEL [Warehouse Name] ,"
strCreateCube = strCreateCube & "MEASURE [Store Invoice] "
strCreateCube = strCreateCube & "Function Sum "
strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Supply Time] "
strCreateCube = strCreateCube & "Function Sum "
strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Warehouse Cost] "
strCreateCube = strCreateCube & "Function Sum "
strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Warehouse Sales] "
strCreateCube = strCreateCube & "Function Sum "
strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Units Shipped] "
strCreateCube = strCreateCube & "Function Sum "
strCreateCube = strCreateCube & "Format '#.#',"
strCreateCube = strCreateCube & "MEASURE [Units Ordered] "
strCreateCube = strCreateCube & "Function Sum "
strCreateCube = strCreateCube & "Format '#.#')"

Figure 13 SyncMgr IDL

  
// ERDDSyncMgr.idl : IDL source for ERDDSyncMgr.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (ERDDSyncMgr.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";
    [
        object,
        uuid(51194C1B-116A-4EFF-A20C-7322B95ECB7E),
    
        helpstring("IDDSyncMgr Interface"),
        pointer_default(unique)
    ]
    interface IDDSyncMgr : IUnknown
    {
    };
    [
        object,
        uuid(2B9CFF60-41AF-4287-9FB1-574DEE547974),
    
        helpstring("IDDSyncEnum Interface"),
        pointer_default(unique)
    ]
    interface IDDSyncEnum : IUnknown
    {
    };

[
    uuid(96CB177C-122B-4C5D-8B34-0532C7E4F11A),
    version(1.0),
    helpstring("ERDDSyncMgr 1.0 Type Library")
]
library ERDDSYNCMGRLib
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    [
        uuid(C7C5F46F-DA9F-4442-A8BE-6FA9A4E72B16),
        helpstring("DDSyncMgr Class")
    ]
    coclass DDSyncMgr
    {
        [default] interface IDDSyncMgr;
    };
    [
        uuid(E64FFE15-15F4-4177-81FF-22AA9984D8C2),
        helpstring("DDSyncEnum Class")
    ]
    coclass DDSyncEnum
    {
        [default] interface IDDSyncEnum;
    };
};

Figure 14 SyncMgr Header

  
// DDSyncMgr.h : Declaration of the CDDSyncMgr

#ifndef __DDSYNCMGR_H_
#define __DDSYNCMGR_H_

#include "resource.h"       // main symbols
#include "DDSyncEnum.h"     // Enumerator

#if FILEOUTPUT
#include <fstream.h>        // File stream library
#endif

// Forward declaration
class CDDSyncMgr;

// List node defintion
typedef struct _Node
{
    WCHAR            src[MAX_PATH+1];
    WCHAR            dest[MAX_PATH+1];
    SYNCMGRITEMID    id;
    int              type;
    BOOL             bReadyToSync;
    CDDSyncMgr *     parent;
} NODE, *LPNODE;

typedef enum DDSyncMgrStatus { NONE=0, SYNC=1, EXIT=-1 };

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr
class ATL_NO_VTABLE CDDSyncMgr : 
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CDDSyncMgr, &CLSID_DDSyncMgr>,
    public IDDSyncMgr,
    public ISyncMgrSynchronize
{
        friend class CDDSyncEnum;

public:
    CDDSyncMgr();
    ~CDDSyncMgr();

DECLARE_REGISTRY_RESOURCEID(IDR_DDSYNCMGR)
DECLARE_NOT_AGGREGATABLE(CDDSyncMgr)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CDDSyncMgr)
    COM_INTERFACE_ENTRY(IDDSyncMgr)
    COM_INTERFACE_ENTRY(ISyncMgrSynchronize)
END_COM_MAP()

// IDDSyncMgr
public:

    STDMETHOD(Initialize)(DWORD dwReserved, DWORD dwSyncMgrFlags, DWORD 
              cbCookie, const BYTE __RPC_FAR *lpCookie);
    STDMETHOD(GetHandlerInfo)(LPSYNCMGRHANDLERINFO __RPC_FAR 
              *ppSyncMgrHandlerInfo);
    STDMETHOD(EnumSyncMgrItems)(ISyncMgrEnumItems __RPC_FAR *__RPC_FAR 
              *ppSyncMgrEnumItems);
    STDMETHOD(GetItemObject)(REFSYNCMGRITEMID ItemID, REFIID riid, void 
              __RPC_FAR *__RPC_FAR *ppv);
    STDMETHOD(ShowProperties)(HWND hWndParent, REFSYNCMGRITEMID ItemID);
    STDMETHOD(SetProgressCallback)(ISyncMgrSynchronizeCallback __RPC_FAR 
              *lpCallBack);
    STDMETHOD(PrepareForSync)(ULONG cbNumItems, SYNCMGRITEMID __RPC_FAR 
              *pItemIDs, HWND hWndParent, DWORD dwReserved);
    STDMETHOD(Synchronize)(HWND hWndParent);
    STDMETHOD(SetItemStatus)(REFSYNCMGRITEMID pItemID, DWORD 
              dwSyncMgrStatus);
    STDMETHOD(ShowError)(HWND hWndParent, REFSYNCMGRERRORID ErrorID);

private:
    int                             m_cItems;
    int                             m_maxItems;
    LPNODE                          m_Items;
    HANDLE                          m_hThread;
    HANDLE                          m_hEvent; 
    DDSyncMgrStatus                 m_iStatus;
    ISyncMgrSynchronizeCallback    *m_pCallback;

    STDMETHOD(AddItem)(LPWSTR sID, LPWSTR src, LPWSTR dest, int type);
    STDMETHOD(SyncFiles)();
    STDMETHOD(Progress)(REFSYNCMGRITEMID pItemID, UINT mask, 
              TCHAR *pStatusText, DWORD dwStatusType, int iProgValue, 
              int iMaxValue);
    STDMETHOD(CheckFileDates)(LPWSTR src, LPWSTR dest);

#ifdef FILEOUTPUT
    ofstream        m_outDebug;
#endif

    static DWORD WINAPI      ThreadProc(LPVOID);
    static DWORD CALLBACK    CopyProgressRoutine(LARGE_INTEGER, 
                             LARGE_INTEGER, LARGE_INTEGER, LARGE_INTEGER, 
                             DWORD, DWORD, HANDLE, HANDLE, LPVOID);
};

#endif //__DDSYNCMGR_H_

Figure 15 SyncMgr Implementation

  
// DDSyncMgr.cpp : Implementation of CDDSyncMgr
#include "stdafx.h"
#include "ERDDSyncMgr.h"

#import "C:\Program Files\Common Files\System\ado\msado15.dll" no_namespace rename ("EOF", "adoEOF")
#include "DDSyncMgr.h"

struct InitOle 
{
    InitOle()  { ::CoInitialize(NULL); }
    ~InitOle() { ::CoUninitialize();   }
} _init_InitOle_;

//HANDLE             CDDSyncMgr::m_hEvent = NULL;
//DDSyncMgrStatus    CDDSyncMgr::m_iStatus = NONE;

///////////////////////////////////////////////////////////////////////////
// Constants
LPCWSTR szName = L"East Region Digital Dashboard";

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::CDDSyncMgr
///////////////////////////////////////////////////////////////////////////
CDDSyncMgr::CDDSyncMgr()
{
    m_cItems = 0; 
    m_maxItems = 0; 
    m_Items = NULL; 
    m_hThread = NULL;

#ifdef FILEOUTPUT
    m_outDebug.open("C:\\SyncMgrDebug.txt");
    m_outDebug << "CDDSyncMgr::CDDSyncMgr\n";
#endif
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::~CDDSyncMgr
///////////////////////////////////////////////////////////////////////////
CDDSyncMgr::~CDDSyncMgr()
{

    // Clean up the worker thread
    if (NULL != m_hThread)
    { 
        // Set the exit code for the thread and signal the worker thread
        m_iStatus = EXIT;
        SetEvent(m_hEvent);

        if (WaitForSingleObject(m_hThread, 5000) != WAIT_OBJECT_0)
            TerminateThread(m_hThread, 0);

        // Close the handle to the thread
        CloseHandle(m_hThread);
        m_hThread = NULL;
    }

    if (NULL != m_hEvent)
    {
        CloseHandle(m_hEvent);
        m_hEvent = NULL;
    }

#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::~CDDSyncMgr\n";
    m_outDebug.close();
#endif

}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::Initialize
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::Initialize(DWORD dwReserved,
                                    DWORD dwSyncMgrFlags,
                                    DWORD cbCookie,
                                    const BYTE __RPC_FAR *lpCookie)
{    
#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::Initialize\n";
#endif


    HRESULT            hr = S_OK;
    _RecordsetPtr   rs = NULL;

    try
    {
        // Create the event and thread
        m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
        if (NULL == m_hEvent)
            return S_FALSE;

        m_hThread = CreateThread(NULL, 0, ThreadProc, this, 0, NULL);
        if (NULL == m_hThread)
            return S_FALSE;

        // open a connection to the local database
        _bstr_t Connect(TEXT("Driver={SQL     
            Server};Network=dbnmpntw;Server=(local);Database=DigitalDashboard"));
        _bstr_t Source(TEXT("SELECT * FROM Synchronization"));

        // create a recordset and execute a query
        rs.CreateInstance(__uuidof(Recordset));
        hr = rs->Open(Source, Connect, adOpenForwardOnly, adLockReadOnly, -1);
        // populate the list of files to synchronize
        while(!rs->adoEOF)
        {
            _variant_t v;
            _variant_t vOffset;

            // Get the item ID
            vOffset = (short) 0;
            _variant_t vID = rs->Fields->GetItem(vOffset)->GetValue();

            // Get the source
            vOffset = (short) 1;
            _variant_t vSrc = rs->Fields->GetItem(vOffset)->GetValue();

            // get the destination
            vOffset = (short) 2;
            _variant_t vDest = rs->Fields->GetItem(vOffset)->GetValue();

            // get the type
            vOffset = (short) 3;
            _variant_t vType = rs->Fields->GetItem(vOffset)->GetValue();

        
            // Add the item to the list
            AddItem(vID.bstrVal, vSrc.bstrVal, vDest.bstrVal, 
                    vType.intVal);

            // advance to the next item
            rs->MoveNext();
        }

        // close the recordset
        rs->Close();
        rs = NULL;

    }
    catch(_com_error &e)
    {
#ifdef FILEOUTPUT
        m_outDebug << "CDDSyncMgr::Initialize Error: " << e.Description() 
            << "\n";
#endif 
        hr = S_FALSE;
    }

#ifdef FILEOUTPUT
    if (S_FALSE == hr)
        m_outDebug << "CDDSyncMgr::Initialize - returning: S_FALSE\n";
    else
        m_outDebug << "CDDSyncMgr::Initialize - returning: S_OK\n";
#endif
    return hr;
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::GetHandlerInfo
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::GetHandlerInfo(LPSYNCMGRHANDLERINFO __RPC_FAR 
    *ppSyncMgrHandlerInfo)
{
#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::GetHandlerInfo\n";
#endif

    if (NULL == ppSyncMgrHandlerInfo)
        return E_INVALIDARG;

    HRESULT hr = S_OK;
    DWORD    dwSize = sizeof(SYNCMGRHANDLERINFO);

    // Allocate the return structure
    *ppSyncMgrHandlerInfo = (LPSYNCMGRHANDLERINFO) 
                            HeapAlloc(GetProcessHeap(), 0, dwSize);
    if (NULL == *ppSyncMgrHandlerInfo)
        return E_OUTOFMEMORY;

    (*ppSyncMgrHandlerInfo)->cbSize = sizeof(SYNCMGRHANDLERINFO);
    (*ppSyncMgrHandlerInfo)->hIcon = LoadIcon(_Module.m_hInst, 
                                              _T("IDI_ICON"));
    (*ppSyncMgrHandlerInfo)->SyncMgrHandlerFlags = 0;
    lstrcpyn((*ppSyncMgrHandlerInfo)->wszHandlerName, szName, 
             MAX_SYNCMGRHANDLERNAME);

    return hr;
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::EnumSyncMgrItems
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::EnumSyncMgrItems(ISyncMgrEnumItems __RPC_FAR 
    *__RPC_FAR *ppSyncMgrEnumItems)
{
#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::EnumSyncMgrItems\n";
#endif

    if (NULL == ppSyncMgrEnumItems)
        return E_INVALIDARG;

    HRESULT hr = 
        CComCoClass<CDDSyncEnum>::CreateInstance(ppSyncMgrEnumItems);
    if (SUCCEEDED(hr))
        ((CDDSyncEnum *) *ppSyncMgrEnumItems)->SetSyncMgr(this);

    return hr;
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::GetItemObject
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::GetItemObject(REFSYNCMGRITEMID ItemID,
                                       REFIID riid,
                                       void __RPC_FAR *__RPC_FAR *ppv)
{
    HRESULT hr = S_OK;
    return hr;
}
///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::ShowProperties
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::ShowProperties(HWND hWndParent,
                                        REFSYNCMGRITEMID ItemID)
{
    HRESULT hr = S_OK;
    return hr;
}
///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::SetProgressCallback
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::SetProgressCallback(ISyncMgrSynchronizeCallback 
    __RPC_FAR *lpCallBack)
{
    m_pCallback = lpCallBack;
    return S_OK;
}
///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::PrepareForSync
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::PrepareForSync(ULONG cbNumItems,
                                        SYNCMGRITEMID __RPC_FAR *pItemIDs,
                                        HWND hWndParent,
                                        DWORD dwReserved)
{
#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::PrepareForSync\n";
#endif


    HRESULT hr = S_OK;

    // For each of the items in the item list, mark as ready for 
    // synchronization
    for(ULONG i=0; i<cbNumItems; i++)
    {
        for(int j=0; j<m_cItems; j++)
        {
            if (IsEqualGUID(pItemIDs[i], m_Items[j].id) == TRUE)
            {
                m_Items[j].bReadyToSync = TRUE;
                break;
            }
        }
    }

    // We're done, notify SyncMgr
    if (NULL != m_pCallback)
        hr = m_pCallback->PrepareForSyncCompleted(hr);

    return hr;
}
///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::Synchronize
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::Synchronize(HWND hWndParent)
{
#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::Synchronize\n";
#endif

    HRESULT hr = S_OK;

    m_iStatus = SYNC;
    SetEvent(m_hEvent);

    return hr;
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::SetItemStatus
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::SetItemStatus(REFSYNCMGRITEMID pItemID,
                                       DWORD dwSyncMgrStatus)
{
#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::SetItemStatus\n";
#endif

    HRESULT hr = S_OK;

    if (SYNCMGRSTATUS_SKIPPED != dwSyncMgrStatus)
        return hr;

    for(int i=0; i<m_cItems; i++)
    {
        if (IsEqualGUID(pItemID, m_Items[i].id) == TRUE)
        {
            m_Items[i].bReadyToSync = FALSE;
            break;
        }
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::ShowError
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::ShowError(HWND hWndParent,
                                   REFSYNCMGRERRORID ErrorID)
{
    HRESULT hr = S_OK;
    return hr;
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::AddItem
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::AddItem(LPWSTR sID, LPWSTR src, LPWSTR dest, 
    int type)
{
#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::AddItem\n";
#endif


    // Are we full ?
    if (m_cItems == m_maxItems)
    {
        m_maxItems = m_cItems + 10;
        SIZE_T cbSize = m_maxItems * sizeof(NODE);

        LPNODE t = (LPNODE) HeapAlloc(GetProcessHeap(), 
                                      HEAP_GENERATE_EXCEPTIONS | 
                                      HEAP_ZERO_MEMORY, cbSize);
        if (NULL == t)
            return E_OUTOFMEMORY;

        // Copy the old items to the next item list
        memcpy(t, m_Items, m_cItems * sizeof(NODE));

        // Free the old list
        HeapFree(GetProcessHeap(), 0, m_Items);
        m_Items = t;
    }

    // Add the new item to the list
    LPNODE pItem = &m_Items[m_cItems];
    lstrcpyn(pItem->src, src, MAX_PATH);
    lstrcpyn(pItem->dest, dest, MAX_PATH);
    CLSIDFromString(sID, &pItem->id);
    pItem->type = type;
    pItem->bReadyToSync = FALSE;
    pItem->parent = this;
    m_cItems++;

    return S_OK;
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::SyncFiles
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::SyncFiles()
{
#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::SyncFiles\n";
#endif

    HRESULT             hr = S_OK;
    BOOL                bCancel = FALSE;

    // Start copying the files
    for(int i=0; i<m_cItems; i++)
    {
        LPNODE node = &m_Items[i];
        if (TRUE == node->bReadyToSync && 3 == node->type)
        {
            // Set the progress for the user
            Progress(node->id,
                     SYNCMGRPROGRESSITEM_STATUSTYPE | 
                     SYNCMGRPROGRESSITEM_PROGVALUE | 
                     SYNCMGRPROGRESSITEM_MAXVALUE,
                     NULL, SYNCMGRSTATUS_UPDATING, 0, 100);

            // Check that the file is newer than the local file
            if (CheckFileDates(node->src, node->dest) == S_FALSE)
            {
                Progress(node->id,
                    SYNCMGRPROGRESSITEM_STATUSTYPE |
                    SYNCMGRPROGRESSITEM_PROGVALUE | 
                    SYNCMGRPROGRESSITEM_MAXVALUE | 
                    SYNCMGRPROGRESSITEM_STATUSTEXT,
                    _T("The file does not need to be copied."),
                    SYNCMGRSTATUS_SUCCEEDED, 100, 100);
                continue;
            }

            // Start the file copy
            if (CopyFileEx(node->src, node->dest, CopyProgressRoutine, 
                           node, &bCancel, COPY_FILE_RESTARTABLE) == TRUE)
            {
                node->bReadyToSync = FALSE;
                Progress(node->id,
                         SYNCMGRPROGRESSITEM_STATUSTYPE | 
                         SYNCMGRPROGRESSITEM_PROGVALUE | 
                         SYNCMGRPROGRESSITEM_MAXVALUE, NULL,
                         SYNCMGRSTATUS_SUCCEEDED, 100, 100);
            }
            else
            {
                if (NULL != m_pCallback)
                {
                    LPWSTR lpMsgBuf;
                    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
                                  FORMAT_MESSAGE_FROM_SYSTEM |
                                  FORMAT_MESSAGE_IGNORE_INSERTS,
                                  NULL,
                                  GetLastError(),
                                  MAKELANGID(LANG_NEUTRAL, 
                                  SUBLANG_DEFAULT), 
                                  (LPWSTR) &lpMsgBuf,
                                  0,
                                  NULL);
                    m_pCallback->LogError(SYNCMGRLOGLEVEL_ERROR, lpMsgBuf, 
                                          NULL);
                    LocalFree(lpMsgBuf);                                  
                }
            }
        }
    }

    // Call the callback to tell that we're done.
    if (NULL != m_pCallback)
        hr = m_pCallback->SynchronizeCompleted(hr);

    return hr;
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::CheckFileDates
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::CheckFileDates(LPWSTR src, LPWSTR dest)
{
    WIN32_FILE_ATTRIBUTE_DATA    fadSrc;
    WIN32_FILE_ATTRIBUTE_DATA    fadDest;

    // Get the source file's attribute data
    if (GetFileAttributesEx(src, GetFileExInfoStandard, &fadSrc) != FALSE)
    {
        if (GetFileAttributesEx(dest, GetFileExInfoStandard, &fadDest) != 
            FALSE)
        {
            if (CompareFileTime(&(fadSrc.ftLastWriteTime), 
                &(fadDest.ftLastWriteTime)) <= 0)
                return S_FALSE;
        }
    }

    return S_OK;
}

///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::ThreadProc
///////////////////////////////////////////////////////////////////////////
DWORD WINAPI CDDSyncMgr::ThreadProc(LPVOID pParam)
{
    DWORD dwReturn = 0;
    CDDSyncMgr    *t = (CDDSyncMgr *) pParam;

    while(1)
    {
        // Wait for the main thread to signal
        dwReturn = WaitForSingleObject(t->m_hEvent, INFINITE);

        switch(t->m_iStatus)
        {
        case SYNC:
            t->SyncFiles();
            break;

        case EXIT:
            return 0;

        case NONE:
        default:
            break;
        }
    }

    return 0;
}
///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::Progress
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CDDSyncMgr::Progress(REFSYNCMGRITEMID pItemID,
                                  UINT mask,
                                  TCHAR *pStatusText,
                                  DWORD dwStatusType,
                                  int iProgValue,
                                  int iMaxValue)
{
#ifdef FILEOUTPUT
    m_outDebug << "CDDSyncMgr::Progress\n";
#endif

    SYNCMGRPROGRESSITEM syncProg;
    if (NULL == m_pCallback)
        return S_FALSE;

    syncProg.cbSize = sizeof(SYNCMGRPROGRESSITEM);
    syncProg.mask = mask;

    if (SYNCMGRPROGRESSITEM_STATUSTEXT & mask)
    {
    #ifdef _UNICODE
        syncProg.lpcStatusText = pStatusText;
    #else
        WCHAR wszStatusText[MAX_PATH];

       MultiByteToWideChar(CP_ACP, 0, pStatusText, 
                           -1, wszStatusText,MAX_PATH);

        syncProg.lpcStatusText = wszStatusText;
    #endif // _UNICODE
    }

    syncProg.dwStatusType = dwStatusType;
    syncProg.iProgValue = iProgValue;
    syncProg.iMaxValue = iMaxValue;

    return m_pCallback->Progress(pItemID, &syncProg);
}
///////////////////////////////////////////////////////////////////////////
// CDDSyncMgr::CopyProgressRoutine
///////////////////////////////////////////////////////////////////////////
DWORD CALLBACK CDDSyncMgr::CopyProgressRoutine(
  LARGE_INTEGER TotalFileSize,          // file size
  LARGE_INTEGER TotalBytesTransferred,  // bytes transferred
  LARGE_INTEGER StreamSize,             // bytes in stream
  LARGE_INTEGER StreamBytesTransferred, // bytes transferred for stream
  DWORD dwStreamNumber,                 // current stream
  DWORD dwCallbackReason,               // callback reason
  HANDLE hSourceFile,                   // handle to source file
  HANDLE hDestinationFile,              // handle to destination file
  LPVOID lpData                         // from CopyFileEx
)
{
    LPNODE pNode = (LPNODE) lpData;
    if (NULL != pNode)
    {
        // Check if this item has been requested cancelled by the user
        if (FALSE == pNode->bReadyToSync)
            return PROGRESS_STOP;

        CDDSyncMgr *pMgr = pNode->parent;

        double dSize = double(TotalBytesTransferred.QuadPart) /        
                       double(TotalFileSize.QuadPart);
        pMgr->Progress(pNode->id,
                       SYNCMGRPROGRESSITEM_PROGVALUE | 
                       SYNCMGRPROGRESSITEM_MAXVALUE,
                       NULL, 0, int(dSize * 100.0), 100);
    }
    else
        return PROGRESS_CANCEL;

    return PROGRESS_CONTINUE;
}