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. 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.

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.

  1. 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++
  2. Visual C++ 6.0.
  3. 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.

  1. In Visual C++ 6.0, on the Tools menu, click Options.
  2. On the Directories tab, in the Show directories for box, click Include files.
  3. 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.
  4. Program Files\Microsoft Provisioning\SDK\inc
  5. Program Files\Microsoft SDK\Include
  6. Program Files\Microsoft Visual Studio\VC98\Include
  7. Program Files\Microsoft Visual Studio\VC98\ATL\Include
  8. In the Show directories for box, click Executable files.
  9. 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.
  10. Program Files\Microsoft SDK\Bin\WinNT
  11. Program Files\Microsoft SDK\Bin
  12. On the Tools menu, click Customize.
  13. On the Add-Ins and Macro Files tab, verify that Provisioning Provider Procedure Add-In is selected.
  14. On the Commands tab, in the Category box, click Add-ins.
  15. 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.

  1. In Visual C++ 6.0, on the File menu, click New.
  2. In Projects, highlight MPF Provider, enter the project name and directory location, and then click OK.

    The project name becomes the default provider name.

  3. 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.
  4. 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:

  1. In Visual C++ 6.0, click the toolbar button button to insert a new provisioning procedure.
  2. In the Add New Procedure window, enter the procedure name and, optionally, the procedure description. Extended characters are not supported.
  3. Choose the procedure type: Standard, No Rollback, or Two Phase.
  4. Write the procedure code for the provisioning procedure.
Building the Provider
  1. On the File menu, click Build, Rebuild all.
  2. Use regsvr32.exe to register the provider in Microsoft® Windows®.
  3. 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.

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> 
See Also

Developing Custom Providers, sample C++ provider in MPF SDK, Provisioning Schema