Copyright © 2024 IMIBO. Privacy Statement
Request # 16
DateTime restriction and Appointments
This example shows how to construct DateTime restriction. With this restriction we will limit the listed Appointments to certain time interval.
Code snip:
// Fills List with Appointments data in format: // Start; End; Busy Status; Subject // If not StartDate and EndDate are defined, restriction is removed Procedure GetFreeBusy(Const MessageStore: IMsgStore; Const MAPIFolder: IMAPIFolder; List: TStrings; const StartDate: TDateTime = 0; const EndDate: TDateTime = 0); Type TaMsgPropArray = Record cValues: ULONG; aulPropTag: Array [0 .. 3] Of ULONG; End; POutlookNamedProp = ^TOutlookNamedProp; TOutlookNamedProp = Record ID: Integer; OulookName: WideString; PROP_TAG: Integer; PROP_TAG_NAMED: TMAPINAMEID; End; Var ContentTable: IMAPITable; hr: HRESULT; Flags, ContentTableRowCount: Cardinal; PropTagStart, PropTagEnd, PropTagBusyStatus: Cardinal; aMsgPropArray: TaMsgPropArray; RowSet: PSRowSet; iCount: Integer; PP: PSPropValue; FreeBusyData: string; Restriction: TSRestriction; RestrictionAND: array [0 .. 3] of TSRestriction; Prop1, Prop2: TSPropValue; SortCriteria: TSSortOrderSet; Const // Outlook GUID's // IPM.Appointment IID_IPMAppointment: TGUID = '{00062002-0000-0000-C000-000000000046}'; strIID_IPMAppointment = '{00062002-0000-0000-C000-000000000046}'; const TBusyStatusDesc: Array [TBusyStatus] Of string = ('Free', 'Tentative', 'Busy', 'OutOfOffice', 'WorkingFromElsewhere'); Const NamedProp: Array [0 .. 2] Of TOutlookNamedProp = ( (ID: 0; OulookName: 'Start'; PROP_TAG: PT_SYSTIME; PROP_TAG_NAMED: (lpguid: @IID_IPMAppointment; ulKind: MNID_ID; KIND: (lID: $820D)); ), (ID: 1; OulookName: 'End'; PROP_TAG: PT_SYSTIME; PROP_TAG_NAMED: (lpguid: @IID_IPMAppointment; ulKind: MNID_ID; KIND: (lID: $820E)); ), (ID: 2; OulookName: 'BusyStatus'; PROP_TAG: PT_LONG; PROP_TAG_NAMED: (lpguid: @IID_IPMAppointment; ulKind: MNID_ID; KIND: (lID: $8205)); ) ); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// Function PropTagFromNamed(Const NamedProp: TMAPINAMEID; Const PropType: Cardinal; Out PropTag: Cardinal; Const Create: Boolean = False): Boolean; Var PsPropTag: PSPropTagArray; PropNamed: Array [0 .. 0] Of PMAPINAMEID; Begin Result := False; PsPropTag := nil; PropTag := 0; If Not Assigned(MAPIFolder) Then Exit; If Create Then Flags := MAPI_CREATE Else Flags := 0; Try PropNamed[0] := @NamedProp; hr := IMAPIProp(MAPIFolder).GetIDsFromNames(1, @PropNamed, Flags, PsPropTag); If failed(hr) Or Not Assigned(PsPropTag) Or (PsPropTag.cValues <> 1) Then Exit; PsPropTag.aulPropTag[0] := CHANGE_PROP_TYPE(PsPropTag.aulPropTag[0], PropType); PropTag := PsPropTag.aulPropTag[0]; Result := True; Finally If Assigned(PsPropTag) Then MAPIFreeBuffer(PsPropTag); End; End; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// Begin // If Not Assigned(MAPIFolder) Or (GetMAPIFolderType(MAPIFolder) <> oFolderCalendar) Then // Exit; If Not Assigned(MAPIFolder) Or (GetMAPIFolderTypeEx(MessageStore, MAPIFolder) <> oFolderCalendar) Then Exit; RowSet := nil; if not Assigned(List) then Exit; Try Flags := 0; hr := MAPIFolder.GetContentsTable(Flags, ContentTable); If failed(hr) Or Not Assigned(ContentTable) Then Raise EMAPIError.CreateMAPI(MAPIFolder, hr); hr := ContentTable.GetRowCount(0, ContentTableRowCount); If failed(hr) Then Raise Exception.Create(GetMapiError(ContentTable, hr)); If Integer(ContentTableRowCount) < 1 Then Exit; If Not PropTagFromNamed(NamedProp[0].PROP_TAG_NAMED, NamedProp[0].PROP_TAG, PropTagStart) Or Not PropTagFromNamed(NamedProp[1].PROP_TAG_NAMED, NamedProp[1].PROP_TAG, PropTagEnd) Or Not PropTagFromNamed(NamedProp[2].PROP_TAG_NAMED, NamedProp[2].PROP_TAG, PropTagBusyStatus) Then Exit; aMsgPropArray.cValues := 4; aMsgPropArray.aulPropTag[0] := PropTagStart; aMsgPropArray.aulPropTag[1] := PropTagEnd; aMsgPropArray.aulPropTag[2] := PropTagBusyStatus; aMsgPropArray.aulPropTag[3] := PR_SUBJECT; ZeroMemory(@SortCriteria, SizeOf(TSSortOrderSet)); SortCriteria.cSorts := 1; SortCriteria.aSort[0].ulPropTag := PropTagStart; SortCriteria.aSort[0].ulOrder := TABLE_SORT_ASCEND; hr := ContentTable.SetColumns(@aMsgPropArray, TBL_BATCH); If failed(hr) And (hr = MAPI_E_BUSY) Then // do we have to wait? Begin hr := ContentTable.WaitForCompletion(0, 2000, ULONG(nil^)); // 2sec?? If hr = S_OK Then // does not run either hr := ContentTable.SetColumns(@aMsgPropArray, TBL_BATCH); End; if (Trunc(StartDate) = 0) and (Trunc(EndDate) = 0) then begin // Clear Restriction!!! hr := ContentTable.Restrict(nil, TBL_BATCH); if failed(hr) and (hr <> MAPI_E_BUSY) then raise Exception.Create(GetMapiError(ContentTable, hr)); if (hr = MAPI_E_BUSY) then begin hr := ContentTable.WaitForCompletion(0, 2000, ULONG(nil^)); // wait 2000 msec if hr = S_OK then // does not run either hr := ContentTable.Restrict(nil, TBL_BATCH); if failed(hr) then raise Exception.Create(GetMapiError(ContentTable, hr)); end; end else begin // request - messges between dates if (Trunc(StartDate) <> 0) and (Trunc(EndDate) <> 0) then begin ZeroMemory(@Restriction, SizeOf(TSRestriction)); ZeroMemory(@RestrictionAND, SizeOf(TSRestriction) * 4); ZeroMemory(@Prop1, SizeOf(TSPropValue)); ZeroMemory(@Prop2, SizeOf(TSPropValue)); Restriction.rt := RES_AND; Restriction.res.resAnd.cRes := 4; RestrictionAND[0].rt := RES_EXIST; RestrictionAND[0].res.resExist.ulPropTag := PropTagStart; RestrictionAND[0].res.resExist.ulReserved1 := 0; RestrictionAND[0].res.resExist.ulReserved2 := 0; RestrictionAND[1].rt := RES_EXIST; RestrictionAND[1].res.resExist.ulPropTag := PropTagEnd; RestrictionAND[1].res.resExist.ulReserved1 := 0; RestrictionAND[1].res.resExist.ulReserved2 := 0; Prop1.ulPropTag := PropTagStart; ConvertVariantToMAPIPropValue(StartDate, Prop1, nil); RestrictionAND[2].rt := RES_PROPERTY; RestrictionAND[2].res.resProperty.relop := RELOP_GE; RestrictionAND[2].res.resProperty.ulPropTag := PropTagStart; RestrictionAND[2].res.resProperty.lpProp := @Prop1; Prop2.ulPropTag := PropTagEnd; ConvertVariantToMAPIPropValue(EndDate, Prop2, nil); RestrictionAND[3].rt := RES_PROPERTY; RestrictionAND[3].res.resProperty.relop := RELOP_LE; RestrictionAND[3].res.resProperty.ulPropTag := PropTagEnd; RestrictionAND[3].res.resProperty.lpProp := @Prop2; Restriction.res.resAnd.lpRes := @RestrictionAND; end else // request - StartDate or EndDate Defined if (Trunc(StartDate) <> 0) or (Trunc(EndDate) <> 0) then begin ZeroMemory(@Restriction, SizeOf(TSRestriction)); ZeroMemory(@RestrictionAND, SizeOf(TSRestriction) * 2); ZeroMemory(@Prop1, SizeOf(TSPropValue)); ZeroMemory(@Prop2, SizeOf(TSPropValue)); Restriction.rt := RES_AND; Restriction.res.resAnd.cRes := 2; RestrictionAND[0].rt := RES_EXIST; if (Trunc(StartDate) <> 0) then RestrictionAND[0].res.resExist.ulPropTag := PropTagStart else RestrictionAND[0].res.resExist.ulPropTag := PropTagEnd; RestrictionAND[0].res.resExist.ulReserved1 := 0; RestrictionAND[0].res.resExist.ulReserved2 := 0; RestrictionAND[1].rt := RES_PROPERTY; if (Trunc(StartDate) <> 0) then begin RestrictionAND[1].res.resProperty.relop := RELOP_GE; { >= } RestrictionAND[1].res.resProperty.ulPropTag := PropTagStart; end else begin RestrictionAND[1].res.resProperty.relop := RELOP_LE; { <= } RestrictionAND[1].res.resProperty.ulPropTag := PropTagEnd; end; if (Trunc(StartDate) <> 0) then begin Prop1.ulPropTag := PropTagStart; ConvertVariantToMAPIPropValue(StartDate, Prop1, nil); end else begin Prop1.ulPropTag := PropTagEnd; ConvertVariantToMAPIPropValue(EndDate, Prop1, nil); end; RestrictionAND[1].res.resProperty.lpProp := @Prop1; Restriction.res.resAnd.lpRes := @RestrictionAND; end; hr := ContentTable.Restrict(@Restriction, TBL_BATCH); if failed(hr) and (hr <> MAPI_E_BUSY) then raise Exception.Create(GetMapiError(ContentTable, hr)); if (hr = MAPI_E_BUSY) then begin hr := ContentTable.WaitForCompletion(0, 2000, ULONG(nil^)); // wait 2000 msec if hr = S_OK then // does not run either hr := ContentTable.Restrict(@Restriction, TBL_BATCH); if failed(hr) then raise Exception.Create(GetMapiError(ContentTable, hr)); end; if (Trunc(StartDate) <> 0) then SortCriteria.aSort[0].ulPropTag := PropTagStart else SortCriteria.aSort[0].ulPropTag := PropTagEnd; end; hr := ContentTable.SortTable(@SortCriteria, TBL_BATCH); if failed(hr) and (hr <> MAPI_E_BUSY) then raise EMAPIError.CreateMAPI(ContentTable, hr); if (hr = MAPI_E_BUSY) then begin hr := ContentTable.WaitForCompletion(0, 2000, ULONG(nil^)); if hr = S_OK then // does not run either hr := ContentTable.SortTable(@SortCriteria, TBL_BATCH); if failed(hr) then raise EMAPIError.CreateMAPI(ContentTable, hr); end; hr := ContentTable.GetRowCount(0, ContentTableRowCount); If failed(hr) Then Raise Exception.Create(GetMapiError(ContentTable, hr)); If Integer(ContentTableRowCount) < 1 Then Exit; While True Do Begin RowSet := nil; hr := ContentTable.QueryRows(25, 0, RowSet); If Not failed(hr) And Assigned(RowSet) Then If RowSet.cRows = 0 Then Begin If Assigned(RowSet) Then FreePRows(RowSet); RowSet := nil; Break; End Else Begin For iCount := 0 To RowSet.cRows - 1 Do Begin FreeBusyData := ''; PP := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PropTagStart); If Assigned(PP) Then FreeBusyData := VarToStr(ConvertMAPIPropValueToVariant(PP)) + '; '; PP := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PropTagEnd); If Assigned(PP) Then FreeBusyData := FreeBusyData + VarToStr(ConvertMAPIPropValueToVariant(PP)) + '; '; PP := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PropTagBusyStatus); If Assigned(PP) Then FreeBusyData := FreeBusyData + TBusyStatusDesc [TBusyStatus(PP.Value.l)] + '; '; PP := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PR_SUBJECT); If Assigned(PP) Then FreeBusyData := FreeBusyData + StrPas(PP.Value.lpsz); List.Add(FreeBusyData); End; // for iCount := 0 to RowSet.cRows - 1 do End; If Assigned(RowSet) Then FreePRows(RowSet); RowSet := nil; End; // while True do Finally If Assigned(RowSet) Then FreePRows(RowSet); RowSet := nil; ContentTable := nil; End; End;
Download Request #16 as Compiled Application
Download Project (DELPHI 10.4) ZIP file
Source Code: In package