Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

611 lines
18 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows/NT **/
  3. /** Copyright(c) Microsoft Corporation, 1997 - 1999 **/
  4. /**********************************************************************/
  5. //
  6. // nbprop.cpp
  7. // IPX summary node property sheet and property pages
  8. //
  9. // FILE HISTORY:
  10. #include "stdafx.h"
  11. #include "rtrutil.h" // smart MPR handle pointers
  12. #include "format.h" // FormatNumber function
  13. #include "IpxStaticSvc.h"
  14. #include "summary.h"
  15. #include "ipxrtdef.h"
  16. #include "filter.h"
  17. #include "ipxutil.h"
  18. extern "C"
  19. {
  20. #include "routprot.h"
  21. };
  22. // ---------------------------------------------------------------------------
  23. // IpxStaticServicePropertySheet::IpxStaticServicePropertySheet
  24. // Initialize the RtrPropertySheet and only Property Page.
  25. // Author: Deonb
  26. // ---------------------------------------------------------------------------
  27. IpxStaticServicePropertySheet::IpxStaticServicePropertySheet(ITFSNode *pNode,
  28. IComponentData *pComponentData,
  29. ITFSComponentData *pTFSCompData,
  30. LPCTSTR pszSheetName,
  31. CWnd *pParent,
  32. UINT iPage,
  33. BOOL fScopePane)
  34. : RtrPropertySheet(pNode, pComponentData, pTFSCompData,
  35. pszSheetName, pParent, iPage, fScopePane),
  36. m_pageGeneral(IDD_STATICSERVICESPROPERTIES_GENERAL)
  37. {
  38. m_spNode = pNode;
  39. }
  40. // ---------------------------------------------------------------------------
  41. // IpxStaticServicePropertySheet::Init
  42. // Initialize the property sheets. The general action here will be
  43. // to initialize/add the various pages.
  44. // Author: Deonb
  45. // ---------------------------------------------------------------------------
  46. HRESULT IpxStaticServicePropertySheet::Init(
  47. BaseIPXResultNodeData *pNodeData,
  48. IInterfaceInfo * spInterfaceInfo)
  49. {
  50. HRESULT hr = hrOK;
  51. BaseIPXResultNodeData * pData;
  52. m_pNodeData = pNodeData;
  53. m_spInterfaceInfo = spInterfaceInfo;
  54. pData = GET_BASEIPXRESULT_NODEDATA(m_spNode);
  55. ASSERT_BASEIPXRESULT_NODEDATA(pData);
  56. // The pages are embedded members of the class
  57. // do not delete them.
  58. m_bAutoDeletePages = FALSE;
  59. m_pageGeneral.Init(pNodeData, this);
  60. AddPageToList((CPropertyPageBase*) &m_pageGeneral);
  61. return S_OK;
  62. }
  63. // ---------------------------------------------------------------------------
  64. // IpxStaticServicePropertySheet::SaveSheetData
  65. // Not sure what this does - this is never called. Kenn had this so I'll just
  66. // copy this too.
  67. // Author: Deonb
  68. // ---------------------------------------------------------------------------
  69. BOOL IpxStaticServicePropertySheet::SaveSheetData()
  70. {
  71. SPITFSNodeHandler spHandler;
  72. SPITFSNode spParent;
  73. // By this time each page should have written its information out
  74. // to the infobase
  75. // Force the node to do a resync
  76. m_spNode->GetParent(&spParent);
  77. spParent->GetHandler(&spHandler);
  78. spHandler->OnCommand(spParent, IDS_MENU_SYNC, CCT_RESULT,
  79. NULL, 0);
  80. return TRUE;
  81. }
  82. // --------------------------------------------------------------------------
  83. // IpxStaticServicePropertySheet::CancelSheetData
  84. // -
  85. // Author: Deonb
  86. // ---------------------------------------------------------------------------
  87. void IpxStaticServicePropertySheet::CancelSheetData()
  88. {
  89. }
  90. // ***************************************************************************
  91. // ---------------------------------------------------------------------------
  92. // IpxStaticServicePropertyPage
  93. // ---------------------------------------------------------------------------
  94. IpxStaticServicePropertyPage::~IpxStaticServicePropertyPage()
  95. {
  96. }
  97. BEGIN_MESSAGE_MAP(IpxStaticServicePropertyPage, RtrPropertyPage)
  98. //{{AFX_MSG_MAP(IpxStaticServicePropertyPage)
  99. ON_BN_CLICKED(IDC_NIG_BTN_ACCEPT, OnChangeButton)
  100. ON_BN_CLICKED(IDC_NIG_BTN_DELIVER_ALWAYS, OnChangeButton)
  101. ON_BN_CLICKED(IDC_NIG_BTN_DELIVER_NEVER, OnChangeButton)
  102. ON_BN_CLICKED(IDC_NIG_BTN_DELIVER_STATIC, OnChangeButton)
  103. ON_BN_CLICKED(IDC_NIG_BTN_DELIVER_WHEN_UP, OnChangeButton)
  104. //}}AFX_MSG_MAP
  105. END_MESSAGE_MAP()
  106. void IpxStaticServicePropertyPage::OnChangeButton()
  107. {
  108. SetDirty(TRUE);
  109. SetModified();
  110. }
  111. //--------------------------------------------------------------------------
  112. // IpxStaticServicePropertyPage::Init
  113. // -
  114. // Author: Deonb
  115. //---------------------------------------------------------------------------
  116. HRESULT IpxStaticServicePropertyPage::Init(BaseIPXResultNodeData *pNodeData,
  117. IpxStaticServicePropertySheet * pIPXPropSheet)
  118. {
  119. ATLASSERT(pSREntry);
  120. ATLASSERT(pIPXPropSheet);
  121. m_pIPXPropSheet = pIPXPropSheet;
  122. m_SREntry.LoadFrom(pNodeData);
  123. m_InitSREntry = m_SREntry;
  124. return hrOK;
  125. }
  126. // --------------------------------------------------------------------------
  127. // IpxStaticServicePropertyPage::OnInitDialog
  128. // -
  129. // Author: Deonb
  130. // ---------------------------------------------------------------------------
  131. BOOL IpxStaticServicePropertyPage::OnInitDialog()
  132. {
  133. HRESULT hr = hrOK;
  134. PBYTE pData;
  135. DWORD dwIfType;
  136. UINT iButton;
  137. RtrPropertyPage::OnInitDialog();
  138. m_spinHopCount.SetRange(0, 15);
  139. m_spinHopCount.SetBuddy(GetDlgItem(IDC_SSD_EDIT_HOP_COUNT));
  140. ((CEdit *) GetDlgItem(IDC_SSD_EDIT_SERVICE_TYPE))->LimitText(4);
  141. ((CEdit *) GetDlgItem(IDC_SSD_EDIT_SERVICE_NAME))->LimitText(48);
  142. ((CEdit *) GetDlgItem(IDC_SSD_EDIT_NETWORK_ADDRESS))->LimitText(8);
  143. ((CEdit *) GetDlgItem(IDC_SSD_EDIT_NODE_ADDRESS))->LimitText(12);
  144. ((CEdit *) GetDlgItem(IDC_SSD_EDIT_SOCKET_ADDRESS))->LimitText(4);
  145. USES_CONVERSION;
  146. TCHAR szNumber[32];
  147. wsprintf(szNumber, _T("%.4x"), m_SREntry.m_service.Type);
  148. SetDlgItemText(IDC_SSD_EDIT_SERVICE_TYPE, szNumber);
  149. SetDlgItemText(IDC_SSD_EDIT_SERVICE_NAME, A2CT((LPSTR) m_SREntry.m_service.Name));
  150. FormatIpxNetworkNumber(szNumber,
  151. DimensionOf(szNumber),
  152. m_SREntry.m_service.Network,
  153. sizeof(m_SREntry.m_service.Network));
  154. SetDlgItemText(IDC_SSD_EDIT_NETWORK_ADDRESS, szNumber);
  155. // Zero out the address beforehand
  156. FormatBytes(szNumber, DimensionOf(szNumber),
  157. (BYTE *) m_SREntry.m_service.Node,
  158. sizeof(m_SREntry.m_service.Node));
  159. SetDlgItemText(IDC_SSD_EDIT_NODE_ADDRESS, szNumber);
  160. FormatBytes(szNumber, DimensionOf(szNumber),
  161. (BYTE *) m_SREntry.m_service.Socket,
  162. sizeof(m_SREntry.m_service.Socket));
  163. SetDlgItemText(IDC_SSD_EDIT_SOCKET_ADDRESS, szNumber);
  164. m_spinHopCount.SetPos(m_SREntry.m_service.HopCount);
  165. // Disable the network number, next hop
  166. GetDlgItem(IDC_SSD_EDIT_SERVICE_TYPE)->EnableWindow(FALSE);
  167. GetDlgItem(IDC_SSD_EDIT_SERVICE_NAME)->EnableWindow(FALSE);
  168. SetDirty(FALSE);
  169. if (!FHrSucceeded(hr))
  170. Cancel();
  171. return FHrSucceeded(hr) ? TRUE : FALSE;
  172. }
  173. // --------------------------------------------------------------------------
  174. // IpxStaticServicePropertyPage::DoDataExchange
  175. // -
  176. // Author: Deonb
  177. // ---------------------------------------------------------------------------
  178. void IpxStaticServicePropertyPage::DoDataExchange(CDataExchange *pDX)
  179. {
  180. RtrPropertyPage::DoDataExchange(pDX);
  181. //{{AFX_DATA_MAP(IpxStaticServicePropertyPage)
  182. DDX_Control(pDX, IDC_SSD_SPIN_HOP_COUNT, m_spinHopCount);
  183. //}}AFX_DATA_MAP
  184. }
  185. // --------------------------------------------------------------------------
  186. // IpxStaticServicePropertyPage::OnApply
  187. // -
  188. // Author: Deonb
  189. // ---------------------------------------------------------------------------
  190. BOOL IpxStaticServicePropertyPage::OnApply()
  191. {
  192. BOOL fReturn;
  193. HRESULT hr = hrOK;
  194. if ( m_pIPXPropSheet->IsCancel() )
  195. {
  196. CancelApply();
  197. return TRUE;
  198. }
  199. CString st;
  200. GetDlgItemText(IDC_SSD_EDIT_SERVICE_TYPE, st);
  201. m_SREntry.m_service.Type = (USHORT) _tcstoul(st, NULL, 16);
  202. GetDlgItemText(IDC_SSD_EDIT_SERVICE_NAME, st);
  203. st.TrimLeft();
  204. st.TrimRight();
  205. if (st.IsEmpty())
  206. {
  207. GetDlgItem(IDC_SSD_EDIT_SERVICE_NAME)->SetFocus();
  208. AfxMessageBox(IDS_ERR_INVALID_SERVICE_NAME);
  209. return FALSE;
  210. }
  211. StrnCpyAFromW((LPSTR) m_SREntry.m_service.Name,
  212. st,
  213. sizeof(m_SREntry.m_service.Name));
  214. GetDlgItemText(IDC_SSD_EDIT_NETWORK_ADDRESS, st);
  215. ConvertToBytes(st,
  216. m_SREntry.m_service.Network,
  217. DimensionOf(m_SREntry.m_service.Network));
  218. GetDlgItemText(IDC_SSD_EDIT_NODE_ADDRESS, st);
  219. ConvertToBytes(st,
  220. m_SREntry.m_service.Node,
  221. DimensionOf(m_SREntry.m_service.Node));
  222. GetDlgItemText(IDC_SSD_EDIT_SOCKET_ADDRESS, st);
  223. ConvertToBytes(st,
  224. m_SREntry.m_service.Socket,
  225. DimensionOf(m_SREntry.m_service.Socket));
  226. m_SREntry.m_service.HopCount = (USHORT) m_spinHopCount.GetPos();
  227. // Updates the route info for this route
  228. ModifyRouteInfo(m_pIPXPropSheet->m_spNode, &m_SREntry, &m_InitSREntry);
  229. // Update the data in the UI
  230. m_SREntry.SaveTo(m_pIPXPropSheet->m_pNodeData);
  231. m_pIPXPropSheet->m_spInterfaceInfo = m_SREntry.m_spIf;
  232. // Force a refresh
  233. m_pIPXPropSheet->m_spNode->ChangeNode(RESULT_PANE_CHANGE_ITEM_DATA);
  234. fReturn = RtrPropertyPage::OnApply();
  235. return fReturn;
  236. }
  237. /*!--------------------------------------------------------------------------
  238. IpxStaticServicePropertyPage::RemoveStaticService
  239. -
  240. Author: KennT
  241. ---------------------------------------------------------------------------*/
  242. HRESULT IpxStaticServicePropertyPage::RemoveStaticService(SafeIPXSSListEntry *pSSEntry,
  243. IInfoBase *pInfoBase)
  244. {
  245. HRESULT hr = hrOK;
  246. InfoBlock * pBlock;
  247. PIPX_STATIC_SERVICE_INFO pRow;
  248. INT i;
  249. // Get the IPX_STATIC_SERVICE_INFO block from the interface
  250. CORg( pInfoBase->GetBlock(IPX_STATIC_SERVICE_INFO_TYPE, &pBlock, 0) );
  251. // Look for the removed route in the IPX_STATIC_SERVICE_INFO
  252. pRow = (IPX_STATIC_SERVICE_INFO*) pBlock->pData;
  253. for (i = 0; i < (INT)pBlock->dwCount; i++, pRow++)
  254. {
  255. // Compare this route to the removed one
  256. if (FAreTwoServicesEqual(pRow, &(pSSEntry->m_service)))
  257. {
  258. // This is the removed route, so modify this block
  259. // to exclude the route:
  260. // Decrement the number of Services
  261. --pBlock->dwCount;
  262. if (pBlock->dwCount && (i < (INT)pBlock->dwCount))
  263. {
  264. // Overwrite this route with the ones which follow it
  265. ::memmove(pRow,
  266. pRow + 1,
  267. (pBlock->dwCount - i) * sizeof(*pRow));
  268. }
  269. break;
  270. }
  271. }
  272. Error:
  273. return hr;
  274. }
  275. /*!--------------------------------------------------------------------------
  276. IpxStaticServicePropertyPage::ModifyRouteInfo
  277. -
  278. Author: KennT
  279. ---------------------------------------------------------------------------*/
  280. HRESULT IpxStaticServicePropertyPage::ModifyRouteInfo(ITFSNode *pNode,
  281. SafeIPXSSListEntry *pSSEntryNew,
  282. SafeIPXSSListEntry *pSSEntryOld)
  283. {
  284. Assert(pSSEntryNew);
  285. Assert(pSSEntryOld);
  286. INT i;
  287. HRESULT hr = hrOK;
  288. InfoBlock* pBlock;
  289. SPIInfoBase spInfoBase;
  290. SPIRtrMgrInterfaceInfo spRmIf;
  291. SPITFSNode spNodeParent;
  292. IPXConnection * pIPXConn;
  293. IPX_STATIC_SERVICE_INFO *psr, *psrOld;
  294. IPX_STATIC_SERVICE_INFO IpxRow;
  295. CWaitCursor wait;
  296. pNode->GetParent(&spNodeParent);
  297. pIPXConn = GET_IPX_SS_NODEDATA(spNodeParent);
  298. Assert(pIPXConn);
  299. // Remove the old route if it is on another interface
  300. if (lstrcmpi(pSSEntryOld->m_spIf->GetId(), pSSEntryNew->m_spIf->GetId()) != 0)
  301. {
  302. // the outgoing interface for a route is to be changed.
  303. CORg( pSSEntryOld->m_spIf->FindRtrMgrInterface(PID_IPX, &spRmIf) );
  304. CORg( spRmIf->GetInfoBase(pIPXConn->GetConfigHandle(),
  305. NULL,
  306. NULL,
  307. &spInfoBase));
  308. // Remove the old interface
  309. CORg( RemoveStaticService(pSSEntryOld, spInfoBase) );
  310. // Update the interface information
  311. CORg( spRmIf->Save(pSSEntryOld->m_spIf->GetMachineName(),
  312. pIPXConn->GetConfigHandle(),
  313. NULL,
  314. NULL,
  315. spInfoBase,
  316. 0));
  317. }
  318. spRmIf.Release();
  319. spInfoBase.Release();
  320. // Either
  321. // (a) a route is being modified (on the same interface)
  322. // (b) a route is being moved from one interface to another.
  323. // Retrieve the configuration for the interface to which the route
  324. // is now attached;
  325. CORg( pSSEntryNew->m_spIf->FindRtrMgrInterface(PID_IPX, &spRmIf) );
  326. CORg( spRmIf->GetInfoBase(pIPXConn->GetConfigHandle(),
  327. NULL,
  328. NULL,
  329. &spInfoBase));
  330. // Get the IPX_STATIC_SERVICE_INFO block from the interface
  331. hr = spInfoBase->GetBlock(IPX_STATIC_SERVICE_INFO_TYPE, &pBlock, 0);
  332. if (!FHrOK(hr))
  333. {
  334. //
  335. // No IPX_STATIC_SERVICE_INFO block was found; we create a new block
  336. // with the new route, and add that block to the interface-info
  337. //
  338. CORg( AddStaticService(pSSEntryNew, spInfoBase, NULL) );
  339. }
  340. else
  341. {
  342. //
  343. // An IPX_STATIC_SERVICE_INFO block was found.
  344. //
  345. // We are modifying an existing route.
  346. // If the route's interface was not changed when it was modified,
  347. // look for the existing route in the IPX_STATIC_SERVICE_INFO, and then
  348. // update its parameters.
  349. // Otherwise, write a completely new route in the IPX_STATIC_SERVICE_INFO;
  350. //
  351. if (lstrcmpi(pSSEntryOld->m_spIf->GetId(), pSSEntryNew->m_spIf->GetId()) == 0)
  352. {
  353. //
  354. // The route's interface was not changed when it was modified;
  355. // We now look for it amongst the existing Services
  356. // for this interface.
  357. // The route's original parameters are in 'preOld',
  358. // so those are the parameters with which we search
  359. // for a route to modify
  360. //
  361. psr = (IPX_STATIC_SERVICE_INFO*)pBlock->pData;
  362. for (i = 0; i < (INT)pBlock->dwCount; i++, psr++)
  363. {
  364. // Compare this route to the re-configured one
  365. if (!FAreTwoServicesEqual(&(pSSEntryOld->m_service), psr))
  366. continue;
  367. // This is the route which was modified;
  368. // We can now modify the parameters for the route in-place.
  369. *psr = pSSEntryNew->m_service;
  370. break;
  371. }
  372. }
  373. else
  374. {
  375. CORg( AddStaticService(pSSEntryNew, spInfoBase, pBlock) );
  376. }
  377. // Save the updated information
  378. CORg( spRmIf->Save(pSSEntryNew->m_spIf->GetMachineName(),
  379. pIPXConn->GetConfigHandle(),
  380. NULL,
  381. NULL,
  382. spInfoBase,
  383. 0));
  384. }
  385. Error:
  386. return hr;
  387. }
  388. // --------------------------------------------------------------------------
  389. // SafeIPXSSListEntry::LoadFrom
  390. // -
  391. // Author: DeonB
  392. // --------------------------------------------------------------------------
  393. void SafeIPXSSListEntry::LoadFrom(BaseIPXResultNodeData *pNodeData)
  394. {
  395. CString stFullAddress;
  396. CString stNumber;
  397. m_spIf = pNodeData->m_spIf;
  398. m_service.Type = (USHORT) _tcstoul(
  399. pNodeData->m_rgData[IPX_SS_SI_SERVICE_TYPE].m_stData,
  400. NULL, 16);
  401. StrnCpyAFromW((LPSTR) m_service.Name,
  402. pNodeData->m_rgData[IPX_SS_SI_SERVICE_NAME].m_stData,
  403. DimensionOf(m_service.Name));
  404. // Need to break the address up into Network.Node.Socket
  405. stFullAddress = pNodeData->m_rgData[IPX_SS_SI_SERVICE_ADDRESS].m_stData;
  406. Assert(StrLen(stFullAddress) == (8 + 1 + 12 + 1 + 4));
  407. stNumber = stFullAddress.Left(8);
  408. ConvertToBytes(stNumber,
  409. m_service.Network, sizeof(m_service.Network));
  410. stNumber = stFullAddress.Mid(9, 12);
  411. ConvertToBytes(stNumber,
  412. m_service.Node, sizeof(m_service.Node));
  413. stNumber = stFullAddress.Mid(22, 4);
  414. ConvertToBytes(stNumber,
  415. m_service.Socket, sizeof(m_service.Socket));
  416. m_service.HopCount = (USHORT) pNodeData->m_rgData[IPX_SS_SI_HOP_COUNT].m_dwData;
  417. }
  418. // --------------------------------------------------------------------------
  419. // SafeIPXSSListEntry::SaveTo
  420. // -
  421. // Author: DeonB
  422. // --------------------------------------------------------------------------
  423. void SafeIPXSSListEntry::SaveTo(BaseIPXResultNodeData *pNodeData)
  424. {
  425. TCHAR szNumber[32];
  426. CString st;
  427. USES_CONVERSION;
  428. pNodeData->m_spIf.Set(m_spIf);
  429. pNodeData->m_rgData[IPX_SS_SI_NAME].m_stData = m_spIf->GetTitle();
  430. wsprintf(szNumber, _T("%.4x"), m_service.Type);
  431. pNodeData->m_rgData[IPX_SS_SI_SERVICE_TYPE].m_stData = szNumber;
  432. pNodeData->m_rgData[IPX_SS_SI_SERVICE_TYPE].m_dwData = (DWORD) m_service.Type;
  433. pNodeData->m_rgData[IPX_SS_SI_SERVICE_NAME].m_stData =
  434. A2CT((LPSTR) m_service.Name);
  435. FormatBytes(szNumber, DimensionOf(szNumber),
  436. m_service.Network, sizeof(m_service.Network));
  437. st = szNumber;
  438. st += _T(".");
  439. FormatBytes(szNumber, DimensionOf(szNumber),
  440. m_service.Node, sizeof(m_service.Node));
  441. st += szNumber;
  442. st += _T(".");
  443. FormatBytes(szNumber, DimensionOf(szNumber),
  444. m_service.Socket, sizeof(m_service.Socket));
  445. st += szNumber;
  446. Assert(st.GetLength() == (8+1+12+1+4));
  447. pNodeData->m_rgData[IPX_SS_SI_SERVICE_ADDRESS].m_stData = st;
  448. FormatNumber(m_service.HopCount,
  449. szNumber,
  450. DimensionOf(szNumber),
  451. FALSE);
  452. pNodeData->m_rgData[IPX_SS_SI_HOP_COUNT].m_stData = szNumber;
  453. pNodeData->m_rgData[IPX_SS_SI_HOP_COUNT].m_dwData = m_service.HopCount;
  454. }
  455. /*!--------------------------------------------------------------------------
  456. AddStaticService
  457. This function ASSUMES that the route is NOT in the block.
  458. Author: KennT
  459. ---------------------------------------------------------------------------*/
  460. HRESULT AddStaticService(SafeIPXSSListEntry *pSSEntryNew,
  461. IInfoBase *pInfoBase,
  462. InfoBlock *pBlock)
  463. {
  464. IPX_STATIC_SERVICE_INFO srRow;
  465. HRESULT hr = hrOK;
  466. if (pBlock == NULL)
  467. {
  468. //
  469. // No IPX_STATIC_SERVICE_INFO block was found; we create a new block
  470. // with the new route, and add that block to the interface-info
  471. //
  472. CORg( pInfoBase->AddBlock(IPX_STATIC_SERVICE_INFO_TYPE,
  473. sizeof(IPX_STATIC_SERVICE_INFO),
  474. (LPBYTE) &(pSSEntryNew->m_service), 1, 0) );
  475. }
  476. else
  477. {
  478. // Either the route is completely new, or it is a route
  479. // which was moved from one interface to another.
  480. // Set a new block as the IPX_STATIC_SERVICE_INFO,
  481. // and include the re-configured route in the new block.
  482. PIPX_STATIC_SERVICE_INFO psrTable;
  483. psrTable = new IPX_STATIC_SERVICE_INFO[pBlock->dwCount + 1];
  484. Assert(psrTable);
  485. // Copy the original table of Services
  486. ::memcpy(psrTable, pBlock->pData,
  487. pBlock->dwCount * sizeof(IPX_STATIC_SERVICE_INFO));
  488. // Append the new route
  489. psrTable[pBlock->dwCount] = pSSEntryNew->m_service;
  490. // Replace the old route-table with the new one
  491. CORg( pInfoBase->SetData(IPX_STATIC_SERVICE_INFO_TYPE,
  492. sizeof(IPX_STATIC_SERVICE_INFO),
  493. (LPBYTE) psrTable, pBlock->dwCount + 1, 0) );
  494. }
  495. Error:
  496. return hr;
  497. }