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.

1275 lines
42 KiB

  1. //*********************************************************************
  2. //* Microsoft Windows **
  3. //* Copyright(c) Microsoft Corp., 1994-1995 **
  4. //*********************************************************************
  5. //
  6. // MAPICALL.C - Functions to call MAPI for internet mail profile configuration
  7. //
  8. //
  9. // HISTORY:
  10. //
  11. // 1/25/95 jeremys Created.
  12. // 96/03/09 markdu Added a wait cursor during loading of MAPI
  13. //
  14. #include "mapicall.h"
  15. #include "obcomglb.h"
  16. #include "resource.h"
  17. /**********************************************************************
  18. Terminology:
  19. profile - a collection of settings for Exchange that determine
  20. the services that are used and the address book and message store
  21. service - a MAPI plug-in that talks to a mail back end
  22. (or address book or message store)
  23. There can be a number of profiles installed on a particular machine.
  24. Each profile contains a set of services.
  25. Stategy:
  26. To configure Microsoft Exchange, we need to do the following:
  27. 1) Establish a profile to modify
  28. - If any profiles currently exist, find the default profile.
  29. Otherwise create a profile, which will be initially empty.
  30. 2) Populate the profile with the required services.
  31. - The profile needs to contain the Internet Mail service, an
  32. address book and a message store. If any of these items are
  33. not present, we add them to the profile.
  34. 3) Configure the internet mail service for this profile.
  35. - stick in the user's email address, email server, etc.
  36. **********************************************************************/
  37. // instance handle must be in per-instance data segment
  38. #pragma data_seg(DATASEG_PERINSTANCE)
  39. #define SMALL_BUF_LEN 48 // convenient size for small text buffers
  40. HINSTANCE hInstMAPIDll=NULL; // handle to MAPI dll we load explicitly
  41. DWORD dwMAPIRefCount = 0;
  42. static const WCHAR cszNull[] = L"";
  43. static const WCHAR sz0[] = L"0";
  44. static const WCHAR sz1[] = L"1";
  45. static const WCHAR szNull[] = L"";
  46. static const WCHAR szSlash[] = L"\\";
  47. LPMAPIINITIALIZE lpMAPIInitialize = NULL;
  48. LPMAPIADMINPROFILES lpMAPIAdminProfiles = NULL;
  49. LPMAPIUNINITIALIZE lpMAPIUninitialize = NULL;
  50. LPMAPIALLOCATEBUFFER lpMAPIAllocateBuffer = NULL;
  51. LPMAPIFREEBUFFER lpMAPIFreeBuffer = NULL;
  52. LPHRQUERYALLROWS lpHrQueryAllRows = NULL;
  53. // global function pointers for MAPI apis
  54. //////////////////////////////////////////////////////
  55. // MAPI api function names
  56. //////////////////////////////////////////////////////
  57. static const CHAR szMAPIInitialize[] = "MAPIInitialize";
  58. static const CHAR szMAPIUninitialize[] = "MAPIUninitialize";
  59. static const CHAR szMAPIAdminProfiles[] = "MAPIAdminProfiles";
  60. static const CHAR szMAPIAllocateBuffer[] = "MAPIAllocateBuffer";
  61. static const CHAR szMAPIFreeBuffer[] = "MAPIFreeBuffer";
  62. static const CHAR szHrQueryAllRows[] = "HrQueryAllRows@24";
  63. #define NUM_MAPI_PROCS 6
  64. // API table for function addresses to fetch
  65. APIFCN MAPIApiList[NUM_MAPI_PROCS] = {
  66. { (PVOID *) &lpMAPIInitialize, szMAPIInitialize},
  67. { (PVOID *) &lpMAPIUninitialize, szMAPIUninitialize},
  68. { (PVOID *) &lpMAPIAdminProfiles, szMAPIAdminProfiles},
  69. { (PVOID *) &lpMAPIAllocateBuffer, szMAPIAllocateBuffer},
  70. { (PVOID *) &lpMAPIFreeBuffer, szMAPIFreeBuffer},
  71. { (PVOID *) &lpHrQueryAllRows, szHrQueryAllRows}};
  72. #pragma data_seg(DATASEG_DEFAULT)
  73. // function prototypes
  74. HRESULT GetProfileList(LPPROFADMIN lpProfAdmin, LPSRowSet * ppRowSet);
  75. HRESULT GetServicesList(LPSERVICEADMIN lpServiceAdmin, LPSRowSet *ppRowSet);
  76. HRESULT CreateProfileIfNecessary(LPPROFADMIN lpProfAdmin, WCHAR * pszSelProfileName);
  77. HRESULT InstallRequiredServices(LPSERVICEADMIN pServiceAdmin,
  78. LPSRowSet pServiceRowSet);
  79. VOID FreeSRowSet(LPSRowSet prws);
  80. BOOL ValidateProperty(LPSPropValue pval, ULONG cVal, ULONG ulPropTag);
  81. BOOL DoesFileExist(WCHAR * pszPath, WCHAR * pszFileName);
  82. HRESULT ConfigInternetService(MAILCONFIGINFO * pMailConfigInfo,
  83. LPSERVICEADMIN lpServiceAdmin);
  84. HRESULT GetServiceUID(WCHAR * pszName, LPSERVICEADMIN lpServiceAdmin,
  85. LPMAPIUID *ppMapiUID);
  86. BOOL MakeUniqueFilename(UINT uIDFilename, UINT uIDAltFilename,
  87. WCHAR * pszFilename, DWORD cbFilename);
  88. extern BOOL GetApiProcAddresses(HMODULE hModDLL, APIFCN * pApiProcList,
  89. UINT nApiProcs);
  90. HRESULT ConfigNewService(LPSERVICEADMIN lpServiceAdmin, LPMAPIUID lpMapiUID,
  91. UINT uIDFilename, UINT uIDFilename1,UINT uPropValID);
  92. // enums
  93. enum { ivalDisplayName, ivalServiceName, ivalResourceFlags, ivalServiceDllName,
  94. ivalServiceEntryName, ivalServiceUID, ivalServiceSupportFiles,
  95. cvalMsgSrvMax };
  96. BOOL gfMAPIActive = FALSE;
  97. /*******************************************************************
  98. NAME: GetApiProcAddresses
  99. SYNOPSIS: Gets proc addresses for a table of functions
  100. EXIT: returns TRUE if successful, FALSE if unable to retrieve
  101. any proc address in table
  102. HISTORY:
  103. 96/02/28 markdu If the api is not found in the module passed in,
  104. try the backup (RNAPH.DLL)
  105. ********************************************************************/
  106. BOOL GetApiProcAddresses(HMODULE hModDLL, APIFCN * pApiProcList,UINT nApiProcs)
  107. {
  108. UINT nIndex;
  109. // cycle through the API table and get proc addresses for all the APIs we
  110. // need
  111. for (nIndex = 0;nIndex < nApiProcs;nIndex++)
  112. {
  113. if (!(*pApiProcList[nIndex].ppFcnPtr = (PVOID) GetProcAddress(hModDLL,
  114. pApiProcList[nIndex].pszName)))
  115. {
  116. return FALSE;
  117. }
  118. }
  119. return TRUE;
  120. }
  121. /*******************************************************************
  122. NAME: InitMAPI
  123. SYNOPSIS: Loads the MAPI dll, gets proc addresses and initializes
  124. MAPI
  125. EXIT: TRUE if successful, or FALSE if fails. Displays its
  126. own error message upon failure.
  127. NOTES: We load MAPI explicitly because the DLL may not be installed
  128. when we begin the wizard... we may have to install it and
  129. then load it.
  130. ********************************************************************/
  131. BOOL InitMAPI(HWND hWnd)
  132. {
  133. HRESULT hr;
  134. // load MAPI only if not already loaded... otherwise just increment
  135. // reference count
  136. if (!hInstMAPIDll) {
  137. // get the filename (MAPI32.DLL) out of resource
  138. // load the MAPI dll
  139. hInstMAPIDll = LoadLibrary(L"MAPI32.DLL");
  140. if (!hInstMAPIDll) {
  141. UINT uErr = GetLastError();
  142. //DisplayErrorMessage(hWnd, IDS_ERRLoadMAPIDll1,uErr,ERRCLS_STANDARD,
  143. // MB_ICONSTOP, szMAPIDll);
  144. return FALSE;
  145. }
  146. // cycle through the API table and get proc addresses for all the APIs we
  147. // need
  148. BOOL fSuccess = GetApiProcAddresses(hInstMAPIDll, MAPIApiList,NUM_MAPI_PROCS);
  149. if (!fSuccess) {
  150. DeInitMAPI();
  151. return FALSE;
  152. }
  153. // initialize MAPI
  154. assert(lpMAPIInitialize);
  155. hr = lpMAPIInitialize(NULL);
  156. if (HR_FAILED(hr)) {
  157. DeInitMAPI();
  158. return FALSE;
  159. }
  160. gfMAPIActive = TRUE;
  161. }
  162. dwMAPIRefCount ++;
  163. return TRUE;
  164. }
  165. /*******************************************************************
  166. NAME: DeInitMAPI
  167. SYNOPSIS: Uninitializes MAPI and unloads MAPI dlls
  168. ********************************************************************/
  169. VOID DeInitMAPI(VOID)
  170. {
  171. // decrease reference count
  172. if (dwMAPIRefCount) {
  173. dwMAPIRefCount--;
  174. }
  175. // shut down and unload MAPI if reference count hits zero
  176. if (!dwMAPIRefCount) {
  177. // uninitialize MAPI
  178. if (gfMAPIActive && lpMAPIUninitialize) {
  179. lpMAPIUninitialize();
  180. gfMAPIActive = FALSE;
  181. }
  182. // free the MAPI dll
  183. if (hInstMAPIDll) {
  184. FreeLibrary(hInstMAPIDll);
  185. hInstMAPIDll = NULL;
  186. }
  187. // set function pointers to NULL
  188. for (UINT nIndex = 0;nIndex<NUM_MAPI_PROCS;nIndex++)
  189. *MAPIApiList[nIndex].ppFcnPtr = NULL;
  190. }
  191. }
  192. /*******************************************************************
  193. NAME: SetMailProfileInformation
  194. SYNOPSIS: Sets up MAPI profile for internet mail and sets
  195. user information in profile.
  196. ENTRY: pMailConfigInfo - pointer to struct with configuration info
  197. EXIT: returns an HRESULT
  198. NOTES: See strategy statement above
  199. ********************************************************************/
  200. HRESULT SetMailProfileInformation(MAILCONFIGINFO * pMailConfigInfo)
  201. {
  202. HRESULT hr;
  203. LPPROFADMIN pProfAdmin=NULL; // interface to administer profiles
  204. LPSERVICEADMIN pServiceAdmin=NULL; // interface to administer services
  205. LPSRowSet pServiceRowSet=NULL;
  206. WCHAR szSelProfileName[cchProfileNameMax+1]=L"";
  207. assert(pMailConfigInfo);
  208. // get a pointer to the interface to administer profiles
  209. assert(lpMAPIAdminProfiles);
  210. hr = lpMAPIAdminProfiles(0, &pProfAdmin);
  211. if (HR_FAILED(hr)) {
  212. //DEBUGMSG(L"MAPIAdminProfiles returned 0x%lx", hr);
  213. return (hr);
  214. }
  215. assert(pProfAdmin);
  216. // release this interface when we leave the function
  217. RELEASE_ME_LATER ReleaseProfAdminLater(pProfAdmin);
  218. // get profile name from passed-in struct, if specified
  219. if (pMailConfigInfo->pszProfileName && lstrlen(pMailConfigInfo->pszProfileName)) {
  220. lstrcpy(szSelProfileName, pMailConfigInfo->pszProfileName);
  221. } else {
  222. // no profile specified, use default name
  223. LoadString( GetModuleHandle(L"msobcomm.dll"), IDS_DEFAULT_PROFILE_NAME, szSelProfileName,MAX_CHARS_IN_BUFFER(szSelProfileName));
  224. }
  225. // create profile if we need to
  226. hr = CreateProfileIfNecessary(pProfAdmin, szSelProfileName);
  227. if (HR_FAILED(hr))
  228. return hr;
  229. // set this profile as default if appropriate
  230. if (pMailConfigInfo->fSetProfileAsDefault) {
  231. hr = pProfAdmin->SetDefaultProfile(szSelProfileName, 0);
  232. if (HR_FAILED(hr))
  233. return hr;
  234. }
  235. assert(lstrlen(szSelProfileName)); // should have profile name at this point
  236. //DEBUGMSG(L"Modifying MAPI profile: %s", szSelProfileName);
  237. // get a pointer to the interface to administer services for this profile
  238. hr = pProfAdmin->AdminServices(szSelProfileName, NULL,NULL,0,
  239. &pServiceAdmin);
  240. if (HR_FAILED(hr))
  241. return hr;
  242. assert(pServiceAdmin);
  243. // release pServiceAdmin interface when done
  244. RELEASE_ME_LATER rlServiceAdmin(pServiceAdmin);
  245. // get a list of services for this profile
  246. hr = GetServicesList(pServiceAdmin, &pServiceRowSet);
  247. if (HR_FAILED(hr))
  248. return hr;
  249. assert(pServiceRowSet);
  250. // install any services we need which aren't already present in the profile
  251. hr = InstallRequiredServices(pServiceAdmin, pServiceRowSet);
  252. // done with profile row set, free the table
  253. FreeSRowSet(pServiceRowSet);
  254. pServiceRowSet = NULL;
  255. if (HR_FAILED(hr))
  256. return hr;
  257. // configure the internet mail service with the passed in email name,
  258. // server, etc.
  259. hr = ConfigInternetService(pMailConfigInfo, pServiceAdmin);
  260. if (HR_FAILED(hr)) {
  261. //DEBUGMSG(L"ConfigInternetService returned 0x%x" , hr);
  262. return hr;
  263. }
  264. return hr;
  265. }
  266. /*******************************************************************
  267. NAME: GetProfileList
  268. SYNOPSIS: retrieves a list of MAPI profiles
  269. ENTRY: lpProfAdmin - pointer to profile admin interface
  270. ppRowSet - pointer to an SRowSet pointer that is filled in
  271. EXIT: returns an HRESULT. If successful, *ppRowSet contains
  272. pointer to SRowSet with profile list.
  273. NOTES: Cloned from MAPI profile control panel code.
  274. Caller MUST call MAPIFreeBuffer to free *ppRowSet when done.
  275. ********************************************************************/
  276. HRESULT GetProfileList(LPPROFADMIN lpProfAdmin, LPSRowSet * ppRowSet)
  277. {
  278. HRESULT hr;
  279. LPMAPITABLE pMapiTable=NULL;
  280. SPropTagArray TagArray= {3,
  281. {PR_DISPLAY_NAME,
  282. PR_COMMENT,
  283. PR_DEFAULT_PROFILE}};
  284. assert(lpProfAdmin);
  285. assert(ppRowSet);
  286. // call the lpProfAdmin interface to get the MAPI profile table
  287. hr = lpProfAdmin->GetProfileTable(0, &pMapiTable);
  288. if (HR_FAILED(hr))
  289. return hr;
  290. assert(pMapiTable);
  291. // release this interface when we leave the function
  292. RELEASE_ME_LATER ReleaseMapiTableLater(pMapiTable);
  293. // set properties of table columns
  294. hr = pMapiTable->SetColumns(&TagArray, 0);
  295. if (!HR_FAILED(hr)) {
  296. // get row set information from table
  297. hr = pMapiTable->QueryRows(4000, 0,ppRowSet);
  298. }
  299. return hr;
  300. }
  301. /*******************************************************************
  302. NAME: GetServicesList
  303. SYNOPSIS: retrieves a list of MAPI services in a profile
  304. ENTRY: lpProfAdmin - pointer to service admin interface
  305. ppRowSet - pointer to an SRowSet pointer that is filled in
  306. EXIT: returns an HRESULT. If successful, *ppRowSet contains
  307. pointer to SRowSet with service list.
  308. NOTES: Cloned from MAPI profile control panel code.
  309. Caller MUST call MAPIFreeBuffer to free *ppRowSet when done.
  310. ********************************************************************/
  311. HRESULT GetServicesList(LPSERVICEADMIN lpServiceAdmin, LPSRowSet *ppRowSet)
  312. {
  313. HRESULT hr;
  314. ULONG iRow;
  315. LPMAPITABLE pMapiTable = NULL;
  316. SCODE sc = S_OK;
  317. static SPropTagArray taga = {7,
  318. { PR_DISPLAY_NAME,
  319. PR_SERVICE_NAME,
  320. PR_RESOURCE_FLAGS,
  321. PR_SERVICE_DLL_NAME,
  322. PR_SERVICE_ENTRY_NAME,
  323. PR_SERVICE_UID,
  324. PR_SERVICE_SUPPORT_FILES }};
  325. *ppRowSet = NULL;
  326. hr = lpServiceAdmin->GetMsgServiceTable(0, &pMapiTable);
  327. if (HR_FAILED(hr))
  328. return hr;
  329. // free this interface when function exits
  330. RELEASE_ME_LATER rlTable(pMapiTable);
  331. hr = pMapiTable->SetColumns(&taga, 0);
  332. if (!HR_FAILED(hr)) {
  333. // BUGBUG get rid of 'magic number' (appears in MAPI
  334. // ctrl panel code, need to find out what it is) jeremys 1/30/95
  335. hr = pMapiTable->QueryRows(4000, 0,ppRowSet);
  336. }
  337. if (HR_FAILED(hr))
  338. return hr;
  339. for(iRow = 0; iRow < (*ppRowSet)->cRows; iRow++)
  340. {
  341. // make sure properties are valid, if not then slam something in
  342. ValidateProperty((*ppRowSet)->aRow[iRow].lpProps, 0, PR_DISPLAY_NAME);
  343. ValidateProperty((*ppRowSet)->aRow[iRow].lpProps, 1, PR_SERVICE_NAME);
  344. }
  345. return hr;
  346. }
  347. /*******************************************************************
  348. NAME: CreateProfileIfNecessary
  349. SYNOPSIS: Creates profile if it doesn't already exist
  350. ENTRY: lpProfAdmin - pointer to profile admin interface
  351. pszSelProfileName - name of profile to create
  352. EXIT: returns an HRESULT.
  353. ********************************************************************/
  354. HRESULT CreateProfileIfNecessary(LPPROFADMIN pProfAdmin, WCHAR * pszSelProfileName)
  355. {
  356. HRESULT hr = hrSuccess;
  357. LPWSTR lpProfileName=NULL;
  358. BOOL fDefault;
  359. assert(pProfAdmin);
  360. assert(pszSelProfileName);
  361. ENUM_MAPI_PROFILE EnumMAPIProfile;
  362. // walk through the profile names, see if we have a match
  363. while (EnumMAPIProfile.Next(&lpProfileName, &fDefault)) {
  364. assert(lpProfileName);
  365. if (!lstrcmpi(lpProfileName, pszSelProfileName)) {
  366. return hrSuccess; // found a match, nothing to do
  367. }
  368. }
  369. // no match, need to create profile
  370. //DEBUGMSG(L"Creating MAPI profile: %s", pszSelProfileName);
  371. // call MAPI to create the profile
  372. hr = pProfAdmin->CreateProfile(pszSelProfileName,
  373. NULL, (ULONG) 0, (ULONG) 0);
  374. return hr;
  375. }
  376. /*******************************************************************
  377. NAME: InstallRequiredServices
  378. SYNOPSIS: Installs the 3 services we need (message store,
  379. address book, internet mail) in the profile
  380. if they're not already present. Calls functions to configure
  381. message store and address book (they both need a filename
  382. to use) if we're adding them.
  383. ENTRY: lpServiceAdmin - pointer to service admin interface
  384. pServiceRowSet - MAPI table with list of installed services
  385. EXIT: returns an HRESULT.
  386. NOTES: We deliberately don't configure internet mail service here--
  387. we do that in the main routine. The reason is that we
  388. need to configure internet mail whether it's already installed
  389. or not, address book and message store we only need to configure
  390. if they're brand new.
  391. ********************************************************************/
  392. HRESULT InstallRequiredServices(LPSERVICEADMIN pServiceAdmin,
  393. LPSRowSet pServiceRowSet)
  394. {
  395. ULONG iRow, iService;
  396. WCHAR szServiceName[SMALL_BUF_LEN+1];
  397. LPMAPIUID pMapiUID=NULL;
  398. HRESULT hr=hrSuccess;
  399. // table for MAPI services we need to make sure are installed in profile
  400. MSGSERVICE MAPIServiceList[NUM_SERVICES] = {
  401. { FALSE, IDS_INTERNETMAIL_SERVICENAME, IDS_INTERNETMAIL_DESCRIPTION, FALSE, 0,0,0},
  402. { FALSE, IDS_MESSAGESTORE_SERVICENAME, IDS_MESSAGESTORE_DESCRIPTION, TRUE,
  403. IDS_MESSAGESTORE_FILENAME, IDS_MESSAGESTORE_FILENAME1,PR_PST_PATH},
  404. { FALSE, IDS_ADDRESSBOOK_SERVICENAME, IDS_ADDRESSBOOK_DESCRIPTION, TRUE,
  405. IDS_ADDRESSBOOK_FILENAME, IDS_ADDRESSBOOK_FILENAME1,PR_PAB_PATH}};
  406. // walk through the list of services
  407. for (iRow = 0;iRow < pServiceRowSet->cRows;iRow ++) {
  408. //DEBUGMSG(L"Profile contains service: %s (%s)",
  409. // pServiceRowSet->aRow[iRow].lpProps[ivalDisplayName].Value.LPSZ,
  410. // pServiceRowSet->aRow[iRow].lpProps[ivalServiceName].Value.LPSZ);
  411. // for each service, walk through our array of required services,
  412. // and see if there's a match
  413. for (iService = 0;iService < NUM_SERVICES;iService ++) {
  414. // load the name for this service out of resource
  415. LoadString( GetModuleHandle(L"msobcomm.dll"), MAPIServiceList[iService].uIDServiceName,
  416. szServiceName, MAX_CHARS_IN_BUFFER(szServiceName));
  417. // compare it to the service name in the table of
  418. // installed services for this profile
  419. if (!lstrcmpi(szServiceName,
  420. pServiceRowSet->aRow[iRow].lpProps[ivalServiceName].Value.LPSZ)) {
  421. // this is a match!
  422. MAPIServiceList[iService].fPresent = TRUE;
  423. break; // break the inner 'for' loop
  424. }
  425. }
  426. }
  427. // install any services we need which are not already present
  428. for (iService = 0;iService < NUM_SERVICES;iService ++) {
  429. if (!MAPIServiceList[iService].fPresent) {
  430. WCHAR szServiceDesc[MAX_RES_LEN+1];
  431. MSGSERVICE * pMsgService = &MAPIServiceList[iService];
  432. // load the service name and description
  433. LoadString( GetModuleHandle(L"msobcomm.dll"), pMsgService->uIDServiceName,
  434. szServiceName, MAX_CHARS_IN_BUFFER(szServiceName));
  435. LoadString( GetModuleHandle(L"msobcomm.dll"), pMsgService->uIDServiceDescription,
  436. szServiceDesc, MAX_CHARS_IN_BUFFER(szServiceDesc));
  437. //DEBUGMSG(L"Adding service: %s (%s)",
  438. // szServiceDesc, szServiceName);
  439. // create the service
  440. hr = pServiceAdmin->CreateMsgService(szServiceName,
  441. szServiceDesc, 0,0);
  442. if (HR_FAILED(hr))
  443. return hr;
  444. // call a creation-time config procedure if specified
  445. if (pMsgService->fNeedConfig) {
  446. // get the UID (identifier) for this service
  447. // based on service name, APIs downstream need this
  448. hr = GetServiceUID(szServiceName, pServiceAdmin,
  449. &pMapiUID);
  450. if (HR_FAILED(hr))
  451. return hr;
  452. assert(pMapiUID);
  453. // call the proc to configure newly-created service
  454. hr = ConfigNewService(pServiceAdmin, pMapiUID,
  455. pMsgService->uIDStoreFilename, pMsgService->uIDStoreFilename1,
  456. pMsgService->uPropID);
  457. // free the buffer with the UID
  458. assert(lpMAPIFreeBuffer);
  459. lpMAPIFreeBuffer(pMapiUID);
  460. pMapiUID = NULL;
  461. }
  462. }
  463. }
  464. return hr;
  465. }
  466. #define NUM_MAIL_PROPS 11
  467. /*******************************************************************
  468. NAME: ConfigInternetService
  469. SYNOPSIS: Configures the Internet Mail service (route 66) with
  470. user's email name, email server, etc.
  471. ENTRY: pMailConfigInfo - pointer to struct with configuration info
  472. pServiceAdmin - pointer to service admin interface
  473. EXIT: returns an HRESULT
  474. NOTES: will stomp any existing settings for properties that it
  475. sets.
  476. ********************************************************************/
  477. HRESULT ConfigInternetService(MAILCONFIGINFO * pMailConfigInfo,
  478. LPSERVICEADMIN pServiceAdmin)
  479. {
  480. HRESULT hr;
  481. SPropValue PropValue[NUM_MAIL_PROPS];
  482. WCHAR szServiceName[SMALL_BUF_LEN+1];
  483. LPMAPIUID pMapiUID=NULL;
  484. UINT nProps = NUM_MAIL_PROPS;
  485. assert(pMailConfigInfo);
  486. assert(pServiceAdmin);
  487. // get service UID for internet mail service
  488. LoadString( GetModuleHandle(L"msobcomm.dll"), IDS_INTERNETMAIL_SERVICENAME, szServiceName,MAX_CHARS_IN_BUFFER(szServiceName));
  489. hr = GetServiceUID(szServiceName, pServiceAdmin,&pMapiUID);
  490. if (HR_FAILED(hr)) {
  491. return hr;
  492. }
  493. assert(pMapiUID);
  494. // set the property value for each property. Note that the order
  495. // of items in the array doesn't mattter. The ulPropTag member indicates
  496. // what property the PropValue item is for, and the lpszW, b or l member
  497. // contains the data for that property.
  498. // need to "encrypt" mail account password with xor bit mask. Mail client
  499. // expects it to be thusly "encrypted" when it reads it out. It's stored
  500. // in the registry in this securely "encrypted" format. Somebody pretty
  501. // smart must have thought of this.
  502. // configure mail service properties
  503. PropValue[0].ulPropTag = PR_CFG_EMAIL_ADDRESS;
  504. PropValue[0].Value.lpszW = pMailConfigInfo->pszEmailAddress;
  505. PropValue[1].ulPropTag = PR_CFG_EMAIL_DISPLAY_NAME;
  506. PropValue[1].Value.lpszW = pMailConfigInfo->pszEmailDisplayName;
  507. PropValue[2].ulPropTag = PR_CFG_SERVER_PATH;
  508. PropValue[2].Value.lpszW = pMailConfigInfo->pszEmailServer;
  509. PropValue[3].ulPropTag = PR_CFG_EMAIL_ACCOUNT;
  510. PropValue[3].Value.lpszW = pMailConfigInfo->pszEmailAccountName;
  511. PropValue[4].ulPropTag = PR_CFG_PASSWORD;
  512. PropValue[4].Value.lpszW = (LPWSTR) szNull;
  513. PropValue[5].ulPropTag = PR_CFG_REMEMBER;
  514. PropValue[5].Value.b = (USHORT) TRUE;
  515. // configure for RNA or LAN as appropriate
  516. PropValue[6].ulPropTag = PR_CFG_RNA_PROFILE;
  517. PropValue[7].ulPropTag = PR_CFG_CONN_TYPE;
  518. PropValue[8].ulPropTag = PR_CFG_DELIVERY_OPTIONS;
  519. if (pMailConfigInfo->pszConnectoidName &&
  520. lstrlen(pMailConfigInfo->pszConnectoidName)) {
  521. PropValue[6].Value.lpszW = pMailConfigInfo->pszConnectoidName;
  522. PropValue[7].Value.l = (long) CONNECT_TYPE_REMOTE;
  523. // set transfer mode for "selective"..
  524. PropValue[8].Value.l = DOWNLOAD_OPTION_HEADERS;
  525. } else {
  526. PropValue[6].Value.lpszW = (LPWSTR) szNull;
  527. PropValue[7].Value.l = (long) CONNECT_TYPE_LAN;
  528. // set automatic transfer mode... mail guys made up the weird
  529. // define name, not me!
  530. PropValue[8].Value.l = DOWNLOAD_OPTION_MAIL_DELETE;
  531. }
  532. PropValue[9].ulPropTag = PR_CFG_REMOTE_USERNAME;
  533. PropValue[9].Value.lpszW = pMailConfigInfo->pszEmailAccountName;
  534. PropValue[10].ulPropTag = PR_CFG_REMOTE_PASSWORD;
  535. PropValue[10].Value.lpszW = pMailConfigInfo->pszEmailAccountPwd;
  536. // call the service admin interface to configure the service with these
  537. // properties
  538. hr = pServiceAdmin->ConfigureMsgService(pMapiUID, NULL,0,
  539. nProps, PropValue);
  540. if (HR_FAILED(hr)) {
  541. //DEBUGMSG(L"ConfigureMsgService returned 0x%x", hr);
  542. }
  543. // free the buffer with the UID
  544. assert(lpMAPIFreeBuffer);
  545. lpMAPIFreeBuffer(pMapiUID);
  546. pMapiUID = NULL;
  547. return hr;
  548. }
  549. /*******************************************************************
  550. NAME: ConfigMessageStore
  551. SYNOPSIS: Generates a unique filename and sets it as the
  552. message store
  553. ENTRY: lpServiceAdmin - pointer to service admin interface
  554. lpMapiUID - UID for this service (message store)
  555. EXIT: returns an HRESULT
  556. NOTES: This code expects to be called only when the service is
  557. newly created. Calling it on an existing service will
  558. cause it to stomp existing settings.
  559. ********************************************************************/
  560. HRESULT ConfigNewService(LPSERVICEADMIN lpServiceAdmin, LPMAPIUID lpMapiUID,
  561. UINT uIDFilename, UINT uIDFilename1,UINT uPropValID)
  562. {
  563. WCHAR szMsgStorePath[MAX_PATH+1];
  564. HRESULT hr=hrSuccess;
  565. assert(lpServiceAdmin);
  566. assert(lpMapiUID);
  567. // build a path for the message store
  568. if (!MakeUniqueFilename(uIDFilename, uIDFilename1,
  569. szMsgStorePath, MAX_CHARS_IN_BUFFER(szMsgStorePath) ))
  570. {
  571. //DEBUGTRAP(L"Unable to create unique filename");
  572. return MAPI_E_COLLISION;
  573. }
  574. //(L"Creating MAPI store %s", szMsgStorePath);
  575. // set this filename for the message store
  576. SPropValue PropVal;
  577. PropVal.ulPropTag = uPropValID;
  578. PropVal.Value.lpszW = szMsgStorePath;
  579. hr = lpServiceAdmin->ConfigureMsgService(lpMapiUID, NULL,0,1,&PropVal);
  580. if (HR_FAILED(hr)) {
  581. //DEBUGMSG(L"ConfigureMsgService returned 0x%x", hr);
  582. }
  583. return hr;
  584. }
  585. /*******************************************************************
  586. NAME: FindInternetMailService
  587. SYNOPSIS: Detects if internet mail is installed, returns
  588. email address and email server if it is.
  589. ********************************************************************/
  590. BOOL FindInternetMailService(WCHAR * pszEmailAddress, DWORD cbEmailAddress,
  591. WCHAR * pszEmailServer, DWORD cbEmailServer)
  592. {
  593. assert(pszEmailAddress);
  594. assert(pszEmailServer);
  595. if (!hInstMAPIDll && !InitMAPI(NULL))
  596. return FALSE;
  597. // look through all profiles. For each profile, look through all
  598. // services. If we find an instance of the internet mail service,
  599. // then return email address and password to caller. If there is
  600. // more than one profile with the internet mail service, we
  601. // will return the first one we find.
  602. ENUM_MAPI_PROFILE EnumMAPIProfile;
  603. LPWSTR lpProfileName, lpServiceName;
  604. BOOL fDefault;
  605. // walk through the list of profiles...
  606. while (EnumMAPIProfile.Next(&lpProfileName, &fDefault)) {
  607. assert(lpProfileName);
  608. //DEBUGMSG(L"Found profile: %s", lpProfileName);
  609. // for each profile, walk through the list of services...
  610. ENUM_MAPI_SERVICE EnumMAPIService(lpProfileName);
  611. while (EnumMAPIService.Next(&lpServiceName)) {
  612. WCHAR szSmallBuf[SMALL_BUF_LEN+1];
  613. //DEBUGMSG(L"Found service: %s", lpServiceName);
  614. LoadString( GetModuleHandle(L"msobcomm.dll"), IDS_INTERNETMAIL_SERVICENAME,
  615. szSmallBuf, MAX_CHARS_IN_BUFFER(szSmallBuf));
  616. if (!lstrcmpi(lpServiceName, szSmallBuf)) {
  617. //BUGBUG 21-May-1995 jeremys Get e-mail server & address from MAPI profile
  618. return TRUE;
  619. }
  620. }
  621. }
  622. return FALSE;
  623. }
  624. ENUM_MAPI_PROFILE::ENUM_MAPI_PROFILE(VOID)
  625. {
  626. LPPROFADMIN pProfAdmin=NULL; // interface to administer profiles
  627. HRESULT hr;
  628. //assert(gfMAPIActive, L"MAPI not initialized!");
  629. _pProfileRowSet = NULL;
  630. _nEntries = 0;
  631. _iRow = 0;
  632. // get a pointer to the interface to administer profiles
  633. assert(lpMAPIAdminProfiles);
  634. hr = lpMAPIAdminProfiles(0, &pProfAdmin);
  635. if (HR_FAILED(hr)) {
  636. //DEBUGMSG(L"MAPIAdminProfiles returned 0x%lx", hr);
  637. return;
  638. }
  639. assert(pProfAdmin);
  640. // release this interface when we leave the function
  641. RELEASE_ME_LATER ReleaseProfAdminLater(pProfAdmin);
  642. // get the rows in the profile table
  643. hr = GetProfileList(pProfAdmin, &_pProfileRowSet);
  644. if (HR_FAILED(hr))
  645. return;
  646. assert(_pProfileRowSet);
  647. _nEntries = _pProfileRowSet->cRows;
  648. }
  649. ENUM_MAPI_PROFILE::~ENUM_MAPI_PROFILE(VOID)
  650. {
  651. if (_pProfileRowSet) {
  652. // done with profile row set, free the table
  653. FreeSRowSet(_pProfileRowSet);
  654. _pProfileRowSet = NULL;
  655. }
  656. }
  657. BOOL ENUM_MAPI_PROFILE::Next(LPWSTR * ppProfileName, BOOL * pfDefault)
  658. {
  659. assert(pfDefault);
  660. if (!_pProfileRowSet)
  661. return FALSE;
  662. if (_iRow < _pProfileRowSet->cRows) {
  663. LPSPropValue pPropVal = _pProfileRowSet->aRow[_iRow].lpProps;
  664. assert(pPropVal);
  665. // get pointer to profile name
  666. *ppProfileName = pPropVal[0].Value.LPSZ;
  667. assert(*ppProfileName);
  668. // set 'this profile is default' flag
  669. *pfDefault = pPropVal[2].Value.b;
  670. _iRow++;
  671. return TRUE;
  672. }
  673. return FALSE;
  674. }
  675. ENUM_MAPI_SERVICE::ENUM_MAPI_SERVICE(LPWSTR pszProfileName)
  676. {
  677. LPPROFADMIN pProfAdmin=NULL; // interface to administer profiles
  678. LPSERVICEADMIN pServiceAdmin=NULL; // interface to administer services
  679. HRESULT hr;
  680. assert(pszProfileName);
  681. //assertSZ(gfMAPIActive, L"MAPI not initialized!");
  682. _pServiceRowSet = NULL;
  683. _nEntries = 0;
  684. _iRow = 0;
  685. // get a pointer to the interface to administer profiles
  686. assert(lpMAPIAdminProfiles);
  687. hr = lpMAPIAdminProfiles(0, &pProfAdmin);
  688. if (HR_FAILED(hr)) {
  689. //DEBUGMSG(L"MAPIAdminProfiles returned 0x%lx", hr);
  690. return;
  691. }
  692. assert(pProfAdmin);
  693. // release this interface when we leave the function
  694. RELEASE_ME_LATER ReleaseProfAdminLater(pProfAdmin);
  695. // get a pointer to the interface to administer services for this profile
  696. hr = pProfAdmin->AdminServices(pszProfileName, NULL,NULL,0,
  697. &pServiceAdmin);
  698. if (HR_FAILED(hr)) {
  699. //DEBUGMSG(L"AdminServices returned 0x%lx", hr);
  700. return;
  701. }
  702. // release this interface when we leave the function
  703. RELEASE_ME_LATER ReleaseServiceAdminLater(pServiceAdmin);
  704. // get the rows in the profile table
  705. hr = GetServicesList(pServiceAdmin, &_pServiceRowSet);
  706. if (HR_FAILED(hr))
  707. return;
  708. assert(_pServiceRowSet);
  709. _nEntries = _pServiceRowSet->cRows;
  710. }
  711. ENUM_MAPI_SERVICE::~ENUM_MAPI_SERVICE(VOID)
  712. {
  713. if (_pServiceRowSet) {
  714. // done with profile row set, free the table
  715. FreeSRowSet(_pServiceRowSet);
  716. _pServiceRowSet = NULL;
  717. }
  718. }
  719. BOOL ENUM_MAPI_SERVICE::Next(LPWSTR * ppServiceName)
  720. {
  721. if (!_pServiceRowSet)
  722. return FALSE;
  723. if (_iRow < _pServiceRowSet->cRows) {
  724. LPSPropValue pPropVal = _pServiceRowSet->aRow[_iRow].lpProps;
  725. assert(pPropVal);
  726. // get pointer to profile name
  727. *ppServiceName = pPropVal[ivalServiceName].Value.LPSZ;
  728. assert(*ppServiceName);
  729. _iRow++;
  730. return TRUE;
  731. }
  732. return FALSE;
  733. }
  734. /*******************************************************************
  735. NAME: MakeUniqueFilename
  736. SYNOPSIS: Generates a filename in the Windows directory that
  737. does not already exist
  738. ENTRY: uIDFilename - ID of string resource for desired name
  739. for the file
  740. uIDAltFilename - ID of string resource with template
  741. for filename to use if file with uIDFilename's name
  742. already exists. Template should contain %u into
  743. which numbers will be inserted to make filename unique.
  744. pszFilename - buffer to return path and filename into
  745. cbFilename - size of pszFilename buffer
  746. EXIT: returns TRUE if successful, FALSE if couldn't make
  747. unique filename within MAX_FILENAME_TRIES tries
  748. ********************************************************************/
  749. // number of times we'll try to create a unique filename before giving up
  750. #define MAX_MAKEFILENAME_TRIES 20
  751. BOOL MakeUniqueFilename(UINT uIDFilename, UINT uIDAltFilename,
  752. WCHAR * pszFilename, DWORD cchFilename)
  753. {
  754. WCHAR szFileName[SMALL_BUF_LEN+1];
  755. BOOL fSuccess = FALSE;
  756. assert(pszFilename);
  757. // build a path for the filename
  758. UINT uRet=GetWindowsDirectory(pszFilename, cchFilename);
  759. //assertSZ(uRet, L"GetWindowsDirectory failed");
  760. // choose a file name that doesn't already exist
  761. // first, try using the string resource specified by uIDFilename
  762. LoadString( GetModuleHandle(L"msobcomm.dll"), uIDFilename, szFileName,MAX_CHARS_IN_BUFFER(szFileName));
  763. if (DoesFileExist(pszFilename, szFileName)) {
  764. // if that file exists, then use the string resource uIDAltFilename
  765. // which has a replacable parameter. We'll try adding numbers to
  766. // the filename to make it unique.
  767. WCHAR szFileFmt[SMALL_BUF_LEN+1];
  768. LoadString( GetModuleHandle(L"msobcomm.dll"), uIDAltFilename, szFileFmt,MAX_CHARS_IN_BUFFER(szFileFmt));
  769. for (UINT nIndex = 0; nIndex < MAX_MAKEFILENAME_TRIES; nIndex ++) {
  770. // make a name e.g. "mailbox4.pst"
  771. wsprintf(szFileName, szFileFmt,nIndex);
  772. if (!DoesFileExist(pszFilename, szFileName)) {
  773. // OK, found a filename that doesn't exist
  774. fSuccess = TRUE;
  775. break;
  776. }
  777. }
  778. } else {
  779. // first try succeeded
  780. fSuccess = TRUE;
  781. }
  782. if (fSuccess) {
  783. // now we have unique filename, build the full path
  784. lstrcat(pszFilename, szSlash);
  785. lstrcat(pszFilename, szFileName);
  786. }
  787. return fSuccess;
  788. }
  789. /*******************************************************************
  790. NAME: DoesFileExist
  791. SYNOPSIS: Checks to see whether the specified file exists
  792. ENTRY: pszPath - path to directory
  793. pszFilename - name of file
  794. EXIT: returns TRUE if file exists, FALSE if it doesn't
  795. ********************************************************************/
  796. BOOL DoesFileExist(WCHAR * pszPath, WCHAR * pszFileName)
  797. {
  798. WCHAR szPath[MAX_PATH+1];
  799. OFSTRUCT of;
  800. assert(pszPath);
  801. assert(pszFileName);
  802. // concatenate the path and file name
  803. lstrcpy(szPath, pszPath);
  804. lstrcat(szPath, szSlash);
  805. lstrcat(szPath, pszFileName);
  806. // find out if this file exists
  807. WIN32_FIND_DATA fd;
  808. HANDLE hfd = FindFirstFile(szPath, &fd);
  809. if (INVALID_HANDLE_VALUE == hfd)
  810. {
  811. return FALSE;
  812. }
  813. CloseHandle(hfd);
  814. return TRUE;
  815. }
  816. /*******************************************************************
  817. NAME: GetServiceUID
  818. SYNOPSIS: Given a MAPI service name, gets the MAPIUID associated
  819. with it.
  820. ENTRY: pszServiceName - name of MAPI service (e.g. "IMAIL", "MSPST AB")
  821. lpServiceAdmin - pointer to service admin interface
  822. ppMapiUID - pointer to pointer for MAPIUID struct
  823. EXIT: returns an HRESULT
  824. NOTES: Cloned from MAPI profile wizard code, if you think this
  825. function is big and ugly now you should have seen it before
  826. I cleaned it up.
  827. This function allocates a MAPIUID, the caller is responsible
  828. for freeing this (use MAPIFreeBuffer) when done.
  829. ********************************************************************/
  830. HRESULT GetServiceUID(WCHAR * pszServiceName, LPSERVICEADMIN lpServiceAdmin,
  831. LPMAPIUID *ppMapiUID)
  832. {
  833. HRESULT hr =hrSuccess;
  834. LPSPropValue pTblProp =NULL;
  835. DWORD iRow, iColumn;
  836. LPMAPITABLE pTable =NULL;
  837. LPSRowSet pRowSet =NULL;
  838. LPSRow pRow =NULL;
  839. int nFound =0;
  840. LPMAPIUID pMapiUID =NULL;
  841. BOOL fContinue = TRUE;
  842. SizedSPropTagArray(2, Tbltaga) = { 2, { PR_SERVICE_NAME,
  843. PR_SERVICE_UID }};
  844. assert(pszServiceName);
  845. assert(lpServiceAdmin);
  846. assert(ppMapiUID);
  847. // get table of message services
  848. hr = lpServiceAdmin->GetMsgServiceTable(0, &pTable);
  849. if (HR_FAILED(hr))
  850. {
  851. //DEBUGMSG(L"GetMsgServiceTable returned 0x%x", hr);
  852. return hr;
  853. }
  854. assert(pTable);
  855. // release this table when we exit this function
  856. RELEASE_ME_LATER rlTable(pTable);
  857. assert(lpHrQueryAllRows);
  858. hr = lpHrQueryAllRows(pTable, (LPSPropTagArray) &Tbltaga, NULL, NULL, 0, &pRowSet);
  859. if (HR_FAILED(hr))
  860. {
  861. //DEBUGMSG(L"HrQueryAllRows returned 0x%x", hr);
  862. return hr;
  863. }
  864. assert(pRowSet);
  865. iRow =0;
  866. while (fContinue && iRow< pRowSet->cRows)
  867. {
  868. pRow = &pRowSet->aRow[iRow];
  869. pTblProp = pRow->lpProps;
  870. nFound = 0;
  871. for (iColumn=0; iColumn<pRow->cValues; iColumn++)
  872. { //Check each property
  873. if (pTblProp->ulPropTag ==PR_SERVICE_UID)
  874. {
  875. nFound++;
  876. assert(lpMAPIAllocateBuffer);
  877. lpMAPIAllocateBuffer(pTblProp->Value.bin.cb, (LPVOID FAR *) &pMapiUID);
  878. if (!pMapiUID)
  879. {
  880. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  881. fContinue = FALSE;
  882. break;
  883. }
  884. memcpy(pMapiUID, pTblProp->Value.bin.lpb, (size_t) pTblProp->Value.bin.cb);
  885. *ppMapiUID = pMapiUID;
  886. }
  887. else if ((pTblProp->ulPropTag ==PR_SERVICE_NAME) &&
  888. !lstrcmpi(pTblProp->Value.lpszW, pszServiceName))
  889. {
  890. nFound++;
  891. }
  892. pTblProp++;
  893. if (nFound == 2) {
  894. // found it!
  895. fContinue = FALSE;
  896. break;
  897. }
  898. }
  899. iRow++;
  900. if (nFound < 2) {
  901. // if one but not both items matched above, then deallocate buffer
  902. if (pMapiUID) {
  903. assert(lpMAPIFreeBuffer);
  904. lpMAPIFreeBuffer(pMapiUID);
  905. pMapiUID =NULL;
  906. }
  907. if (*ppMapiUID)
  908. *ppMapiUID = NULL;
  909. }
  910. }
  911. if (HR_FAILED(hr) || nFound < 2) {
  912. // free buffer if we didn't find the UID
  913. if (pMapiUID) {
  914. assert(lpMAPIFreeBuffer);
  915. lpMAPIFreeBuffer(pMapiUID);
  916. }
  917. if (*ppMapiUID)
  918. *ppMapiUID = NULL;
  919. }
  920. if (pRowSet)
  921. FreeSRowSet(pRowSet);
  922. return hr;
  923. }
  924. /*******************************************************************
  925. NAME: FreeSRowSet
  926. SYNOPSIS: Frees an SRowSet structure and the rows therein
  927. ENTRY: prws - the row set to free
  928. NOTES: Cloned from MAPI profile ctrl panel code
  929. ********************************************************************/
  930. VOID FreeSRowSet(LPSRowSet prws)
  931. {
  932. ULONG irw;
  933. if (!prws)
  934. return;
  935. assert(lpMAPIFreeBuffer);
  936. // Free each row
  937. for (irw = 0; irw < prws->cRows; irw++)
  938. lpMAPIFreeBuffer(prws->aRow[irw].lpProps);
  939. // Free the top level structure
  940. lpMAPIFreeBuffer(prws);
  941. }
  942. /*
  943. * ValidateProperty
  944. *
  945. * Purpose:
  946. * Given a string prop, make sure it contains a valid string.
  947. *
  948. * Arguments:
  949. * pval
  950. * cVal
  951. * ulPropTag
  952. *
  953. * Returns:
  954. * BOOL
  955. */
  956. WCHAR szUnk[] = L"???";
  957. BOOL ValidateProperty(LPSPropValue pval, ULONG cVal, ULONG ulPropTag)
  958. {
  959. if(pval[cVal].ulPropTag != ulPropTag)
  960. {
  961. // make sure we're not stomping on good properties.
  962. assert(PROP_TYPE(pval[cVal].ulPropTag) == PT_ERROR);
  963. pval[cVal].ulPropTag = ulPropTag;
  964. pval[cVal].Value.LPSZ = szUnk;
  965. return TRUE;
  966. }
  967. return FALSE;
  968. }
  969. #pragma data_seg(DATASEG_READONLY)
  970. // note: this array depends on errors in rc file being in this order
  971. // starting with S_OK. Don't rearrange one without rearranging the other.
  972. static SCODE mpIdsScode[] =
  973. {
  974. S_OK,
  975. MAPI_E_NO_ACCESS,
  976. E_NOINTERFACE,
  977. E_INVALIDARG,
  978. MAPI_E_CALL_FAILED,
  979. MAPI_E_NOT_FOUND,
  980. MAPI_E_NO_SUPPORT,
  981. MAPI_W_ERRORS_RETURNED,
  982. MAPI_W_PARTIAL_COMPLETION,
  983. MAPI_E_BAD_CHARWIDTH,
  984. MAPI_E_BAD_VALUE,
  985. MAPI_E_BUSY,
  986. MAPI_E_COLLISION,
  987. MAPI_E_COMPUTED,
  988. MAPI_E_CORRUPT_DATA,
  989. MAPI_E_CORRUPT_STORE,
  990. MAPI_E_DISK_ERROR,
  991. MAPI_E_HAS_FOLDERS,
  992. MAPI_E_HAS_MESSAGES,
  993. MAPI_E_INVALID_ENTRYID,
  994. MAPI_E_INVALID_OBJECT,
  995. MAPI_E_LOGON_FAILED,
  996. MAPI_E_NETWORK_ERROR,
  997. MAPI_E_NON_STANDARD,
  998. MAPI_E_NOT_ENOUGH_DISK,
  999. MAPI_E_NOT_ENOUGH_MEMORY,
  1000. MAPI_E_NOT_ENOUGH_RESOURCES,
  1001. MAPI_E_NOT_IN_QUEUE,
  1002. MAPI_E_OBJECT_CHANGED,
  1003. MAPI_E_OBJECT_DELETED,
  1004. MAPI_E_STRING_TOO_LONG,
  1005. MAPI_E_SUBMITTED,
  1006. MAPI_E_TOO_BIG,
  1007. MAPI_E_UNABLE_TO_ABORT,
  1008. MAPI_E_UNCONFIGURED,
  1009. MAPI_E_UNEXPECTED_TYPE,
  1010. MAPI_E_UNKNOWN_FLAGS,
  1011. MAPI_E_USER_CANCEL,
  1012. MAPI_E_VERSION
  1013. };
  1014. #pragma data_seg()