Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wskazówka
Metoda BackgroundTaskBuilder.SetTaskEntryPointClsid jest dostępna w systemie Windows 10 w wersji 2004.
Uwaga / Notatka
Ten scenariusz dotyczy tylko spakowanych aplikacji Win32. Aplikacje platformy UWP napotkają błędy próbujące zaimplementować ten scenariusz.
ważne interfejsy API
Utwórz klasę zadań w tle COM i zarejestruj ją, aby była uruchamiana w Twojej zaufanej, spakowanej aplikacji Win32 w odpowiedzi na wyzwalacze. Możesz użyć zadań w tle, aby zapewnić funkcjonalność, gdy aplikacja jest zawieszona lub nie działa. W tym temacie pokazano, jak utworzyć i zarejestrować zadanie w tle, które można uruchomić w procesie aplikacji pierwszego planu lub w innym procesie.
Utwórz klasę Zadania w tle
Kod w tle można uruchomić, pisząc klasy implementujące interfejs IBackgroundTask . Ten kod jest uruchamiany, gdy określone zdarzenie jest wyzwalane przy użyciu, na przykład SystemTrigger lub TimeTrigger.
W poniższych krokach pokazano, jak napisać nową klasę, która implementuje interfejs IBackgroundTask i dodać ją do głównego procesu.
- Zapoznaj się z tymi instrukcjami, aby odwołać się do interfejsów API WinRT w spakowanym rozwiązaniu aplikacji Win32. Jest to wymagane do korzystania z IBackgroundTask i powiązanych API.
- W tej nowej klasie zaimplementuj interfejs IBackgroundTask. Metoda IBackgroundTask.Run jest wymaganym punktem wejścia, który będzie wywoływany po wyzwoleniu określonego zdarzenia; ta metoda jest wymagana w każdym zadaniu w tle.
Uwaga / Notatka
Sama klasa zadań w tle — i wszystkie inne klasy w projekcie zadań w tle — muszą być publiczne.
Poniższy przykładowy kod przedstawia podstawową klasę zadania w tle, która zlicza liczby pierwsze i zapisuje je w pliku, dopóki nie zostanie to zażądane anulowanie.
Przykład C++/WinRT implementuje klasę zadań w tle jako coklasę COM .
Przykładowy kod zadania w tle
using System;
using System.IO; // Path
using System.Threading; // EventWaitHandle
using System.Collections.Generic; // Queue
using System.Runtime.InteropServices; // Guid, RegistrationServices
using Windows.ApplicationModel.Background; // IBackgroundTask
namespace PackagedWinMainBackgroundTaskSample
{
// {14C5882B-35D3-41BE-86B2-5106269B97E6} is GUID to register this task with BackgroundTaskBuilder. Generate a random GUID before implementing.
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("14C5882B-35D3-41BE-86B2-5106269B97E6")]
[ComSourceInterfaces(typeof(IBackgroundTask))]
public class SampleTask : IBackgroundTask
{
private volatile int cleanupTask; // flag used to indicate to Run method that it should exit
private Queue<int> numbersQueue; // the data structure holding the set of primes in memory
private const int maxPrimeNumber = 1000000000; // the number up to which task will attempt to calculate primes
private const int queueDepthToWrite = 10; // how frequently this task should flush its queue of primes
private const string numbersQueueFile = "numbersQueue.log"; // the file to write to relative to AppData
public SampleTask()
{
cleanupTask = 0;
numbersQueue = new Queue<int>(queueDepthToWrite);
}
/// <summary>
/// This method writes all the numbers in the current queue to the specified file.
/// </summary>
private void FlushNumbersToFile(Queue<int> queueToWrite)
{
string logPath = Path.Combine(ApplicationData.Current.LocalFolder.Path,
System.Diagnostics.Process.GetCurrentProcess().ProcessName);
if (!Directory.Exists(logPath))
{
Directory.CreateDirectory(logPath);
}
logPath = Path.Combine(logPath, numbersQueueFile);
const string delimiter = ", ";
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
// convert the queue to a list of comma separated values.
string stringToWrite = String.Join(delimiter, queueToWrite);
// Add the comma at the end.
stringToWrite += delimiter;
File.AppendAllText(logPath, stringToWrite);
}
/// <summary>
/// This method determines if the specified number is a prime number.
/// </summary>
private bool IsPrimeNumber(int dividend)
{
bool isPrime = true;
for (int divisor = dividend - 1; divisor > 1; divisor -= 1)
{
if ((dividend % divisor) == 0)
{
isPrime = false;
break;
}
}
return isPrime;
}
/// <summary>
/// Given the current number, this method calculates the next prime number (excluding the specified number).
/// </summary>
private int GetNextPrime(int previousNumber)
{
int currentNumber = previousNumber + 1;
while (!IsPrimeNumber(currentNumber))
{
currentNumber += 1;
}
return currentNumber;
}
/// <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)
{
// Start with the first applicable number.
int currentNumber = 1;
taskDeferral = taskInstance.GetDeferral();
// Wire the cancellation handler.
taskInstance.Canceled += this.OnCanceled;
// Set the progress to indicate this task has started
taskInstance.Progress = 10;
// Calculate primes until a cancellation has been requested or until
// the maximum number is reached.
while ((cleanupTask == 0) && (currentNumber < maxPrimeNumber)) {
// Compute the next prime number and add it to our queue.
currentNumber = GetNextPrime(currentNumber);
numbersQueue.Enqueue(currentNumber);
// Once the queue is filled to its max size, flush the numbers to the file.
if (numbersQueue.Count >= queueDepthToWrite)
{
FlushNumbersToFile(numbersQueue);
numbersQueue.Clear();
}
}
// Flush any remaining numbers to the file as part of cleanup.
FlushNumbersToFile(numbersQueue);
if (taskDeferral != null)
{
taskDeferral.Complete();
}
}
/// <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)
{
cleanupTask = 1;
}
}
}
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.ApplicationModel.Background.h>
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::ApplicationModel::Background;
namespace PackagedWinMainBackgroundTaskSample {
// Note insert unique UUID.
struct __declspec(uuid("14C5882B-35D3-41BE-86B2-5106269B97E6"))
SampleTask : implements<SampleTask, IBackgroundTask>
{
const unsigned int MaximumPotentialPrime = 1000000000;
volatile bool isCanceled = false;
BackgroundTaskDeferral taskDeferral = nullptr;
void __stdcall Run (_In_ IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled({ this, &SampleTask::OnCanceled });
taskDeferral = taskInstance.GetDeferral();
unsigned int currentPrimeNumber = 1;
while (!isCanceled && (currentPrimeNumber < MaximumPotentialPrime))
{
currentPrimeNumber = GetNextPrime(currentPrimeNumber);
}
taskDeferral.Complete();
}
void __stdcall OnCanceled (_In_ IBackgroundTaskInstance, _In_ BackgroundTaskCancellationReason)
{
isCanceled = true;
}
};
struct TaskFactory : implements<TaskFactory, IClassFactory>
{
HRESULT __stdcall CreateInstance (_In_opt_ IUnknown* aggregateInterface, _In_ REFIID interfaceId, _Outptr_ VOID** object) noexcept final
{
if (aggregateInterface != NULL) {
return CLASS_E_NOAGGREGATION;
}
return make<SampleTask>().as(interfaceId, object);
}
HRESULT __stdcall LockServer (BOOL) noexcept final
{
return S_OK;
}
};
}
Aby dodać kod wspomagający do utworzenia wystąpienia klasy COM
Aby zadanie w tle zostało aktywowane jako aplikacja Win32 z pełnym zaufaniem, klasa zadań w tle musi zawierać kod wspierający, tak aby COM mógł zrozumieć, jak uruchomić proces aplikacji, jeśli nie jest on uruchomiony, oraz zrozumieć, które wystąpienie procesu pełni obecnie rolę serwera do obsługi nowych aktywacji dla tego zadania w tle.
- Com musi zrozumieć, jak uruchomić proces aplikacji, jeśli jeszcze nie jest uruchomiony. Proces aplikacji hostujący kod zadania w tle musi zostać zadeklarowany w manifeście pakietu. Poniższy przykładowy kod pokazuje, jak przykładowa usługa SampleTask jest hostowana w SampleBackgroundApp.exe. Po uruchomieniu zadania w tle, gdy żaden proces nie działa, SampleBackgroundApp.exe zostanie uruchomiony z argumentami procesu "-StartSampleTaskServer".
<Extensions>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="SampleBackgroundApp\SampleBackgroundApp.exe" DisplayName="SampleBackgroundApp" Arguments="-StartSampleTaskServer">
<com:Class Id="14C5882B-35D3-41BE-86B2-5106269B97E6" DisplayName="Sample Task" />
</com:ExeServer>
</com:ComServer>
</com:Extension>
</Extensions>
- Po rozpoczęciu procesu z odpowiednimi argumentami powinien on poinformować COM, że jest to bieżący serwer COM dla nowych wystąpień sampleTask. Poniższy przykładowy kod pokazuje, jak proces aplikacji powinien zarejestrować się w modelu COM. Należy pamiętać, że te przykłady wskazują, jak proces zadeklaruje się jako serwer COM dla SampleTask, dopóki przynajmniej jedno zadanie nie zostanie zakończone przed zakończeniem. Jest to opcjonalne, a obsługa zadania w tle może uruchomić główne funkcje procesu.
class SampleTaskServer
{
SampleTaskServer()
{
comRegistrationToken = 0;
waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
}
~SampleTaskServer()
{
Stop();
}
public void Start()
{
RegistrationServices registrationServices = new RegistrationServices();
comRegistrationToken = registrationServices.RegisterTypeForComClients(typeof(SampleTask), RegistrationClassContext.LocalServer, RegistrationConnectionType.MultipleUse);
// Either have the background task signal this handle when it completes, or never signal this handle to keep this
// process as the COM server until the process is closed.
waitHandle.WaitOne();
}
public void Stop()
{
if (comRegistrationToken != 0)
{
RegistrationServices registrationServices = new RegistrationServices();
registrationServices.UnregisterTypeForComClients(registrationCookie);
}
waitHandle.Set();
}
private int comRegistrationToken;
private EventWaitHandle waitHandle;
}
var sampleTaskServer = new SampleTaskServer();
sampleTaskServer.Start();
class SampleTaskServer
{
public:
SampleTaskServer()
{
waitHandle = EventWaitHandle(false, EventResetMode::AutoResetEvent);
comRegistrationToken = 0;
}
~SampleTaskServer()
{
Stop();
}
void Start()
{
try
{
com_ptr<IClassFactory> taskFactory = make<TaskFactory>();
winrt::check_hresult(CoRegisterClassObject(__uuidof(SampleTask),
taskFactory.get(),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&comRegistrationToken));
// Either have the background task signal this handle when it completes, or never signal this handle to
// keep this process as the COM server until the process is closed.
waitHandle.WaitOne();
}
catch (...)
{
// Indicate an error has been encountered.
}
}
void Stop()
{
if (comRegistrationToken != 0)
{
CoRevokeClassObject(comRegistrationToken);
}
waitHandle.Set();
}
private:
DWORD comRegistrationToken;
EventWaitHandle waitHandle;
};
SampleTaskServer sampleTaskServer;
sampleTaskServer.Start();
Zarejestruj zadanie w tle do uruchomienia
- Sprawdź, czy zadanie w tle zostało już zarejestrowane przez iterowanie za pomocą właściwości BackgroundTaskRegistration.AllTasks . Ten krok jest ważny; Jeśli Twoja aplikacja nie sprawdza istniejących rejestracji zadań w tle, może łatwo zarejestrować zadanie wielokrotnie, powodując problemy z wydajnością i maksymalnym czasem procesora zadania, zanim praca zostanie ukończona. Aplikacja może używać tego samego punktu wejścia, aby obsługiwać wszystkie zadania w tle i używać innych właściwości, takich jak Nazwa lub TaskId przypisany do BackgroundTaskRegistration, aby zdecydować, co powinno się wykonać.
W poniższym przykładzie iteruje właściwość AllTasks i ustawia zmienną flagową na true, jeśli zadanie zostało już zarejestrowane.
var taskRegistered = false;
var sampleTaskName = "SampleTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == sampleTaskName)
{
taskRegistered = true;
break;
}
}
// The code in the next step goes here.
bool taskRegistered = false;
std::wstring sampleTaskName = L"SampleTask";
auto allTasks = BackgroundTaskRegistration::AllTasks();
for (auto const& task : allTasks)
{
if (task.Value().Name() == sampleTaskName)
{
taskRegistered = true;
break;
}
}
// The code in the next step goes here.
- Jeśli zadanie w tle nie zostało jeszcze zarejestrowane, użyj narzędzia BackgroundTaskBuilder , aby utworzyć wystąpienie zadania w tle. Punkt wejścia zadania powinien mieć postać nazwy klasy dla zadania w tle, poprzedzonej przestrzenią nazw.
Wyzwalacz zadania w tle kontroluje, kiedy zadanie w tle zostanie uruchomione. Aby uzyskać listę możliwych wyzwalaczy, zobacz Windows.ApplicationModel.Background Przestrzeni nazw.
Uwaga / Notatka
Tylko podzbiór wyzwalaczy jest obsługiwany w przypadku spakowanych zadań w tle Win32.
Na przykład ten kod tworzy nowe zadanie w tle i ustawia je do uruchamiania co 15 minut z cyklicznym wyzwalaczem TimeTrigger .
if (!taskRegistered)
{
var builder = new BackgroundTaskBuilder();
builder.Name = sampleTaskName;
builder.SetTaskEntryPointClsid(typeof(SampleTask).GUID);
builder.SetTrigger(new TimeTrigger(15, false));
}
// The code in the next step goes here.
if (!taskRegistered)
{
BackgroundTaskBuilder builder;
builder.Name(sampleTaskName);
builder.SetTaskEntryPointClsid(__uuidof(SampleTask));
builder.SetTrigger(TimeTrigger(15, false));
}
// The code in the next step goes here.
- Możesz dodać warunek, aby określić, kiedy zadanie zostanie uruchomione po wystąpieniu zdarzenia wyzwalacza (opcjonalnie). Jeśli na przykład nie chcesz, aby zadanie było uruchamiane do momentu udostępnienia Internetu, użyj warunku InternetAvailable. Aby uzyskać listę możliwych warunków, zobacz SystemConditionType.
Poniższy przykładowy kod przypisuje warunek wymagający obecności użytkownika:
builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
// The code in the next step goes here.
builder.AddCondition(SystemCondition{ SystemConditionType::InternetAvailable });
// The code in the next step goes here.
- Zarejestruj zadanie w tle, wywołując metodę Register w obiekcie BackgroundTaskBuilder . Zapisz wynik BackgroundTaskRegistration , aby można było go użyć w następnym kroku. Należy pamiętać, że funkcja register może zwracać błędy w postaci wyjątków. Pamiętaj, aby wywołać metodę Register w instrukcji try-catch.
Poniższy kod rejestruje zadanie w tle i przechowuje wynik:
try
{
var task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
try
{
auto task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
Połączenie wszystkiego
Poniższe przykłady kodu pokazują pełny kod wymagany do uruchomienia i zarejestrowania zadania w tle WIN32 COM:
Ukończ manifest pakietu aplikacji Win32
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
IgnorableNamespaces="uap rescap com">
<Identity
Name="SamplePackagedWinMainBackgroundApp"
Publisher="CN=Contoso"
Version="1.0.0.0" />
<Properties>
<DisplayName>SamplePackagedWinMainBackgroundApp</DisplayName>
<PublisherDisplayName>Contoso</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="SampleBackgroundApp\$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="SampleBackgroundApp"
Description="SampleBackgroundApp"
BackgroundColor="transparent"
Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" />
<uap:SplashScreen Image="Images\SplashScreen.png" />
</uap:VisualElements>
<Extensions>
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="SampleBackgroundApp\SampleBackgroundApp.exe" DisplayName="SampleBackgroundApp" Arguments="-StartSampleTaskServer">
<com:Class Id="14C5882B-35D3-41BE-86B2-5106269B97E6" DisplayName="Sample Task" />
</com:ExeServer>
</com:ComServer>
</com:Extension>
</Extensions>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>
przykładowy kod zadania w tle
using System;
using System.IO; // Path
using System.Threading; // EventWaitHandle
using System.Collections.Generic; // Queue
using System.Runtime.InteropServices; // Guid, RegistrationServices
using Windows.ApplicationModel.Background; // IBackgroundTask
namespace PackagedWinMainBackgroundTaskSample
{
// Background task implementation.
// {14C5882B-35D3-41BE-86B2-5106269B97E6} is GUID to register this task with BackgroundTaskBuilder. Generate a random GUID before implementing.
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("14C5882B-35D3-41BE-86B2-5106269B97E6")]
[ComSourceInterfaces(typeof(IBackgroundTask))]
public class SampleTask : IBackgroundTask
{
private volatile int cleanupTask; // flag used to indicate to Run method that it should exit
private Queue<int> numbersQueue; // the data structure holding the set of primes in memory
private const int maxPrimeNumber = 1000000000; // the number up to which task will attempt to calculate primes
private const int queueDepthToWrite = 10; // how frequently this task should flush its queue of primes
private const string numbersQueueFile = "numbersQueue.log"; // the file to write to relative to AppData
public SampleTask()
{
cleanupTask = 0;
numbersQueue = new Queue<int>(queueDepthToWrite);
}
/// <summary>
/// This method writes all the numbers in the current queue to the specified file.
/// </summary>
private void FlushNumbersToFile(Queue<int> queueToWrite)
{
string logPath = Path.Combine(ApplicationData.Current.LocalFolder.Path,
System.Diagnostics.Process.GetCurrentProcess().ProcessName);
if (!Directory.Exists(logPath))
{
Directory.CreateDirectory(logPath);
}
logPath = Path.Combine(logPath, numbersQueueFile);
const string delimiter = ", ";
UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
// convert the queue to a list of comma separated values.
string stringToWrite = String.Join(delimiter, queueToWrite);
// Add the comma at the end.
stringToWrite += delimiter;
File.AppendAllText(logPath, stringToWrite);
}
/// <summary>
/// This method determines if the specified number is a prime number.
/// </summary>
private bool IsPrimeNumber(int dividend)
{
bool isPrime = true;
for (int divisor = dividend - 1; divisor > 1; divisor -= 1)
{
if ((dividend % divisor) == 0)
{
isPrime = false;
break;
}
}
return isPrime;
}
/// <summary>
/// Given the current number, this method calculates the next prime number (excluding the specified number).
/// </summary>
private int GetNextPrime(int previousNumber)
{
int currentNumber = previousNumber + 1;
while (!IsPrimeNumber(currentNumber))
{
currentNumber += 1;
}
return currentNumber;
}
/// <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)
{
// Start with the first applicable number.
int currentNumber = 1;
taskDeferral = taskInstance.GetDeferral();
// Wire the cancellation handler.
taskInstance.Canceled += this.OnCanceled;
// Set the progress to indicate this task has started
taskInstance.Progress = 10;
// Calculate primes until a cancellation has been requested or until
// the maximum number is reached.
while ((cleanupTask == 0) && (currentNumber < maxPrimeNumber)) {
// Compute the next prime number and add it to our queue.
currentNumber = GetNextPrime(currentNumber);
numbersQueue.Enqueue(currentNumber);
// Once the queue is filled to its max size, flush the numbers to the file.
if (numbersQueue.Count >= queueDepthToWrite)
{
FlushNumbersToFile(numbersQueue);
numbersQueue.Clear();
}
}
// Flush any remaining numbers to the file as part of cleanup.
FlushNumbersToFile(numbersQueue);
if (taskDeferral != null)
{
taskDeferral.Complete();
}
}
/// <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)
{
cleanupTask = 1;
}
}
// COM server startup code.
class SampleTaskServer
{
SampleTaskServer()
{
comRegistrationToken = 0;
waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
}
~SampleTaskServer()
{
Stop();
}
public void Start()
{
RegistrationServices registrationServices = new RegistrationServices();
comRegistrationToken = registrationServices.RegisterTypeForComClients(typeof(SampleTask), RegistrationClassContext.LocalServer, RegistrationConnectionType.MultipleUse);
// Either have the background task signal this handle when it completes, or never signal this handle to keep this
// process as the COM server until the process is closed.
waitHandle.WaitOne();
}
public void Stop()
{
if (comRegistrationToken != 0)
{
RegistrationServices registrationServices = new RegistrationServices();
registrationServices.UnregisterTypeForComClients(registrationCookie);
}
waitHandle.Set();
}
private int comRegistrationToken;
private EventWaitHandle waitHandle;
}
// Background task registration code.
class SampleTaskRegistrar
{
public static void Register()
{
var taskRegistered = false;
var sampleTaskName = "SampleTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == sampleTaskName)
{
taskRegistered = true;
break;
}
}
if (!taskRegistered)
{
var builder = new BackgroundTaskBuilder();
builder.Name = sampleTaskName;
builder.SetTaskEntryPointClsid(typeof(SampleTask).GUID);
builder.SetTrigger(new TimeTrigger(15, false));
}
try
{
var task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
}
}
// Application entry point.
static class Program
{
[MTAThread]
static void Main()
{
string[] commandLineArgs = Environment.GetCommandLineArgs();
if (commandLineArgs.Length < 2)
{
// Open the WPF UI when no arguments are specified.
}
else
{
if (commandLineArgs.Contains("-RegisterSampleTask", StringComparer.InvariantCultureIgnoreCase))
{
SampleTaskRegistrar.Register();
}
if (commandLineArgs.Contains("-StartSampleTaskServer", StringComparer.InvariantCultureIgnoreCase))
{
var sampleTaskServer = new SampleTaskServer();
sampleTaskServer.Start();
}
}
return;
}
}
}
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.ApplicationModel.Background.h>
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::ApplicationModel::Background;
namespace PackagedWinMainBackgroundTaskSample
{
// Background task implementation.
// {14C5882B-35D3-41BE-86B2-5106269B97E6} is GUID to register this task with BackgroundTaskBuilder. Generate a random GUID before implementing.
struct __declspec(uuid("14C5882B-35D3-41BE-86B2-5106269B97E6"))
SampleTask : implements<SampleTask, IBackgroundTask>
{
const unsigned int maxPrimeNumber = 1000000000;
volatile bool isCanceled = false;
BackgroundTaskDeferral taskDeferral = nullptr;
void __stdcall Run (_In_ IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled({ this, &SampleTask::OnCanceled });
taskDeferral = taskInstance.GetDeferral();
unsigned int currentPrimeNumber = 1;
while (!isCanceled && (currentPrimeNumber < maxPrimeNumber))
{
currentPrimeNumber = GetNextPrime(currentPrimeNumber);
}
taskDeferral.Complete();
}
void __stdcall OnCanceled (_In_ IBackgroundTaskInstance, _In_ BackgroundTaskCancellationReason)
{
isCanceled = true;
}
};
struct TaskFactory : implements<TaskFactory, IClassFactory>
{
HRESULT __stdcall CreateInstance (_In_opt_ IUnknown* aggregateInterface, _In_ REFIID interfaceId, _Outptr_ VOID** object) noexcept final
{
if (aggregateInterface != nullptr) {
return CLASS_E_NOAGGREGATION;
}
return make<SampleTask>().as(interfaceId, object);
}
HRESULT __stdcall LockServer (BOOL) noexcept final
{
return S_OK;
}
};
// COM server startup code.
class SampleTaskServer
{
public:
SampleTaskServer()
{
waitHandle = EventWaitHandle(false, EventResetMode::AutoResetEvent);
comRegistrationToken = 0;
}
~SampleTaskServer()
{
Stop();
}
void Start()
{
try
{
com_ptr<IClassFactory> taskFactory = make<TaskFactory>();
winrt::check_hresult(CoRegisterClassObject(__uuidof(SampleTask),
taskFactory.get(),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&comRegistrationToken));
// Either have the background task signal this handle when it completes, or never signal this handle to
// keep this process as the COM server until the process is closed.
waitHandle.WaitOne();
}
catch (...)
{
// Indicate an error has been encountered.
}
}
void Stop()
{
if (comRegistrationToken != 0)
{
CoRevokeClassObject(comRegistrationToken);
}
waitHandle.Set();
}
private:
DWORD comRegistrationToken;
EventWaitHandle waitHandle;
};
// Background task registration code.
class SampleTaskRegistrar
{
public static void Register()
{
bool taskRegistered = false;
std::wstring sampleTaskName = L"SampleTask";
auto allTasks = BackgroundTaskRegistration::AllTasks();
for (auto const& task : allTasks)
{
if (task.Value().Name() == sampleTaskName)
{
taskRegistered = true;
break;
}
}
if (!taskRegistered)
{
BackgroundTaskBuilder builder;
builder.Name(sampleTaskName);
builder.SetTaskEntryPointClsid(__uuidof(SampleTask));
builder.SetTrigger(TimeTrigger(15, false));
}
try
{
auto task = builder.Register();
}
catch (...)
{
// Indicate an error was encountered.
}
}
}
}
using namespace PackagedWinMainBackgroundTaskSample;
// Application entry point.
int wmain(_In_ int argc, _In_reads_(argc) const wchar** argv)
{
unsigned int argumentIndex;
winrt::init_apartment();
if (argc <= 1)
{
return E_INVALIDARG;
}
for (argumentIndex = 0; argumentIndex < argc ; argumentIndex += 1)
{
if (_wcsnicmp(L"RegisterSampleTask",
argv[argumentIndex],
wcslen(L"RegisterSampleTask")) == 0)
{
SampleTaskRegistrar::Register();
}
if (_wcsnicmp(L"StartSampleTaskServer",
argv[argumentIndex],
wcslen(L"StartSampleTaskServer")) == 0)
{
SampleTaskServer sampleTaskServer;
sampleTaskServer.Start();
}
}
return S_OK;
}
Uwagi
W przeciwieństwie do aplikacji platformy UWP, które mogą uruchamiać zadania w tle w gotowości nowoczesnej, aplikacje Win32 nie mogą uruchamiać kodu z niższych poziomów zasilania nowoczesnego trybu uśpienia. Aby dowiedzieć się więcej, zobacz Nowoczesne rezerwowe.
[!UWAGA] Pobierz przykładowe zadanie w tle Win32 COM, aby zobaczyć podobne przykłady kodu w kontekście kompletnej aplikacji Desktop Bridge, która korzysta z zadań w tle.
Zapoznaj się z następującymi tematami pokrewnymi, aby zapoznać się z dokumentacją interfejsu API, wskazówkami koncepcyjnymi dotyczącymi zadań w tle i bardziej szczegółowymi instrukcjami dotyczącymi pisania aplikacji korzystających z zadań w tle.
Tematy pokrewne
- Reagowanie na zdarzenia systemowe za pomocą zadań w tle
- Zarejestruj zadanie w tle
- Ustawianie warunków uruchamiania zadania w tle
- Skorzystaj z wyzwalacza konserwacji
- Zarządzanie anulowanym zadaniem w tle
- Monitorowanie postępu i ukończenia zadania w tle
- Uruchamianie zadania w tle na czasomierzu
- Utwórz i zarejestruj zadanie w tle w ramach procesu.
- Konwertowanie zadania w tle poza procesem na zadanie w tle w trakcie przetwarzania
Wskazówki dotyczące zadań w tle
- Wytyczne dotyczące zadań w tle
- Debugowanie zadania w tle
- Jak wyzwalać zdarzenia związane z wstrzymywaniem, wznawianiem oraz działaniem w tle w aplikacjach UWP (podczas debugowania)
Dokumentacja referencyjna interfejsu API zadań w tle