none
[E2010][EWSMA][C# / PS]LoadItemProperty failure RRS feed

  • Question

  • Hello fellow forum dwellers,

    I am currently in the process of creating a calendar sync using EWS. I am coding it in PS (as that's the language I actually can use fairly reliably), but afterwards compile it into an .exe file using PowerShell Studio 2012.

    Now if I run it precompiled, it works just fine, but afterwards it messes up a little at this function:

    function Get-EWSAppointments([Microsoft.Exchange.WebServices.Data.CalendarFolder]$calendarfolder,[System.DateTime]$from,[System.DateTime]$to,$service) { # Interrupt if no time-span exists if ($from -eq $to) { Write-Error -Message "Timespan can't be 0 seconds" } else { # Switch time if in wrong order if (($from.CompareTo($to)) -eq 1) { $Temp = $from
    $from = $to $to = $Temp } # Get Appointments and return them try { $Calendarview = new-object Microsoft.Exchange.WebServices.Data.CalendarView($from,$to) $Calendarview.PropertySet = [Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties $testcv = $Calendarview $Error.Clear() $results = $calendarfolder.FindAppointments($Calendarview) if (($service -ne $null) -and ($results.TotalCount -gt 0)) { $schema = [Microsoft.Exchange.WebServices.Data.ItemSchema]::Body $property = New-Object Microsoft.Exchange.WebServices.Data.PropertySet($schema) $property.BasePropertySet = [Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties $results = $service.LoadPropertiesForItems($results,$property) $script:tempResults = $results $ResultSet = @() if ($tempResults.Count -gt 0) { $i = 0 while ($i -lt $tempResults.Count) { $Temp = $tempResults.get_Item($i) if ($Temp.Result.value__ -eq 0){$ResultSet += $Temp.Item} $i++ } } return $ResultSet } else { return $results } } catch { $temp2 = $Error[0] if ($temp2.Exception.Message.Contains("Sie haben die maximale Anzahl von Objekten überschritten")) { # Calculate middle value between dates $middle = $to - $from $middle = $from.Add([System.TimeSpan]::FromSeconds(($middle.TotalSeconds / 2))) # Do the same call again twice, but this time with each only half the time $ResultSet = @() $ResultSet += Get-EWSAppointments -calendarfolder $calendarfolder -from $from -to $middle -service $service $ResultSet += Get-EWSAppointments -calendarfolder $calendarfolder -from $middle -to $to -service $service return $ResultSet } } } }

    This function returns all appointments, when run as a PowerShell script. However when I run it packaged as an executable, it will not work out. What happens then is one of two things:

    - if less than a thousand appointments are in the range, it returns $null
    - if more than a thousand appointments are in the range, it will return an array with a complicated number of $null-elements: (#appointments / 1000) * 2 - 1 (That number is more of a guesstimate than a proven result - if I truly understood the system, I could solve the problem myself).

    Anybody with an idea why that is happening?

    best regards and thanks in advance,
    Fred

    Monday, July 29, 2013 12:50 PM

Answers

  • You can't use the results of FindAppointments directly with LoadPropertiesFromItem (like you can with FindItems) have a read of http://social.msdn.microsoft.com/Forums/exchange/en-US/ce1e0527-e2db-490d-817e-83f586fb1b44/ews-managed-api-findappointments-loadpropertiesforitems. So you'll need to do something like

    $Calendarview = new-object Microsoft.Exchange.WebServices.Data.CalendarView($startdate,$enddate)
    $fiAppointments = $Calendar.FindAppointments($Calendarview)
    $type = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
    $type = $type.MakeGenericType("Microsoft.Exchange.WebServices.Data.Item" -as "Type")
    $ItemColl = [Activator]::CreateInstance($type)
    foreach($Item in $fiAppointments.Items){
    	$ItemColl.Add($Item)
    }
    [Void]$service.LoadPropertiesForItems($ItemColl,$psPropset)  
    foreach($Item in $ItemColl){  
    	$Item.Subject
    			#Process Item          
    }  

    If you want to have this compiled as an EXE you maybe better just writing a Console App in .net directly. (if you can use c# that's going to save you a lot of time)

    Cheers
    Glen

    • Marked as answer by FWN Tuesday, July 30, 2013 10:03 AM
    Tuesday, July 30, 2013 7:12 AM

All replies

  • alright, did a lot more research and logging errors and I think I got the culprit:

    $results = $calendarfolder.FindAppointments($Calendarview)

    this makes $results an IEnumerable of Appointments. Or at least it ought to. What actually appears to happen is that when run as an executable - for what reason ever - $results ends up as an Object[].

    $results = $service.LoadPropertiesForItems($results,$property)

    the LoadPropertiesForItems method however wants an IEnumerable of Items and can hardly be expected to convert an Object[] into an IEnumerable of Items.

    Any ideas why this is happening?

    Monday, July 29, 2013 3:07 PM
  • That is exactly what it is supposed to return.  If you enumerate into a variable that variable will be an object array 'Object[]'.


    ¯\_(ツ)_/¯

    Monday, July 29, 2013 4:10 PM
  • You can't use the results of FindAppointments directly with LoadPropertiesFromItem (like you can with FindItems) have a read of http://social.msdn.microsoft.com/Forums/exchange/en-US/ce1e0527-e2db-490d-817e-83f586fb1b44/ews-managed-api-findappointments-loadpropertiesforitems. So you'll need to do something like

    $Calendarview = new-object Microsoft.Exchange.WebServices.Data.CalendarView($startdate,$enddate)
    $fiAppointments = $Calendar.FindAppointments($Calendarview)
    $type = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
    $type = $type.MakeGenericType("Microsoft.Exchange.WebServices.Data.Item" -as "Type")
    $ItemColl = [Activator]::CreateInstance($type)
    foreach($Item in $fiAppointments.Items){
    	$ItemColl.Add($Item)
    }
    [Void]$service.LoadPropertiesForItems($ItemColl,$psPropset)  
    foreach($Item in $ItemColl){  
    	$Item.Subject
    			#Process Item          
    }  

    If you want to have this compiled as an EXE you maybe better just writing a Console App in .net directly. (if you can use c# that's going to save you a lot of time)

    Cheers
    Glen

    • Marked as answer by FWN Tuesday, July 30, 2013 10:03 AM
    Tuesday, July 30, 2013 7:12 AM
  • Hello Guys,

    thanks for the replies. Especially Glen, your method worked like a cursed charm. "cursed" as in "worked but had me going insane", though in a positive way: It would always crash when used in script format - as if cursed - but after compiling it into a console app it worked like a charm without a hitch.

    As you might imagine, that had me running a bit wild searching for 'the bug' as I'd always test it first by running it within my scripting tool (PowerShell Studio 2012 from Sapien).

    As for your advice regarding C#: You're very likely right in that it would be better all around, but I can only use the skills I have and don't have the time to get into C# right now (I'll fix that deficit in the near future though - if I can handle EWS in PowerShell reasonably well, I guess completing the transition can't be all that difficult). The Sapien environment I use however compiles it for me, so the result is alright so far.

    Thanks again,
    Fred

    P.s.: In case anybody cares, here's the final and working code:

    function Get-EWSAppointments
    {
    	Param(
    		[Microsoft.Exchange.WebServices.Data.CalendarFolder]$calendarfolder,
    		[System.DateTime]$from,
    		[System.DateTime]$to,
    		[Microsoft.Exchange.WebServices.Data.ExchangeService]$service
    	)
    	
    	# Interrupt if no time-span exists
    	if ($from -eq $to)
    	{
    		Write-Error -Message "Timespan can't be 0 seconds"
    		Return $null
    	}
    	
    	else 
    	{
    		# Switch time if in wrong order
    		if (($from.CompareTo($to)) -eq 1)
    		{
    			$Temp = $from
    			$from = $to
    			$to = $Temp
    		}
    		
    		# Setup variables
    		$test = $true
    		$appointments = @()
    		
    		# Keep doing it until you have it all
    		while ($test)
    		{
    			# Get Items without any information beyond ID
    			$Calendarview = new-object Microsoft.Exchange.WebServices.Data.CalendarView($from,$to,1000)
    			$property = New-Object Microsoft.Exchange.WebServices.Data.PropertySet
    			$Calendarview.PropertySet = $property
    			$results = $folder.FindAppointments($Calendarview)
    						
    			# Convert Items to other format
    			$type = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
    			$type = $type.MakeGenericType("Microsoft.Exchange.WebServices.Data.Item" -as "Type")
    			$Items = [Activator]::CreateInstance($type)
    			foreach($item in $results.Items)
    			{
    				[Microsoft.Exchange.WebServices.Data.Item]$item = $item
    				$Items.Add($item)
    			}
    			
    			# Load the relevant properties
    			$property.Add([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::Body)
    			$property.Add([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::Subject)
    			$property.Add([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::Start)
    			$property.Add([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::End)
    			$service.LoadPropertiesForItems($Items,$property) | Out-Null
    			
    			# Add the items to the storage array
    			foreach ($item in $results.Items){$appointments += $item}
    			
    			# if more is available, load the next set of appointments
    			if ($results.MoreAvailable)
    			{
    				$from = $appointments[($appointments.Count - 1)].Start
    			}
    			
    			# If no more is available, end iteration
    			else
    			{
    				$test = $false
    			}
    		}
    		
    		Return $appointments
    	}
    }

    Tuesday, July 30, 2013 11:39 AM