Building a DTS Custom Task from a Standard ATL Template

DTS Programming

DTS Programming

Building a DTS Custom Task from a Standard ATL Template

One way to build a custom task is to create a project from a standard Active Template Library (ATL) template, add the interface and other elements required by all Data Transformation Services (DTS) tasks, and then add the features of the specific custom task.

This topic explains how to add the elements required by all DTS tasks. You can also use the basic ATL custom task template supplied as a sample with Microsoft® SQL Server™ 2000 to build the custom task framework. Even if you plan to use the custom task template, you need to understand the features that were added to create the basic custom task template from the standard object template. For more information, see Building a Custom Task from the ATL Custom Task Basic Template.

Building a Standard ATL Component

You can create a standard ATL component that includes a class for the custom task using Microsoft Visual C++® version 6.0.

To build a standard ATL component with a class

  1. On the File menu, click New, and then click the Projects tab.

  2. Click ATL COM AppWizard, and then enter a project name and location.

    For this discussion, assume you entered DTSCusTskBasic for the project name.

  3. Click Dynamic Link Library (DLL), click Finish, and in the New Project Information dialog box, click OK.

  4. On the Insert menu, click New ATL Object, click Objects, click Simple Object and then click Next.

  5. On the Names tab, enter a short name.

    For this discussion, assume you entered TaskNoUI. The wizard will fill in the other fields. The COM/Type field is the name that will appear in the Tasks menu of DTS Designer. You can change it from the default TaskNoUI Class.

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

    • Under Interface, click Dual.

    • Under Aggregation, click No.

    • Select the Support ISupportErrorInfo check box.

The wizards will create files for the DTSCusTskBasic component and the TaskNoUI class and save them to the project location folder specified in Step 1.

Adding Custom Task Features

After creating a standard ATL component with TaskNoUI class files, you need to add custom task elements. In this section, all files will have the same names you specified in Step 4 of the previous procedure.

File Features
TaskNoUI.h Function prototypes, private declarations and COM map entries for the IDTSCustomTask interface elements
TaskNoUI.cpp Initial function definitions for the IDTSCustomTask interface elements and the CTaskNoUI constructor and destructor
TaskNoUI.rgs Registry subkeys required for DTS tasks
DTSCusTskBasic.idl Declaration of the IDTSCustomTask interface elements in the ITaskNoUI interface

TaskNoUI.h

In this header file, 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 IDTSCustomTask interface

  • Prototypes for the IDTSCustomTask 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

The constructor body will be moved to TaskNoUI.cpp. Replace the following lines:

    CTaskNoUI()
    {
    }

with these prototype declarations:

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

Immediately following the COM map entry for IDispatch:

   COM_INTERFACE_ENTRY(IDispatch)

add this COM map entry for IDTSCustomTask:

    COM_INTERFACE_ENTRY2(IDTSCustomTask, ITaskNoUI)
Supplying Function Prototypes

You must supply the function prototypes for the IDTSCustomTask interface and declarations for storage for the properties.

Immediately after the following lines:

// ITaskNoUI
public:

add these lines of code:

    STDMETHOD(get_Properties)( 
            /* [retval][out] */ IDispatch  **pRetVal);
        
    STDMETHOD(get_Name)( 
            /* [retval][out] */ BSTR  *pRetVal);
        
    STDMETHOD(put_Name)( 
            /* [in] */ BSTR NewValue);
        
    STDMETHOD(get_Description)( 
            /* [retval][out] */ BSTR  *pRetVal);
        
    STDMETHOD(put_Description)( 
            /* [in] */ BSTR NewValue);
        
    STDMETHOD(Execute)( 
            /* [in] */ IDispatch  *pPackage,
            /* [in] */ IDispatch  *pPackageEvents,
            /* [in] */ IDispatch  *pPackageLog,
            /* [out][in] */ LONG  *pTaskResult);
        
private:
    // Internal storage for Name, Description properties.
    BSTR    m_bstrName;
    BSTR    m_bstrDescription;
TaskNoUI.cpp

This file is where you provide the initial function definitions for the elements of the IDTSCustomTask interface and the class constructor and destructor.

Adding Initial Function Definitions

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

CTaskNoUI::CTaskNoUI()
    {
        m_bstrName = SysAllocString( OLESTR("") );
        m_bstrDescription = SysAllocString( OLESTR("") );
    }
CTaskNoUI::~CTaskNoUI()
    {
        if (m_bstrName) SysFreeString(m_bstrName);
        if (m_bstrDescription) SysFreeString(m_bstrDescription);
    }

STDMETHODIMP CTaskNoUI::get_Properties( 
            /* [retval][out] */ IDispatch  **pRetVal)
{
    // You can implement a properties collection if you want or just return NULL. 
    //  DTS will implement a 'PropertiesProvider' utility object which will
    //   extract the properties out of your IDispatch typeinfo.

    *pRetVal = NULL;
    return NOERROR;
}

STDMETHODIMP CTaskNoUI::get_Name( 
            /* [retval][out] */ BSTR  *pRetVal)
{
    if (!pRetVal)
        return E_POINTER;
    *pRetVal = SysAllocString(m_bstrName);
    if (!*pRetVal)
        return E_OUTOFMEMORY;
    return NOERROR;
}

STDMETHODIMP CTaskNoUI::put_Name( 
            /* [in] */ BSTR NewValue)
{
    if (m_bstrName)
        SysFreeString(m_bstrName);
    m_bstrName = SysAllocString(NewValue);
    if (!m_bstrName)
        return E_OUTOFMEMORY;
    return NOERROR;
}

STDMETHODIMP CTaskNoUI::get_Description( 
            /* [retval][out] */ BSTR  *pRetVal)
{
    if (!pRetVal)
        return E_POINTER;
    *pRetVal = SysAllocString(m_bstrDescription);
    if (!*pRetVal)
        return E_OUTOFMEMORY;
    return NOERROR;
}

STDMETHODIMP CTaskNoUI::put_Description( 
            /* [in] */ BSTR NewValue)
{
    if (m_bstrDescription)
        SysFreeString(m_bstrDescription);
    m_bstrDescription = SysAllocString(NewValue);
    if (!m_bstrDescription)
        return E_OUTOFMEMORY;
    return NOERROR;
}

STDMETHODIMP CTaskNoUI::Execute( 
            /* [in] */ IDispatch  *pPackage,
            /* [in] */ IDispatch  *pPackageEvents,
            /* [in] */ IDispatch  *pPackageLog,
            /* [out][in] */ LONG  *pTaskResult)
{
    USES_CONVERSION;        // Needed for functions like A2W, OLE2T, etc.
    HRESULT hr = NOERROR;

    // TODO: Put functionality of custom task here.

    *pTaskResult = DTSTaskExecResult_Success;
    return hr;
}
TaskNoUI.rgs

This file contains the registry script for the task class. The lines to be added define the task icon location, localizable task description, and the component category for DTS tasks.

Adding Registry Script

Insert these lines immediately following the definition of the TypeLib subkey:

            'Implemented Categories'
            {
                '{10020200-EB1C-11CF-AE6E-00AA004A34D5}'
            }
            DTSTask
            {
                '1033'
                {
                    val DTSIconFile = s '%MODULE%'
                    val DTSIconIndex = d 0
                    val DTSTaskDescription = s 'TaskNoUI Class'
                }
            }
            val DTSIconFile = s '%MODULE%'
            val DTSIconIndex = d 0
            val DTSTaskDescription = s 'TaskNoUI Class'

To verify that the Implemented Categories globally unique identifier (GUID) is correct, look for it in dtspkg.h under the definition for CATID_DTSCustomTask. You can verify that it is among the subkeys of HKEY_CLASSES_ROOT\Component Categories\ in the registry of a computer on which SQL Server 2000 client tools (or the full product) have been installed.

DTSCusTskBasic.idl

This file contains the definitions for the interfaces of the project. You need to add the definitions of the elements of the IDTSCustomTask interface to the ITaskNoUI definition.

Adding IDTSCustomTask Element Definitions

Insert the following interface element definitions into the definition of the ITaskNoUI interface, immediately following these lines:

    interface ITaskNoUI : IDispatch
    {

Add these lines of code:

        //**********************************************************************
        // This interface implements IDTSCustomTask.  We cannot directly inherit 
        // because ATL requires us to explicitly inherit from IDispatch. 
        //**********************************************************************

        [id(7), propget, helpstring("List of Properties for this object")]
        HRESULT Properties([out, retval] IDispatch **pRetVal);
        [id(9), propget, helpstring("Name of Task")]
        HRESULT Name([out, retval] BSTR *pRetVal);
        [id(9), propput]
        HRESULT Name([in] BSTR NewValue);
        [id(10), propget, helpstring("Description of the task")]
        HRESULT Description([out, retval] BSTR *pRetVal);
        [id(10), propput]
        HRESULT Description([in] BSTR NewValue);

        [id(11), helpstring("Execute Task.  Reference to Package allows access to objects in the hierarchy.  Package object and all objects in its hierachy must not be used after returning from this call.  Check event and logging interfaces for NULL/Nothing before using them.")]
        HRESULT Execute([in] IDispatch * pPackage, [in] IDispatch * pPackageEvents, [in] IDispatch * pPackageLog, [in, out] LONG * pTaskResult);

        //************************************************************
        // Make sure that any elements you add to this interface go at
        // the end of the vtable.
        //************************************************************