Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Důležité
Úlohy na pozadí používající Windows App SDK BackgroundTaskBuilder vyžadují, aby vaše aplikace byla zabalená pomocí MSIX. Pro WPF (Windows Presentation Foundation) nebo model Windows Forms aplikace nasazené bez balení MSIX použijte místo toho Task Scheduler nebo .NET Worker Services.
Tento článek obsahuje přehled použití úloh na pozadí a popisuje, jak vytvořit nový úkol na pozadí v aplikaci WinUI 3 nebo jiné aplikaci s balíčkem MSIX (včetně WPF (Windows Presentation Foundation) a model Windows Forms). Informace o migraci vašich UWP aplikací s úlohami na pozadí na WinUI najdete v tématu Windows App SDK Strategie migrace úlohy na pozadí.
Úlohy na pozadí jsou komponenty aplikace, které běží na pozadí bez uživatelského rozhraní. Můžou provádět akce, jako je stahování souborů, synchronizace dat, odesílání oznámení nebo aktualizace dlaždic. Můžou se aktivovat různými událostmi, jako jsou čas, změny systému, akce uživatelů nebo nabízená oznámení. Tyto úlohy se můžou spustit, když dojde k odpovídající aktivační události, i když aplikace není ve spuštěném stavu.
Implementace úloh na pozadí se u aplikací UPW a WinUI liší. Informace o migraci vašich UWP aplikací s úlohami na pozadí na WinUI najdete v dokumentu Windows App SDK Strategie migrace úloh na pozadí.
Plánovač úloh pomáhá desktopovým aplikacím dosáhnout stejné funkce, které poskytuje BackgroundTaskBuilder v aplikacích pro UPW. Další podrobnosti o implementacích používajících TaskScheduler najdete tady.
Zaregistrujte úlohu na pozadí
Pomocí třídy BackgroundTaskBuilder, která je součástí Windows App SDK, zaregistrujte úlohu na pozadí, která používá plně důvěryhodnou komponentu COM.
Následující příklad ukazuje, jak zaregistrovat úlohu na pozadí pomocí jazyka C++. V ukázce githubu Windows App SDK můžete tento registrační kód zobrazit v MainWindow.Xaml.cpp
auto access = co_await BackgroundExecutionManager::RequestAccessAsync();
// Unregister all existing background task registrations
auto allRegistrations = BackgroundTaskRegistration::AllTasks();
for (const auto& taskPair : allRegistrations)
{
IBackgroundTaskRegistration task = taskPair.Value();
task.Unregister(true);
}
//Using the Windows App SDK API for BackgroundTaskBuilder
winrt::Microsoft::Windows::ApplicationModel::Background::BackgroundTaskBuilder builder;
builder.Name(L"TimeZoneChangeTask");
SystemTrigger trigger = SystemTrigger(SystemTriggerType::TimeZoneChange, false);
auto backgroundTrigger = trigger.as<IBackgroundTrigger>();
builder.SetTrigger(backgroundTrigger);
builder.AddCondition(SystemCondition(SystemConditionType::InternetAvailable));
builder.SetTaskEntryPointClsid(__uuidof(winrt::BackgroundTaskInProcCPP::BackgroundTask));
try
{
builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
Následující příklad ukazuje, jak zaregistrovat úlohu na pozadí pomocí jazyka C#. V ukázce Windows App SDK GitHubu uvidíte tento registrační kód v MainWindow.Xaml.cpp.
await BackgroundExecutionManager.RequestAccessAsync();
// Unregister all existing background task registrations
var allRegistrations = BackgroundTaskRegistration.AllTasks;
foreach (var taskPair in allRegistrations)
{
IBackgroundTaskRegistration task = taskPair.Value;
task.Unregister(true);
}
//Using the Windows App SDK API for BackgroundTaskBuilder
var builder = new Microsoft.Windows.ApplicationModel.Background.BackgroundTaskBuilder();
builder.Name = "TimeZoneChangeTask";
var trigger = new SystemTrigger(SystemTriggerType.TimeZoneChange, false);
var backgroundTrigger = trigger as IBackgroundTrigger;
builder.SetTrigger(backgroundTrigger);
builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
builder.SetTaskEntryPointClsid(typeof(BackgroundTask).GUID);
builder.Register();
Všimněte si, že volání SetEntryPointClsid metoda přebírá jako argument GUID pro třídu definovanou aplikací, která implementuje IBackgroundTask. Toto rozhraní je popsáno v části Implementace IBackgroundTask dále v tomto článku.
Osvědčené postupy pro registraci úloh na pozadí
Při registraci úloh na pozadí použijte následující osvědčené postupy.
Před registrací úloh na pozadí zavolejte BackgroundExecutionManager.RequestAccessAsync .
Neregistrujte úlohu na pozadí několikrát. Buď ověřte, že před registrací ještě není zaregistrovaný úkol na pozadí, nebo jako v ukázce Windows App SDK zrušit registraci všech úloh na pozadí a potom úkoly znovu zaregistrovat. Pomocí třídy BackgroundTaskRegistration můžete dotazovat na existující úlohy na pozadí.
Pomocí vlastnosti BackgroundTaskBuilder.Name zadejte smysluplný název úlohy na pozadí pro zjednodušení ladění a údržby.
Naimplementujte IBackgroundTask
IBackgroundTask je rozhraní, které zveřejňuje jednu metodu Run, která se spustí při vyvolání úlohy na pozadí. Aplikace, které používají úlohy na pozadí, musí obsahovat třídu, která implementuje IBackgroundTask.
Následující příklad ukazuje, jak implementovat IBackgroundTask pomocí jazyka C++. V ukázce Windows App SDK GitHubu uvidíte tento registrační kód v BackgroundTask.cpp.
void BackgroundTask::Run(_In_ IBackgroundTaskInstance taskInstance)
{
// Get deferral to indicate not to kill the background task process as soon as the Run method returns
m_deferral = taskInstance.GetDeferral();
m_progress = 0;
taskInstance.Canceled({ this, &BackgroundTask::OnCanceled });
// Calling a method on the Window to inform that the background task is executed
winrt::Microsoft::UI::Xaml::Window window = winrt::BackgroundTaskBuilder::implementation::App::Window();
m_mainWindow = window.as<winrt::BackgroundTaskBuilder::IMainWindow>();
Windows::Foundation::TimeSpan period{ std::chrono::seconds{2} };
m_periodicTimer = Windows::System::Threading::ThreadPoolTimer::CreatePeriodicTimer([this, lifetime = get_strong()](Windows::System::Threading::ThreadPoolTimer timer)
{
if (!m_cancelRequested && m_progress < 100)
{
m_progress += 10;
}
else
{
m_periodicTimer.Cancel();
// Indicate that the background task has completed.
m_deferral.Complete();
if (m_cancelRequested) m_progress = -1;
}
m_mainWindow.BackgroundTaskExecuted(m_progress);
}, period);
}
void BackgroundTask::OnCanceled(_In_ IBackgroundTaskInstance /* taskInstance */, _In_ BackgroundTaskCancellationReason /* cancelReason */)
{
m_cancelRequested = true;
}
Následující příklad ukazuje, jak implementovat IBackgroundTask pomocí jazyka C#. V ukázce Windows App SDK GitHubu uvidíte tento registrační kód v BackgroundTask.cpp.
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("00001111-aaaa-2222-bbbb-3333cccc4444")]
[ComSourceInterfaces(typeof(IBackgroundTask))]
public class BackgroundTask : IBackgroundTask
{
/// <summary>
/// This method is the main entry point for the background task. The system will believe this background task
/// is complete when this method returns.
/// </summary>
[MTAThread]
public void Run(IBackgroundTaskInstance taskInstance)
{
// Get deferral to indicate not to kill the background task process as soon as the Run method returns
_deferral = taskInstance.GetDeferral();
// Wire the cancellation handler.
taskInstance.Canceled += this.OnCanceled;
// Set the progress to indicate this task has started
taskInstance.Progress = 0;
_periodicTimer = ThreadPoolTimer.CreatePeriodicTimer(new TimerElapsedHandler(PeriodicTimerCallback), TimeSpan.FromSeconds(1));
}
// Simulate the background task activity.
private void PeriodicTimerCallback(ThreadPoolTimer timer)
{
if ((_cancelRequested == false) && (_progress < 100))
{
_progress += 10;
}
else
{
if (_cancelRequested) _progress = -1;
if (_periodicTimer != null) _periodicTimer.Cancel();
// Indicate that the background task has completed.
if (_deferral != null) _deferral.Complete();
}
BackgroundTaskBuilder.MainWindow.taskStatus(_progress);
}
/// <summary>
/// This method is signaled when the system requests the background task be canceled. This method will signal
/// to the Run method to clean up and return.
/// </summary>
[MTAThread]
public void OnCanceled(IBackgroundTaskInstance taskInstance, BackgroundTaskCancellationReason cancellationReason)
{
// Handle cancellation operations and flag the task to end
_cancelRequested = true;
}
Osvědčené postupy pro implementaci IBackgroundTask
Při implementaci IBackgroundTask použijte následující osvědčené postupy.
- Pokud úloha na pozadí provede asynchronní operace, získejte odložený objekt voláním GetDeferral u objektu ITaskInstance předaného do Run. Tím se zabrání tomu, aby se hostitel úloh na pozadí,
backgroundtaskhost.exe, předčasně ukončil před dokončením operací. Po dokončení všech asynchronních úloh uvolněte odložení. - Udržujte úkoly co nejlehčí. Systém může ukončit dlouhotrvající úlohy a nedoporučuje se.
- Protokolování slouží k zaznamenání podrobností o spuštění pro řešení potíží.
- Další osvědčené postupy pro implementaci úloh na pozadí najdete v tématu Pokyny pro úlohy na pozadí.
Deklarace rozšíření aplikace úloh na pozadí v manifestu aplikace
Pokud chcete zaregistrovat úlohu na pozadí v systému, musíte deklarovat rozšíření aplikace v souboru aplikace Package.appxmanifest , když je aplikace nainstalovaná, a poskytnout informace, které systém potřebuje ke spuštění úlohy na pozadí.
Abyste mohli úspěšně zaregistrovat úlohu na pozadí při instalaci aplikace, musíte do manifestu aplikace přidat rozšíření s kategorií windows.backgroundTasks . Aplikace C# musí zadat hodnotu atributu EntryPoint "Microsoft.Windows.ApplicationModel.Background.UniversalBGTask.Task". U aplikací C++ se tato možnost přidá automaticky nastavením WindowsAppSDKBackgroundTask na true v souboru project.
Musíte také deklarovat com:Extension s hodnotou kategorie "windows.comServer". Je nutné zadat atribut LaunchAndActivationPermission v elementu com:ExeServer, aby se explicitně udělující backgroundtaskhost.exe procesu oprávnění k vyvolání třídy COM. Informace o formátu tohoto řetězce naleznete v tématu Formát řetězce popisovače zabezpečení.
Ujistěte se, že ID třídy, které zadáte v elementu com:Class , odpovídá ID třídy pro vaši implementaci IBackgroundTask.
Následující příklad ukazuje syntaxi deklarací rozšíření aplikace v souboru manifestu aplikace, aby systém mohl zjišťovat a spouštět úlohu na pozadí. Úplný soubor manifestu aplikace pro úlohu na pozadí na github najdete v tématu Package.appxmanifest.
<Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="Microsoft.Windows.ApplicationModel.Background.UniversalBGTask.Task">
<BackgroundTasks>
<Task Type="general"/>
</BackgroundTasks>
</Extension>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="BackgroundTaskBuilder.exe" DisplayName="BackgroundTask"
LaunchAndActivationPermission="O:PSG:BUD:(A;;11;;;IU)(A;;11;;;S-1-15-2-1)S:(ML;;NX;;;LW)">
<com:Class Id="00001111-aaaa-2222-bbbb-3333cccc4444" DisplayName="BackgroundTask" />
</com:ExeServer>
</com:ComServer>
</com:Extension>
</Extensions>
Registrace serveru COM pro úlohu na pozadí
Registrace serveru COM zajišťuje, že systém ví, jak instancovat třídu úlohy na pozadí, když je CoCreateInstance volána backgroundtaskhost.exe. Pro úlohu na pozadí je nutné zaregistrovat objekt pro vytváření tříd COM voláním CoRegisterClassObject, nebo COM aktivace selže.
Registrace serveru COM v jazyce C++
Následující příklad ukazuje pomocnou funkci jazyka C++ , RegisterBackgroundTaskFactory , která registruje objekt pro vytváření tříd pro třídu, která implementuje IBackgroundTask. V tomto příkladu se tato třída nazývá BackgroundTask. V destruktoru pomocné třídy je volána funkce CoRevokeClassObject pro odvolání registrace class factory.
Tuto pomocnou třídu můžete zobrazit v ukázkovém úložišti v souboru RegisterForCOM.cpp.
hresult RegisterForCom::RegisterBackgroundTaskFactory()
{
hresult hr;
try
{
com_ptr<IClassFactory> taskFactory = make<BackgroundTaskFactory>();
check_hresult(CoRegisterClassObject(__uuidof(BackgroundTask),
taskFactory.detach(),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&ComRegistrationToken));
OutputDebugString(L"COM Registration done");
hr = S_OK;
}
CATCH_RETURN();
}
RegisterForCom::~RegisterForCom()
{
if (ComRegistrationToken != 0)
{
CoRevokeClassObject(ComRegistrationToken);
}
}
V případě registrace serveru v proc v jazyce C++ zavolejte pomocnou třídu pro registraci objektu pro vytváření tříd v rámci metody Application.OnLaunched . Podívejte se na volání pomocné metody v ukázkovém úložišti v App.xaml.cpp.
void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
{
window = make<MainWindow>();
window.Activate();
// Start COM server for the COM calls to complete
comRegister.RegisterBackgroundTaskFactory();
}
V případě úloh na pozadí mimo proces musí být registrace serveru COM provedena během procesu spuštění. Můžete vidět volání pomocné třídy v App.xaml.cpp.
int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR lpCmdLine, _In_ int)
{
if (std::wcsncmp(lpCmdLine, RegisterForCom::RegisterForComToken, sizeof(RegisterForCom::RegisterForComToken)) == 0)
{
winrt::init_apartment(winrt::apartment_type::multi_threaded);
RegisterForCom comRegister;
// Start COM server and wait for the COM calls to complete
comRegister.RegisterAndWait(__uuidof(BackgroundTask));
OutputDebugString(L"COM Server Shutting Down");
}
else
{
// put your fancy code somewhere here
::winrt::Microsoft::UI::Xaml::Application::Start(
[](auto&&)
{
::winrt::make<::winrt::BackgroundTaskBuilder::implementation::App>();
});
}
return 0;
}
Registrace serveru COM v jazyce C#
Následující příklad ukazuje pomocnou funkci jazyka C# CreateInstance , která registruje objekt pro vytváření tříd pro třídu, která implementuje IBackgroundTask. V tomto příkladu se tato třída nazývá BackgroundTask. Pomocná třída používá LibraryImportAttribute k přístupu na nativní metody registrace COM z jazyka C#. Další informace najdete v tématu Generování zdroje pro vyvolání platformy. Implementaci pomocné třídy můžete zobrazit v ukázkovém úložišti v ComServer.cs.
static partial class ComServer
{
[LibraryImport("ole32.dll")]
public static partial int CoRegisterClassObject(
ref Guid classId,
[MarshalAs(UnmanagedType.Interface)] IClassFactory objectAsUnknown,
uint executionContext,
uint flags,
out uint registrationToken);
[LibraryImport("ole32.dll")]
public static partial int CoRevokeObject(out uint registrationToken);
public const uint CLSCTX_LOCAL_SERVER = 4;
public const uint REGCLS_MULTIPLEUSE = 1;
public const uint S_OK = 0x00000000;
public const uint CLASS_E_NOAGGREGATION = 0x80040110;
public const uint E_NOINTERFACE = 0x80004002;
public const string IID_IUnknown = "00000000-0000-0000-C000-000000000046";
public const string IID_IClassFactory = "00000001-0000-0000-C000-000000000046";
[GeneratedComInterface]
[Guid(IID_IClassFactory)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public partial interface IClassFactory
{
[PreserveSig]
uint CreateInstance(IntPtr objectAsUnknown, in Guid interfaceId, out IntPtr objectPointer);
[PreserveSig]
uint LockServer([MarshalAs(UnmanagedType.Bool)] bool Lock);
}
[GeneratedComClass]
internal sealed partial class BackgroundTaskFactory : IClassFactory
{
public uint CreateInstance(IntPtr objectAsUnknown, in Guid interfaceId, out IntPtr objectPointer)
{
if (objectAsUnknown != IntPtr.Zero)
{
objectPointer = IntPtr.Zero;
return CLASS_E_NOAGGREGATION;
}
if ((interfaceId != typeof(BackgroundTask).GUID) && (interfaceId != new Guid(IID_IUnknown)))
{
objectPointer = IntPtr.Zero;
return E_NOINTERFACE;
}
objectPointer = MarshalInterface<IBackgroundTask>.FromManaged(new BackgroundTask());
return S_OK;
}
public uint LockServer(bool lockServer) => S_OK;
}
}
V případě úloh na pozadí v C# se registrace COM provádí při spuštění aplikace v konstruktoru objektu Application. Volání pomocné metody uvidíte v ukázkovém úložišti v App.xaml.cs.
public App()
{
this.InitializeComponent();
Guid taskGuid = typeof(BackgroundTask).GUID;
ComServer.CoRegisterClassObject(ref taskGuid,
new ComServer.BackgroundTaskFactory(),
ComServer.CLSCTX_LOCAL_SERVER,
ComServer.REGCLS_MULTIPLEUSE,
out _RegistrationToken);
}
~App()
{
ComServer.CoRevokeObject(out _RegistrationToken);
}
V případě úloh mimo proces v jazyce C# je nutné provést registraci COM při spuštění aplikace. Aby to bylo možné, je nutné zakázat výchozí XAML-generovaný vstupní bod aktualizací projektového souboru vaší aplikace.
Ve výchozím projektové šabloně je vstupní bod metody Main automaticky generován kompilátorem. Tento příklad zakáže automatické generování main, aby bylo možné spustit potřebný aktivační kód při spuštění.
- V Průzkumník řešení klikněte pravým tlačítkem myši na ikonu projektu a vyberte Upravit projektový soubor.
- Do elementu PropertyGroup přidejte následující podřízený prvek, který zakáže automaticky vygenerovanou hlavní funkci.
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
Volání pomocné třídy pro registraci třídy COM v ukázkovém úložišti můžete zobrazit v Program.cs
public class Program
{
static private uint _RegistrationToken;
static private ManualResetEvent _exitEvent = new ManualResetEvent(false);
static void Main(string[] args)
{
if (args.Contains("-RegisterForBGTaskServer"))
{
Guid taskGuid = typeof(BackgroundTask).GUID;
ComServer.CoRegisterClassObject(ref taskGuid,
new ComServer.BackgroundTaskFactory(),
ComServer.CLSCTX_LOCAL_SERVER,
ComServer.REGCLS_MULTIPLEUSE,
out _RegistrationToken);
// Wait for the exit event to be signaled before exiting the program
_exitEvent.WaitOne();
}
else
{
App.Start(p => new App());
}
}
public static void SignalExit()
{
_exitEvent.Set();
}
}
Související obsah
- Pokyny pro úlohy na pozadí v aplikacích pro UPW
- strategie migrace úloh na pozadí
Windows developer