public SearchCandidateResponse(SearchCandidateProviderProfile provider, string queryString, string[] candidates, TimeSpan elapsed, Exception exception = null) { Provider = provider; QueryString = queryString; Candidates = candidates; Elaplsed = elapsed; Exception = exception; }
/// <summary> /// Starts initialization of SearchCandidateProvider with <paramref name="profile"/>. /// </summary> /// <remarks>The caller must ensure that the IME settings is kept to be per-thread mode from the method is called until the returned task is completed.</remarks> /// <param name="profile">The profile information with which SearchCandidateProvider is instantiated.</param> /// <returns>A task object which represents an asynchronous operation to initialize a SearchCandidateProvider object. /// The resolved value can be null if any error occurs.</returns> static public Task <SearchCandidateProvider> FromProfile(SearchCandidateProviderProfile profile) { if (!(GetThreadLocalInputSettings(defaultValue: true))) { throw new InvalidOperationException("Aborted because this operation is dangerous while SPI_GETTHREADLOCALINPUTSETTINGS is returning true."); } if (profile == null) { throw new ArgumentException("null is not allowed", "profile"); } return(CreateInternal(profile)); }
static private Task <SearchCandidateProvider> CreateInternal(SearchCandidateProviderProfile profile) { var createComplete = new TaskCompletionSource <SearchCandidateProvider>(); var thread = new Thread(() => SearchCandidateProvider.ThreadMain(profile, createComplete)); thread.SetApartmentState(ApartmentState.STA); thread.Start(); var threadName = "Query Thread" + (profile == null ? "" : ": " + profile.Name); thread.Name = threadName; return(createComplete.Task); }
/// <summary> /// Starts initialization of SearchCandidateProvider with <paramref name="profile"/>. /// </summary> /// <remarks>The caller must ensure that the IME settings is kept to be per-thread mode from the method is called until the returned task is completed.</remarks> /// <param name="profile">The profile information with which SearchCandidateProvider is instantiated.</param> /// <returns>A task object which represents an asynchronous operation to initialize a SearchCandidateProvider object. /// The resolved value can be null if any error occurs.</returns> public static Task<SearchCandidateProvider> FromProfile(SearchCandidateProviderProfile profile) { if (!(GetThreadLocalInputSettings(defaultValue: true))) { throw new InvalidOperationException("Aborted because this operation is dangerous while SPI_GETTHREADLOCALINPUTSETTINGS is returning true."); } if (profile == null) { throw new ArgumentException("null is not allowed", "profile"); } return CreateInternal(profile); }
internal static void ThreadMain(SearchCandidateProviderProfile profile, TaskCompletionSource <SearchCandidateProvider> createComplete) { using (var mainReleaser = new ComReleaser()) { try { var profileMgr = mainReleaser.CreateComObject <ITfInputProcessorProfileMgr>(); if (profile == null) { var keyboardGuid = Guids.GUID_TFCAT_TIP_KEYBOARD; var activeProfile = default(TF_INPUTPROCESSORPROFILE); if (!profileMgr.GetActiveProfile(ref keyboardGuid, out activeProfile)) { return; } if (activeProfile.dwProfileType == TF_PROFILETYPE.TF_PROFILETYPE_KEYBOARDLAYOUT) { // This is not a TIP. return; } profile = new SearchCandidateProviderProfile(activeProfile.langid.ToCultureInfo(), activeProfile.clsid, activeProfile.guidProfile); } var name = profile.Name; var langid = profile.LangId; var clsid = profile.Clsid; var profileGuid = profile.ProfileGuid; if (!profileMgr.ActivateProfile(TF_PROFILETYPE.TF_PROFILETYPE_INPUTPROCESSOR, langid, ref clsid, ref profileGuid, IntPtr.Zero, TF_IPPMF.TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE)) { return; } var threadMgr = mainReleaser.CreateComObject <ITfThreadMgr2>(); var clientId = TextFrameworkDeclarations.TF_CLIENTID_NULL; if (!threadMgr.ActivateEx(out clientId, default(TF_TMAE))) { return; } mainReleaser.RegisterCleanup(() => threadMgr.Deactivate()); var functionProvider = mainReleaser.ReceiveObject((out ITfFunctionProvider _) => threadMgr.GetFunctionProvider(ref clsid, out _)); if (functionProvider == null) { return; } if (string.IsNullOrEmpty(name)) { functionProvider.GetDescription(out name); } var guidNull = new Guid(); var iid = typeof(ITfFnSearchCandidateProvider).GUID; var searchCandidateProvider = mainReleaser.ReceiveObject((out object _) => functionProvider.GetFunction(ref guidNull, ref iid, out _)) as ITfFnSearchCandidateProvider; if (searchCandidateProvider == null) { return; } if (string.IsNullOrEmpty(name)) { searchCandidateProvider.GetDisplayName(out name); } // Refresh the |profile| with the latest |name|. profile = new SearchCandidateProviderProfile(profile.LangId.ToCultureInfo(), profile.Clsid, profile.ProfileGuid, name); // It's time to enter the event loop. using (var queue = new BlockingCollection <RequestQueueItem>()) { createComplete.SetResult(new SearchCandidateProvider { RequestQueue = queue, Profile = profile }); foreach (var task in queue.GetConsumingEnumerable()) { using (var releaser = new ComReleaser()) { var stopWatch = Stopwatch.StartNew(); var response = task.Response; try { var result = searchCandidateProvider.GetSearchCandidates(task.Query).ToArray(); response.SetResult(new SearchCandidateResponse(profile, task.Query, result, stopWatch.Elapsed)); } catch (Exception e) { if (!response.Task.IsCompleted) { response.SetResult(new SearchCandidateResponse(profile, task.Query, Enumerable.Empty <string>().ToArray(), stopWatch.Elapsed, e)); } } finally { if (!response.Task.IsCompleted) { response.SetResult(new SearchCandidateResponse(profile, task.Query, Enumerable.Empty <string>().ToArray(), stopWatch.Elapsed)); } } } } } } catch (Exception e) { if (!createComplete.Task.IsCompleted) { createComplete.SetException(e); } } finally { if (!createComplete.Task.IsCompleted) { createComplete.SetResult(null); } } } }
internal static void ThreadMain(SearchCandidateProviderProfile profile, TaskCompletionSource<SearchCandidateProvider> createComplete) { using (var mainReleaser = new ComReleaser()) { try { var profileMgr = mainReleaser.CreateComObject<ITfInputProcessorProfileMgr>(); if (profile == null) { var keyboardGuid = Guids.GUID_TFCAT_TIP_KEYBOARD; var activeProfile = default(TF_INPUTPROCESSORPROFILE); if (!profileMgr.GetActiveProfile(ref keyboardGuid, out activeProfile)) { return; } if (activeProfile.dwProfileType == TF_PROFILETYPE.TF_PROFILETYPE_KEYBOARDLAYOUT) { // This is not a TIP. return; } profile = new SearchCandidateProviderProfile(activeProfile.langid.ToCultureInfo(), activeProfile.clsid, activeProfile.guidProfile); } var name = profile.Name; var langid = profile.LangId; var clsid = profile.Clsid; var profileGuid = profile.ProfileGuid; if (!profileMgr.ActivateProfile(TF_PROFILETYPE.TF_PROFILETYPE_INPUTPROCESSOR, langid, ref clsid, ref profileGuid, IntPtr.Zero, TF_IPPMF.TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE)) { return; } var threadMgr = mainReleaser.CreateComObject<ITfThreadMgr2>(); var clientId = TextFrameworkDeclarations.TF_CLIENTID_NULL; if (!threadMgr.ActivateEx(out clientId, default(TF_TMAE))) { return; } mainReleaser.RegisterCleanup(() => threadMgr.Deactivate()); var functionProvider = mainReleaser.ReceiveObject((out ITfFunctionProvider _) => threadMgr.GetFunctionProvider(ref clsid, out _)); if (functionProvider == null) { return; } if (string.IsNullOrEmpty(name)) { functionProvider.GetDescription(out name); } var guidNull = new Guid(); var iid = typeof(ITfFnSearchCandidateProvider).GUID; var searchCandidateProvider = mainReleaser.ReceiveObject((out object _) => functionProvider.GetFunction(ref guidNull, ref iid, out _)) as ITfFnSearchCandidateProvider; if (searchCandidateProvider == null) { return; } if (string.IsNullOrEmpty(name)) { searchCandidateProvider.GetDisplayName(out name); } // Refresh the |profile| with the latest |name|. profile = new SearchCandidateProviderProfile(profile.LangId.ToCultureInfo(), profile.Clsid, profile.ProfileGuid, name); // It's time to enter the event loop. using (var queue = new BlockingCollection<RequestQueueItem>()) { createComplete.SetResult(new SearchCandidateProvider { RequestQueue = queue, Profile = profile }); foreach (var task in queue.GetConsumingEnumerable()) { using (var releaser = new ComReleaser()) { var stopWatch = Stopwatch.StartNew(); var response = task.Response; try { var result = searchCandidateProvider.GetSearchCandidates(task.Query).ToArray(); response.SetResult(new SearchCandidateResponse(profile, task.Query, result, stopWatch.Elapsed)); } catch (Exception e) { if (!response.Task.IsCompleted) { response.SetResult(new SearchCandidateResponse(profile, task.Query, Enumerable.Empty<string>().ToArray(), stopWatch.Elapsed, e)); } } finally { if (!response.Task.IsCompleted) { response.SetResult(new SearchCandidateResponse(profile, task.Query, Enumerable.Empty<string>().ToArray(), stopWatch.Elapsed)); } } } } } } catch (Exception e) { if (!createComplete.Task.IsCompleted) { createComplete.SetException(e); } } finally { if (!createComplete.Task.IsCompleted) { createComplete.SetResult(null); } } } }
private static Task<SearchCandidateProvider> CreateInternal(SearchCandidateProviderProfile profile) { var createComplete = new TaskCompletionSource<SearchCandidateProvider>(); var thread = new Thread(() => SearchCandidateProvider.ThreadMain(profile, createComplete)); thread.SetApartmentState(ApartmentState.STA); thread.Start(); var threadName = "Query Thread" + (profile == null ? "" : ": " + profile.Name); thread.Name = threadName; return createComplete.Task; }