In Microsoft® Visual C++®, a custom provider for Microsoft Provisioning Framework (MPF) is implemented as a COM object class with sets of functions for each provisioning procedure. MPS SDK adds functions to the Visual C++ integrated development environment (IDE) that help you generate a custom provider.
Visual C++ generates several files, but the only ones you must modify are ProviderName.h, ProviderName.cpp, and ProjectName.xml.
Procedure Type | Action Map Entry | Methods | Description |
---|---|---|---|
Standard | ACTION_MAP_ENTRY | ProcedureName_Execute ProcedureName_Rollback |
A procedure that must undo updates whenever a transaction is terminated. The corresponding rollback function is called for each action that already successfully executed. Rollback functions are called in the reverse order that the execute functions were called. No rollback functions are called if the transaction succeeds. |
No Rollback | ACTION_MAP_ENTRY_NOROLLBACK | ProcedureName_Execute | An action that never has to be rolled back, such as a query. |
Two Phase | ACTION_MAP_ENTRY_TWOPHASE | ProcedureName_Execute ProcedureName_Rollback ProcedureName_Commit ProcedureName_Prepare |
An action that implements a two-phase commit plus rollback. This resembles the Standard procedure type, except that the Prepare and Commit methods are called before the transaction is completed. |
Note For the automatic updates to occur, you must install and use the special MPF SDK functions to set up the provider and its procedures. Create a new MPF Provider project to generate the custom provider project files, and click the button on the toolbar to insert a new procedure.
To develop providers in Visual C++ 6.0, the following software must be installed on the development computer, in the following order.
Use MPF to test the provider. For more information, see System Requirements and Getting Started.
To use Visual C++ to create a provider, you must first set up the development environment. This involves verifying that SDK files are in the proper location and adding a toolbar button to insert procedures into provider projects.
Use the MPF Provider Wizard to generate a skeleton project.
The project name becomes the default provider name.
To generate a skeleton procedure, follow these steps:
The following example shows a SysTime provider with the procedure GetSystemTime. The toolbar button generates the following skeleton code for GetSystemTime.cls.
HRESULT CSysTime1::GetSystemTime_Execute( IXMLDOMNode *pXMLNode )
{
HRESULT hr = S_OK;
//CComVariant varData(L"test");
//hr = m_pProvHelper->SetRollbackData(L"key", varData );
return hr;
}
GetSystemTime can be implemented in this skeleton code as follows:
HRESULT CSysTime1::GetSystemTime_Execute( IXMLDOMNode *pXMLNode )
{
// The Execute method contains code that should be executed during the
// provisioning request.
HRESULT hr = S_OK;
CComPtr<IXMLDOMNode> pXMLExecData;
CComPtr<IXMLDOMNode> pMode;
CComBSTR bstrDate;
CComBSTR bstrTime;
hr = pXMLNode->selectSingleNode(L"executeData", &pXMLExecData);
if(hr != S_OK)
{
goto LocalCleanup;
}
// Check for required element.
hr = pXMLExecData->selectSingleNode(L"mode", &pMode);
if(hr != S_OK)
{
RaiseError(L"SysTime1.SysTime1", L"The required element 'mode' is missing.");
goto LocalCleanup;
}
// Get the date and time, unless explicitly not wanted.
if(GetOptionalBoolValue(pMode, L"@date", TRUE))
{
hr = GetDate(&bstrDate);
if(FAILED(hr))
{
goto LocalCleanup;
}
PutValue(pXMLExecData, L"date", bstrDate);
}
if(GetOptionalBoolValue(pMode, L"@time", TRUE))
{
hr = GetTime(&bstrTime);
if(FAILED(hr))
{
goto LocalCleanup;
}
PutValue(pXMLExecData, L"time", bstrTime);
}
LocalCleanup:
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// HELPERS
HRESULT CSysTime1::RaiseError(LPWSTR szErrorSource, LPWSTR szErrorDescription)
{
HRESULT hr = S_OK;
CComPtr<ICreateErrorInfo> pCreateErrorInfo;
CComPtr<IErrorInfo> pErrorInfo;
if(szErrorDescription == NULL)
{
hr = E_INVALIDARG;
goto LocalCleanup;
}
hr = CreateErrorInfo(&pCreateErrorInfo);
if(FAILED(hr))
{
goto LocalCleanup;
}
hr = pCreateErrorInfo->SetDescription(szErrorDescription);
if(FAILED(hr))
{
goto LocalCleanup;
}
hr = pCreateErrorInfo->SetGUID(__uuidof(IProvProvider));
if(FAILED(hr))
{
goto LocalCleanup;
}
hr = pCreateErrorInfo->SetSource(szErrorSource);
if(FAILED(hr))
{
goto LocalCleanup;
}
hr = pCreateErrorInfo->QueryInterface(IID_IErrorInfo, (void **) &pErrorInfo);
if(FAILED(hr))
{
goto LocalCleanup;
}
hr = SetErrorInfo(0, pErrorInfo);
LocalCleanup:
return hr;
}
BOOL CSysTime1::GetOptionalBoolValue(IXMLDOMNode* pMode, LPWSTR szPath, BOOL fDefault)
{
HRESULT hr = S_OK;
CComPtr<IXMLDOMNode> pAttrNode;
CComBSTR bstrAttrValue;
BOOL fResult = fDefault;
hr = pMode->selectSingleNode(szPath, &pAttrNode);
if(hr != S_OK)
{
goto LocalCleanup;
}
hr = pAttrNode->get_text(&bstrAttrValue);
if(hr != S_OK)
{
goto LocalCleanup;
}
if(bstrAttrValue == L"0")
{
fResult = FALSE;
}
else if(bstrAttrValue == L"1")
{
fResult = TRUE;
}
LocalCleanup:
return fResult;
}
HRESULT CSysTime1::PutValue(IXMLDOMNode* pXMLExecData, LPWSTR szName, BSTR bstrValue)
{
HRESULT hr = S_OK;
CComPtr<IXMLDOMDocument> pDoc;
CComPtr<IXMLDOMElement> pElemNode;
CComPtr<IXMLDOMText> pTextNode;
hr = pXMLExecData->get_ownerDocument(&pDoc);
if(FAILED(hr))
{
goto LocalCleanup;
}
hr = pDoc->createElement(szName, &pElemNode);
if(FAILED(hr))
{
goto LocalCleanup;
}
hr = pDoc->createTextNode(bstrValue, &pTextNode);
if(FAILED(hr))
{
goto LocalCleanup;
}
hr = pElemNode->appendChild(pTextNode, NULL);
if(FAILED(hr))
{
goto LocalCleanup;
}
hr = pXMLExecData->appendChild(pElemNode, NULL);
LocalCleanup:
return hr;
}
HRESULT CSysTime1::GetTime(BSTR* pbstrTime)
{
HRESULT hr = S_OK;
DWORD dwTimeSize;
DWORD dwError;
WCHAR* szTime;
CComBSTR bstrTime;
dwTimeSize = GetTimeFormat(NULL, 0, NULL, L"hh':'mm':'ss tt", NULL, 0);
szTime = new WCHAR[dwTimeSize + 1];
if(szTime == NULL)
{
hr = E_OUTOFMEMORY;
goto LocalCleanup;
}
if(!GetTimeFormat(NULL, 0, NULL, L"hh':'mm':'ss tt", szTime, dwTimeSize))
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32(dwError);
goto LocalCleanup;
}
bstrTime = szTime;
*pbstrTime = bstrTime.Detach();
LocalCleanup:
if(szTime)
{
delete szTime;
}
return hr;
}
HRESULT CSysTime1::GetDate(BSTR* pbstrDate)
{
HRESULT hr = S_OK;
DWORD dwDateSize;
DWORD dwError;
WCHAR* szDate;
CComBSTR bstrDate;
dwDateSize = GetDateFormat(NULL, 0, NULL, L"dd'/'MM'/'yyyy", NULL, 0);
szDate = new WCHAR[dwDateSize + 1];
if(szDate == NULL)
{
hr = E_OUTOFMEMORY;
goto LocalCleanup;
}
if(!GetDateFormat(NULL, 0, NULL, L"dd'/'MM'/'yyyy", szDate, dwDateSize))
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32(dwError);
goto LocalCleanup;
}
bstrDate = szDate;
*pbstrDate = bstrDate.Detach();
LocalCleanup:
if(szDate)
{
delete szDate;
}
return hr;
}
SysTime.xml is the namespace XML generated when you insert the procedure using the toolbar button. As noted earlier, to deploy the provider, you must import this file into the configuration database.
<namespace name="SysTime"
version="1"
providerSource="SysTime1.SysTime1.1"
description="Namespace for SampleProvider1 containing a sample procedure.">
<procedure name="GetSystemTime"
type="read"
access="public"
description="Sample action gets system time on MPF server"/>
</namespace>
To test the completed procedure, use a sample request with input XML, as in the following code example:
<request>
<procedure>
<execute namespace='SysTime' procedure='GetSystemTime'>
<executeData>
<mode date='1' time='1'/>
</executeData>
<after source='executeData' destination='data' mode='merge'/>
</execute>
</procedure>
</request>
Finally, the request would return an XML response in the following format:
<response>
<data>
<mode date="1" time="1"/>
<date>10/10/2001</date>
<time>1:12:30 PM</time>
</data>
</response>
Developing Custom Providers, sample C++ provider in MPF SDK, Provisioning Schema