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.

651 lines
18 KiB

  1. // LogGenPg.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include <iadmw.h>
  5. #include "logui.h"
  6. #include "LogGenPg.h"
  7. #include "wrapmb.h"
  8. #include "metatool.h"
  9. #include <iiscnfg.h>
  10. #include <shlobj.h>
  11. #ifdef _DEBUG
  12. #define new DEBUG_NEW
  13. #undef THIS_FILE
  14. static char THIS_FILE[] = __FILE__;
  15. #endif
  16. #define SIZE_MBYTE 1048576
  17. #define MAX_LOGFILE_SIZE 4000
  18. #define MD_LOGFILE_PERIOD_UNLIMITED MD_LOGFILE_PERIOD_HOURLY + 1
  19. //
  20. // Support functions to map & unmap the weird logfile ordering to the UI ordering
  21. //
  22. /////////////////////////////////////////////////////////////////////////////
  23. int MapLogFileTypeToUIIndex(int iLogFileType)
  24. {
  25. int iUIIndex;
  26. switch (iLogFileType)
  27. {
  28. case MD_LOGFILE_PERIOD_HOURLY:
  29. iUIIndex = 0;
  30. break;
  31. case MD_LOGFILE_PERIOD_DAILY:
  32. iUIIndex = 1;
  33. break;
  34. case MD_LOGFILE_PERIOD_WEEKLY:
  35. iUIIndex = 2;
  36. break;
  37. case MD_LOGFILE_PERIOD_MONTHLY:
  38. iUIIndex = 3;
  39. break;
  40. case MD_LOGFILE_PERIOD_UNLIMITED:
  41. iUIIndex = 4;
  42. break;
  43. case MD_LOGFILE_PERIOD_NONE:
  44. iUIIndex = 5;
  45. break;
  46. }
  47. return iUIIndex;
  48. }
  49. /////////////////////////////////////////////////////////////////////////////
  50. int MapUIIndexToLogFileType(int iUIIndex)
  51. {
  52. int iLogFileType;
  53. switch (iUIIndex)
  54. {
  55. case 0:
  56. iLogFileType = MD_LOGFILE_PERIOD_HOURLY;
  57. break;
  58. case 1:
  59. iLogFileType = MD_LOGFILE_PERIOD_DAILY;
  60. break;
  61. case 2:
  62. iLogFileType = MD_LOGFILE_PERIOD_WEEKLY;
  63. break;
  64. case 3:
  65. iLogFileType = MD_LOGFILE_PERIOD_MONTHLY;
  66. break;
  67. case 4:
  68. iLogFileType = MD_LOGFILE_PERIOD_UNLIMITED;
  69. break;
  70. case 5:
  71. iLogFileType = MD_LOGFILE_PERIOD_NONE;
  72. break;
  73. }
  74. return iLogFileType;
  75. }
  76. /////////////////////////////////////////////////////////////////////////////
  77. // CLogGeneral property page
  78. IMPLEMENT_DYNCREATE(CLogGeneral, CPropertyPage)
  79. //--------------------------------------------------------------------------
  80. CLogGeneral::CLogGeneral() : CPropertyPage(CLogGeneral::IDD),
  81. m_fInitialized( FALSE ),
  82. m_pComboLog( NULL ),
  83. m_fLocalMachine( FALSE )
  84. {
  85. //{{AFX_DATA_INIT(CLogGeneral)
  86. m_sz_directory = _T("");
  87. m_sz_filesample = _T("");
  88. m_fShowLocalTimeCheckBox = FALSE;
  89. m_int_period = -1;
  90. //}}AFX_DATA_INIT
  91. m_fIsModified = FALSE;
  92. }
  93. //--------------------------------------------------------------------------
  94. CLogGeneral::~CLogGeneral()
  95. {
  96. }
  97. //--------------------------------------------------------------------------
  98. void CLogGeneral::DoDataExchange(CDataExchange* pDX)
  99. {
  100. CPropertyPage::DoDataExchange(pDX);
  101. //{{AFX_DATA_MAP(CLogGeneral)
  102. DDX_Control(pDX, IDC_LOG_HOURLY, m_wndPeriod);
  103. DDX_Control(pDX, IDC_USE_LOCAL_TIME, m_wndUseLocalTime);
  104. DDX_Control(pDX, IDC_LOG_BROWSE, m_cbttn_browse);
  105. DDX_Control(pDX, IDC_LOG_DIRECTORY, m_cedit_directory);
  106. DDX_Control(pDX, IDC_LOG_SIZE, m_cedit_size);
  107. DDX_Control(pDX, IDC_SPIN, m_cspin_spin);
  108. DDX_Control(pDX, IDC_LOG_SIZE_UNITS, m_cstatic_units);
  109. DDX_Text(pDX, IDC_LOG_DIRECTORY, m_sz_directory);
  110. DDX_Text(pDX, IDC_LOG_FILE_SAMPLE, m_sz_filesample);
  111. DDX_Check(pDX, IDC_USE_LOCAL_TIME, m_fUseLocalTime);
  112. // DDX_Radio(pDX, IDC_LOG_HOURLY, m_int_period);
  113. //}}AFX_DATA_MAP
  114. DDX_Text(pDX, IDC_LOG_SIZE, m_dword_filesize);
  115. DDV_MinMaxLong(pDX, m_dword_filesize, 0, MAX_LOGFILE_SIZE);
  116. //
  117. // Do the map & unmap between UI Index & Log File Type
  118. //
  119. if (pDX->m_bSaveAndValidate)
  120. {
  121. DDX_Radio(pDX, IDC_LOG_HOURLY, m_int_period);
  122. m_int_period = MapUIIndexToLogFileType(m_int_period);
  123. }
  124. else
  125. {
  126. int iUIIndex = MapLogFileTypeToUIIndex(m_int_period);
  127. DDX_Radio(pDX, IDC_LOG_HOURLY, iUIIndex);
  128. }
  129. }
  130. BEGIN_MESSAGE_MAP(CLogGeneral, CPropertyPage)
  131. //{{AFX_MSG_MAP(CLogGeneral)
  132. ON_BN_CLICKED(IDC_LOG_BROWSE, OnBrowse)
  133. ON_BN_CLICKED(IDC_LOG_DAILY, OnLogDaily)
  134. ON_BN_CLICKED(IDC_LOG_MONTHLY, OnLogMonthly)
  135. ON_BN_CLICKED(IDC_LOG_WHENSIZE, OnLogWhensize)
  136. ON_BN_CLICKED(IDC_LOG_WEEKLY, OnLogWeekly)
  137. ON_EN_CHANGE(IDC_LOG_DIRECTORY, OnChangeLogDirectory)
  138. ON_EN_CHANGE(IDC_LOG_SIZE, OnChangeLogSize)
  139. ON_BN_CLICKED(IDC_LOG_UNLIMITED, OnLogUnlimited)
  140. ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN, OnDeltaposSpin)
  141. ON_BN_CLICKED(IDC_LOG_HOURLY, OnLogHourly)
  142. ON_BN_CLICKED(IDC_USE_LOCAL_TIME, OnUseLocalTime)
  143. //}}AFX_MSG_MAP
  144. ON_COMMAND(ID_HELP_FINDER, DoHelp)
  145. ON_COMMAND(ID_HELP, DoHelp)
  146. ON_COMMAND(ID_CONTEXT_HELP, DoHelp)
  147. ON_COMMAND(ID_DEFAULT_HELP, DoHelp)
  148. END_MESSAGE_MAP()
  149. //---------------------------------------------------------------------------
  150. void CLogGeneral::DoHelp()
  151. {
  152. WinHelp( HIDD_LOGUI_GENERIC );
  153. }
  154. //--------------------------------------------------------------------------
  155. void CLogGeneral::Init()
  156. {
  157. DWORD dw;
  158. LPCTSTR pstr;
  159. UpdateData( TRUE );
  160. // we will just be pulling stuff out of the metabase here
  161. // prepare the metabase wrapper
  162. CWrapMetaBase mbWrap;
  163. if ( !mbWrap.FInit(m_pMB) ) return;
  164. // open the target
  165. if ( mbWrap.Open( m_szMeta, METADATA_PERMISSION_READ ) )
  166. {
  167. // start with the logging period
  168. if ( mbWrap.GetDword( _T(""), MD_LOGFILE_PERIOD, IIS_MD_UT_SERVER, &dw, METADATA_INHERIT ) )
  169. {
  170. // hey hey - period matches the metabase value. Thats so handy
  171. m_int_period = dw;
  172. }
  173. // now the truncate size
  174. if ( mbWrap.GetDword( _T(""), MD_LOGFILE_TRUNCATE_SIZE, IIS_MD_UT_SERVER, &dw, METADATA_INHERIT ) )
  175. {
  176. m_dword_filesize = dw / SIZE_MBYTE;
  177. }
  178. // check for the unlimited case - larger than 4 gigabytes
  179. if ( (m_dword_filesize > MAX_LOGFILE_SIZE) && (m_int_period == MD_LOGFILE_PERIOD_NONE) )
  180. {
  181. m_int_period = MD_LOGFILE_PERIOD_UNLIMITED;
  182. m_dword_filesize = 512;
  183. }
  184. // now the target directory
  185. pstr = (LPCTSTR)mbWrap.GetData( _T(""), MD_LOGFILE_DIRECTORY, IIS_MD_UT_SERVER, EXPANDSZ_METADATA, &dw, METADATA_INHERIT );
  186. if ( pstr )
  187. {
  188. m_sz_directory = pstr;
  189. // free it
  190. mbWrap.FreeWrapData( (PVOID)pstr );
  191. }
  192. // now the use local time flag (only for w3c)
  193. if ( m_fShowLocalTimeCheckBox)
  194. {
  195. m_wndUseLocalTime.ShowWindow(SW_SHOW);
  196. if (mbWrap.GetDword( _T(""), MD_LOGFILE_LOCALTIME_ROLLOVER, IIS_MD_UT_SERVER, &dw, METADATA_INHERIT))
  197. {
  198. m_fUseLocalTime = dw;
  199. }
  200. if (( MD_LOGFILE_PERIOD_NONE == m_int_period) || ( MD_LOGFILE_PERIOD_UNLIMITED == m_int_period))
  201. {
  202. m_wndUseLocalTime.EnableWindow(FALSE);
  203. }
  204. }
  205. // close the metabase
  206. mbWrap.Close();
  207. }
  208. // put the date into place
  209. UpdateData( FALSE );
  210. // update the dependant items
  211. UpdateDependants();
  212. // and the sample file string
  213. UpdateSampleFileString();
  214. // finally, test if we are editing a remote machine. If we are not,
  215. // then disable the remote browsing function.
  216. if ( !m_fLocalMachine )
  217. m_cbttn_browse.EnableWindow( FALSE );
  218. }
  219. //--------------------------------------------------------------------------
  220. void CLogGeneral::UpdateDependants()
  221. {
  222. UpdateData( TRUE );
  223. // enable or disable the file size depending on the period selected
  224. if ( m_int_period == MD_LOGFILE_PERIOD_MAXSIZE )
  225. {
  226. m_cspin_spin.EnableWindow( TRUE );
  227. m_cstatic_units.EnableWindow( TRUE );
  228. m_cedit_size.EnableWindow( TRUE );
  229. }
  230. else
  231. {
  232. m_cspin_spin.EnableWindow( FALSE );
  233. m_cstatic_units.EnableWindow( FALSE );
  234. m_cedit_size.EnableWindow( FALSE );
  235. }
  236. // put the date into place
  237. UpdateData( FALSE );
  238. }
  239. //--------------------------------------------------------------------------
  240. // update the sample file stirng
  241. void CLogGeneral::UpdateSampleFileString()
  242. {
  243. CString szSample;
  244. UpdateData( TRUE );
  245. // ok first we have to generate a string to show what sub-node the logging stuff
  246. // is going to go into. This would be of the general form of the name of the server
  247. // followed by the virtual node of the server. Example: LM/W3SVC/1 would
  248. // become "W3SVC1/example" Unfortunately, all we have to build this thing out of
  249. // is the target metabase path. So we strip off the preceding LM/. Then we find the
  250. // next / character and take the number that follows it. If we are editing the
  251. // master root properties then there will be no slash/number at the end at which point
  252. // we can just append a capital X character to signifiy this. The MMC is currently set
  253. // up to only show the logging properties if we are editing the master props or a virtual
  254. // server, so we shouldn't have to worry about stuff after the virtual server number
  255. // get rid of the preceding LM/ (Always three characters)
  256. m_sz_filesample = m_szMeta.Right( m_szMeta.GetLength() - 3 );
  257. // Find the location of the '/' character
  258. INT iSlash = m_sz_filesample.Find( _T('/') );
  259. // if there was no last slash, then append the X, otherwise append the number
  260. if ( iSlash < 0 )
  261. {
  262. m_sz_filesample += _T('X');
  263. }
  264. else
  265. {
  266. m_sz_filesample = m_sz_filesample.Left(iSlash) +
  267. m_sz_filesample.Right( m_sz_filesample.GetLength() - (iSlash+1) );
  268. }
  269. // add a final path type slash to signify that it is a partial path
  270. m_sz_filesample += _T('\\');
  271. // build the sample string
  272. switch( m_int_period )
  273. {
  274. case MD_LOGFILE_PERIOD_MAXSIZE:
  275. m_sz_filesample += szSizePrefix;
  276. szSample.LoadString( IDS_LOG_SIZE_FILESAMPLE );
  277. break;
  278. case MD_LOGFILE_PERIOD_DAILY:
  279. m_sz_filesample += szPrefix;
  280. szSample.LoadString( IDS_LOG_DAILY_FILESAMPLE );
  281. break;
  282. case MD_LOGFILE_PERIOD_WEEKLY:
  283. m_sz_filesample += szPrefix;
  284. szSample.LoadString( IDS_LOG_WEEKLY_FILESAMPLE );
  285. break;
  286. case MD_LOGFILE_PERIOD_MONTHLY:
  287. m_sz_filesample += szPrefix;
  288. szSample.LoadString( IDS_LOG_MONTHLY_FILESAMPLE );
  289. break;
  290. case MD_LOGFILE_PERIOD_HOURLY:
  291. m_sz_filesample += szPrefix;
  292. szSample.LoadString( IDS_LOG_HOURLY_FILE_SAMPLE );
  293. break;
  294. case MD_LOGFILE_PERIOD_UNLIMITED:
  295. m_sz_filesample += szSizePrefix;
  296. szSample.LoadString( IDS_LOG_UNLIMITED_FILESAMPLE );
  297. break;
  298. };
  299. // add the two together
  300. m_sz_filesample += szSample;
  301. // update the display
  302. UpdateData( FALSE );
  303. }
  304. /////////////////////////////////////////////////////////////////////////////
  305. // CLogGeneral message handlers
  306. //--------------------------------------------------------------------------
  307. BOOL CLogGeneral::OnSetActive()
  308. {
  309. // if this is the first time, inititalize the dialog
  310. if( !m_fInitialized )
  311. {
  312. Init();
  313. // set the flag so we don't do it again
  314. m_fInitialized = TRUE;
  315. }
  316. return CPropertyPage::OnSetActive();
  317. }
  318. //--------------------------------------------------------------------------
  319. BOOL CLogGeneral::OnApply()
  320. {
  321. DWORD dw;
  322. if (!m_fIsModified)
  323. {
  324. // do the default action
  325. return CPropertyPage::OnApply();
  326. }
  327. UpdateData( TRUE );
  328. CString szDir = m_sz_directory;
  329. // while we can't confirm the existence of a remote directory,
  330. // we can at least make sure they entered something
  331. szDir.TrimLeft();
  332. if ( szDir.IsEmpty() )
  333. {
  334. AfxMessageBox( IDS_NEED_DIRECTORY );
  335. m_cedit_directory.SetFocus();
  336. return FALSE;
  337. }
  338. // we do not allow UNC names or Remote Drives
  339. if ( (_T('\\') == szDir[0] ) && ( _T('\\') == szDir[1]) )
  340. {
  341. AfxMessageBox( IDS_REMOTE_NOT_SUPPORTED );
  342. m_cedit_directory.SetFocus();
  343. return FALSE;
  344. }
  345. if ( ( _T(':') == szDir[1] ) && ( _T('\\') == szDir[2]) )
  346. {
  347. TCHAR szDrive[4];
  348. CopyMemory(szDrive, (LPCTSTR)szDir, 3*sizeof(TCHAR));
  349. szDrive[3] = _T('\0');
  350. if ( DRIVE_REMOTE == GetDriveType(szDrive))
  351. {
  352. AfxMessageBox( IDS_REMOTE_NOT_SUPPORTED );
  353. m_cedit_directory.SetFocus();
  354. return FALSE;
  355. }
  356. }
  357. // prepare and open the metabase object
  358. CWrapMetaBase mb;
  359. if ( !mb.FInit(m_pMB) ) return FALSE;
  360. // open the target
  361. if ( OpenAndCreate( &mb, m_szMeta,
  362. METADATA_PERMISSION_WRITE | METADATA_PERMISSION_READ, TRUE ) )
  363. {
  364. // prepare for the inheritence checks
  365. CCheckInheritList listInherit;
  366. // start with the logging period
  367. dw = m_int_period;
  368. if ( m_int_period == MD_LOGFILE_PERIOD_UNLIMITED )
  369. dw = MD_LOGFILE_PERIOD_NONE;
  370. SetMBDword( &mb, &listInherit, _T(""), MD_LOGFILE_PERIOD,
  371. IIS_MD_UT_SERVER, dw);
  372. // now the truncate size
  373. dw = m_dword_filesize * SIZE_MBYTE;
  374. if ( m_int_period == MD_LOGFILE_PERIOD_UNLIMITED )
  375. dw = 0xFFFFFFFF;
  376. SetMBDword(&mb, &listInherit, _T(""), MD_LOGFILE_TRUNCATE_SIZE,
  377. IIS_MD_UT_SERVER, dw);
  378. // now the target directory
  379. SetMBData( &mb, &listInherit, _T(""), MD_LOGFILE_DIRECTORY, IIS_MD_UT_SERVER,
  380. EXPANDSZ_METADATA, (PVOID)(LPCTSTR)szDir,
  381. ((szDir.GetLength()+1) * sizeof(TCHAR)) + sizeof(TCHAR), FALSE );
  382. // now the use local time flag (only for w3c)
  383. if ( m_fShowLocalTimeCheckBox)
  384. {
  385. dw = m_fUseLocalTime;
  386. SetMBDword(&mb, &listInherit, _T(""), MD_LOGFILE_LOCALTIME_ROLLOVER,
  387. IIS_MD_UT_SERVER, dw);
  388. }
  389. // close the metabase
  390. mb.Close();
  391. // do all the inheritence checks
  392. listInherit.CheckInheritence( m_szServer, m_szMeta );
  393. }
  394. // do the default action
  395. return CPropertyPage::OnApply();
  396. }
  397. //--------------------------------------------------------------------------
  398. void CLogGeneral::OnBrowse()
  399. {
  400. UpdateData( TRUE );
  401. BROWSEINFO bi;
  402. LPTSTR lpBuffer;
  403. LPITEMIDLIST pidlBrowse; // PIDL selected by user
  404. // Allocate a buffer to receive browse information.
  405. //
  406. // RONALDM: Insufficient buffer size fix, bug # 231144
  407. //
  408. //lpBuffer = (LPTSTR) GlobalAlloc( GPTR, MAX_PATH );
  409. lpBuffer = (LPTSTR) GlobalAlloc( GPTR, (MAX_PATH + 1) * sizeof(TCHAR) );
  410. if ( !lpBuffer )
  411. {
  412. return;
  413. }
  414. // Fill in the BROWSEINFO structure.
  415. bi.hwndOwner = this->m_hWnd;
  416. bi.pidlRoot = NULL;
  417. bi.pszDisplayName = lpBuffer;
  418. bi.lpszTitle = NULL;
  419. // bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS | BIF_DONTGOBELOWDOMAIN;
  420. bi.ulFlags = BIF_RETURNONLYFSDIRS;
  421. bi.lpfn = NULL;
  422. bi.lParam = 0;
  423. // Browse for a folder and return its PIDL.
  424. pidlBrowse = SHBrowseForFolder(&bi);
  425. if (pidlBrowse != NULL)
  426. {
  427. // Show the display name, title, and file system path.
  428. if (SHGetPathFromIDList(pidlBrowse, lpBuffer))
  429. m_sz_directory = lpBuffer;
  430. // Free the PIDL returned by SHBrowseForFolder.
  431. GlobalFree(pidlBrowse);
  432. // put the string back
  433. UpdateData( FALSE );
  434. SetModified();
  435. m_fIsModified = TRUE;
  436. }
  437. // Clean up.
  438. GlobalFree( lpBuffer );
  439. }
  440. //--------------------------------------------------------------------------
  441. void CLogGeneral::OnLogDaily()
  442. {
  443. m_wndUseLocalTime.EnableWindow(TRUE);
  444. UpdateDependants();
  445. UpdateSampleFileString();
  446. SetModified();
  447. m_fIsModified = TRUE;
  448. }
  449. //--------------------------------------------------------------------------
  450. void CLogGeneral::OnLogMonthly()
  451. {
  452. m_wndUseLocalTime.EnableWindow(TRUE);
  453. UpdateDependants();
  454. UpdateSampleFileString();
  455. SetModified();
  456. m_fIsModified = TRUE;
  457. }
  458. //--------------------------------------------------------------------------
  459. void CLogGeneral::OnLogWhensize()
  460. {
  461. m_wndUseLocalTime.EnableWindow(FALSE);
  462. UpdateDependants();
  463. UpdateSampleFileString();
  464. SetModified();
  465. m_fIsModified = TRUE;
  466. }
  467. //--------------------------------------------------------------------------
  468. void CLogGeneral::OnLogUnlimited()
  469. {
  470. m_wndUseLocalTime.EnableWindow(FALSE);
  471. UpdateDependants();
  472. UpdateSampleFileString();
  473. SetModified();
  474. m_fIsModified = TRUE;
  475. }
  476. //--------------------------------------------------------------------------
  477. void CLogGeneral::OnLogWeekly()
  478. {
  479. m_wndUseLocalTime.EnableWindow(TRUE);
  480. UpdateDependants();
  481. UpdateSampleFileString();
  482. SetModified();
  483. m_fIsModified = TRUE;
  484. }
  485. //--------------------------------------------------------------------------
  486. void CLogGeneral::OnLogHourly()
  487. {
  488. m_wndUseLocalTime.EnableWindow(TRUE);
  489. UpdateDependants();
  490. UpdateSampleFileString();
  491. SetModified();
  492. m_fIsModified = TRUE;
  493. }
  494. //--------------------------------------------------------------------------
  495. void CLogGeneral::OnChangeLogDirectory()
  496. {
  497. SetModified();
  498. m_fIsModified = TRUE;
  499. }
  500. //--------------------------------------------------------------------------
  501. void CLogGeneral::OnChangeLogSize()
  502. {
  503. SetModified();
  504. m_fIsModified = TRUE;
  505. }
  506. //--------------------------------------------------------------------------
  507. void CLogGeneral::OnUseLocalTime()
  508. {
  509. SetModified();
  510. m_fIsModified = TRUE;
  511. }
  512. //--------------------------------------------------------------------------
  513. // the spinbox has been spun. Alter the size of the log file
  514. void CLogGeneral::OnDeltaposSpin(NMHDR* pNMHDR, LRESULT* pResult)
  515. {
  516. NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
  517. *pResult = 0;
  518. // based on the delta in the pNMUpDown affect the value of the file
  519. // size. Note that pressing the up arrow in the spinbox passes us
  520. // a delta of -1, and pressing the down arrow passes 1. Since they
  521. // want the up arrow to increase the size of the file, we have to
  522. // add the opposite of the delta to the file size to get the right answer.
  523. UpdateData( TRUE );
  524. // move it by the amount in the delta
  525. m_dword_filesize -= pNMUpDown->iDelta;
  526. // check the boundaries. Since it is a dword, don't look for negatives
  527. if ( (m_dword_filesize < 0) && (pNMUpDown->iDelta > 0) )
  528. m_dword_filesize = (LONG)0;
  529. if ( (m_dword_filesize >= MAX_LOGFILE_SIZE) && (pNMUpDown->iDelta > 0) )
  530. m_dword_filesize = (LONG)0;
  531. else if ( (m_dword_filesize >= MAX_LOGFILE_SIZE) && (pNMUpDown->iDelta < 0) )
  532. m_dword_filesize = MAX_LOGFILE_SIZE;
  533. UpdateData( FALSE );
  534. }