Adding a DTS User Interface to the Custom Task Framework

DTS Programming

DTS Programming

Adding a DTS User Interface to the Custom Task Framework

To build a custom task that supports a custom task user interface, create a project from the Active Template Library (ATL) custom task basic template, add another class and the interface and other elements required by tasks that support a custom user interface, and then add the features of the specific custom task.

This topic explains how to add the elements required by a Data Transformation Services (DTS) task with a custom user interface. You can also use the ATL custom task user interface templates supplied as samples with Microsoft® SQL Server™ 2000. Even if you plan to use the custom task user interface templates, you need to understand the features that were added to create the custom task user interface templates from the basic custom task template. For more information, see Building a Custom Task with User Interface from the ATL Custom Task Templates.

Building a Custom Task Framework with a UI Class

You can create a Custom Task Framework that includes a class for the custom user interface using Microsoft Visual C++® version 6.0.

To build a custom task framework with a user interface class

  1. Create a framework for a custom task using the ATL custom task basic template.

    Assume you named the component DTSCusTskWUI and the custom task class TaskUISupp. For more information, see Building a Custom Task from the ATL Custom Task Basic Template.

  2. Add another class for the user interface. On the Insert menu, click New ATL Object. On the ATL Object Wizard screen, click Objects, and then click Simple Object. Click Next.

  3. On Names, enter a short name.

    Assume you entered UserIF. The wizard will fill in the other fields.

  4. Click the Attributes tab, and then do the following:
    • Under Threading Model, click Apartment.

    • Under Interface, click Dual.

    • Under Aggregation, click Yes.

      The user interface class will not work unless it can be aggregated.

The wizards will create files for the DTSCusTskWUI component and the TaskUISupp and UserIF classes, and save them to the project location folder specified when you created the framework in the first step.

Adding Features to Support a Custom UI

After creating the custom task framework with TaskUISupp and UserIF class files, you need to add features to support the user interface. In this section, all files will have the same names you specified for the classes.

File Features
TaskUISupp.h Function prototypes, private declarations, and COM map entries for the elements that connect the custom task class to the user interface class.
TaskUISupp.cpp Definitions for the functions that connect the task class to the user interface class.
UserIF.h Function prototypes, private declarations, and COM map entries for the IDTSCustomTaskUI interface elements.
UserIF.cpp Initial function definitions for the IDTSCustomTaskUI interface methods.
DTSCusTskWUI.idl Declaration of the IDTSCustomTaskUI interface elements in the IUserIF interface.

TaskUISupp.h

In this header file, you need to add the following:

  • Prototypes for the functions that connect the custom task class to the user interface class

  • COM Map entries for the IDTSCustomTask and IDTSCustomTaskUI interfaces

  • A declaration of an interface pointer variable
Adding Hook Function Prototypes

These prototypes are for functions that are called when a QueryInterface request for the IDTSCustomTaskUI interface is made to the task class.

Following the constructor and destructor prototypes for the task class:

    CTaskUISupp();
    ~CTaskUISupp();

add these lines of code:

    static HRESULT WINAPI FuncPreQueryInterface(void* pv, REFIID riid, 
                                                LPVOID* ppv, DWORD dw);
    HRESULT PreQueryInterface(REFIID riid, LPVOID *ppv);
Adding COM Map Entries

You need to replace the COM map entry for the ITaskUISupp interface so that it responds when presented with the interface ID for the IDTSCustomTask. You need to add a COM map entry that invokes the hook function when presented with the interface ID for IDTSCustomTaskUI.

Replace the following COM map entries:

    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY2(IDTSCustomTask, ITaskUISupp)

with these lines:

    COM_INTERFACE_ENTRY2(IDispatch, ITaskUISupp)
    COM_INTERFACE_ENTRY_IID(IID_IDTSCustomTask, ITaskUISupp)
    COM_INTERFACE_ENTRY_FUNC(IID_IDTSCustomTaskUI, 0, CTaskUISupp::FuncPreQueryInterface)
Adding an Interface Pointer Variable

This declaration is for a pointer variable for the user interface class, which is set by the hook functions.

Immediately following these lines:

    BSTR        m_bstrName;
    BSTR        m_bstrDescription;

add this declaration:

    IUnknown    * m_pIUnkDTSCustomTaskUI;
TaskUISupp.cpp

In this Visual C++ file for the task class, you need to add the following:

  • An external reference for the class id of the user interface class

  • Code to initialize the user interface class interface pointer variable in the task class constructor

  • Code to release the pointer to the user interface class in the task class destructor

  • Functions that create an instance of the user interface class and issue a QueryInterface for IDTSCustomTaskUI
Adding an External Reference for the class id

Following this include statement:

#include "TaskUISupp.h"

add this external reference:

extern  const CLSID         CLSID_UserIF;
Initializing the User Interface Pointer

At the end of the task class constructor (before the right curly bracket):

CTaskUISupp::CTaskUISupp()

add this line of code:

    m_pIUnkDTSCustomTaskUI = NULL;
Releasing the Interface Pointer

Release the reference to the IDTSCustomTaskUI interface if it exists in the task class destructor.

At the end of the destructor (before the right curly bracket):

CTaskUISupp::~CTaskUISupp()

add these lines of code:

    if(m_pIUnkDTSCustomTaskUI)
        if(m_pIUnkDTSCustomTaskUI->Release() != 0)
            /* _ASSERT(0) */    NULL;
Adding Hook Function Definitions

These functions first obtain a reference to the aggregating object, which is the custom task class. If the QueryInterface request is for the IDTSCustomTaskUI interface and the user interface has not yet been created, an instance of the user interface is created. Then the QueryInterface is requested from the aggregated object, the user interface class.

After the task class destructor:

CTaskUISupp::~CTaskUISupp() { ... }

add these lines of code:

HRESULT WINAPI CTaskUISupp::FuncPreQueryInterface(void* pv, REFIID riid, LPVOID* ppv, DWORD dw)
{
    HRESULT hr = E_FAIL;
    _ASSERT(pv);
    CTaskUISupp * pDTSCustTask = (CTaskUISupp *)pv;
    return pDTSCustTask->PreQueryInterface(riid, ppv);
}

HRESULT CTaskUISupp::PreQueryInterface(REFIID riid, LPVOID *ppv)
{
    HRESULT     hr = S_FALSE;
    IUnknown    * pIUnknownOuter;

    if (!ppv)
    {
        hr = E_POINTER;
        goto error;
    }
    if FAILED(hr = QueryInterface(IID_IUnknown, (void **)&pIUnknownOuter))
        goto error;

    *ppv = NULL;
    if (IID_IDTSCustomTaskUI == riid) 
    {
        if(!m_pIUnkDTSCustomTaskUI)
            if FAILED(hr = CoCreateInstance(CLSID_UserIF,
                                            pIUnknownOuter, CLSCTX_INPROC_SERVER,
                                            IID_IUnknown, (LPVOID*)&m_pIUnkDTSCustomTaskUI))
                goto error;

        hr = m_pIUnkDTSCustomTaskUI->QueryInterface(riid, ppv);
    }
    else
        hr = S_FALSE;

error:
    return hr;
}
UserIF.h

In this header file for the user interface class, you need to add the following:

  • Include statements for the DTS package header file

  • Prototypes for the class constructor and destructor

  • A COM Map entry for the IDTSCustomTaskUI interface

  • Prototypes for the IDTSCustomTaskUI interface elements
Adding an Include Statement

Immediately preceding the include statement for resource.h:

#include "resource.h"       // main symbols

add this header file include statement:

#include "dtspkg.h"
Adding Constructor and Destructor Prototypes

You need to add prototypes for the class constructor and destructor. The constructor body will be moved to UserIF.cpp

Replace the following lines:

    CUserIF()
    {
    }

with these prototype declarations:

    CUserIF();
    ~CUserIF();
Adding a COM Map Entry

Immediately following the COM map entry for IDispatch:

    COM_INTERFACE_ENTRY(IDispatch)

add this COM map entry for IDTSCustomTaskUI:

    COM_INTERFACE_ENTRY_IID(IID_IDTSCustomTaskUI, IUserIF)
Adding Function Prototypes

You must supply the function prototypes for the IDTSCustomTask interface and declarations for pointers to the generic and custom task interfaces.

Immediately after the following lines:

// IUserIF
public:

add these lines of code:

    STDMETHOD(CreateCustomToolTip)(long hwndParent, long x, long y, long *plTipWindow);
    STDMETHOD(Help)(long hwndParent);
    STDMETHOD(Delete)(long hwndParent);
    STDMETHOD(Edit)(long hwndParent);
    STDMETHOD(New)(long hwndParent);
    STDMETHOD(GetUIInfo)(BSTR *pbstrToolTip, BSTR *pbstrDescription, long *plVersion, /*DTSCustomTaskUIFlags*/long *pFlags);
    STDMETHOD(Initialize)(IUnknown *pTask);

private:
    IDTSTask        * m_pIDTSTask;
    IDTSCustomTask  * m_pIDTSCustomTask;
UserIF.cpp

In this file, you must provide the initial function definitions for the elements of the IDTSCustomTaskUI interface and the class constructor and destructor.

Adding Initial IDTSCustomTaskUI Function Definitions

Add these lines of code at the end of the existing file:

CUserIF::CUserIF()
{
    m_pIDTSTask = NULL;
    m_pIDTSCustomTask = NULL;
}
CUserIF::~CUserIF()
{
}

STDMETHODIMP CUserIF::CreateCustomToolTip(long hwndParent, long x, long y, long *plTipWindow)
{
    // TODO: Add your implementation code here.

    return E_NOTIMPL;
}

STDMETHODIMP CUserIF::Help(long hwndParent)
{
    // TODO: Add your implementation code here.

    return E_NOTIMPL;
}

STDMETHODIMP CUserIF::Delete(long hwndParent)
{
    // TODO: Add your implementation code here.

    return S_OK;
}

// Pop up dialog box to get user's property information.
STDMETHODIMP CUserIF::New(long hwndParent)
{
    //if(!m_pIDTSTask || !m_pIDTSCustomTask)
    //  return E_FAIL;
    //return NOERROR;

    // TODO: Add your implementation code here.

    return E_NOTIMPL;
}

// Pop up dialog box to get user's property information.
// Fill the existing information in the controls.
STDMETHODIMP CUserIF::Edit(long hwndParent)
{
    //if(!m_pIDTSTask || !m_pIDTSCustomTask)
    //  return E_FAIL;
    //return NOERROR;

    // TODO: Add your implementation code here.

    return E_NOTIMPL;
}

STDMETHODIMP CUserIF::GetUIInfo(BSTR *pbstrToolTip, BSTR *pbstrDescription, long *plVersion, long *pFlags)
{
    // TODO: Add your implementation code here.

    return S_OK;
}

// Get custom task interface pointer.
STDMETHODIMP CUserIF::Initialize(IUnknown *pTask)
{
    HRESULT hr = E_FAIL;

    m_pIDTSTask = NULL;

    if FAILED(hr = pTask->QueryInterface(IID_IDTSTask, (void **) &m_pIDTSTask))
        return hr;

    // Release it immediately. Do not add ref outer object.
    m_pIDTSTask->Release();

    if FAILED(hr = m_pIDTSTask->GetCustomTask(&m_pIDTSCustomTask))
        return hr;

    // Release it immediately. Do not add ref outer object.
    m_pIDTSCustomTask->Release();

    return NOERROR;
}
DTSCusTskWUI.idl

In this file, which contains the definitions for the interfaces of the project, you need to add the definitions of the elements of the IDTSCustomTaskUI interface to the IUserIF definition.

Adding IDTSCustomTaskUI Element Definitions

Into the definition of the ITaskNoUI interface, immediately following these lines:

    interface IUserIF : IDispatch
    {

add these lines of code:

        [id(106), helpstring("Initialize the object at design time with any information needed. The Task object associated with the custom task is passed in.")] 
        HRESULT Initialize(IUnknown *pTask);
        [id(100), helpstring("This method is called to get top level UI information for the task.")] 
        HRESULT GetUIInfo(BSTR *pbstrToolTip, BSTR *pbstrDescription, long *plVersion, long *pFlags);
        [id(101), helpstring("A New instance of the custom task is to be added. Please display appropriate UI (usually a wizard or property sheet).")] 
        HRESULT New(long hwndParent);
        [id(102), helpstring("The 'Edit' command has been invoked on the custom task object. Please display appropriate UI (a property sheet usually).")] 
        HRESULT Edit(long hwndParent);
        [id(103), helpstring("The 'Delete' command has been invoked on the custom task object.")] 
        HRESULT Delete(long hwndParent);
        [id(104), helpstring("The 'Help' command has been invoked on the custom task object.")] 
        HRESULT Help(long hwndParent);
        [id(105), helpstring("If the custom task requested a custom tooltip then this method will be called to create the tip window.")] 
        HRESULT CreateCustomToolTip(long hwndParent, long x, long y, long *plTipWindow);