Developing Providers in Visual C++
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. MPF 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.
- The ProviderName.h file defines the custom provider class and implements the IProvProvider interface. This file declares the functions that map to provisioning procedures, as well as other helper functions. In addition, an action map associates the provisioning procedures with their corresponding C++ functions. Whenever you add a procedure, Visual C++ automatically updates the action map and inserts corresponding member functions.
- The ProviderName.cpp file contains the function implementations for all phases of each provisioning procedure. These methods vary per procedure, depending on the procedure type.
Procedure Type Action Map Entry Methods Description Standard ACTION_MAP_ENTRY ProcedureName_Execute
ProcedureName_RollbackA 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_PrepareAn 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. - As you create the provider, Visual C++ updates the ProjectName.xml file, the namespace file that registers the provider and its procedures in the configuration database. When you are ready to deploy the provider you must add this file to the database using the procedure in Registering Namespaces in MPF.
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.
System Requirements
To develop providers in Visual C++ 6.0, the following software must be installed on the development computer, in the following order.
- Microsoft® Platform SDK (July 2001 or later). Depending on your installation method, choose either the Typical Install option or the following individual components and options:
- Microsoft Core SDK (Build Environment only)
- Register Environment Variables
- Integrate with Microsoft Visual C++
- Visual C++ 6.0.
- MPF SDK. For more information, see Installing MPF SDK.
Use MPF to test the provider. For more information, see System Requirements and Getting Started.
Setting Up the Development Environment
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.
- In Visual C++ 6.0, on the Tools menu, click Options.
- On the Directories tab, in the Show directories for box, click Include files.
- Verify that the directory listing includes the following files in the order they are listed here. The SDK include files must be listed above Microsoft® Visual Studio® files. If the SDK include files are not listed or are listed below the Visual Studio files, you must make the necessary changes. Otherwise, you will be unable to compile a provider.
Program Files\Microsoft Provisioning\SDK\inc
Program Files\Microsoft SDK\Include
Program Files\Microsoft Visual Studio\VC98\Include
Program Files\Microsoft Visual Studio\VC98\ATL\Include- In the Show directories for box, click Executable files.
- Verify that the directory listing includes the following files in the order they are listed here, or fix them if they are not. Otherwise, you will be unable to compile a provider.
Program Files\Microsoft SDK\Bin\WinNT
Program Files\Microsoft SDK\Bin- On the Tools menu, click Customize.
- On the Add-Ins and Macro Files tab, verify that Provisioning Provider Procedure Add-In is selected.
- On the Commands tab, in the Category box, click Add-ins.
- Drag the
button to the desired toolbar, and then click Close
Creating a Skeleton Project
Use the MPF Provider Wizard to generate a skeleton project.
- In Visual C++ 6.0, on the File menu, click New.
- In Projects, highlight MPF Provider, enter the project name and directory location, and then click OK.
The project name becomes the default provider name.
- In the MPF Provider window, enter the short name of the provider, and then click Finish. Extended characters are not supported.
The remaining fields fill in automatically based on the provider name. The New Project Information window displays the specifications for the provider project, and the ClassView window displays the generated class and member functions.- To build the project, on the Build menu, click Build ProjectName; or press F7.
Adding a Procedure
To generate a skeleton procedure, follow these steps:
- In Visual C++ 6.0, click the toolbar button
button to insert a new provisioning procedure.
- In the Add New Procedure window, enter the procedure name and, optionally, the procedure description. Extended characters are not supported.
- Choose the procedure type: Standard, No Rollback, or Two Phase.
- Write the procedure code for the provisioning procedure.
Building the Provider
- On the File menu, click Build, Rebuild all.
- Use regsvr32.exe to register the provider in Microsoft® Windows®.
- Register the namespace from either Provisioning Manager or the command line.
Sample Provider
The following example shows a SysTime provider with the procedure GetSystemTime. The
toolbar button generates the following skeleton code for GetSystemTime.cls.
GetSystemTime can be implemented in this skeleton code as follows:HRESULT CSysTime1::GetSystemTime_Execute( IXMLDOMNode *pXMLNode ) { HRESULT hr = S_OK; //CComVariant varData(L"test"); //hr = m_pProvHelper->SetRollbackData(L"key", varData ); return hr; }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>See Also
Developing Custom Providers, sample C++ provider in MPF SDK, Provisioning Schema
Top of Page
© 1999-2002 Microsoft Corporation. All rights reserved.