1

I have a program with a long running Active Directory query. I wanted to take advantage of VB.NET's Async technology, but when I converted my function to Async, I started getting an InvalidCastException. When I switch back, the error goes away. Why is Async causing an InvalidCastException for my COM object?

Exception Message:

Unable to cast COM object of type 'System.__ComObject' to interface type 'IDirectorySearch'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{109BA8EC-92F0-11D0-A790-00C04FD8D5A8}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

This must be happening somewhere within the core library, because I don't have any references to IDirectorySearch in my code. Indeed, the stack trace is not very illuminating:

enter image description here

Here's where the exception is thrown (according to the debugger):

Private Overloads Sub OnPropertyChanged(propertyName As String)
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

Here's the actual code. I've created two versions to demonstrate the code before (FindAll1) and after (FindAll2) async:

Private Async Sub FindAllButton_Click(sender As Object, e As RoutedEventArgs) Handles FindAllButton.Click
    'Me.Entries = Await FindAll1(Me.FilterText) ' Works
    Me.Entries = Await FindAll2(Me.FilterText) ' Doesn't Work
End Sub

Private Async Function FindAll1(filterText As String) As Task(Of IEnumerable(Of DirectoryEntryWrapper))
    Dim l_searcher As New DirectorySearcher()
    l_searcher.SizeLimit = Me.QuerySizeLimit
    l_searcher.Filter = filterText

    Me.IsLoading = True
    Dim l_results =
        From result In l_searcher.FindAll().Cast(Of SearchResult)()
        Select entry =
            New DirectoryEntryWrapper(result.GetDirectoryEntry(), AddressOf DirectoryEntryWrapperEventHandler)
        Order By entry.Name
    Me.IsLoading = False

    Return l_results
End Function

Private Async Function FindAll2(filterText As String) As Task(Of IEnumerable(Of DirectoryEntryWrapper))
    Dim l_searcher As New DirectorySearcher()
    l_searcher.SizeLimit = Me.QuerySizeLimit
    l_searcher.Filter = filterText

    Me.IsLoading = True
    Dim l_results =
        Await Task.Run(
            Function() _
                From result In l_searcher.FindAll().Cast(Of SearchResult)()
                Select entry =
                    New DirectoryEntryWrapper(result.GetDirectoryEntry(), AddressOf DirectoryEntryWrapperEventHandler)
                Order By entry.Name
            )
    Me.IsLoading = False

    Return l_results
End Function
JDB
  • 25,172
  • 5
  • 72
  • 123
  • 1
    This is a threading error, COM is trying to find a way to marshal the call so it is thread-safe but comes up empty. The final gasp is querying for IMarshal, that produced E_NOINTERFACE. Afaik this is accurate, ADSI is not thread-safe and I don't see a proxy/stub registered for this interface on my machine. Although it has never been close to a domain controller. You thus need to make sure that all calls are made on the same thread, blows a hole in your async approach. Consider [this approach](http://stackoverflow.com/a/21684059/17034) as an alternative. – Hans Passant Feb 26 '14 at 18:32
  • @HansPassant - Oh... thanks... but I'm confused. I thought the point of Async was that everything was executed on the same thread, just asynchronously. Clearly I still don't quite "get" .NET's async. – JDB Feb 26 '14 at 18:56
  • 1
    The point of asynchronous code is to prevent blocking your thread. That almost always requires code to run on another thread to get the job done, the code that's invoked by the Await expression. The point of Async is to let the *continuation* run on the same thread, the code that you wrote *after* the Await. – Hans Passant Feb 26 '14 at 19:03
  • @JDB: The problem is with `Task.Run`, which executes its delegate on the thread pool. – Stephen Cleary Feb 26 '14 at 20:14
  • @StephenCleary - Thanks. Hans' links sent me in the right direction. I'm currently looking at the [StaTaskScheduler](http://blogs.msdn.com/b/pfxteam/archive/2010/04/07/9990421.aspx) in ParallelExtensionsExtras, but it's looking like a bit too much overkill for my little project. I'm thinking of just accepting the lag at this point. – JDB Feb 26 '14 at 21:46
  • @JDB, check this for a potential solution: http://stackoverflow.com/q/21211998/1768303 – noseratio Feb 26 '14 at 23:38

0 Answers0