Windows 10 RTM Release - May 2016 Update
This commit is contained in:
Родитель
2d839d3090
Коммит
6b625670fa
|
@ -74,6 +74,8 @@ ipch/
|
|||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
|
|
|
@ -34,7 +34,7 @@ Array<Scenario>^ MainPage::scenariosInner = ref new Array<Scenario>
|
|||
{
|
||||
// The format here is the following:
|
||||
// { "Description for the sample", "Fully quaified name for the class that implements the scenario" }
|
||||
{ "Background task", "SDKTemplate.SampleBackgroundTask" },
|
||||
{ "Background task", "SDKTemplate.SampleBackgroundTask" },
|
||||
{ "Background task with a condition", "SDKTemplate.SampleBackgroundTaskWithCondition" },
|
||||
{ "Servicing complete task", "SDKTemplate.ServicingCompleteTask" },
|
||||
{ "Background task with time trigger", "SDKTemplate.TimeTriggeredTask" },
|
||||
|
@ -102,7 +102,7 @@ BackgroundTaskRegistration^ BackgroundTaskSample::RegisterBackgroundTask(String^
|
|||
|
||||
auto task = builder->Register();
|
||||
|
||||
UpdateBackgroundTaskStatus(name, true);
|
||||
UpdateBackgroundTaskRegistrationStatus(name, true);
|
||||
|
||||
//
|
||||
// Remove previous completion status from local settings.
|
||||
|
@ -142,17 +142,17 @@ void BackgroundTaskSample::UnregisterBackgroundTasks(String^ name)
|
|||
{
|
||||
auto cur = iter->Current->Value;
|
||||
|
||||
if(cur->Name == name)
|
||||
if (cur->Name == name)
|
||||
{
|
||||
cur->Unregister(true);
|
||||
UpdateBackgroundTaskStatus(name, false);
|
||||
UpdateBackgroundTaskRegistrationStatus(name, false);
|
||||
}
|
||||
|
||||
hascur = iter->MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundTaskSample::UpdateBackgroundTaskStatus(String^ name, bool registered)
|
||||
void BackgroundTaskSample::UpdateBackgroundTaskRegistrationStatus(String^ name, bool registered)
|
||||
{
|
||||
if (name == SampleBackgroundTaskName)
|
||||
{
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace SDKTemplate
|
|||
static BackgroundTaskRegistration^ RegisterBackgroundTask(String^ taskEntryPoint, String^ name, IBackgroundTrigger^ trigger, IBackgroundCondition^ condition);
|
||||
static bool TaskRequiresBackgroundAccess(String^ name);
|
||||
static void UnregisterBackgroundTasks(String^ name);
|
||||
static void UpdateBackgroundTaskStatus(String^ name, bool registered);
|
||||
static void UpdateBackgroundTaskRegistrationStatus(String^ name, bool registered);
|
||||
|
||||
static String^ SampleBackgroundTaskProgress;
|
||||
static bool SampleBackgroundTaskRegistered;
|
||||
|
|
|
@ -44,20 +44,15 @@ void SampleBackgroundTask::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
//
|
||||
// Attach progress and completed handlers to any existing tasks.
|
||||
//
|
||||
auto iter = BackgroundTaskRegistration::AllTasks->First();
|
||||
auto hascur = iter->HasCurrent;
|
||||
while (hascur)
|
||||
for (auto pair : BackgroundTaskRegistration::AllTasks)
|
||||
{
|
||||
auto cur = iter->Current->Value;
|
||||
|
||||
if (cur->Name == SampleBackgroundTaskName)
|
||||
auto task = pair->Value;
|
||||
if (task->Name == SampleBackgroundTaskName)
|
||||
{
|
||||
BackgroundTaskSample::UpdateBackgroundTaskStatus(cur->Name, true);
|
||||
AttachProgressAndCompletedHandlers(cur);
|
||||
BackgroundTaskSample::UpdateBackgroundTaskRegistrationStatus(task->Name, true);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
break;
|
||||
}
|
||||
|
||||
hascur = iter->MoveNext();
|
||||
}
|
||||
|
||||
UpdateUI();
|
||||
|
@ -69,19 +64,8 @@ void SampleBackgroundTask::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
/// <param name="task">The task to attach progress and completed handlers to.</param>
|
||||
void SampleBackgroundTask::AttachProgressAndCompletedHandlers(IBackgroundTaskRegistration^ task)
|
||||
{
|
||||
auto progress = [this](BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::SampleBackgroundTaskProgress = progress;
|
||||
UpdateUI();
|
||||
};
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(progress);
|
||||
|
||||
auto completed = [this](BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
};
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(completed);
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(this, &SampleBackgroundTask::OnProgress);
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(this, &SampleBackgroundTask::OnCompleted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -110,6 +94,31 @@ void SampleBackgroundTask::UnregisterBackgroundTask(Platform::Object^ sender, Wi
|
|||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task progress.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting progress.</param>
|
||||
/// <param name="args">Arguments of the progress report.</param>
|
||||
void SampleBackgroundTask::OnProgress(BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, args]()
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::SampleBackgroundTaskProgress = progress;
|
||||
UpdateUI();
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task completion.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting completion.</param>
|
||||
/// <param name="args">Arguments of the completion report.</param>
|
||||
void SampleBackgroundTask::OnCompleted(BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the scenario UI.
|
||||
/// </summary>
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace SDKTemplate
|
|||
void AttachProgressAndCompletedHandlers(Windows::ApplicationModel::Background::IBackgroundTaskRegistration^ task);
|
||||
void RegisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void UnregisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void OnProgress(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskProgressEventArgs^ args);
|
||||
void OnCompleted(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskCompletedEventArgs^ args);
|
||||
void UpdateUI();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -44,20 +44,15 @@ void SampleBackgroundTaskWithCondition::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
//
|
||||
// Attach progress and completed handlers to any existing tasks.
|
||||
//
|
||||
auto iter = BackgroundTaskRegistration::AllTasks->First();
|
||||
auto hascur = iter->HasCurrent;
|
||||
while (hascur)
|
||||
for (auto pair : BackgroundTaskRegistration::AllTasks)
|
||||
{
|
||||
auto cur = iter->Current->Value;
|
||||
|
||||
if (cur->Name == SampleBackgroundTaskWithConditionName)
|
||||
auto task = pair->Value;
|
||||
if (task->Name == SampleBackgroundTaskWithConditionName)
|
||||
{
|
||||
BackgroundTaskSample::UpdateBackgroundTaskStatus(cur->Name, true);
|
||||
AttachProgressAndCompletedHandlers(cur);
|
||||
BackgroundTaskSample::UpdateBackgroundTaskRegistrationStatus(task->Name, true);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
break;
|
||||
}
|
||||
|
||||
hascur = iter->MoveNext();
|
||||
}
|
||||
|
||||
UpdateUI();
|
||||
|
@ -69,19 +64,8 @@ void SampleBackgroundTaskWithCondition::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
/// <param name="task">The task to attach progress and completed handlers to.</param>
|
||||
void SampleBackgroundTaskWithCondition::AttachProgressAndCompletedHandlers(IBackgroundTaskRegistration^ task)
|
||||
{
|
||||
auto progress = [this](BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::SampleBackgroundTaskWithConditionProgress = progress;
|
||||
UpdateUI();
|
||||
};
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(progress);
|
||||
|
||||
auto completed = [this](BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
};
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(completed);
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(this, &SampleBackgroundTaskWithCondition::OnProgress);
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(this, &SampleBackgroundTaskWithCondition::OnCompleted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -110,6 +94,31 @@ void SampleBackgroundTaskWithCondition::UnregisterBackgroundTask(Platform::Objec
|
|||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task progress.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting progress.</param>
|
||||
/// <param name="args">Arguments of the progress report.</param>
|
||||
void SampleBackgroundTaskWithCondition::OnProgress(BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, args]()
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::SampleBackgroundTaskWithConditionProgress = progress;
|
||||
UpdateUI();
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task completion.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting completion.</param>
|
||||
/// <param name="args">Arguments of the completion report.</param>
|
||||
void SampleBackgroundTaskWithCondition::OnCompleted(BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the scenario UI.
|
||||
/// </summary>
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace SDKTemplate
|
|||
void AttachProgressAndCompletedHandlers(Windows::ApplicationModel::Background::IBackgroundTaskRegistration^ task);
|
||||
void RegisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void UnregisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void OnProgress(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskProgressEventArgs^ args);
|
||||
void OnCompleted(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskCompletedEventArgs^ args);
|
||||
void UpdateUI();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -44,20 +44,15 @@ void ServicingCompleteTask::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
//
|
||||
// Attach progress and completed handlers to any existing tasks.
|
||||
//
|
||||
auto iter = BackgroundTaskRegistration::AllTasks->First();
|
||||
auto hascur = iter->HasCurrent;
|
||||
while (hascur)
|
||||
for (auto pair : BackgroundTaskRegistration::AllTasks)
|
||||
{
|
||||
auto cur = iter->Current->Value;
|
||||
|
||||
if (cur->Name == ServicingCompleteTaskName)
|
||||
auto task = pair->Value;
|
||||
if (task->Name == ServicingCompleteTaskName)
|
||||
{
|
||||
BackgroundTaskSample::UpdateBackgroundTaskStatus(cur->Name, true);
|
||||
AttachProgressAndCompletedHandlers(cur);
|
||||
BackgroundTaskSample::UpdateBackgroundTaskRegistrationStatus(task->Name, true);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
break;
|
||||
}
|
||||
|
||||
hascur = iter->MoveNext();
|
||||
}
|
||||
|
||||
UpdateUI();
|
||||
|
@ -69,19 +64,8 @@ void ServicingCompleteTask::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
/// <param name="task">The task to attach progress and completed handlers to.</param>
|
||||
void ServicingCompleteTask::AttachProgressAndCompletedHandlers(IBackgroundTaskRegistration^ task)
|
||||
{
|
||||
auto progress = [this](BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::ServicingCompleteTaskProgress = progress;
|
||||
UpdateUI();
|
||||
};
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(progress);
|
||||
|
||||
auto completed = [this](BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
};
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(completed);
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(this, &ServicingCompleteTask::OnProgress);
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(this, &ServicingCompleteTask::OnCompleted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -111,6 +95,31 @@ void ServicingCompleteTask::UnregisterBackgroundTask(Platform::Object^ sender, W
|
|||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task progress.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting progress.</param>
|
||||
/// <param name="args">Arguments of the progress report.</param>
|
||||
void ServicingCompleteTask::OnProgress(BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, args]()
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::ServicingCompleteTaskProgress = progress;
|
||||
UpdateUI();
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task completion.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting completion.</param>
|
||||
/// <param name="args">Arguments of the completion report.</param>
|
||||
void ServicingCompleteTask::OnCompleted(BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the scenario UI.
|
||||
/// </summary>
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace SDKTemplate
|
|||
void AttachProgressAndCompletedHandlers(Windows::ApplicationModel::Background::IBackgroundTaskRegistration^ task);
|
||||
void RegisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void UnregisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void OnProgress(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskProgressEventArgs^ args);
|
||||
void OnCompleted(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskCompletedEventArgs^ args);
|
||||
void UpdateUI();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -44,20 +44,15 @@ void TimeTriggeredTask::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
//
|
||||
// Attach progress and completed handlers to any existing tasks.
|
||||
//
|
||||
auto iter = BackgroundTaskRegistration::AllTasks->First();
|
||||
auto hascur = iter->HasCurrent;
|
||||
while (hascur)
|
||||
for (auto pair : BackgroundTaskRegistration::AllTasks)
|
||||
{
|
||||
auto cur = iter->Current->Value;
|
||||
|
||||
if (cur->Name == TimeTriggeredTaskName)
|
||||
auto task = pair->Value;
|
||||
if (task->Name == TimeTriggeredTaskName)
|
||||
{
|
||||
BackgroundTaskSample::UpdateBackgroundTaskStatus(cur->Name, true);
|
||||
AttachProgressAndCompletedHandlers(cur);
|
||||
BackgroundTaskSample::UpdateBackgroundTaskRegistrationStatus(task->Name, true);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
break;
|
||||
}
|
||||
|
||||
hascur = iter->MoveNext();
|
||||
}
|
||||
|
||||
UpdateUI();
|
||||
|
@ -69,19 +64,8 @@ void TimeTriggeredTask::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
/// <param name="task">The task to attach progress and completed handlers to.</param>
|
||||
void TimeTriggeredTask::AttachProgressAndCompletedHandlers(IBackgroundTaskRegistration^ task)
|
||||
{
|
||||
auto progress = [this](BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::TimeTriggeredTaskProgress = progress;
|
||||
UpdateUI();
|
||||
};
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(progress);
|
||||
|
||||
auto completed = [this](BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
};
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(completed);
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(this, &TimeTriggeredTask::OnProgress);
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(this, &TimeTriggeredTask::OnCompleted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -110,6 +94,31 @@ void TimeTriggeredTask::UnregisterBackgroundTask(Platform::Object^ sender, Windo
|
|||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task progress.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting progress.</param>
|
||||
/// <param name="args">Arguments of the progress report.</param>
|
||||
void TimeTriggeredTask::OnProgress(BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, args]()
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::TimeTriggeredTaskProgress = progress;
|
||||
UpdateUI();
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task completion.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting completion.</param>
|
||||
/// <param name="args">Arguments of the completion report.</param>
|
||||
void TimeTriggeredTask::OnCompleted(BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the scenario UI.
|
||||
/// </summary>
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace SDKTemplate
|
|||
void AttachProgressAndCompletedHandlers(Windows::ApplicationModel::Background::IBackgroundTaskRegistration^ task);
|
||||
void RegisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void UnregisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void OnProgress(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskProgressEventArgs^ args);
|
||||
void OnCompleted(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskCompletedEventArgs^ args);
|
||||
void UpdateUI();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -44,20 +44,15 @@ void ApplicationTriggerTask::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
//
|
||||
// Attach progress and completed handlers to any existing tasks.
|
||||
//
|
||||
auto iter = BackgroundTaskRegistration::AllTasks->First();
|
||||
auto hascur = iter->HasCurrent;
|
||||
while (hascur)
|
||||
for (auto pair : BackgroundTaskRegistration::AllTasks)
|
||||
{
|
||||
auto cur = iter->Current->Value;
|
||||
|
||||
if (cur->Name == ApplicationTriggerTaskName)
|
||||
auto task = pair->Value;
|
||||
if (task->Name == ApplicationTriggerTaskName)
|
||||
{
|
||||
BackgroundTaskSample::UpdateBackgroundTaskStatus(cur->Name, true);
|
||||
AttachProgressAndCompletedHandlers(cur);
|
||||
BackgroundTaskSample::UpdateBackgroundTaskRegistrationStatus(task->Name, true);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
break;
|
||||
}
|
||||
|
||||
hascur = iter->MoveNext();
|
||||
}
|
||||
|
||||
UpdateUI();
|
||||
|
@ -69,19 +64,8 @@ void ApplicationTriggerTask::OnNavigatedTo(NavigationEventArgs^ e)
|
|||
/// <param name="task">The task to attach progress and completed handlers to.</param>
|
||||
void ApplicationTriggerTask::AttachProgressAndCompletedHandlers(IBackgroundTaskRegistration^ task)
|
||||
{
|
||||
auto progress = [this](BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::ApplicationTriggerTaskProgress = progress;
|
||||
UpdateUI();
|
||||
};
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(progress);
|
||||
|
||||
auto completed = [this](BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
};
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(completed);
|
||||
task->Progress += ref new BackgroundTaskProgressEventHandler(this, &ApplicationTriggerTask::OnProgress);
|
||||
task->Completed += ref new BackgroundTaskCompletedEventHandler(this, &ApplicationTriggerTask::OnCompleted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -136,6 +120,31 @@ void ApplicationTriggerTask::SignalBackgroundTask(Platform::Object^ sender, Wind
|
|||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task progress.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting progress.</param>
|
||||
/// <param name="args">Arguments of the progress report.</param>
|
||||
void ApplicationTriggerTask::OnProgress(BackgroundTaskRegistration^ task, BackgroundTaskProgressEventArgs^ args)
|
||||
{
|
||||
Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, args]()
|
||||
{
|
||||
auto progress = "Progress: " + args->Progress + "%";
|
||||
BackgroundTaskSample::ApplicationTriggerTaskProgress = progress;
|
||||
UpdateUI();
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle background task completion.
|
||||
/// </summary>
|
||||
/// <param name="task">The task that is reporting completion.</param>
|
||||
/// <param name="args">Arguments of the completion report.</param>
|
||||
void ApplicationTriggerTask::OnCompleted(BackgroundTaskRegistration^ task, BackgroundTaskCompletedEventArgs^ args)
|
||||
{
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the scenario UI.
|
||||
/// </summary>
|
||||
|
|
|
@ -41,6 +41,8 @@ namespace SDKTemplate
|
|||
void RegisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void UnregisterBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void SignalBackgroundTask(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void OnProgress(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskProgressEventArgs^ args);
|
||||
void OnCompleted(Windows::ApplicationModel::Background::BackgroundTaskRegistration^ task, Windows::ApplicationModel::Background::BackgroundTaskCompletedEventArgs^ args);
|
||||
void UpdateUI();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,17 +13,10 @@
|
|||
#include "SampleBackgroundTask.h"
|
||||
|
||||
using namespace Tasks;
|
||||
using namespace Windows::ApplicationModel::Background;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Storage;
|
||||
|
||||
SampleBackgroundTask::SampleBackgroundTask() :
|
||||
CancelReason(BackgroundTaskCancellationReason::Abort), CancelRequested(false), TaskDeferral(nullptr), PeriodicTimer(nullptr), Progress(0), TaskInstance(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SampleBackgroundTask::~SampleBackgroundTask()
|
||||
{
|
||||
}
|
||||
using namespace Windows::System::Threading;
|
||||
|
||||
void SampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
|
||||
{
|
||||
|
@ -60,11 +53,12 @@ void SampleBackgroundTask::Run(IBackgroundTaskInstance^ taskInstance)
|
|||
PeriodicTimer->Cancel();
|
||||
|
||||
//
|
||||
// Write to LocalSettings to indicate that this background task ran.
|
||||
// Record that this background task ran.
|
||||
//
|
||||
auto settings = ApplicationData::Current->LocalSettings;
|
||||
auto taskStatus = (Progress < 100) ? "Canceled with reason: " + CancelReason.ToString() : "Completed";
|
||||
auto key = TaskInstance->Task->Name;
|
||||
settings->Values->Insert(key, (Progress < 100) ? "Canceled with reason: " + CancelReason.ToString() : "Completed");
|
||||
auto settings = ApplicationData::Current->LocalSettings;
|
||||
settings->Values->Insert(key, taskStatus);
|
||||
|
||||
//
|
||||
// Indicate that the background task has completed.
|
||||
|
|
|
@ -10,32 +10,22 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include <agile.h>
|
||||
|
||||
using namespace Windows::ApplicationModel::Background;
|
||||
using namespace Windows::System::Threading;
|
||||
|
||||
namespace Tasks
|
||||
{
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class SampleBackgroundTask sealed : public IBackgroundTask
|
||||
public ref class SampleBackgroundTask sealed : public Windows::ApplicationModel::Background::IBackgroundTask
|
||||
{
|
||||
|
||||
public:
|
||||
SampleBackgroundTask();
|
||||
|
||||
virtual void Run(IBackgroundTaskInstance^ taskInstance);
|
||||
void OnCanceled(IBackgroundTaskInstance^ taskInstance, BackgroundTaskCancellationReason reason);
|
||||
virtual void Run(Windows::ApplicationModel::Background::IBackgroundTaskInstance^ taskInstance);
|
||||
void OnCanceled(Windows::ApplicationModel::Background::IBackgroundTaskInstance^ taskInstance, Windows::ApplicationModel::Background::BackgroundTaskCancellationReason reason);
|
||||
|
||||
private:
|
||||
~SampleBackgroundTask();
|
||||
|
||||
BackgroundTaskCancellationReason CancelReason;
|
||||
volatile bool CancelRequested;
|
||||
Platform::Agile<Windows::ApplicationModel::Background::BackgroundTaskDeferral> TaskDeferral;
|
||||
ThreadPoolTimer^ PeriodicTimer;
|
||||
unsigned int Progress;
|
||||
IBackgroundTaskInstance^ TaskInstance;
|
||||
Windows::ApplicationModel::Background::BackgroundTaskCancellationReason CancelReason = Windows::ApplicationModel::Background::BackgroundTaskCancellationReason::Abort;
|
||||
volatile bool CancelRequested = false;
|
||||
Platform::Agile<Windows::ApplicationModel::Background::BackgroundTaskDeferral> TaskDeferral = nullptr;
|
||||
Windows::System::Threading::ThreadPoolTimer^ PeriodicTimer = nullptr;
|
||||
unsigned int Progress = 0;
|
||||
Windows::ApplicationModel::Background::IBackgroundTaskInstance^ TaskInstance = nullptr;
|
||||
};
|
||||
}
|
|
@ -74,11 +74,12 @@ namespace SDKTemplate
|
|||
/// <param name="name">A name for the background task.</param>
|
||||
/// <param name="trigger">The trigger for the background task.</param>
|
||||
/// <param name="condition">An optional conditional event that must be true for the task to fire.</param>
|
||||
public static async Task<BackgroundTaskRegistration> RegisterBackgroundTask(String taskEntryPoint, String name, IBackgroundTrigger trigger, IBackgroundCondition condition)
|
||||
public static BackgroundTaskRegistration RegisterBackgroundTask(String taskEntryPoint, String name, IBackgroundTrigger trigger, IBackgroundCondition condition)
|
||||
{
|
||||
if (TaskRequiresBackgroundAccess(name))
|
||||
{
|
||||
await BackgroundExecutionManager.RequestAccessAsync();
|
||||
// If the user denies access, the task will not run.
|
||||
var requestTask = BackgroundExecutionManager.RequestAccessAsync();
|
||||
}
|
||||
|
||||
var builder = new BackgroundTaskBuilder();
|
||||
|
@ -100,10 +101,10 @@ namespace SDKTemplate
|
|||
|
||||
BackgroundTaskRegistration task = builder.Register();
|
||||
|
||||
UpdateBackgroundTaskStatus(name, true);
|
||||
UpdateBackgroundTaskRegistrationStatus(name, true);
|
||||
|
||||
//
|
||||
// Remove previous completion status from local settings.
|
||||
// Remove previous completion status.
|
||||
//
|
||||
var settings = ApplicationData.Current.LocalSettings;
|
||||
settings.Values.Remove(name);
|
||||
|
@ -129,7 +130,7 @@ namespace SDKTemplate
|
|||
}
|
||||
}
|
||||
|
||||
UpdateBackgroundTaskStatus(name, false);
|
||||
UpdateBackgroundTaskRegistrationStatus(name, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -137,7 +138,7 @@ namespace SDKTemplate
|
|||
/// </summary>
|
||||
/// <param name="name">Name of background task to store registration status for.</param>
|
||||
/// <param name="registered">TRUE if registered, FALSE if unregistered.</param>
|
||||
public static void UpdateBackgroundTaskStatus(String name, bool registered)
|
||||
public static void UpdateBackgroundTaskRegistrationStatus(String name, bool registered)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
|
@ -188,10 +189,11 @@ namespace SDKTemplate
|
|||
|
||||
var status = registered ? "Registered" : "Unregistered";
|
||||
|
||||
object taskStatus;
|
||||
var settings = ApplicationData.Current.LocalSettings;
|
||||
if (settings.Values.ContainsKey(name))
|
||||
if (settings.Values.TryGetValue(name, out taskStatus))
|
||||
{
|
||||
status += " - " + settings.Values[name].ToString();
|
||||
status += " - " + taskStatus.ToString();
|
||||
}
|
||||
|
||||
return status;
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace SDKTemplate
|
|||
if (task.Value.Name == BackgroundTaskSample.SampleBackgroundTaskName)
|
||||
{
|
||||
AttachProgressAndCompletedHandlers(task.Value);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.SampleBackgroundTaskName, true);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.SampleBackgroundTaskName, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -61,14 +61,13 @@ namespace SDKTemplate
|
|||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
private void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.SampleBackgroundTaskEntryPoint,
|
||||
BackgroundTaskSample.SampleBackgroundTaskName,
|
||||
new SystemTrigger(SystemTriggerType.TimeZoneChange, false),
|
||||
null);
|
||||
await task;
|
||||
AttachProgressAndCompletedHandlers(task.Result);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
|
@ -100,9 +99,12 @@ namespace SDKTemplate
|
|||
/// <param name="e">Arguments of the progress report.</param>
|
||||
private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.SampleBackgroundTaskProgress = progress;
|
||||
UpdateUI();
|
||||
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.SampleBackgroundTaskProgress = progress;
|
||||
UpdateUI();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace SDKTemplate
|
|||
if (task.Value.Name == BackgroundTaskSample.SampleBackgroundTaskWithConditionName)
|
||||
{
|
||||
AttachProgressAndCompletedHandlers(task.Value);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.SampleBackgroundTaskWithConditionName, true);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.SampleBackgroundTaskWithConditionName, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -62,14 +62,13 @@ namespace SDKTemplate
|
|||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
private void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.SampleBackgroundTaskEntryPoint,
|
||||
BackgroundTaskSample.SampleBackgroundTaskWithConditionName,
|
||||
new SystemTrigger(SystemTriggerType.TimeZoneChange, false),
|
||||
new SystemCondition(SystemConditionType.InternetAvailable));
|
||||
await task;
|
||||
AttachProgressAndCompletedHandlers(task.Result);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
|
@ -101,9 +100,12 @@ namespace SDKTemplate
|
|||
/// <param name="e">Arguments of the progress report.</param>
|
||||
private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.SampleBackgroundTaskWithConditionProgress = progress;
|
||||
UpdateUI();
|
||||
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.SampleBackgroundTaskWithConditionProgress = progress;
|
||||
UpdateUI();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace SDKTemplate
|
|||
if (task.Value.Name == BackgroundTaskSample.ServicingCompleteTaskName)
|
||||
{
|
||||
AttachProgressAndCompletedHandlers(task.Value);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.ServicingCompleteTaskName, true);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.ServicingCompleteTaskName, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -61,14 +61,13 @@ namespace SDKTemplate
|
|||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
private void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.ServicingCompleteTaskEntryPoint,
|
||||
BackgroundTaskSample.ServicingCompleteTaskName,
|
||||
new SystemTrigger(SystemTriggerType.ServicingComplete, false),
|
||||
null);
|
||||
await task;
|
||||
AttachProgressAndCompletedHandlers(task.Result);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
|
@ -100,9 +99,12 @@ namespace SDKTemplate
|
|||
/// <param name="e">Arguments of the progress report.</param>
|
||||
private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.ServicingCompleteTaskProgress = progress;
|
||||
UpdateUI();
|
||||
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.ServicingCompleteTaskProgress = progress;
|
||||
UpdateUI();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace SDKTemplate
|
|||
if (task.Value.Name == BackgroundTaskSample.TimeTriggeredTaskName)
|
||||
{
|
||||
AttachProgressAndCompletedHandlers(task.Value);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.TimeTriggeredTaskName, true);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.TimeTriggeredTaskName, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -61,14 +61,13 @@ namespace SDKTemplate
|
|||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
private void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.SampleBackgroundTaskEntryPoint,
|
||||
BackgroundTaskSample.TimeTriggeredTaskName,
|
||||
new TimeTrigger(15, false),
|
||||
null);
|
||||
await task;
|
||||
AttachProgressAndCompletedHandlers(task.Result);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
|
@ -100,9 +99,12 @@ namespace SDKTemplate
|
|||
/// <param name="e">Arguments of the progress report.</param>
|
||||
private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.TimeTriggeredTaskProgress = progress;
|
||||
UpdateUI();
|
||||
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.TimeTriggeredTaskProgress = progress;
|
||||
UpdateUI();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace SDKTemplate
|
|||
if (task.Value.Name == BackgroundTaskSample.ApplicationTriggerTaskName)
|
||||
{
|
||||
AttachProgressAndCompletedHandlers(task.Value);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.ApplicationTriggerTaskName, true);
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.ApplicationTriggerTaskName, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ namespace SDKTemplate
|
|||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
private void RegisterBackgroundTask(object sender, RoutedEventArgs e)
|
||||
{
|
||||
trigger = new ApplicationTrigger();
|
||||
|
||||
|
@ -72,8 +72,7 @@ namespace SDKTemplate
|
|||
BackgroundTaskSample.ApplicationTriggerTaskName,
|
||||
trigger,
|
||||
null);
|
||||
await task;
|
||||
AttachProgressAndCompletedHandlers(task.Result);
|
||||
AttachProgressAndCompletedHandlers(task);
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
|
@ -123,9 +122,12 @@ namespace SDKTemplate
|
|||
/// <param name="e">Arguments of the progress report.</param>
|
||||
private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.ApplicationTriggerTaskProgress = progress;
|
||||
UpdateUI();
|
||||
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
var progress = "Progress: " + args.Progress + "%";
|
||||
BackgroundTaskSample.ApplicationTriggerTaskProgress = progress;
|
||||
UpdateUI();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -91,13 +91,14 @@ namespace Tasks
|
|||
{
|
||||
_periodicTimer.Cancel();
|
||||
|
||||
var settings = ApplicationData.Current.LocalSettings;
|
||||
var key = _taskInstance.Task.Name;
|
||||
|
||||
//
|
||||
// Write to LocalSettings to indicate that this background task ran.
|
||||
// Record that this background task ran.
|
||||
//
|
||||
settings.Values[key] = (_progress < 100) ? "Canceled with reason: " + _cancelReason.ToString() : "Completed";
|
||||
String taskStatus = (_progress < 100) ? "Canceled with reason: " + _cancelReason.ToString() : "Completed";
|
||||
var settings = ApplicationData.Current.LocalSettings;
|
||||
settings.Values[key] = taskStatus;
|
||||
Debug.WriteLine("Background " + _taskInstance.Task.Name + settings.Values[key]);
|
||||
|
||||
//
|
||||
|
|
|
@ -81,9 +81,10 @@ Namespace Global.SDKTemplate
|
|||
''' <param name="name">A name for the background task.</param>
|
||||
''' <param name="trigger">The trigger for the background task.</param>
|
||||
''' <param name="condition">An optional conditional event that must be true for the task to fire.</param>
|
||||
Public Shared Async Function RegisterBackgroundTask(taskEntryPoint As String, name As String, trigger As IBackgroundTrigger, condition As IBackgroundCondition) As Task(Of BackgroundTaskRegistration)
|
||||
Public Shared Function RegisterBackgroundTask(taskEntryPoint As String, name As String, trigger As IBackgroundTrigger, condition As IBackgroundCondition) As BackgroundTaskRegistration
|
||||
If TaskRequiresBackgroundAccess(name) Then
|
||||
Await BackgroundExecutionManager.RequestAccessAsync()
|
||||
' If the user denies access, the task will not run.
|
||||
Dim requestTask = BackgroundExecutionManager.RequestAccessAsync()
|
||||
End If
|
||||
|
||||
Dim builder = New BackgroundTaskBuilder()
|
||||
|
@ -96,9 +97,9 @@ Namespace Global.SDKTemplate
|
|||
End If
|
||||
|
||||
Dim task As BackgroundTaskRegistration = builder.Register()
|
||||
UpdateBackgroundTaskStatus(name, True)
|
||||
UpdateBackgroundTaskRegistrationStatus(name, True)
|
||||
'
|
||||
' Remove previous completion status from local settings.
|
||||
' Remove previous completion status.
|
||||
'
|
||||
Dim settings = ApplicationData.Current.LocalSettings
|
||||
settings.Values.Remove(name)
|
||||
|
@ -116,7 +117,7 @@ Namespace Global.SDKTemplate
|
|||
End If
|
||||
Next
|
||||
|
||||
UpdateBackgroundTaskStatus(name, False)
|
||||
UpdateBackgroundTaskRegistrationStatus(name, False)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
|
@ -124,7 +125,7 @@ Namespace Global.SDKTemplate
|
|||
''' </summary>
|
||||
''' <param name="name">Name of background task to store registration status for.</param>
|
||||
''' <param name="registered">TRUE if registered, FALSE if unregistered.</param>
|
||||
Public Shared Sub UpdateBackgroundTaskStatus(name As String, registered As Boolean)
|
||||
Public Shared Sub UpdateBackgroundTaskRegistrationStatus(name As String, registered As Boolean)
|
||||
Select name
|
||||
Case SampleBackgroundTaskName
|
||||
SampleBackgroundTaskRegistered = registered
|
||||
|
@ -160,9 +161,10 @@ Namespace Global.SDKTemplate
|
|||
End Select
|
||||
|
||||
Dim status = If(registered, "Registered", "Unregistered")
|
||||
Dim taskStatus As Object = Nothing
|
||||
Dim settings = ApplicationData.Current.LocalSettings
|
||||
If settings.Values.ContainsKey(name) Then
|
||||
status &= " - " & settings.Values(name).ToString()
|
||||
If settings.Values.TryGetValue(name, taskStatus) Then
|
||||
status &= " - " & taskStatus.ToString()
|
||||
End If
|
||||
|
||||
Return status
|
||||
|
@ -173,7 +175,7 @@ Namespace Global.SDKTemplate
|
|||
''' </summary>
|
||||
''' <param name="name">Name of background task to query background access requirement.</param>
|
||||
Public Shared Function TaskRequiresBackgroundAccess(name As String) As Boolean
|
||||
If(name = TimeTriggeredTaskName) OrElse (name = ApplicationTriggerTaskName) Then
|
||||
If (name = TimeTriggeredTaskName) OrElse (name = ApplicationTriggerTaskName) Then
|
||||
Return True
|
||||
Else
|
||||
Return False
|
||||
|
|
|
@ -42,7 +42,7 @@ Namespace Global.SDKTemplate
|
|||
For Each task In BackgroundTaskRegistration.AllTasks
|
||||
If task.Value.Name = BackgroundTaskSample.SampleBackgroundTaskName Then
|
||||
AttachProgressAndCompletedHandlers(task.Value)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.SampleBackgroundTaskName, True)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.SampleBackgroundTaskName, True)
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
@ -55,10 +55,9 @@ Namespace Global.SDKTemplate
|
|||
''' </summary>
|
||||
''' <param name="sender"></param>
|
||||
''' <param name="e"></param>
|
||||
Private Async Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
Private Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
Dim task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.SampleBackgroundTaskEntryPoint, BackgroundTaskSample.SampleBackgroundTaskName, New SystemTrigger(SystemTriggerType.TimeZoneChange, False), Nothing)
|
||||
Await task
|
||||
AttachProgressAndCompletedHandlers(task.Result)
|
||||
AttachProgressAndCompletedHandlers(task)
|
||||
UpdateUI()
|
||||
End Sub
|
||||
|
||||
|
@ -87,9 +86,11 @@ Namespace Global.SDKTemplate
|
|||
''' <param name="task">The task that is reporting progress.</param>
|
||||
''' <param name="e">Arguments of the progress report.</param>
|
||||
Private Sub OnProgress(task As IBackgroundTaskRegistration, args As BackgroundTaskProgressEventArgs)
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.SampleBackgroundTaskProgress = progress
|
||||
UpdateUI()
|
||||
Dim ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Sub()
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.SampleBackgroundTaskProgress = progress
|
||||
UpdateUI()
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
|
|
|
@ -40,7 +40,7 @@ Namespace Global.SDKTemplate
|
|||
For Each task In BackgroundTaskRegistration.AllTasks
|
||||
If task.Value.Name = BackgroundTaskSample.SampleBackgroundTaskWithConditionName Then
|
||||
AttachProgressAndCompletedHandlers(task.Value)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.SampleBackgroundTaskWithConditionName, True)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.SampleBackgroundTaskWithConditionName, True)
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
@ -53,10 +53,9 @@ Namespace Global.SDKTemplate
|
|||
''' </summary>
|
||||
''' <param name="sender"></param>
|
||||
''' <param name="e"></param>
|
||||
Private Async Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
Private Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
Dim task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.SampleBackgroundTaskEntryPoint, BackgroundTaskSample.SampleBackgroundTaskWithConditionName, New SystemTrigger(SystemTriggerType.TimeZoneChange, False), New SystemCondition(SystemConditionType.InternetAvailable))
|
||||
Await task
|
||||
AttachProgressAndCompletedHandlers(task.Result)
|
||||
AttachProgressAndCompletedHandlers(task)
|
||||
UpdateUI()
|
||||
End Sub
|
||||
|
||||
|
@ -85,9 +84,11 @@ Namespace Global.SDKTemplate
|
|||
''' <param name="task">The task that is reporting progress.</param>
|
||||
''' <param name="e">Arguments of the progress report.</param>
|
||||
Private Sub OnProgress(task As IBackgroundTaskRegistration, args As BackgroundTaskProgressEventArgs)
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.SampleBackgroundTaskWithConditionProgress = progress
|
||||
UpdateUI()
|
||||
Dim ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Sub()
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.SampleBackgroundTaskWithConditionProgress = progress
|
||||
UpdateUI()
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
|
|
|
@ -40,7 +40,7 @@ Namespace Global.SDKTemplate
|
|||
For Each task In BackgroundTaskRegistration.AllTasks
|
||||
If task.Value.Name = BackgroundTaskSample.ServicingCompleteTaskName Then
|
||||
AttachProgressAndCompletedHandlers(task.Value)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.ServicingCompleteTaskName, True)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.ServicingCompleteTaskName, True)
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
@ -53,10 +53,9 @@ Namespace Global.SDKTemplate
|
|||
''' </summary>
|
||||
''' <param name="sender"></param>
|
||||
''' <param name="e"></param>
|
||||
Private Async Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
Private Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
Dim task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.ServicingCompleteTaskEntryPoint, BackgroundTaskSample.ServicingCompleteTaskName, New SystemTrigger(SystemTriggerType.ServicingComplete, False), Nothing)
|
||||
Await task
|
||||
AttachProgressAndCompletedHandlers(task.Result)
|
||||
AttachProgressAndCompletedHandlers(task)
|
||||
UpdateUI()
|
||||
End Sub
|
||||
|
||||
|
@ -85,9 +84,11 @@ Namespace Global.SDKTemplate
|
|||
''' <param name="task">The task that is reporting progress.</param>
|
||||
''' <param name="e">Arguments of the progress report.</param>
|
||||
Private Sub OnProgress(task As IBackgroundTaskRegistration, args As BackgroundTaskProgressEventArgs)
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.ServicingCompleteTaskProgress = progress
|
||||
UpdateUI()
|
||||
Dim ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Sub()
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.ServicingCompleteTaskProgress = progress
|
||||
UpdateUI()
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
|
|
|
@ -40,7 +40,7 @@ Namespace Global.SDKTemplate
|
|||
For Each task In BackgroundTaskRegistration.AllTasks
|
||||
If task.Value.Name = BackgroundTaskSample.TimeTriggeredTaskName Then
|
||||
AttachProgressAndCompletedHandlers(task.Value)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.TimeTriggeredTaskName, True)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.TimeTriggeredTaskName, True)
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
@ -53,10 +53,9 @@ Namespace Global.SDKTemplate
|
|||
''' </summary>
|
||||
''' <param name="sender"></param>
|
||||
''' <param name="e"></param>
|
||||
Private Async Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
Private Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
Dim task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.SampleBackgroundTaskEntryPoint, BackgroundTaskSample.TimeTriggeredTaskName, New TimeTrigger(15, False), Nothing)
|
||||
Await task
|
||||
AttachProgressAndCompletedHandlers(task.Result)
|
||||
AttachProgressAndCompletedHandlers(task)
|
||||
UpdateUI()
|
||||
End Sub
|
||||
|
||||
|
@ -85,9 +84,11 @@ Namespace Global.SDKTemplate
|
|||
''' <param name="task">The task that is reporting progress.</param>
|
||||
''' <param name="e">Arguments of the progress report.</param>
|
||||
Private Sub OnProgress(task As IBackgroundTaskRegistration, args As BackgroundTaskProgressEventArgs)
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.TimeTriggeredTaskProgress = progress
|
||||
UpdateUI()
|
||||
Dim ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Sub()
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.TimeTriggeredTaskProgress = progress
|
||||
UpdateUI()
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
|
|
|
@ -42,7 +42,7 @@ Namespace Global.SDKTemplate
|
|||
For Each task In BackgroundTaskRegistration.AllTasks
|
||||
If task.Value.Name = BackgroundTaskSample.ApplicationTriggerTaskName Then
|
||||
AttachProgressAndCompletedHandlers(task.Value)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskStatus(BackgroundTaskSample.ApplicationTriggerTaskName, True)
|
||||
BackgroundTaskSample.UpdateBackgroundTaskRegistrationStatus(BackgroundTaskSample.ApplicationTriggerTaskName, True)
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
|
@ -55,11 +55,10 @@ Namespace Global.SDKTemplate
|
|||
''' </summary>
|
||||
''' <param name="sender"></param>
|
||||
''' <param name="e"></param>
|
||||
Private Async Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
Private Sub RegisterBackgroundTask(sender As Object, e As RoutedEventArgs)
|
||||
trigger = New ApplicationTrigger()
|
||||
Dim task = BackgroundTaskSample.RegisterBackgroundTask(BackgroundTaskSample.SampleBackgroundTaskEntryPoint, BackgroundTaskSample.ApplicationTriggerTaskName, trigger, Nothing)
|
||||
Await task
|
||||
AttachProgressAndCompletedHandlers(task.Result)
|
||||
AttachProgressAndCompletedHandlers(task)
|
||||
UpdateUI()
|
||||
End Sub
|
||||
|
||||
|
@ -104,9 +103,11 @@ Namespace Global.SDKTemplate
|
|||
''' <param name="task">The task that is reporting progress.</param>
|
||||
''' <param name="e">Arguments of the progress report.</param>
|
||||
Private Sub OnProgress(task As IBackgroundTaskRegistration, args As BackgroundTaskProgressEventArgs)
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.ApplicationTriggerTaskProgress = progress
|
||||
UpdateUI()
|
||||
Dim ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Sub()
|
||||
Dim progress = "Progress: " & args.Progress & "%"
|
||||
BackgroundTaskSample.ApplicationTriggerTaskProgress = progress
|
||||
UpdateUI()
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
|
|
|
@ -72,10 +72,11 @@ Namespace Global.Tasks
|
|||
_taskInstance.Progress = _progress
|
||||
Else
|
||||
_periodicTimer.Cancel()
|
||||
Dim settings = ApplicationData.Current.LocalSettings
|
||||
Dim status As String = If((_progress < 100), "Canceled with reason: " & _cancelReason.ToString(), "Completed")
|
||||
Dim key = _taskInstance.Task.Name
|
||||
settings.Values(key) = If((_progress < 100), "Canceled with reason: " & _cancelReason.ToString(), "Completed")
|
||||
Debug.WriteLine("Background " & _taskInstance.Task.Name & settings.Values(key))
|
||||
Dim settings = ApplicationData.Current.LocalSettings
|
||||
settings.Values(key) = status
|
||||
Debug.WriteLine("Background " & _taskInstance.Task.Name & status)
|
||||
_deferral.Complete()
|
||||
End If
|
||||
End Sub
|
||||
|
|
|
@ -5,17 +5,18 @@
|
|||
|
||||
# Barcode scanner sample
|
||||
|
||||
This sample shows how to create a barcode scanner, claim it for exclusive use, enable it to receive data, and read a barcode. This sample uses [**Windows.Devices.PointOfService**](http://msdn.microsoft.com/library/windows/apps/dn298071) API.
|
||||
This sample shows how to obtain a barcode scanner,
|
||||
claim it for exclusive use, enable it to receive data, and read a barcode.
|
||||
|
||||
Specifically, this sample shows how to:
|
||||
|
||||
1. **Create the barcode scanner**
|
||||
1. **Obtain the barcode scanner**
|
||||
|
||||
Uses the [**BarcodeScanner.GetDefaultAsync**](http://msdn.microsoft.com/library/windows/apps/dn263790) to get the first available barcode scanner.
|
||||
Uses [**BarcodeScanner.GetDefaultAsync**](http://msdn.microsoft.com/library/windows/apps/dn263790) to get the first available barcode scanner.
|
||||
|
||||
2. **Claim the barcode scanner for exclusive use**
|
||||
|
||||
Uses the [**ClaimScannerAsync**](http://msdn.microsoft.com/library/windows/apps/dn297696) to claim the device.
|
||||
Uses [**ClaimScannerAsync**](http://msdn.microsoft.com/library/windows/apps/dn297696) to claim the device.
|
||||
|
||||
3. **Add event handlers**
|
||||
|
||||
|
@ -41,13 +42,9 @@ To obtain information about Microsoft Visual Studio 2015 and the tools for devel
|
|||
|
||||
## Related topics
|
||||
|
||||
### Samples
|
||||
|
||||
[Barcode Scanner sample](/Samples/BarcodeScanner)
|
||||
|
||||
### Reference
|
||||
|
||||
[Windows.Devices.PointOfService](http://msdn.microsoft.com/library/windows/apps/dn298071)
|
||||
[Windows.Devices.PointOfService namespace](http://msdn.microsoft.com/library/windows/apps/dn298071)
|
||||
|
||||
[USB HID POS Scanner specification](http://go.microsoft.com/fwlink/p/?linkid=309230)
|
||||
|
||||
|
|
|
@ -50,14 +50,15 @@ void MainPage::NotifyUserFileNotExist()
|
|||
NotifyUser("The file '" + Filename + "' does not exist. Use scenario one to create this file.", NotifyType::ErrorMessage);
|
||||
}
|
||||
|
||||
void MainPage::HandleFileNotFoundException(Platform::COMException^ e)
|
||||
// I/O errors are reported as exceptions.
|
||||
void MainPage::HandleIoException(Platform::COMException^ e, Platform::String^ description)
|
||||
{
|
||||
if (e->HResult == 0x80070002) // Catch FileNotExistException
|
||||
if (e->HResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
NotifyUserFileNotExist();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
NotifyUser(description + ": " + e->Message, NotifyType::ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace SDKTemplate
|
|||
void Initialize();
|
||||
void ValidateFile();
|
||||
void NotifyUserFileNotExist();
|
||||
void HandleFileNotFoundException(Platform::COMException^ e);
|
||||
void HandleIoException(Platform::COMException^ e, Platform::String^ description);
|
||||
|
||||
private:
|
||||
static Platform::Array<Scenario>^ scenariosInner;
|
||||
|
|
|
@ -44,7 +44,7 @@ void Scenario10::DeleteFileButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
rootPage->HandleIoException(ex, "Error deleting file '" + fileName + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
using namespace SDKTemplate;
|
||||
|
||||
using namespace concurrency;
|
||||
using namespace Platform;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
||||
|
@ -27,9 +28,18 @@ Scenario1::Scenario1() : rootPage(MainPage::Current)
|
|||
|
||||
void Scenario1::CreateFileButton_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
create_task(KnownFolders::PicturesLibrary->CreateFileAsync(rootPage->Filename, CreationCollisionOption::ReplaceExisting)).then([this](StorageFile^ file)
|
||||
create_task(KnownFolders::PicturesLibrary->CreateFileAsync(rootPage->Filename, CreationCollisionOption::ReplaceExisting)).then([this](task<StorageFile^> task)
|
||||
{
|
||||
rootPage->SampleFile = file;
|
||||
rootPage->NotifyUser("The file '" + file->Name + "' was created.", NotifyType::StatusMessage);
|
||||
try
|
||||
{
|
||||
StorageFile^ file = task.get();
|
||||
rootPage->SampleFile = file;
|
||||
rootPage->NotifyUser("The file '" + file->Name + "' was created.", NotifyType::StatusMessage);
|
||||
}
|
||||
catch (Exception^ e)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage->NotifyUser("Error creating file '" + MainPage::Filename + "': " + e->Message, NotifyType::ErrorMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ void Scenario3::WriteTextButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
rootPage->HandleIoException(ex, "Error writing to '" + file->Name + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ void Scenario3::ReadTextButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
catch(COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
rootPage->HandleIoException(ex, "Error reading from '" + file->Name + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ void Scenario4::WriteBytesButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
rootPage->HandleIoException(ex, "Error writing to '" + file->Name + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ void Scenario4::ReadBytesButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
rootPage->HandleIoException(ex, "Error reading from '" + file->Name + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ void Scenario5::WriteToStreamButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
rootPage->HandleIoException(ex, "Error writing to '" + file->Name + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ void Scenario5::ReadFromStreamButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
rootPage->HandleIoException(ex, "Error reading from '" + file->Name + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -47,22 +47,13 @@ void Scenario6::ShowPropertiesButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
*outputText += "\nFile type: " + file->FileType;
|
||||
|
||||
// Get basic properties
|
||||
create_task(file->GetBasicPropertiesAsync()).then([this, outputText](task<BasicProperties^> task)
|
||||
create_task(file->GetBasicPropertiesAsync()).then([this, file, outputText](BasicProperties^ basicProperties)
|
||||
{
|
||||
try
|
||||
{
|
||||
BasicProperties^ basicProperties = task.get();
|
||||
*outputText += "\nFile size: " + basicProperties->Size.ToString() + " bytes";
|
||||
*outputText += "\nFile size: " + basicProperties->Size.ToString() + " bytes";
|
||||
|
||||
String^ dateModifiedString = dateFormat->Format(basicProperties->DateModified) + " " + timeFormat->Format(basicProperties->DateModified);
|
||||
*outputText += "\nDate modified: " + dateModifiedString;
|
||||
|
||||
String^ dateModifiedString = dateFormat->Format(basicProperties->DateModified) + " " + timeFormat->Format(basicProperties->DateModified);
|
||||
*outputText += "\nDate modified: " + dateModifiedString;
|
||||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
}
|
||||
}).then([this, file]()
|
||||
{
|
||||
// Get extra properties
|
||||
auto propertiesName = ref new Vector<String^>();
|
||||
propertiesName->Append(dateAccessedProperty);
|
||||
|
@ -83,6 +74,16 @@ void Scenario6::ShowPropertiesButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
|
||||
rootPage->NotifyUser(*outputText, NotifyType::StatusMessage);
|
||||
}).then([this, file](task<void> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
task.get();
|
||||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleIoException(ex, "Error retrieving properties for '" + file->Name + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
|
|
|
@ -42,11 +42,26 @@ void Scenario7::AddToListButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
rootPage->MruToken = StorageApplicationPermissions::MostRecentlyUsedList->Add(file, file->Name, visibility);
|
||||
rootPage->NotifyUser("The file '" + file->Name + "' was added to the MRU list and a token was stored.", NotifyType::StatusMessage);
|
||||
}
|
||||
else if (FALRadioButton->IsChecked->Value)
|
||||
else
|
||||
{
|
||||
// Add the file to the MRU
|
||||
rootPage->FalToken = StorageApplicationPermissions::FutureAccessList->Add(file, file->Name);
|
||||
rootPage->NotifyUser("The file '" + file->Name + "' was added to the FAL list and a token was stored.", NotifyType::StatusMessage);
|
||||
// Add the file to the FAL
|
||||
try
|
||||
{
|
||||
rootPage->FalToken = StorageApplicationPermissions::FutureAccessList->Add(file, file->Name);
|
||||
rootPage->NotifyUser("The file '" + file->Name + "' was added to the FAL list and a token was stored.", NotifyType::StatusMessage);
|
||||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
if (ex->HResult == FA_E_MAX_PERSISTED_ITEMS_REACHED)
|
||||
{
|
||||
// A real program would call Remove() to create room in the FAL.
|
||||
rootPage->NotifyUser("The file '" + file->Name + "' was not added to the FAL list because the FAL list is full.", NotifyType::ErrorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -57,115 +72,94 @@ void Scenario7::AddToListButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
|
||||
void Scenario7::ShowListButton_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
StorageFile^ file = rootPage->SampleFile;
|
||||
if (file != nullptr)
|
||||
AccessListEntryView^ entries = nullptr;
|
||||
String^ listName;
|
||||
|
||||
if (MRURadioButton->IsChecked->Value)
|
||||
{
|
||||
String^ outputText;
|
||||
NotifyType statusOrError = NotifyType::StatusMessage;
|
||||
if (MRURadioButton->IsChecked->Value)
|
||||
{
|
||||
AccessListEntryView^ entries = StorageApplicationPermissions::MostRecentlyUsedList->Entries;
|
||||
if (entries->Size > 0)
|
||||
{
|
||||
outputText = "The MRU list contains the following item(s):";
|
||||
std::for_each(begin(entries), end(entries), [this, &outputText](const AccessListEntry& entry)
|
||||
{
|
||||
outputText += "\n" + entry.Metadata; // Application previously chose to store sampleFile->Name in this field
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
outputText = "The MRU list is empty, please select 'Most Recently Used' list and click 'Add to List' to add a file to the MRU list.";
|
||||
statusOrError = NotifyType::ErrorMessage;
|
||||
}
|
||||
}
|
||||
else if (FALRadioButton->IsChecked->Value)
|
||||
{
|
||||
AccessListEntryView^ entries = StorageApplicationPermissions::FutureAccessList->Entries;
|
||||
if (entries->Size > 0)
|
||||
{
|
||||
outputText = "The FAL list contains the following item(s):";
|
||||
std::for_each(begin(entries), end(entries), [this, &outputText](const AccessListEntry& entry)
|
||||
{
|
||||
outputText += "\n" + entry.Metadata; // Application previously chose to store sampleFile->Name in this field
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
outputText = "The FAL list is empty, please select 'Future Access List' list and click 'Add to List' to add a file to the FAL list.";
|
||||
statusOrError = NotifyType::ErrorMessage;
|
||||
}
|
||||
}
|
||||
rootPage->NotifyUser(outputText, statusOrError);
|
||||
listName = "MRU";
|
||||
entries = StorageApplicationPermissions::MostRecentlyUsedList->Entries;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUserFileNotExist();
|
||||
listName = "FAL";
|
||||
entries = StorageApplicationPermissions::FutureAccessList->Entries;
|
||||
}
|
||||
|
||||
if (entries->Size > 0)
|
||||
{
|
||||
String^ outputText = "The " + listName + " + list contains the following item(s):";
|
||||
for (const AccessListEntry& entry : entries)
|
||||
{
|
||||
outputText += "\n" + entry.Metadata; // Application previously chose to store sampleFile->Name in this field
|
||||
}
|
||||
rootPage->NotifyUser(outputText, NotifyType::StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("The " + listName + " list is empty.", NotifyType::ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void Scenario7::OpenFromListButton_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
StorageFile^ file = rootPage->SampleFile;
|
||||
if (file != nullptr)
|
||||
task<StorageFile^> fileTask = task_from_result<StorageFile^>(nullptr);
|
||||
|
||||
if (MRURadioButton->IsChecked->Value)
|
||||
{
|
||||
if (MRURadioButton->IsChecked->Value)
|
||||
if (rootPage->MruToken != nullptr)
|
||||
{
|
||||
if (rootPage->MruToken != nullptr)
|
||||
// Open the file via the token that was stored when adding this file into the MRU list
|
||||
fileTask = create_task(StorageApplicationPermissions::MostRecentlyUsedList->GetFileAsync(rootPage->MruToken)).then([this](task<StorageFile^> task)
|
||||
{
|
||||
// Open the file via the token that was stored when adding this file into the MRU list
|
||||
create_task(StorageApplicationPermissions::MostRecentlyUsedList->GetFileAsync(rootPage->MruToken)).then([this](task<StorageFile^> task)
|
||||
StorageFile^ file = nullptr;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
StorageFile^ file = task.get();
|
||||
// Read the file
|
||||
create_task(FileIO::ReadTextAsync(file)).then([this, file](String^ fileContent)
|
||||
{
|
||||
rootPage->NotifyUser("The file '" + file->Name + "' was opened by a stored token from the MRU list, it contains the following text:\n" + fileContent, NotifyType::StatusMessage);
|
||||
});
|
||||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("The MRU list is empty, please select 'Most Recently Used' list and click 'Add to List' to add a file to the MRU list.", NotifyType::ErrorMessage);
|
||||
}
|
||||
file = task.get();
|
||||
}
|
||||
catch (InvalidArgumentException^)
|
||||
{
|
||||
// When the MRU becomes full, older entries are automatically deleted.
|
||||
rootPage->NotifyUser("The token is no longer valid.", NotifyType::ErrorMessage);
|
||||
}
|
||||
return file;
|
||||
});
|
||||
}
|
||||
else if (FALRadioButton->IsChecked->Value)
|
||||
else
|
||||
{
|
||||
if (rootPage->FalToken != nullptr)
|
||||
{
|
||||
// Open the file via the token that was stored when adding this file into the FAL list
|
||||
create_task(StorageApplicationPermissions::FutureAccessList->GetFileAsync(rootPage->FalToken)).then([this](task<StorageFile^> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
StorageFile^ file = task.get();
|
||||
// Read the file
|
||||
create_task(FileIO::ReadTextAsync(file)).then([this, file](String^ fileContent)
|
||||
{
|
||||
rootPage->NotifyUser("The file '" + file->Name + "' was opened by a stored token from the FAL list, it contains the following text:\n" + fileContent, NotifyType::StatusMessage);
|
||||
});
|
||||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("The FAL list is empty, please select 'Future Access List' list and click 'Add to List' to add a file to the FAL list.", NotifyType::ErrorMessage);
|
||||
}
|
||||
rootPage->NotifyUser("This operation requires a token. Add file to the MRU list first.", NotifyType::ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUserFileNotExist();
|
||||
if (rootPage->FalToken != nullptr)
|
||||
{
|
||||
// Open the file via the token that was stored when adding this file into the FAL list
|
||||
fileTask = create_task(StorageApplicationPermissions::FutureAccessList->GetFileAsync(rootPage->FalToken));
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("This operation requires a token. Add file to the FAL list first.", NotifyType::ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
fileTask.then([this](StorageFile^ file)
|
||||
{
|
||||
if (file != nullptr)
|
||||
{
|
||||
create_task(FileIO::ReadTextAsync(file)).then([this, file](task<String^> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
String^ fileContent = task.get();
|
||||
rootPage->NotifyUser("The file '" + file->Name + "' was opened by a stored token. It contains the following text:\n" + fileContent, NotifyType::StatusMessage);
|
||||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage->HandleIoException(ex, "Error reading file opened from list");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ void Scenario8::CopyFileButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
rootPage->HandleIoException(ex, "Error copying file '" + file->Name + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ void Scenario9::CompareFilesButton_Click(Object^ sender, RoutedEventArgs^ e)
|
|||
}
|
||||
catch (COMException^ ex)
|
||||
{
|
||||
rootPage->HandleFileNotFoundException(ex);
|
||||
rootPage->HandleIoException(ex, "Error determining whether two files are the same");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -53,6 +53,11 @@ namespace SDKTemplate
|
|||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error deleting file '{0}': {1}", file.Name, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -38,8 +38,16 @@ namespace SDKTemplate
|
|||
private async void CreateFileButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
StorageFolder storageFolder = KnownFolders.PicturesLibrary;
|
||||
rootPage.sampleFile = await storageFolder.CreateFileAsync(MainPage.filename, CreationCollisionOption.ReplaceExisting);
|
||||
rootPage.NotifyUser(String.Format("The file '{0}' was created.", rootPage.sampleFile.Name), NotifyType.StatusMessage);
|
||||
try
|
||||
{
|
||||
rootPage.sampleFile = await storageFolder.CreateFileAsync(MainPage.filename, CreationCollisionOption.ReplaceExisting);
|
||||
rootPage.NotifyUser(String.Format("The file '{0}' was created.", rootPage.sampleFile.Name), NotifyType.StatusMessage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error creating file '{0}': {1}", MainPage.filename, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,11 @@ namespace SDKTemplate
|
|||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error writing to '{0}': {1}", file.Name, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -81,6 +86,11 @@ namespace SDKTemplate
|
|||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error reading from '{0}': {1}", file.Name, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -74,6 +74,11 @@ namespace SDKTemplate
|
|||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error writing to '{0}': {1}", file.Name, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -99,6 +104,11 @@ namespace SDKTemplate
|
|||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error reading from '{0}': {1}", file.Name, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -69,6 +69,11 @@ namespace SDKTemplate
|
|||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error writing to '{0}': {1}", file.Name, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -105,6 +110,11 @@ namespace SDKTemplate
|
|||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error reading from '{0}': {1}", file.Name, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -82,6 +82,11 @@ namespace SDKTemplate
|
|||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error retrieving properties for '{0}': {1}", file.Name, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -28,6 +28,8 @@ namespace SDKTemplate
|
|||
{
|
||||
MainPage rootPage;
|
||||
|
||||
const int FA_E_MAX_PERSISTED_ITEMS_REACHED = unchecked((int)0x80270220);
|
||||
|
||||
public Scenario7()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
@ -54,10 +56,18 @@ namespace SDKTemplate
|
|||
rootPage.mruToken = StorageApplicationPermissions.MostRecentlyUsedList.Add(file, file.Name, visibility);
|
||||
rootPage.NotifyUser(String.Format("The file '{0}' was added to the MRU list and a token was stored.", file.Name), NotifyType.StatusMessage);
|
||||
}
|
||||
else if (FALRadioButton.IsChecked.Value)
|
||||
else
|
||||
{
|
||||
rootPage.falToken = StorageApplicationPermissions.FutureAccessList.Add(file, file.Name);
|
||||
rootPage.NotifyUser(String.Format("The file '{0}' was added to the FAL list and a token was stored.", file.Name), NotifyType.StatusMessage);
|
||||
try
|
||||
{
|
||||
rootPage.falToken = StorageApplicationPermissions.FutureAccessList.Add(file, file.Name);
|
||||
rootPage.NotifyUser(String.Format("The file '{0}' was added to the FAL list and a token was stored.", file.Name), NotifyType.StatusMessage);
|
||||
}
|
||||
catch (Exception ex) when (ex.HResult == FA_E_MAX_PERSISTED_ITEMS_REACHED)
|
||||
{
|
||||
// A real program would call Remove() to create room in the FAL.
|
||||
rootPage.NotifyUser(String.Format("The file '{0}' was not added to the FAL list because the FAL list is full.", file.Name), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -68,101 +78,87 @@ namespace SDKTemplate
|
|||
|
||||
private void ShowListButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
StorageFile file = rootPage.sampleFile;
|
||||
if (file != null)
|
||||
AccessListEntryView entries;
|
||||
string listName;
|
||||
if (MRURadioButton.IsChecked.Value)
|
||||
{
|
||||
if (MRURadioButton.IsChecked.Value)
|
||||
{
|
||||
AccessListEntryView entries = StorageApplicationPermissions.MostRecentlyUsedList.Entries;
|
||||
if (entries.Count > 0)
|
||||
{
|
||||
StringBuilder outputText = new StringBuilder("The MRU list contains the following item(s):");
|
||||
foreach (AccessListEntry entry in entries)
|
||||
{
|
||||
outputText.AppendLine();
|
||||
outputText.Append(entry.Metadata); // Application previously chose to store file.Name in this field
|
||||
}
|
||||
|
||||
rootPage.NotifyUser(outputText.ToString(), NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("The MRU list is empty, please select 'Most Recently Used' list and click 'Add to List' to add a file to the MRU list.", NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else if (FALRadioButton.IsChecked.Value)
|
||||
{
|
||||
AccessListEntryView entries = StorageApplicationPermissions.FutureAccessList.Entries;
|
||||
if (entries.Count > 0)
|
||||
{
|
||||
StringBuilder outputText = new StringBuilder("The FAL list contains the following item(s):");
|
||||
foreach (AccessListEntry entry in entries)
|
||||
{
|
||||
outputText.AppendLine();
|
||||
outputText.Append(entry.Metadata); // Application previously chose to store file.Name in this field
|
||||
}
|
||||
|
||||
rootPage.NotifyUser(outputText.ToString(), NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("The FAL list is empty, please select 'Future Access List' list and click 'Add to List' to add a file to the FAL list.", NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
listName = "MRU";
|
||||
entries = StorageApplicationPermissions.MostRecentlyUsedList.Entries;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
listName = "FAL";
|
||||
entries = StorageApplicationPermissions.FutureAccessList.Entries;
|
||||
}
|
||||
|
||||
if (entries.Count > 0)
|
||||
{
|
||||
StringBuilder outputText = new StringBuilder("The " + listName + " list contains the following item(s):");
|
||||
foreach (AccessListEntry entry in entries)
|
||||
{
|
||||
outputText.AppendLine();
|
||||
outputText.Append(entry.Metadata); // Application previously chose to store file.Name in this field
|
||||
}
|
||||
|
||||
rootPage.NotifyUser(outputText.ToString(), NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("The " + listName + " list is empty.", NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OpenFromListButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (rootPage.sampleFile != null)
|
||||
StorageFile file = null;
|
||||
|
||||
if (MRURadioButton.IsChecked.Value)
|
||||
{
|
||||
try
|
||||
if (rootPage.mruToken != null)
|
||||
{
|
||||
if (MRURadioButton.IsChecked.Value)
|
||||
try
|
||||
{
|
||||
if (rootPage.mruToken != null)
|
||||
{
|
||||
// Open the file via the token that was stored when adding this file into the MRU list
|
||||
StorageFile file = await StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(rootPage.mruToken);
|
||||
|
||||
// Read the file
|
||||
string fileContent = await FileIO.ReadTextAsync(file);
|
||||
rootPage.NotifyUser(String.Format("The file '{0}' was opened by a stored token from the MRU list, it contains the following text:{1}{2}", file.Name, Environment.NewLine, fileContent), NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("The MRU list is empty, please select 'Most Recently Used' list and click 'Add to List' to add a file to the MRU list.", NotifyType.ErrorMessage);
|
||||
}
|
||||
// Open the file via the token that was stored when adding this file into the MRU list
|
||||
file = await StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(rootPage.mruToken);
|
||||
}
|
||||
else if (FALRadioButton.IsChecked.Value)
|
||||
catch (ArgumentException)
|
||||
{
|
||||
if (rootPage.falToken != null)
|
||||
{
|
||||
// Open the file via the token that was stored when adding this file into the FAL list
|
||||
StorageFile file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(rootPage.falToken);
|
||||
|
||||
// Read the file
|
||||
string fileContent = await FileIO.ReadTextAsync(file);
|
||||
rootPage.NotifyUser(String.Format("The file '{0}' was opened by a stored token from the FAL list, it contains the following text:{1}{2}", file.Name, Environment.NewLine, fileContent), NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("The FAL list is empty, please select 'Future Access List' list and click 'Add to List' to add a file to the FAL list.", NotifyType.ErrorMessage);
|
||||
}
|
||||
// When the MRU becomes full, older entries are automatically deleted.
|
||||
rootPage.NotifyUser("The token is no longer valid.", NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
rootPage.NotifyUser("This operation requires a token. Add file to the MRU list first.", NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
if (rootPage.falToken != null)
|
||||
{
|
||||
// Open the file via the token that was stored when adding this file into the FAL list.
|
||||
file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(rootPage.falToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("This operation requires a token. Add file to the FAL list first.", NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (file != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Read the file
|
||||
string fileContent = await FileIO.ReadTextAsync(file);
|
||||
rootPage.NotifyUser(String.Format("The file '{0}' was opened by a stored token. It contains the following text:{1}{2}", file.Name, Environment.NewLine, fileContent), NotifyType.StatusMessage);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error reading file opened from list: {0}", ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,11 @@ namespace SDKTemplate
|
|||
{
|
||||
rootPage.NotifyUserFileNotExist();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// I/O errors are reported as exceptions.
|
||||
rootPage.NotifyUser(String.Format("Error copying file '{0}': {1}", file.Name, ex.Message), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
function writeText() {
|
||||
if (SdkSample.sampleFile !== null) {
|
||||
var textArea = document.getElementById("textarea");
|
||||
var userContent = textArea.innerText;
|
||||
var userContent = textArea.value;
|
||||
if (userContent !== "") {
|
||||
Windows.Storage.FileIO.writeTextAsync(SdkSample.sampleFile, userContent).done(function () {
|
||||
WinJS.log && WinJS.log("The following text was written to '" + SdkSample.sampleFile.name + "':\n" + userContent, "sample", "status");
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
function writeBytes() {
|
||||
if (SdkSample.sampleFile !== null) {
|
||||
var textArea = document.getElementById("textarea");
|
||||
var userContent = textArea.innerText;
|
||||
var userContent = textArea.value;
|
||||
if (userContent !== "") {
|
||||
var buffer = getBufferFromString(userContent);
|
||||
Windows.Storage.FileIO.writeBufferAsync(SdkSample.sampleFile, buffer).done(function () {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
function writeToStream() {
|
||||
if (SdkSample.sampleFile !== null) {
|
||||
var textArea = document.getElementById("textarea");
|
||||
var userContent = textArea.innerText;
|
||||
var userContent = textArea.value;
|
||||
if (userContent !== "") {
|
||||
SdkSample.sampleFile.openTransactedWriteAsync().then(function (transaction) {
|
||||
var dataWriter = new Windows.Storage.Streams.DataWriter(transaction.stream);
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
var FA_E_MAX_PERSISTED_ITEMS_REACHED = 0x80270220 | 0;
|
||||
var E_INVALIDARG = 0x80070057 | 0;
|
||||
|
||||
var page = WinJS.UI.Pages.define("/html/scenario7_TrackAFileOrFolderSoThatYouCanAccessItLater.html", {
|
||||
ready: function (element, options) {
|
||||
document.getElementById("addToList").addEventListener("click", addToList, false);
|
||||
document.getElementById("showList").addEventListener("click", showList, false);
|
||||
document.getElementById("openFromList").addEventListener("click", openFromList, false);
|
||||
SdkSample.validateFileExistence();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -16,7 +18,6 @@
|
|||
if (SdkSample.sampleFile !== null) {
|
||||
var MRUradio = document.getElementById("MRUradio");
|
||||
var systemMRUcheckbox = document.getElementById("systemMRUcheckbox");
|
||||
var FALradio = document.getElementById("FALradio");
|
||||
if (MRUradio.checked) {
|
||||
// Add the file to app and possibly system MRU
|
||||
var visibility = systemMRUcheckbox.checked ?
|
||||
|
@ -25,86 +26,86 @@
|
|||
SdkSample.mruToken = Windows.Storage.AccessCache.StorageApplicationPermissions.mostRecentlyUsedList.add(SdkSample.sampleFile, SdkSample.sampleFile.name,
|
||||
visibility);
|
||||
WinJS.log && WinJS.log("The file '" + SdkSample.sampleFile.name + "' was added to the MRU list and a token was stored.", "sample", "status");
|
||||
} else if (FALradio.checked) {
|
||||
SdkSample.falToken = Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.add(SdkSample.sampleFile, SdkSample.sampleFile.name);
|
||||
WinJS.log && WinJS.log("The file '" + SdkSample.sampleFile.name + "' was added to the FAL list and a token was stored.", "sample", "status");
|
||||
} else {
|
||||
try {
|
||||
SdkSample.falToken = Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.add(SdkSample.sampleFile, SdkSample.sampleFile.name);
|
||||
WinJS.log && WinJS.log("The file '" + SdkSample.sampleFile.name + "' was added to the FAL list and a token was stored.", "sample", "status");
|
||||
} catch (error) {
|
||||
if (error.number == FA_E_MAX_PERSISTED_ITEMS_REACHED) {
|
||||
// A real program would call remove() to create room in the FAL.
|
||||
WinJS.log && WinJS.log("The file '" + SdkSample.sampleFile.name + "' was not added to the FAL list because the FAL list is full.", "sample", "error");
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SdkSample.validateFileExistence();
|
||||
}
|
||||
}
|
||||
|
||||
function showList() {
|
||||
if (SdkSample.sampleFile !== null) {
|
||||
var MRUradio = document.getElementById("MRUradio");
|
||||
var FALradio = document.getElementById("FALradio");
|
||||
if (MRUradio.checked) {
|
||||
var mruEntries = Windows.Storage.AccessCache.StorageApplicationPermissions.mostRecentlyUsedList.entries;
|
||||
if (mruEntries.size > 0) {
|
||||
var mruOutputText = "The MRU list contains the following item(s):";
|
||||
mruEntries.forEach(function (entry) {
|
||||
mruOutputText += "\n" + entry.metadata; // Application previously chose to store sampleFile.name in this field
|
||||
});
|
||||
var entries;
|
||||
var listName;
|
||||
var MRUradio = document.getElementById("MRUradio");
|
||||
if (MRUradio.checked) {
|
||||
listName = "MRU";
|
||||
entries = Windows.Storage.AccessCache.StorageApplicationPermissions.mostRecentlyUsedList.entries;
|
||||
} else {
|
||||
listName = "FAL";
|
||||
entries = Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.entries;
|
||||
}
|
||||
|
||||
WinJS.log && WinJS.log(mruOutputText, "sample", "status");
|
||||
} else {
|
||||
WinJS.log && WinJS.log("The MRU list is empty, please select 'Most Recently Used' and click 'Add to List' to add a file to the MRU list.", "sample", "error");;
|
||||
}
|
||||
} else if (FALradio.checked) {
|
||||
var falEntries = Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.entries;
|
||||
if (falEntries.size > 0) {
|
||||
var falOutputText = "The FAL list contains the following item(s):";
|
||||
falEntries.forEach(function (entry) {
|
||||
falOutputText += "\n" + entry.metadata; // Application previously chose to store sampleFile.name in this field
|
||||
});
|
||||
if (entries.size > 0) {
|
||||
var outputText = "The " + listName + " list contains the following item(s):";
|
||||
entries.forEach(function (entry) {
|
||||
outputText += "\n" + entry.metadata; // Application previously chose to store sampleFile.name in this field
|
||||
});
|
||||
|
||||
WinJS.log && WinJS.log(falOutputText, "sample", "status");
|
||||
} else {
|
||||
WinJS.log && WinJS.log("The FAL list is empty, please select 'Future Access List' and click 'Add to List' to add a file to the FAL list.", "sample", "error");
|
||||
}
|
||||
}
|
||||
WinJS.log && WinJS.log(outputText, "sample", "status");
|
||||
} else {
|
||||
WinJS.log && WinJS.log("The " + listName + " list is empty.", "sample", "error");
|
||||
}
|
||||
}
|
||||
|
||||
function openFromList() {
|
||||
if (SdkSample.sampleFile !== null) {
|
||||
var MRUradio = document.getElementById("MRUradio");
|
||||
var FALradio = document.getElementById("FALradio");
|
||||
if (MRUradio.checked) {
|
||||
if (SdkSample.mruToken !== null) {
|
||||
// Open the 'sample.dat' via the token that was stored when adding this file into the MRU list
|
||||
Windows.Storage.AccessCache.StorageApplicationPermissions.mostRecentlyUsedList.getFileAsync(SdkSample.mruToken).then(function (file) {
|
||||
// Read the file
|
||||
Windows.Storage.FileIO.readTextAsync(file).done(function (fileContent) {
|
||||
WinJS.log && WinJS.log("The file '" + file.name + "' was opened by a stored token from the MRU list, it contains the following text:\n" + fileContent, "sample", "status");
|
||||
},
|
||||
function (error) {
|
||||
WinJS.log && WinJS.log(error, "sample", "error");
|
||||
});
|
||||
},
|
||||
function (error) {
|
||||
WinJS.log && WinJS.log(error, "sample", "error");
|
||||
});
|
||||
} else {
|
||||
WinJS.log && WinJS.log("The MRU list is empty, please select 'Most Recently Used' list and click 'Add to List' to add a file to the MRU list.", "sample", "error");
|
||||
}
|
||||
} else if (FALradio.checked) {
|
||||
if (SdkSample.falToken !== null) {
|
||||
// Open the 'sample.dat' via the token that was stored when adding this file into the FAL list
|
||||
Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.getFileAsync(SdkSample.falToken).then(function (file) {
|
||||
// Read the file
|
||||
Windows.Storage.FileIO.readTextAsync(file).done(function (fileContent) {
|
||||
WinJS.log && WinJS.log("The file '" + file.name + "' was opened by a stored token from the FAL list, it contains the following text:\n" + fileContent, "sample", "status");
|
||||
},
|
||||
function (error) {
|
||||
WinJS.log && WinJS.log(error, "sample", "error");
|
||||
});
|
||||
},
|
||||
function (error) {
|
||||
WinJS.log && WinJS.log(error, "sample", "error");
|
||||
});
|
||||
} else {
|
||||
WinJS.log && WinJS.log("The FAL list is empty, please select 'Future Access List' list and click 'Add to List' to add a file to the FAL list.", "sample", "error");
|
||||
var fileTask = WinJS.Promise.wrap();
|
||||
var MRUradio = document.getElementById("MRUradio");
|
||||
if (MRUradio.checked) {
|
||||
if (SdkSample.mruToken !== null) {
|
||||
// Open the 'sample.dat' via the token that was stored when adding this file into the MRU list
|
||||
try {
|
||||
fileTask = Windows.Storage.AccessCache.StorageApplicationPermissions.mostRecentlyUsedList.getFileAsync(SdkSample.mruToken);
|
||||
} catch (error) {
|
||||
// When the MRU becomes full, older entries are automatically deleted.
|
||||
if (error.number == E_INVALIDARG) {
|
||||
WinJS.log && WinJS.log("The token is no longer valid.", "sample", "error");
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WinJS.log && WinJS.log("This operation requires a token. Add file to the MRU list first.", "sample", "error");
|
||||
}
|
||||
} else {
|
||||
if (SdkSample.falToken !== null) {
|
||||
// Open the 'sample.dat' via the token that was stored when adding this file into the FAL list
|
||||
fileTask = Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.getFileAsync(SdkSample.falToken);
|
||||
} else {
|
||||
WinJS.log && WinJS.log("This operation requires a token. Add file to the FAL list first.", "sample", "error");
|
||||
}
|
||||
}
|
||||
|
||||
fileTask.done(function (file) {
|
||||
if (file) {
|
||||
// Read the file
|
||||
Windows.Storage.FileIO.readTextAsync(file).done(function (fileContent) {
|
||||
WinJS.log && WinJS.log("The file '" + file.name + "' was opened by a stored token. It contains the following text:\n" + fileContent, "sample", "status");
|
||||
},
|
||||
function (error) {
|
||||
WinJS.log && WinJS.log(error, "sample", "error");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -44,7 +44,7 @@ To obtain information about Microsoft Visual Studio 2015 and the tools for devel
|
|||
### Samples
|
||||
|
||||
* [**Calendar** sample](../Calendar)
|
||||
* [**DateTimeFormatting** sample](../DatetimeFormatting)
|
||||
* [**DateTimeFormatting** sample](../DateTimeFormatting)
|
||||
* [**NumberFormatting** sample](../NumberFormatting)
|
||||
|
||||
### Reference
|
||||
|
|
|
@ -14,11 +14,77 @@
|
|||
#include "SampleConfiguration.h"
|
||||
|
||||
using namespace SDKTemplate;
|
||||
using namespace Concurrency;
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Networking::Sockets;
|
||||
using namespace Windows::Security::Cryptography::Certificates;
|
||||
using namespace Windows::Web;
|
||||
|
||||
Platform::Array<Scenario>^ MainPage::scenariosInner = ref new Platform::Array<Scenario>
|
||||
{
|
||||
// The format here is the following:
|
||||
// { "Description for the sample", "Fully qualified name for the class that implements the scenario" }
|
||||
{ "UTF-8 text messages", "SDKTemplate.WebSocket.Scenario1" },
|
||||
{ "Binary data stream", "SDKTemplate.WebSocket.Scenario2" }
|
||||
{ "UTF-8 text messages", "SDKTemplate.Scenario1" },
|
||||
{ "Binary data stream", "SDKTemplate.Scenario2" }
|
||||
};
|
||||
|
||||
Uri^ MainPage::TryGetUri(String^ uriString)
|
||||
{
|
||||
Windows::Foundation::Uri^ webSocketUri;
|
||||
|
||||
// Create a Uri instance and catch exceptions related to invalid input. This method returns 'true'
|
||||
// if the Uri instance was successfully created and 'false' otherwise.
|
||||
try
|
||||
{
|
||||
webSocketUri = ref new Uri(uriString);
|
||||
}
|
||||
catch (NullReferenceException^)
|
||||
{
|
||||
NotifyUser("Error: URI must not be null or empty.", NotifyType::ErrorMessage);
|
||||
return nullptr;
|
||||
}
|
||||
catch (InvalidArgumentException^)
|
||||
{
|
||||
NotifyUser("Error: Invalid URI", NotifyType::ErrorMessage);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (webSocketUri->Fragment != "")
|
||||
{
|
||||
NotifyUser("Error: URI fragments not supported in WebSocket URIs.", NotifyType::ErrorMessage);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Uri->SchemeName returns the canonicalized scheme name so we can use case-sensitive, ordinal string
|
||||
// comparison.
|
||||
if (webSocketUri->SchemeName != "ws" && webSocketUri->SchemeName != "wss")
|
||||
{
|
||||
NotifyUser("Error: WebSockets only support ws:// and wss:// schemes.", NotifyType::ErrorMessage);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return webSocketUri;
|
||||
}
|
||||
|
||||
String^ MainPage::BuildWebSocketError(Exception^ ex)
|
||||
{
|
||||
// Normally we'd use the HResult and status to test for specific conditions we want to handle.
|
||||
// In this sample, we'll just output them for demonstration purposes.
|
||||
|
||||
WebErrorStatus status = WebSocketError::GetStatus(ex->HResult);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case WebErrorStatus::CannotConnect:
|
||||
case WebErrorStatus::NotFound:
|
||||
case WebErrorStatus::RequestTimeout:
|
||||
return "Cannot connect to the server. Please make sure " +
|
||||
"to run the server setup script before running the sample.";
|
||||
|
||||
case WebErrorStatus::Unknown:
|
||||
return "COM error: " + ex->HResult.ToString();
|
||||
|
||||
default:
|
||||
return "Error: " + status.ToString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace SDKTemplate
|
|||
{
|
||||
Platform::String^ get()
|
||||
{
|
||||
return ref new Platform::String(L"WebSocket");
|
||||
return "WebSocket C++ Sample";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,76 +40,11 @@ namespace SDKTemplate
|
|||
}
|
||||
}
|
||||
|
||||
bool TryGetUri(Platform::String^ uriString, Windows::Foundation::Uri^* uri)
|
||||
{
|
||||
*uri = nullptr;
|
||||
|
||||
Windows::Foundation::Uri^ webSocketUri;
|
||||
|
||||
// Create a Uri instance and catch exceptions related to invalid input. This method returns 'true'
|
||||
// if the Uri instance was successfully created and 'false' otherwise.
|
||||
try
|
||||
{
|
||||
webSocketUri = ref new Windows::Foundation::Uri(StringTrimmer::Trim(uriString));
|
||||
}
|
||||
catch (Platform::NullReferenceException^ exception)
|
||||
{
|
||||
NotifyUser("Error: URI must not be null or empty.", NotifyType::ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
catch (Platform::InvalidArgumentException^ exception)
|
||||
{
|
||||
NotifyUser("Error: Invalid URI", NotifyType::ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (webSocketUri->Fragment != "")
|
||||
{
|
||||
NotifyUser("Error: URI fragments not supported in WebSocket URIs.", NotifyType::ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
Platform::String^ wsScheme = "ws";
|
||||
Platform::String^ wssScheme = "wss";
|
||||
Platform::String^ scheme = webSocketUri->SchemeName;
|
||||
|
||||
// Uri->SchemeName returns the canonicalized scheme name so we can use case-sensitive, ordinal string
|
||||
// comparison.
|
||||
if ((CompareStringOrdinal(scheme->Begin(), scheme->Length(), wsScheme->Begin(), wsScheme->Length(), false) != CSTR_EQUAL) &&
|
||||
(CompareStringOrdinal(scheme->Begin(), scheme->Length(), wssScheme->Begin(), wssScheme->Length(), false) != CSTR_EQUAL))
|
||||
{
|
||||
NotifyUser("Error: WebSockets only support ws:// and wss:// schemes.", NotifyType::ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
*uri = webSocketUri;
|
||||
|
||||
return true;
|
||||
}
|
||||
internal:
|
||||
Windows::Foundation::Uri^ TryGetUri(Platform::String^ uriString);
|
||||
static Platform::String^ BuildWebSocketError(Platform::Exception^ ex);
|
||||
|
||||
private:
|
||||
static Platform::Array<Scenario>^ scenariosInner;
|
||||
};
|
||||
|
||||
class StringTrimmer
|
||||
{
|
||||
public:
|
||||
static Platform::String^ Trim(Platform::String^ s)
|
||||
{
|
||||
const WCHAR* first = s->Begin();
|
||||
const WCHAR* last = s->End();
|
||||
|
||||
while (first != last && iswspace(*first))
|
||||
{
|
||||
++first;
|
||||
}
|
||||
|
||||
while (first != last && iswspace(last[-1]))
|
||||
{
|
||||
--last;
|
||||
}
|
||||
|
||||
return ref new Platform::String(first, static_cast<unsigned int>(last - first));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page x:Class="SDKTemplate.WebSocket.Scenario1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:SDKTemplate.WebSocket" xmlns:common="using:SDKTemplate.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
|
||||
<Grid x:Name="LayoutRoot" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="Input" Grid.Row="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap" HorizontalAlignment="Left"> This shows how to use a MessageWebSocket to send UTF-8 strings.</TextBlock>
|
||||
<TextBlock Grid.Row="1" Text="Server Address:" HorizontalAlignment="Left" Style="{StaticResource BasicTextStyle}"/>
|
||||
<TextBox Grid.Row="2" Name="ServerAddressField" InputScope="Url" Text="ws://localhost/WebSocketSample/EchoWebSocket.ashx" HorizontalAlignment="Stretch"/>
|
||||
<TextBlock Grid.Row="3" TextWrapping="Wrap" HorizontalAlignment="Left" Style="{StaticResource BasicTextStyle}">Enter text to send to the server.</TextBlock>
|
||||
<TextBox Grid.Row="4" Name="InputField" Text="Hello World" TextWrapping="Wrap" AcceptsReturn="True" HorizontalAlignment="Stretch"/>
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button Name="StartButton" Content="Start" Margin="0,0,10,0" Click="Start_Click"/>
|
||||
<Button Name="CloseButton" Content="Close" Margin="0,0,10,0" Click="Close_Click"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="6" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Name="OutputField" HorizontalAlignment="Stretch" MaxHeight="160" TextWrapping="Wrap" IsReadOnly="True"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<!-- Add Storyboards to the visual states below as necessary for supporting the various layouts -->
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="DefaultLayout"/>
|
||||
<VisualState x:Name="Below768Layout"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,217 +0,0 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
//
|
||||
// Scenario1.xaml.cpp
|
||||
// Implementation of the Scenario1 class
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
#include <strsafe.h>
|
||||
#include "Scenario1.xaml.h"
|
||||
|
||||
using namespace concurrency;
|
||||
using namespace SDKTemplate::WebSocket;
|
||||
using namespace Windows::UI::Core;
|
||||
|
||||
Scenario1::Scenario1()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
Scenario1::~Scenario1()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this page is about to be displayed in a Frame.
|
||||
/// </summary>
|
||||
/// <param name="e">Event data that describes how this page was reached. The Parameter
|
||||
/// property is typically used to configure the page.</param>
|
||||
void Scenario1::OnNavigatedTo(NavigationEventArgs^ e)
|
||||
{
|
||||
// A pointer back to the main page. This is needed if you want to call methods in MainPage such
|
||||
// as NotifyUser()
|
||||
rootPage = MainPage::Current;
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario1::Start_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
|
||||
{
|
||||
// Make a local copy to avoid races with Closed events.
|
||||
MessageWebSocket^ webSocket = messageWebSocket;
|
||||
|
||||
if (InputField->Text == "")
|
||||
{
|
||||
rootPage->NotifyUser("Please specify text to send", NotifyType::ErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Have we connected yet?
|
||||
if (webSocket == nullptr)
|
||||
{
|
||||
// Validating the URI is required since it was received from an untrusted source (user input).
|
||||
// The URI is validated by calling TryGetUri() that will return 'false' for strings that are not
|
||||
// valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines on the intrAnet or intErnet. In
|
||||
// these cases the app requires the "Home or Work Networking" or "Internet (Client)" capability respectively.
|
||||
Uri^ server;
|
||||
if (!rootPage->TryGetUri(ServerAddressField->Text, &server))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rootPage->NotifyUser("Connecting to: " + server->DisplayUri, NotifyType::StatusMessage);
|
||||
|
||||
webSocket = ref new MessageWebSocket();
|
||||
webSocket->Control->MessageType = SocketMessageType::Utf8;
|
||||
webSocket->MessageReceived += ref new TypedEventHandler<MessageWebSocket^, MessageWebSocketMessageReceivedEventArgs^>(this, &Scenario1::MessageReceived);
|
||||
webSocket->Closed += ref new TypedEventHandler<IWebSocket^, WebSocketClosedEventArgs^>(this, &Scenario1::Closed);
|
||||
|
||||
task<void>(webSocket->ConnectAsync(server)).then([this, webSocket] (task<void> previousTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try getting all exceptions from the continuation chain above this point.
|
||||
previousTask.get();
|
||||
|
||||
messageWebSocket = webSocket; // Only store it after successfully connecting.
|
||||
messageWriter = ref new DataWriter(webSocket->OutputStream);
|
||||
|
||||
rootPage->NotifyUser("Connected", NotifyType::StatusMessage);
|
||||
SendMessage();
|
||||
}
|
||||
catch (Exception^ exception)
|
||||
{
|
||||
HandleException(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("Already connected", NotifyType::StatusMessage);
|
||||
SendMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario1::HandleException(Exception^ exception)
|
||||
{
|
||||
WebErrorStatus status = WebSocketError::GetStatus(exception->HResult);
|
||||
|
||||
if (status == WebErrorStatus::CannotConnect ||
|
||||
status == WebErrorStatus::NotFound ||
|
||||
status == WebErrorStatus::RequestTimeout)
|
||||
{
|
||||
rootPage->NotifyUser("Cannot connect to the server. Please make sure "
|
||||
"to run the server setup script before running the sample.", NotifyType::ErrorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("Error: " + status.ToString(), NotifyType::ErrorMessage);
|
||||
}
|
||||
|
||||
OutputField->Text += exception->Message + "\r\n";
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario1::SendMessage()
|
||||
{
|
||||
String^ message = InputField->Text;
|
||||
|
||||
OutputField->Text += "Sending Message:\r\n" + message + "\r\n";
|
||||
|
||||
// Buffer any data we want to send.
|
||||
messageWriter->WriteString(message);
|
||||
|
||||
// Send the data as one complete message.
|
||||
task<unsigned int>(messageWriter->StoreAsync()).then([this] (unsigned int)
|
||||
{
|
||||
rootPage->NotifyUser("Send Complete", NotifyType::StatusMessage);
|
||||
}).then([this] (task<void> previousTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try getting all exceptions from the continuation chain above this point.
|
||||
previousTask.get();
|
||||
}
|
||||
catch (Exception^ exception)
|
||||
{
|
||||
HandleException(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario1::MessageReceived(MessageWebSocket^ sender, MessageWebSocketMessageReceivedEventArgs^ args)
|
||||
{
|
||||
MarshalText(OutputField, "Message Received; Type: " + args->MessageType.ToString() + "\r\n");
|
||||
DataReader^ reader = args->GetDataReader();
|
||||
reader->UnicodeEncoding = UnicodeEncoding::Utf8;
|
||||
|
||||
String^ read = reader->ReadString(reader->UnconsumedBufferLength);
|
||||
MarshalText(OutputField, read + "\r\n");
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario1::Close_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (messageWebSocket != nullptr)
|
||||
{
|
||||
rootPage->NotifyUser("Closing", NotifyType::StatusMessage);
|
||||
messageWebSocket->Close(1000, "Closed due to user request.");
|
||||
messageWebSocket = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("No active WebSocket, send something first", NotifyType::StatusMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception^ exception)
|
||||
{
|
||||
HandleException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario1::Closed(IWebSocket^ sender, WebSocketClosedEventArgs^ args)
|
||||
{
|
||||
// The method may be triggered remotely by the server sending unsolicited close frame or locally by Close()/delete operator.
|
||||
// Dispatch the event to the UI thread so we do not need to synchronize access to messageWebSocket.
|
||||
Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, args] ()
|
||||
{
|
||||
OutputField->Text += "Closed; Code: " + args->Code.ToString() + ", Reason: " + args->Reason + "\r\n";
|
||||
|
||||
if (messageWebSocket != nullptr)
|
||||
{
|
||||
delete messageWebSocket;
|
||||
messageWebSocket = nullptr;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario1::MarshalText(TextBox^ output, String^ value)
|
||||
{
|
||||
MarshalText(output, value, true);
|
||||
}
|
||||
|
||||
// When operations happen on a background thread we have to marshal UI updates back to the UI thread.
|
||||
void SDKTemplate::WebSocket::Scenario1::MarshalText(TextBox^ output, String^ value, bool append)
|
||||
{
|
||||
Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, output, value, append] ()
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
output->Text += value;
|
||||
}
|
||||
else
|
||||
{
|
||||
output->Text = value;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
//
|
||||
// Scenario1.xaml.h
|
||||
// Declaration of the Scenario1 class
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "Scenario1.g.h"
|
||||
#include "MainPage.xaml.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Networking::Sockets;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Navigation;
|
||||
using namespace Windows::Web;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
namespace WebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class Scenario1 sealed
|
||||
{
|
||||
public:
|
||||
Scenario1();
|
||||
|
||||
protected:
|
||||
virtual void OnNavigatedTo(NavigationEventArgs^ e) override;
|
||||
private:
|
||||
~Scenario1();
|
||||
|
||||
MainPage^ rootPage;
|
||||
MessageWebSocket^ messageWebSocket;
|
||||
DataWriter^ messageWriter;
|
||||
CRITICAL_SECTION criticalSection;
|
||||
|
||||
void Start_Click(Object^ sender, RoutedEventArgs^ e);
|
||||
void Close_Click(Object^ sender, RoutedEventArgs^ e);
|
||||
void SendMessage();
|
||||
void MessageReceived(MessageWebSocket^ sender, MessageWebSocketMessageReceivedEventArgs^ args);
|
||||
void Closed(IWebSocket^ sender, WebSocketClosedEventArgs^ args);
|
||||
void MarshalText(TextBox^ output, String^ value);
|
||||
void MarshalText(TextBox^ output, String^ value, bool append);
|
||||
void HandleException(Exception^ exception);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
//
|
||||
// Scenario1.xaml.cpp
|
||||
// Implementation of the Scenario1 class
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
#include "Scenario1_UTF8.xaml.h"
|
||||
|
||||
using namespace concurrency;
|
||||
using namespace Platform;
|
||||
using namespace SDKTemplate;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Networking::Sockets;
|
||||
using namespace Windows::Security::Cryptography::Certificates;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Navigation;
|
||||
using namespace Windows::Web;
|
||||
|
||||
Scenario1::Scenario1()
|
||||
{
|
||||
InitializeComponent();
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
void Scenario1::OnNavigatedFrom(NavigationEventArgs^ e)
|
||||
{
|
||||
CloseSocket();
|
||||
}
|
||||
|
||||
void Scenario1::UpdateVisualState()
|
||||
{
|
||||
if (busy)
|
||||
{
|
||||
VisualStateManager::GoToState(this, "Busy", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool connected = (messageWebSocket != nullptr);
|
||||
VisualStateManager::GoToState(this, connected ? "Connected" : "Disconnected", false);
|
||||
}
|
||||
}
|
||||
|
||||
void Scenario1::SetBusy(bool value)
|
||||
{
|
||||
busy = value;
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
void Scenario1::OnConnect()
|
||||
{
|
||||
SetBusy(true);
|
||||
ConnectAsync().then([this]()
|
||||
{
|
||||
SetBusy(false);
|
||||
});
|
||||
}
|
||||
|
||||
task<void> Scenario1::ConnectAsync()
|
||||
{
|
||||
if (InputField->Text == "")
|
||||
{
|
||||
rootPage->NotifyUser("Please specify text to send", NotifyType::ErrorMessage);
|
||||
return task_from_result();
|
||||
}
|
||||
|
||||
// Validating the URI is required since it was received from an untrusted source (user input).
|
||||
// The URI is validated by calling TryGetUri() that will return 'nullptr' for strings that are not
|
||||
// valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines on the intrAnet or intErnet. In
|
||||
// these cases the app requires the "Home or Work Networking" or "Internet (Client)" capability respectively.
|
||||
Uri^ server = rootPage->TryGetUri(ServerAddressField->Text);
|
||||
if (!server)
|
||||
{
|
||||
return task_from_result();
|
||||
}
|
||||
|
||||
messageWebSocket = ref new MessageWebSocket();
|
||||
messageWebSocket->Control->MessageType = SocketMessageType::Utf8;
|
||||
messageWebSocket->MessageReceived +=
|
||||
ref new TypedEventHandler<
|
||||
MessageWebSocket^,
|
||||
MessageWebSocketMessageReceivedEventArgs^>(this, &Scenario1::MessageReceived);
|
||||
messageWebSocket->Closed += ref new TypedEventHandler<IWebSocket^, WebSocketClosedEventArgs^>(this, &Scenario1::OnClosed);
|
||||
|
||||
AppendOutputLine("Connecting to " + server->DisplayUri + "...");
|
||||
|
||||
return create_task(messageWebSocket->ConnectAsync(server))
|
||||
.then([this](task<void> previousTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Reraise any exception that occurred in the task.
|
||||
previousTask.get();
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
// Error happened during connect operation.
|
||||
delete messageWebSocket;
|
||||
messageWebSocket = nullptr;
|
||||
|
||||
AppendOutputLine(MainPage::BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex->Message);
|
||||
return;
|
||||
}
|
||||
|
||||
// The default DataWriter encoding is Utf8.
|
||||
messageWriter = ref new DataWriter(messageWebSocket->OutputStream);
|
||||
rootPage->NotifyUser("Connected", NotifyType::StatusMessage);
|
||||
});
|
||||
}
|
||||
|
||||
void Scenario1::OnSend()
|
||||
{
|
||||
SetBusy(true);
|
||||
SendAsync().then([this]()
|
||||
{
|
||||
SetBusy(false);
|
||||
});
|
||||
}
|
||||
|
||||
task<void> Scenario1::SendAsync()
|
||||
{
|
||||
String^ message = InputField->Text;
|
||||
if (message == "")
|
||||
{
|
||||
rootPage->NotifyUser("Please specify text to send", NotifyType::ErrorMessage);
|
||||
return task_from_result();
|
||||
}
|
||||
|
||||
AppendOutputLine("Sending Message: " + message);
|
||||
|
||||
// Buffer any data we want to send.
|
||||
messageWriter->WriteString(message);
|
||||
|
||||
// Send the data as one complete message.
|
||||
return create_task(messageWriter->StoreAsync())
|
||||
.then([this](task<unsigned int> previousTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Reraise any exception that occurred in the task.
|
||||
previousTask.get();
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
AppendOutputLine(MainPage::BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex->Message);
|
||||
return;
|
||||
}
|
||||
|
||||
rootPage->NotifyUser("Send Complete", NotifyType::StatusMessage);
|
||||
});
|
||||
}
|
||||
|
||||
void Scenario1::MessageReceived(MessageWebSocket^ sender, MessageWebSocketMessageReceivedEventArgs^ args)
|
||||
{
|
||||
// Dispatch the event to the UI thread so we can update UI.
|
||||
Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, args]()
|
||||
{
|
||||
AppendOutputLine("Message Received; Type: " + args->MessageType.ToString());
|
||||
DataReader^ reader = args->GetDataReader();
|
||||
reader->UnicodeEncoding = UnicodeEncoding::Utf8;
|
||||
try
|
||||
{
|
||||
String^ read = reader->ReadString(reader->UnconsumedBufferLength);
|
||||
AppendOutputLine(read);
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
AppendOutputLine(MainPage::BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex->Message);
|
||||
}
|
||||
delete reader;
|
||||
}));
|
||||
}
|
||||
|
||||
void Scenario1::OnDisconnect()
|
||||
{
|
||||
SetBusy(true);
|
||||
rootPage->NotifyUser("Closing", NotifyType::StatusMessage);
|
||||
CloseSocket();
|
||||
SetBusy(false);
|
||||
}
|
||||
|
||||
// The method may be triggered remotely by the server sending unsolicited close frame or locally by Close()/delete operator.
|
||||
void Scenario1::OnClosed(IWebSocket^ sender, WebSocketClosedEventArgs^ args)
|
||||
{
|
||||
// Dispatch the event to the UI thread so we do not need to synchronize access to messageWebSocket.
|
||||
Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, sender, args] ()
|
||||
{
|
||||
AppendOutputLine("Closed; Code: " + args->Code.ToString() + ", Reason: " + args->Reason);
|
||||
|
||||
if (messageWebSocket == sender)
|
||||
{
|
||||
CloseSocket();
|
||||
UpdateVisualState();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void Scenario1::CloseSocket()
|
||||
{
|
||||
if (messageWebSocket != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
messageWebSocket->Close(1000, "Closed due to user request.");
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
AppendOutputLine(MainPage::BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex->Message);
|
||||
}
|
||||
messageWebSocket = nullptr;
|
||||
}
|
||||
|
||||
if (messageWriter != nullptr)
|
||||
{
|
||||
delete messageWriter;
|
||||
messageWriter = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Scenario1::AppendOutputLine(String^ value)
|
||||
{
|
||||
OutputField->Text += value + "\r\n";
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
//
|
||||
// Scenario1.xaml.h
|
||||
// Declaration of the Scenario1 class
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "Scenario1_UTF8.g.h"
|
||||
#include "MainPage.xaml.h"
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class Scenario1 sealed
|
||||
{
|
||||
public:
|
||||
Scenario1();
|
||||
|
||||
protected:
|
||||
void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
|
||||
|
||||
void OnConnect();
|
||||
void OnSend();
|
||||
void OnDisconnect();
|
||||
|
||||
private:
|
||||
MainPage^ rootPage = MainPage::Current;
|
||||
Windows::Networking::Sockets::MessageWebSocket^ messageWebSocket;
|
||||
Windows::Storage::Streams::DataWriter^ messageWriter;
|
||||
bool busy = false;
|
||||
|
||||
void UpdateVisualState();
|
||||
void SetBusy(bool value);
|
||||
Concurrency::task<void> ConnectAsync();
|
||||
Concurrency::task<void> SendAsync();
|
||||
void MessageReceived(Windows::Networking::Sockets::MessageWebSocket^ sender, Windows::Networking::Sockets::MessageWebSocketMessageReceivedEventArgs^ args);
|
||||
void OnClosed(Windows::Networking::Sockets::IWebSocket^ sender, Windows::Networking::Sockets::WebSocketClosedEventArgs^ args);
|
||||
void CloseSocket();
|
||||
void AppendOutputLine(Platform::String^ value);
|
||||
};
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page x:Class="SDKTemplate.WebSocket.Scenario2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:SDKTemplate.WebSocket" xmlns:common="using:SDKTemplate.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
|
||||
<Grid x:Name="LayoutRoot">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="Input" Grid.Row="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" x:Name="InputTextBlock1" TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left"> This shows how to use a StreamWebSocket to send binary data.</TextBlock>
|
||||
<TextBlock Grid.Row="1" Text="Server Address:" HorizontalAlignment="Left" Style="{StaticResource BasicTextStyle}"/>
|
||||
<TextBox Grid.Row="2" Name="ServerAddressField" InputScope="Url" Text="ws://localhost/WebSocketSample/EchoWebSocket.ashx" HorizontalAlignment="Stretch"/>
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button Name="StartButton" Content="Start" Margin="0,0,10,0" Click="Start_Click"/>
|
||||
<Button Name="StopButton" Content="Stop" Margin="0,0,10,0" Click="Stop_Click"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="4" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="Data Sent:" Width="100" Margin="10,7,10,0" Style="{StaticResource BasicTextStyle}"/>
|
||||
<TextBox Name="DataSentField" MinWidth="100" IsReadOnly="True"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="Data Received:" Width="100" Margin="10,7,10,0" Style="{StaticResource BasicTextStyle}"/>
|
||||
<TextBox Name="DataReceivedField" MinWidth="100" IsReadOnly="True"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="6" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Name="OutputField" HorizontalAlignment="Stretch" MaxHeight="140" TextWrapping="Wrap" IsReadOnly="True"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<!-- Add Storyboards to the visual states below as necessary for supporting the various layouts -->
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="DefaultLayout"/>
|
||||
<VisualState x:Name="Below768Layout"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,296 +0,0 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
//
|
||||
// Scenario2.xaml.cpp
|
||||
// Implementation of the Scenario2 class
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
#include "Scenario2.xaml.h"
|
||||
|
||||
using namespace concurrency;
|
||||
using namespace SDKTemplate::WebSocket;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Navigation;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::System::Threading;
|
||||
using namespace Windows::Security::Cryptography;
|
||||
|
||||
Scenario2::Scenario2()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
Scenario2::~Scenario2()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this page is about to be displayed in a Frame.
|
||||
/// </summary>
|
||||
/// <param name="e">Event data that describes how this page was reached. The Parameter
|
||||
/// property is typically used to configure the page.</param>
|
||||
void Scenario2::OnNavigatedTo(NavigationEventArgs^ e)
|
||||
{
|
||||
// A pointer back to the main page. This is needed if you want to call methods in MainPage such
|
||||
// as NotifyUser()
|
||||
rootPage = MainPage::Current;
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario2::Start_Click(Platform::Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
// Make a local copy to avoid races with the Closed event.
|
||||
StreamWebSocket^ webSocket = this->streamWebSocket;
|
||||
|
||||
// Have we connected yet?
|
||||
if (webSocket != nullptr)
|
||||
{
|
||||
rootPage->NotifyUser("Already connected", NotifyType::StatusMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validating the URI is required since it was received from an untrusted source (user input).
|
||||
// The URI is validated by calling TryGetUri() that will return 'false' for strings that are not
|
||||
// valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines on the intrAnet or intErnet. In
|
||||
// these cases the app requires the "Home or Work Networking" or "Internet (Client)" capability respectively.
|
||||
Uri^ server;
|
||||
if (!rootPage->TryGetUri(ServerAddressField->Text, &server))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rootPage->NotifyUser("Connecting to: " + server->DisplayUri, NotifyType::StatusMessage);
|
||||
|
||||
webSocket = ref new StreamWebSocket();
|
||||
webSocket->Closed += ref new TypedEventHandler<IWebSocket^, WebSocketClosedEventArgs^>(this, &Scenario2::Scenario2Closed);
|
||||
|
||||
task<void>(webSocket->ConnectAsync(server)).then([this, webSocket] (task<void> previousTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try getting all exceptions from the continuation chain above this point.
|
||||
previousTask.get();
|
||||
|
||||
rootPage->NotifyUser("Connected", NotifyType::StatusMessage);
|
||||
|
||||
streamWebSocket = webSocket; // Only store it after successfully connecting.
|
||||
|
||||
this->readBuffer = ref new Buffer(1000);
|
||||
|
||||
OutputField->Text += "Background read starting.\r\n";
|
||||
bytesReceived = 0;
|
||||
|
||||
// Start a loop to continuously read for incoming data.
|
||||
Scenario2ReceiveData();
|
||||
|
||||
// Start a loop to continuously write outgoing data.
|
||||
BYTE bytes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
|
||||
auto data = ref new Platform::Array<byte>(ARRAYSIZE(bytes));
|
||||
memcpy(data->Data, bytes, ARRAYSIZE(bytes));
|
||||
sendBuffer = CryptographicBuffer::CreateFromByteArray(data);
|
||||
MarshalText(OutputField, "Background sending data in " + sendBuffer->Length.ToString()
|
||||
+ " byte chunks each second.\r\n");
|
||||
dataSent = 0;
|
||||
Scenario2SendData();
|
||||
}
|
||||
catch(Exception^ exception)
|
||||
{
|
||||
HandleException(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario2::HandleException(Exception^ exception)
|
||||
{
|
||||
WebErrorStatus status = WebSocketError::GetStatus(exception->HResult);
|
||||
|
||||
if (status == WebErrorStatus::CannotConnect ||
|
||||
status == WebErrorStatus::NotFound ||
|
||||
status == WebErrorStatus::RequestTimeout)
|
||||
{
|
||||
rootPage->NotifyUser("Cannot connect to the server. Please make sure "
|
||||
"to run the server setup script before running the sample.", NotifyType::ErrorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("Error: " + status.ToString(), NotifyType::ErrorMessage);
|
||||
}
|
||||
|
||||
OutputField->Text += exception->Message + "\r\n";
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario2::Scenario2SendData()
|
||||
{
|
||||
// Make a local copy to avoid races with the Closed event.
|
||||
StreamWebSocket^ webSocket = this->streamWebSocket;
|
||||
if (webSocket == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IOutputStream^ writeStream = webSocket->OutputStream;
|
||||
task<UINT32>(writeStream->WriteAsync(sendBuffer)).then([this] (UINT32 sent)
|
||||
{
|
||||
dataSent += sent;
|
||||
MarshalText(DataSentField, dataSent.ToString(), false);
|
||||
|
||||
// Delay so the user can watch what's going on.
|
||||
TimeSpan time = { 10000000 }; // 1 second.
|
||||
ThreadPoolTimer::CreateTimer(ref new TimerElapsedHandler([this](ThreadPoolTimer^ timer) {
|
||||
Scenario2SendData();
|
||||
}), time);
|
||||
}).then([this] (task<void> previousTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try getting all exceptions from the continuation chain above this point.
|
||||
previousTask.get();
|
||||
}
|
||||
catch (Exception^ exception)
|
||||
{
|
||||
MarshalText(OutputField, exception->Message + "\r\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception^ exception)
|
||||
{
|
||||
if (exception->HResult == RO_E_CLOSED)
|
||||
{
|
||||
MarshalText(OutputField, "Output stream already closed by the user\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Continuously read incoming data. For reading data we'll show how to use webSocket.InputStream.AsStream()
|
||||
// to get a .NET stream. Alternatively you could call readBuffer.AsBuffer() to use IBuffer with
|
||||
// webSocket->InputStream->ReadAsync.
|
||||
void SDKTemplate::WebSocket::Scenario2::Scenario2ReceiveData()
|
||||
{
|
||||
// Make a local copy to avoid races with the Closed event.
|
||||
StreamWebSocket^ webSocket = this->streamWebSocket;
|
||||
if (webSocket == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IInputStream^ readStream = webSocket->InputStream;
|
||||
|
||||
task<IBuffer^>(readStream->ReadAsync(readBuffer, readBuffer->Capacity, InputStreamOptions::Partial)).then([this] (IBuffer^ buffer)
|
||||
{
|
||||
bytesReceived += buffer->Length;
|
||||
DataReceivedField->Text = bytesReceived.ToString();
|
||||
|
||||
// Do something with the data.
|
||||
}).then([this] (task<void> previousTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try getting all exceptions from the continuation chain above this point.
|
||||
previousTask.get();
|
||||
}
|
||||
catch (Exception^ exception)
|
||||
{
|
||||
WebErrorStatus status = WebSocketError::GetStatus(exception->HResult);
|
||||
if (status == WebErrorStatus::OperationCanceled)
|
||||
{
|
||||
OutputField->Text += "Background read canceled.\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
// Continue reading until closed and ReadAsync fails.
|
||||
Scenario2ReceiveData();
|
||||
});
|
||||
}
|
||||
catch (Exception^ exception)
|
||||
{
|
||||
if (exception->HResult == RO_E_CLOSED)
|
||||
{
|
||||
OutputField->Text += "Input stream already closed by the user\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario2::Stop_Click(Platform::Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (streamWebSocket != nullptr)
|
||||
{
|
||||
rootPage->NotifyUser("Stopping", NotifyType::StatusMessage);
|
||||
streamWebSocket->Close(1000, "Closed due to user request.");
|
||||
streamWebSocket = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("There is no active socket to stop.", NotifyType::StatusMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception^ exception)
|
||||
{
|
||||
HandleException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario2::Scenario2Closed(IWebSocket^ sender, WebSocketClosedEventArgs^ args)
|
||||
{
|
||||
// The method may be triggered remotely by the server sending unsolicited close frame or locally by Close()/delete operator.
|
||||
// Dispatch the event to the UI thread so we do not need to synchronize access to streamWebSocket.
|
||||
Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, args] ()
|
||||
{
|
||||
OutputField->Text += "Closed; Code: " + args->Code.ToString() + ", Reason: " + args->Reason + "\r\n";
|
||||
|
||||
if (streamWebSocket != nullptr)
|
||||
{
|
||||
delete streamWebSocket;
|
||||
streamWebSocket = nullptr;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void SDKTemplate::WebSocket::Scenario2::MarshalText(TextBox^ output, String^ value)
|
||||
{
|
||||
MarshalText(output, value, true);
|
||||
}
|
||||
|
||||
// When operations happen on a background thread we have to marshal UI updates back to the UI thread.
|
||||
void SDKTemplate::WebSocket::Scenario2::MarshalText(TextBox^ output, String^ value, bool append)
|
||||
{
|
||||
Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, output, value, append] ()
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
output->Text += value;
|
||||
}
|
||||
else
|
||||
{
|
||||
output->Text = value;
|
||||
}
|
||||
}, CallbackContext::Any));
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
//
|
||||
// Scenario2.xaml.h
|
||||
// Declaration of the Scenario2 class
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "Scenario2.g.h"
|
||||
#include "MainPage.xaml.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Networking::Sockets;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Navigation;
|
||||
using namespace Windows::Web;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
namespace WebSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class Scenario2 sealed
|
||||
{
|
||||
public:
|
||||
Scenario2();
|
||||
|
||||
protected:
|
||||
virtual void OnNavigatedTo(NavigationEventArgs^ e) override;
|
||||
private:
|
||||
~Scenario2();
|
||||
|
||||
MainPage^ rootPage;
|
||||
StreamWebSocket^ streamWebSocket;
|
||||
IBuffer^ readBuffer;
|
||||
IBuffer^ sendBuffer;
|
||||
int bytesReceived;
|
||||
int dataSent;
|
||||
CRITICAL_SECTION criticalSection;
|
||||
|
||||
void Start_Click(Object^ sender, RoutedEventArgs^ e);
|
||||
void Stop_Click(Object^ sender, RoutedEventArgs^ e);
|
||||
void Scenario2ReceiveData();
|
||||
void Scenario2SendData();
|
||||
void Scenario2Closed(IWebSocket^ sender, WebSocketClosedEventArgs^ args);
|
||||
void HandleException(Exception^ exception);
|
||||
void MarshalText(TextBox^ output, String^ value);
|
||||
void MarshalText(TextBox^ output, String^ value, bool append);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
//
|
||||
// Scenario2.xaml.cpp
|
||||
// Implementation of the Scenario2 class
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
#include "Scenario2_Binary.xaml.h"
|
||||
|
||||
using namespace concurrency;
|
||||
using namespace Platform;
|
||||
using namespace SDKTemplate;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Networking::Sockets;
|
||||
using namespace Windows::Security::Cryptography;
|
||||
using namespace Windows::Security::Cryptography::Certificates;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::System::Threading;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Navigation;
|
||||
using namespace Windows::Web;
|
||||
|
||||
Scenario2::Scenario2()
|
||||
{
|
||||
InitializeComponent();
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
void Scenario2::OnNavigatedFrom(NavigationEventArgs^ e)
|
||||
{
|
||||
CloseSocket();
|
||||
}
|
||||
|
||||
void Scenario2::UpdateVisualState()
|
||||
{
|
||||
if (busy)
|
||||
{
|
||||
VisualStateManager::GoToState(this, "Busy", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool connected = (streamWebSocket != nullptr);
|
||||
VisualStateManager::GoToState(this, connected ? "Connected" : "Disconnected", false);
|
||||
}
|
||||
}
|
||||
|
||||
void Scenario2::SetBusy(bool value)
|
||||
{
|
||||
busy = value;
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
void Scenario2::OnStart()
|
||||
{
|
||||
SetBusy(true);
|
||||
StartAsync().then([this]()
|
||||
{
|
||||
SetBusy(false);
|
||||
});
|
||||
}
|
||||
|
||||
task<void> Scenario2::StartAsync()
|
||||
{
|
||||
// Validating the URI is required since it was received from an untrusted source (user input).
|
||||
// The URI is validated by calling TryGetUri() that will return 'nullptr' for strings that are not
|
||||
// valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines on the intrAnet or intErnet. In
|
||||
// these cases the app requires the "Home or Work Networking" or "Internet (Client)" capability respectively.
|
||||
Uri^ server = rootPage->TryGetUri(ServerAddressField->Text);
|
||||
if (!server)
|
||||
{
|
||||
return task_from_result();
|
||||
}
|
||||
|
||||
streamWebSocket = ref new StreamWebSocket();
|
||||
streamWebSocket->Closed += ref new TypedEventHandler<IWebSocket^, WebSocketClosedEventArgs^>(this, &Scenario2::OnClosed);
|
||||
|
||||
AppendOutputLine("Connecting to " + server->DisplayUri + "...");
|
||||
|
||||
return create_task(streamWebSocket->ConnectAsync(server))
|
||||
.then([this](task<void> previousTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Reraise any exception that occurred in the task.
|
||||
previousTask.get();
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
// Error happened during connect operation.
|
||||
delete streamWebSocket;
|
||||
streamWebSocket = nullptr;
|
||||
|
||||
AppendOutputLine(MainPage::BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex->Message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start a task to continuously read for incoming data
|
||||
StartReceiveData(streamWebSocket);
|
||||
|
||||
// Start a task to continuously write outgoing data
|
||||
StartSendData(streamWebSocket);
|
||||
});
|
||||
}
|
||||
|
||||
void Scenario2::StartSendData(StreamWebSocket^ activeSocket)
|
||||
{
|
||||
// Start a loop to continuously write outgoing data.
|
||||
BYTE bytes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
|
||||
auto data = ref new Platform::Array<byte>(ARRAYSIZE(bytes));
|
||||
memcpy(data->Data, bytes, ARRAYSIZE(bytes));
|
||||
IBuffer^ sendBuffer = CryptographicBuffer::CreateFromByteArray(data);
|
||||
|
||||
AppendOutputLine("Background write starting.");
|
||||
|
||||
SendDataLoop(streamWebSocket, sendBuffer, 0);
|
||||
}
|
||||
|
||||
void Scenario2::SendDataLoop(StreamWebSocket^ activeSocket, IBuffer^ sendBuffer, UINT32 bytesSentSoFar)
|
||||
{
|
||||
if (streamWebSocket != activeSocket) {
|
||||
// Our socket is no longer active. Stop sending.
|
||||
AppendOutputLine("Background write stopped.");
|
||||
return;
|
||||
}
|
||||
|
||||
create_task(activeSocket->OutputStream->WriteAsync(sendBuffer))
|
||||
.then([this, activeSocket, sendBuffer, bytesSentSoFar](task<UINT32> previousTask)
|
||||
{
|
||||
UINT32 bytesSentThisCall = 0;
|
||||
try
|
||||
{
|
||||
// Reraise any exception that occurred in the task.
|
||||
bytesSentThisCall = previousTask.get();
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
WebErrorStatus status = WebSocketError::GetStatus(ex->HResult);
|
||||
if (status == WebErrorStatus::OperationCanceled)
|
||||
{
|
||||
AppendOutputLine("Background write canceled.");
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendOutputLine("Error: " + status.ToString());
|
||||
AppendOutputLine(ex->Message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
UINT32 totalBytesSent = bytesSentSoFar + bytesSentThisCall;
|
||||
DataSentField->Text = totalBytesSent.ToString();
|
||||
|
||||
// Delay so the user can watch what's going on.
|
||||
ThreadPoolTimer::CreateTimer(ref new TimerElapsedHandler(
|
||||
[this, activeSocket, sendBuffer, totalBytesSent](ThreadPoolTimer^ timer)
|
||||
{
|
||||
SendDataLoop(activeSocket, sendBuffer, totalBytesSent);
|
||||
}, CallbackContext::Same), TimeSpan{ 10000000 }); // 1 second
|
||||
});
|
||||
}
|
||||
|
||||
// Continuously read incoming data.
|
||||
void Scenario2::StartReceiveData(StreamWebSocket^ activeSocket)
|
||||
{
|
||||
AppendOutputLine("Background read starting.");
|
||||
ReceiveDataLoop(activeSocket, ref new Buffer(1000), 0);
|
||||
}
|
||||
|
||||
void Scenario2::ReceiveDataLoop(StreamWebSocket^ activeSocket, IBuffer^ readBuffer, UINT32 bytesReceivedSoFar)
|
||||
{
|
||||
if (streamWebSocket != activeSocket) {
|
||||
// Our socket is no longer active. Stop reading.
|
||||
AppendOutputLine("Background read stopped.");
|
||||
return;
|
||||
}
|
||||
|
||||
create_task(activeSocket->InputStream->ReadAsync(readBuffer, readBuffer->Capacity, InputStreamOptions::Partial))
|
||||
.then([this, activeSocket, readBuffer, bytesReceivedSoFar](task<IBuffer^> previousTask)
|
||||
{
|
||||
IBuffer^ resultBuffer;
|
||||
try
|
||||
{
|
||||
// Reraise any exception that occurred in the task.
|
||||
resultBuffer = previousTask.get();
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
AppendOutputLine("During read: " + rootPage->BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex->Message);
|
||||
return;
|
||||
}
|
||||
|
||||
UINT32 totalBytesReceived = bytesReceivedSoFar + resultBuffer->Length;
|
||||
DataReceivedField->Text = totalBytesReceived.ToString();
|
||||
|
||||
// Do something with the data.
|
||||
|
||||
// Continue reading until closed and ReadAsync fails.
|
||||
ReceiveDataLoop(activeSocket, readBuffer, totalBytesReceived);
|
||||
});
|
||||
}
|
||||
|
||||
void Scenario2::OnStop()
|
||||
{
|
||||
SetBusy(true);
|
||||
rootPage->NotifyUser("Stopping", NotifyType::StatusMessage);
|
||||
CloseSocket();
|
||||
SetBusy(false);
|
||||
}
|
||||
|
||||
// The method may be triggered remotely by the server sending unsolicited close frame or locally by Close()/delete operator.
|
||||
void Scenario2::OnClosed(IWebSocket^ sender, WebSocketClosedEventArgs^ args)
|
||||
{
|
||||
// Dispatch the event to the UI thread so we do not need to synchronize access to streamWebSocket.
|
||||
Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, sender, args]()
|
||||
{
|
||||
AppendOutputLine("Closed; Code: " + args->Code.ToString() + ", Reason: " + args->Reason);
|
||||
|
||||
if (streamWebSocket == sender)
|
||||
{
|
||||
CloseSocket();
|
||||
UpdateVisualState();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void Scenario2::CloseSocket()
|
||||
{
|
||||
if (streamWebSocket != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
streamWebSocket->Close(1000, "Closed due to user request.");
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
AppendOutputLine(MainPage::BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex->Message);
|
||||
}
|
||||
streamWebSocket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Scenario2::AppendOutputLine(String^ value)
|
||||
{
|
||||
OutputField->Text += value + "\r\n";
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
//
|
||||
// Scenario2.xaml.h
|
||||
// Declaration of the Scenario2 class
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "Scenario2_Binary.g.h"
|
||||
#include "MainPage.xaml.h"
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class Scenario2 sealed
|
||||
{
|
||||
public:
|
||||
Scenario2();
|
||||
|
||||
protected:
|
||||
void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
|
||||
|
||||
void OnStart();
|
||||
void OnStop();
|
||||
|
||||
private:
|
||||
MainPage^ rootPage = MainPage::Current;
|
||||
Windows::Networking::Sockets::StreamWebSocket^ streamWebSocket;
|
||||
bool busy = false;
|
||||
|
||||
void UpdateVisualState();
|
||||
void SetBusy(bool value);
|
||||
Concurrency::task<void> StartAsync();
|
||||
Concurrency::task<void> StopAsync();
|
||||
|
||||
void StartReceiveData(Windows::Networking::Sockets::StreamWebSocket^ activeSocket);
|
||||
void ReceiveDataLoop(
|
||||
Windows::Networking::Sockets::StreamWebSocket^ activeSocket,
|
||||
Windows::Storage::Streams::IBuffer^ readBuffer,
|
||||
UINT32 bytesReceivedSoFar);
|
||||
void StartSendData(Windows::Networking::Sockets::StreamWebSocket^ activeSocket);
|
||||
void SendDataLoop(
|
||||
Windows::Networking::Sockets::StreamWebSocket^ activeSocket,
|
||||
Windows::Storage::Streams::IBuffer^ sendBuffer,
|
||||
UINT32 bytesSentSoFar);
|
||||
|
||||
void OnClosed(Windows::Networking::Sockets::IWebSocket^ sender, Windows::Networking::Sockets::WebSocketClosedEventArgs^ args);
|
||||
void CloseSocket();
|
||||
void AppendOutputLine(Platform::String^ value);
|
||||
};
|
||||
}
|
|
@ -145,11 +145,11 @@
|
|||
<DependentUpon>..\..\..\SharedContent\cpp\MainPage.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SampleConfiguration.h" />
|
||||
<ClInclude Include="Scenario1.xaml.h">
|
||||
<DependentUpon>Scenario1.xaml</DependentUpon>
|
||||
<ClInclude Include="Scenario1_UTF8.xaml.h">
|
||||
<DependentUpon>..\shared\Scenario1_UTF8.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Scenario2.xaml.h">
|
||||
<DependentUpon>Scenario2.xaml</DependentUpon>
|
||||
<ClInclude Include="Scenario2_Binary.xaml.h">
|
||||
<DependentUpon>..\shared\Scenario2_Binary.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -159,8 +159,8 @@
|
|||
<Page Include="..\..\..\SharedContent\cpp\MainPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario1.xaml" />
|
||||
<Page Include="Scenario2.xaml" />
|
||||
<Page Include="..\shared\Scenario1_UTF8.xaml" />
|
||||
<Page Include="..\shared\Scenario2_Binary.xaml" />
|
||||
<Page Include="..\..\..\SharedContent\xaml\Styles.xaml">
|
||||
<Link>Styles\Styles.xaml</Link>
|
||||
</Page>
|
||||
|
@ -186,11 +186,11 @@
|
|||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SampleConfiguration.cpp" />
|
||||
<ClCompile Include="Scenario1.xaml.cpp">
|
||||
<DependentUpon>Scenario1.xaml</DependentUpon>
|
||||
<ClCompile Include="Scenario1_UTF8.xaml.cpp">
|
||||
<DependentUpon>..\shared\Scenario1_UTF8.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Scenario2.xaml.cpp">
|
||||
<DependentUpon>Scenario2.xaml</DependentUpon>
|
||||
<ClCompile Include="Scenario2_Binary.xaml.cpp">
|
||||
<DependentUpon>..\shared\Scenario2_Binary.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -8,16 +8,16 @@
|
|||
<ClCompile Include="..\..\..\SharedContent\cpp\MainPage.xaml.cpp" />
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="SampleConfiguration.cpp" />
|
||||
<ClCompile Include="Scenario1.xaml.cpp" />
|
||||
<ClCompile Include="Scenario2.xaml.cpp" />
|
||||
<ClCompile Include="Scenario1_UTF8.xaml.cpp" />
|
||||
<ClCompile Include="Scenario2_Binary.xaml.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="..\..\..\SharedContent\cpp\App.xaml.h" />
|
||||
<ClInclude Include="..\..\..\SharedContent\cpp\MainPage.xaml.h" />
|
||||
<ClInclude Include="SampleConfiguration.h" />
|
||||
<ClInclude Include="Scenario1.xaml.h" />
|
||||
<ClInclude Include="Scenario2.xaml.h" />
|
||||
<ClInclude Include="Scenario1_UTF8.xaml.h" />
|
||||
<ClInclude Include="Scenario2_Binary.xaml.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="..\..\..\SharedContent\media\microsoft-sdk.png">
|
||||
|
@ -47,8 +47,8 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="..\..\..\SharedContent\cpp\MainPage.xaml" />
|
||||
<Page Include="Scenario1.xaml" />
|
||||
<Page Include="Scenario2.xaml" />
|
||||
<Page Include="..\shared\Scenario1_UTF8.xaml" />
|
||||
<Page Include="..\shared\Scenario2_Binary.xaml" />
|
||||
<Page Include="..\..\..\SharedContent\xaml\Styles.xaml">
|
||||
<Filter>Styles</Filter>
|
||||
</Page>
|
||||
|
|
|
@ -9,45 +9,40 @@
|
|||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.UI.Xaml.Controls;using System;
|
||||
using Microsoft.Samples.Networking.WebSocket;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Networking.Sockets;
|
||||
using Windows.Security.Cryptography.Certificates;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.Web;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
public partial class MainPage : Page
|
||||
{
|
||||
// Change the string below to reflect the name of your sample.
|
||||
// This is used on the main page as the title of the sample.
|
||||
public const string FEATURE_NAME = "WebSocket";
|
||||
public const string FEATURE_NAME = "WebSocket C# Sample";
|
||||
|
||||
// Change the array below to reflect the name of your scenarios.
|
||||
// This will be used to populate the list of scenarios on the main page with
|
||||
// which the user will choose the specific scenario that they are interested in.
|
||||
// These should be in the form: "Navigating to a web page".
|
||||
// The code in MainPage will take care of turning this into: "1) Navigating to a web page"
|
||||
List<Scenario> scenarios = new List<Scenario>
|
||||
{
|
||||
new Scenario() { Title = "UTF-8 text messages", ClassType = typeof(Scenario1) },
|
||||
new Scenario() { Title = "Binary data stream", ClassType = typeof(Scenario2) },
|
||||
};
|
||||
|
||||
public bool TryGetUri(string uriString, out Uri uri)
|
||||
public Uri TryGetUri(string uriString)
|
||||
{
|
||||
uri = null;
|
||||
|
||||
Uri webSocketUri;
|
||||
if (!Uri.TryCreate(uriString.Trim(), UriKind.Absolute, out webSocketUri))
|
||||
{
|
||||
NotifyUser("Error: Invalid URI", NotifyType.ErrorMessage);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Fragments are not allowed in WebSocket URIs.
|
||||
if (!String.IsNullOrEmpty(webSocketUri.Fragment))
|
||||
{
|
||||
NotifyUser("Error: URI fragments not supported in WebSocket URIs.", NotifyType.ErrorMessage);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Uri.SchemeName returns the canonicalized scheme name so we can use case-sensitive, ordinal string
|
||||
|
@ -55,12 +50,33 @@ namespace SDKTemplate
|
|||
if ((webSocketUri.Scheme != "ws") && (webSocketUri.Scheme != "wss"))
|
||||
{
|
||||
NotifyUser("Error: WebSockets only support ws:// and wss:// schemes.", NotifyType.ErrorMessage);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
uri = webSocketUri;
|
||||
return webSocketUri;
|
||||
}
|
||||
|
||||
return true;
|
||||
public static string BuildWebSocketError(Exception ex)
|
||||
{
|
||||
ex = ex.GetBaseException();
|
||||
WebErrorStatus status = WebSocketError.GetStatus(ex.HResult);
|
||||
|
||||
// Normally we'd use the HResult and status to test for specific conditions we want to handle.
|
||||
// In this sample, we'll just output them for demonstration purposes.
|
||||
switch (status)
|
||||
{
|
||||
case WebErrorStatus.CannotConnect:
|
||||
case WebErrorStatus.NotFound:
|
||||
case WebErrorStatus.RequestTimeout:
|
||||
return "Cannot connect to the server. Please make sure " +
|
||||
"to run the server setup script before running the sample.";
|
||||
|
||||
case WebErrorStatus.Unknown:
|
||||
return "COM error: " + ex.HResult;
|
||||
|
||||
default:
|
||||
return "Error: " + status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page x:Class="Microsoft.Samples.Networking.WebSocket.Scenario1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Microsoft.Samples.Networking.WebSocket" xmlns:common="using:SDKTemplate.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
|
||||
<Grid x:Name="LayoutRoot" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="Input" Grid.Row="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap" HorizontalAlignment="Left"> This shows how to use a MessageWebSocket to send UTF-8 strings. </TextBlock>
|
||||
<TextBlock Grid.Row="1" Text="Server Address:" HorizontalAlignment="Left" Style="{StaticResource BasicTextStyle}"/>
|
||||
<TextBox Grid.Row="2" Name="ServerAddressField" InputScope="Url" Text="ws://localhost/WebSocketSample/EchoWebSocket.ashx" HorizontalAlignment="Stretch"/>
|
||||
<TextBlock Grid.Row="3" TextWrapping="Wrap" HorizontalAlignment="Left" Style="{StaticResource BasicTextStyle}">Enter text to send to the server.</TextBlock>
|
||||
<TextBox Grid.Row="4" Name="InputField" Text="Hello World" TextWrapping="Wrap" AcceptsReturn="True" HorizontalAlignment="Stretch"/>
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button Name="StartButton" Content="Start" Margin="0,0,10,0" Click="Start_Click"/>
|
||||
<Button Name="CloseButton" Content="Close" Margin="0,0,10,0" Click="Close_Click"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="6" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Name="OutputField" HorizontalAlignment="Stretch" MaxHeight="160" TextWrapping="Wrap" IsReadOnly="True"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<!-- Add Storyboards to the visual states below as necessary for supporting the various layouts -->
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="DefaultLayout"/>
|
||||
<VisualState x:Name="Below768Layout"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -9,17 +9,17 @@
|
|||
//
|
||||
//*********************************************************
|
||||
|
||||
using SDKTemplate;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.Networking.Sockets;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Windows.Web;
|
||||
|
||||
namespace Microsoft.Samples.Networking.WebSocket
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
|
@ -32,22 +32,46 @@ namespace Microsoft.Samples.Networking.WebSocket
|
|||
|
||||
private MessageWebSocket messageWebSocket;
|
||||
private DataWriter messageWriter;
|
||||
private bool busy;
|
||||
|
||||
public Scenario1()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this page is about to be displayed in a Frame.
|
||||
/// </summary>
|
||||
/// <param name="e">Event data that describes how this page was reached. The Parameter
|
||||
/// property is typically used to configure the page.</param>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
CloseSocket();
|
||||
}
|
||||
|
||||
private async void Start_Click(object sender, RoutedEventArgs e)
|
||||
private void UpdateVisualState()
|
||||
{
|
||||
if (busy)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "Busy", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool connected = (messageWebSocket != null);
|
||||
VisualStateManager.GoToState(this, connected ? "Connected" : "Disconnected", false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBusy(bool value)
|
||||
{
|
||||
busy = value;
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
private async void OnConnect()
|
||||
{
|
||||
SetBusy(true);
|
||||
await ConnectAsync();
|
||||
SetBusy(false);
|
||||
}
|
||||
|
||||
private async Task ConnectAsync()
|
||||
{
|
||||
if (String.IsNullOrEmpty(InputField.Text))
|
||||
{
|
||||
|
@ -55,185 +79,153 @@ namespace Microsoft.Samples.Networking.WebSocket
|
|||
return;
|
||||
}
|
||||
|
||||
bool connecting = true;
|
||||
// Validating the URI is required since it was received from an untrusted source (user input).
|
||||
// The URI is validated by calling TryGetUri() that will return 'false' for strings that are not
|
||||
// valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines on the intrAnet
|
||||
// or intErnet. In these cases the app requires the "Home or Work Networking" or
|
||||
// "Internet (Client)" capability respectively.
|
||||
Uri server = rootPage.TryGetUri(ServerAddressField.Text);
|
||||
if (server == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
messageWebSocket = new MessageWebSocket();
|
||||
messageWebSocket.Control.MessageType = SocketMessageType.Utf8;
|
||||
messageWebSocket.MessageReceived += MessageReceived;
|
||||
messageWebSocket.Closed += OnClosed;
|
||||
|
||||
AppendOutputLine($"Connecting to {server}...");
|
||||
try
|
||||
{
|
||||
// Have we connected yet?
|
||||
if (messageWebSocket == null)
|
||||
{
|
||||
// Validating the URI is required since it was received from an untrusted source (user input).
|
||||
// The URI is validated by calling TryGetUri() that will return 'false' for strings that are not
|
||||
// valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines on the intrAnet
|
||||
// or intErnet. In these cases the app requires the "Home or Work Networking" or
|
||||
// "Internet (Client)" capability respectively.
|
||||
Uri server;
|
||||
if (!rootPage.TryGetUri(ServerAddressField.Text, out server))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rootPage.NotifyUser("Connecting to: " + server, NotifyType.StatusMessage);
|
||||
|
||||
messageWebSocket = new MessageWebSocket();
|
||||
messageWebSocket.Control.MessageType = SocketMessageType.Utf8;
|
||||
messageWebSocket.MessageReceived += MessageReceived;
|
||||
|
||||
// Dispatch close event on UI thread. This allows us to avoid synchronizing access to messageWebSocket.
|
||||
messageWebSocket.Closed += async (senderSocket, args) =>
|
||||
{
|
||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Closed(senderSocket, args));
|
||||
};
|
||||
|
||||
await messageWebSocket.ConnectAsync(server);
|
||||
messageWriter = new DataWriter(messageWebSocket.OutputStream);
|
||||
|
||||
rootPage.NotifyUser("Connected", NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("Already connected", NotifyType.StatusMessage);
|
||||
}
|
||||
|
||||
connecting = false;
|
||||
string message = InputField.Text;
|
||||
OutputField.Text += "Sending Message:\r\n" + message + "\r\n";
|
||||
|
||||
// Buffer any data we want to send.
|
||||
messageWriter.WriteString(message);
|
||||
|
||||
// Send the data as one complete message.
|
||||
await messageWriter.StoreAsync();
|
||||
|
||||
rootPage.NotifyUser("Send Complete", NotifyType.StatusMessage);
|
||||
await messageWebSocket.ConnectAsync(server);
|
||||
}
|
||||
catch (Exception ex) // For debugging
|
||||
{
|
||||
// Error happened during connect operation.
|
||||
if (connecting && messageWebSocket != null)
|
||||
{
|
||||
messageWebSocket.Dispose();
|
||||
messageWebSocket = null;
|
||||
}
|
||||
messageWebSocket.Dispose();
|
||||
messageWebSocket = null;
|
||||
|
||||
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case WebErrorStatus.CannotConnect:
|
||||
case WebErrorStatus.NotFound:
|
||||
case WebErrorStatus.RequestTimeout:
|
||||
rootPage.NotifyUser("Cannot connect to the server. Please make sure " +
|
||||
"to run the server setup script before running the sample.", NotifyType.ErrorMessage);
|
||||
break;
|
||||
|
||||
case WebErrorStatus.Unknown:
|
||||
throw;
|
||||
|
||||
default:
|
||||
rootPage.NotifyUser("Error: " + status, NotifyType.ErrorMessage);
|
||||
break;
|
||||
}
|
||||
|
||||
OutputField.Text += ex.Message + "\r\n";
|
||||
AppendOutputLine(MainPage.BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
// The default DataWriter encoding is Utf8.
|
||||
messageWriter = new DataWriter(messageWebSocket.OutputStream);
|
||||
rootPage.NotifyUser("Connected", NotifyType.StatusMessage);
|
||||
}
|
||||
|
||||
async void OnSend()
|
||||
{
|
||||
SetBusy(true);
|
||||
await SendAsync();
|
||||
SetBusy(false);
|
||||
}
|
||||
|
||||
async Task SendAsync()
|
||||
{
|
||||
string message = InputField.Text;
|
||||
if (String.IsNullOrEmpty(message))
|
||||
{
|
||||
rootPage.NotifyUser("Please specify text to send", NotifyType.ErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
AppendOutputLine("Sending Message: " + message);
|
||||
|
||||
// Buffer any data we want to send.
|
||||
messageWriter.WriteString(message);
|
||||
|
||||
try
|
||||
{
|
||||
// Send the data as one complete message.
|
||||
await messageWriter.StoreAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendOutputLine(MainPage.BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
rootPage.NotifyUser("Send Complete", NotifyType.StatusMessage);
|
||||
}
|
||||
|
||||
private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)
|
||||
{
|
||||
try
|
||||
// Dispatch the event to the UI thread so we can update UI.
|
||||
var ignore = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
MarshalText(OutputField, "Message Received; Type: " + args.MessageType + "\r\n");
|
||||
AppendOutputLine("Message Received; Type: " + args.MessageType);
|
||||
using (DataReader reader = args.GetDataReader())
|
||||
{
|
||||
reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
|
||||
reader.UnicodeEncoding = UnicodeEncoding.Utf8;
|
||||
|
||||
string read = reader.ReadString(reader.UnconsumedBufferLength);
|
||||
MarshalText(OutputField, read + "\r\n");
|
||||
}
|
||||
}
|
||||
catch (Exception ex) // For debugging
|
||||
{
|
||||
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
|
||||
|
||||
if (status == WebErrorStatus.Unknown)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
// Normally we'd use the status to test for specific conditions we want to handle specially,
|
||||
// and only use ex.Message for display purposes. In this sample, we'll just output the
|
||||
// status for debugging here, but still use ex.Message below.
|
||||
MarshalText(OutputField, "Error: " + status + "\r\n");
|
||||
|
||||
MarshalText(OutputField, ex.Message + "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void Close_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (messageWebSocket != null)
|
||||
{
|
||||
rootPage.NotifyUser("Closing", NotifyType.StatusMessage);
|
||||
messageWebSocket.Close(1000, "Closed due to user request.");
|
||||
messageWebSocket = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("No active WebSocket, send something first", NotifyType.StatusMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
|
||||
|
||||
if (status == WebErrorStatus.Unknown)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
// Normally we'd use the status to test for specific conditions we want to handle specially,
|
||||
// and only use ex.Message for display purposes. In this sample, we'll just output the
|
||||
// status for debugging here, but still use ex.Message below.
|
||||
rootPage.NotifyUser("Error: " + status, NotifyType.ErrorMessage);
|
||||
|
||||
OutputField.Text += ex.Message + "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
// This may be triggered remotely by the server or locally by Close/Dispose()
|
||||
private void Closed(IWebSocket sender, WebSocketClosedEventArgs args)
|
||||
{
|
||||
MarshalText(OutputField, "Closed; Code: " + args.Code + ", Reason: " + args.Reason + "\r\n");
|
||||
|
||||
if (messageWebSocket != null)
|
||||
{
|
||||
messageWebSocket.Dispose();
|
||||
messageWebSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void MarshalText(TextBox output, string value)
|
||||
{
|
||||
MarshalText(output, value, true);
|
||||
}
|
||||
|
||||
// When operations happen on a background thread we have to marshal UI updates back to the UI thread.
|
||||
private void MarshalText(TextBox output, string value, bool append)
|
||||
{
|
||||
var ignore = output.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
output.Text += value;
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Text = value;
|
||||
try
|
||||
{
|
||||
string read = reader.ReadString(reader.UnconsumedBufferLength);
|
||||
AppendOutputLine(read);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendOutputLine(MainPage.BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex.Message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void OnDisconnect()
|
||||
{
|
||||
SetBusy(true);
|
||||
rootPage.NotifyUser("Closing", NotifyType.StatusMessage);
|
||||
CloseSocket();
|
||||
SetBusy(false);
|
||||
}
|
||||
|
||||
// This may be triggered remotely by the server or locally by Close/Dispose()
|
||||
private async void OnClosed(IWebSocket sender, WebSocketClosedEventArgs args)
|
||||
{
|
||||
// Dispatch the event to the UI thread so we do not need to synchronize access to messageWebSocket.
|
||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
AppendOutputLine("Closed; Code: " + args.Code + ", Reason: " + args.Reason);
|
||||
|
||||
if (messageWebSocket == sender)
|
||||
{
|
||||
CloseSocket();
|
||||
UpdateVisualState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void CloseSocket()
|
||||
{
|
||||
if (messageWebSocket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
messageWebSocket.Close(1000, "Closed due to user request.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendOutputLine(MainPage.BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex.Message);
|
||||
}
|
||||
messageWebSocket = null;
|
||||
}
|
||||
if (messageWriter != null)
|
||||
{
|
||||
messageWriter.Dispose();
|
||||
messageWriter = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendOutputLine(string value)
|
||||
{
|
||||
OutputField.Text += value + "\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page x:Class="Microsoft.Samples.Networking.WebSocket.Scenario2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Microsoft.Samples.Networking.WebSocket" xmlns:common="using:SDKTemplate.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
|
||||
<Grid x:Name="LayoutRoot">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="Input" Grid.Row="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" x:Name="InputTextBlock1" TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left"> This shows how to use a StreamWebSocket to send binary data. </TextBlock>
|
||||
<TextBlock Grid.Row="1" Text="Server Address:" HorizontalAlignment="Left" Style="{StaticResource BasicTextStyle}"/>
|
||||
<TextBox Grid.Row="2" Name="ServerAddressField" InputScope="Url" Text="ws://localhost/WebSocketSample/EchoWebSocket.ashx" HorizontalAlignment="Stretch"/>
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button Name="StartButton" Content="Start" Margin="0,0,10,0" Click="Start_Click"/>
|
||||
<Button Name="StopButton" Content="Stop" Margin="0,0,10,0" Click="Stop_Click"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="4" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="Data Sent:" Width="100" Margin="10,7,10,0" Style="{StaticResource BasicTextStyle}"/>
|
||||
<TextBox Name="DataSentField" MinWidth="100" IsReadOnly="True"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="Data Received:" Width="100" Margin="10,7,10,0" Style="{StaticResource BasicTextStyle}"/>
|
||||
<TextBox Name="DataReceivedField" MinWidth="100" IsReadOnly="True"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="6" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Name="OutputField" HorizontalAlignment="Stretch" MaxHeight="140" TextWrapping="Wrap" IsReadOnly="True"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<!-- Add Storyboards to the visual states below as necessary for supporting the various layouts -->
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="DefaultLayout"/>
|
||||
<VisualState x:Name="Below768Layout"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -9,21 +9,19 @@
|
|||
//
|
||||
//*********************************************************
|
||||
|
||||
using SDKTemplate;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.Networking.Sockets;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Windows.Web;
|
||||
|
||||
namespace Microsoft.Samples.Networking.WebSocket
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
|
@ -35,132 +33,116 @@ namespace Microsoft.Samples.Networking.WebSocket
|
|||
MainPage rootPage = MainPage.Current;
|
||||
|
||||
private StreamWebSocket streamWebSocket;
|
||||
private byte[] readBuffer;
|
||||
private bool busy;
|
||||
|
||||
public Scenario2()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this page is about to be displayed in a Frame.
|
||||
/// </summary>
|
||||
/// <param name="e">Event data that describes how this page was reached. The Parameter
|
||||
/// property is typically used to configure the page.</param>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
CloseSocket();
|
||||
}
|
||||
|
||||
private async void Start_Click(object sender, RoutedEventArgs e)
|
||||
private void UpdateVisualState()
|
||||
{
|
||||
// Have we connected yet?
|
||||
if (streamWebSocket != null)
|
||||
if (busy)
|
||||
{
|
||||
rootPage.NotifyUser("Already connected", NotifyType.StatusMessage);
|
||||
return;
|
||||
VisualStateManager.GoToState(this, "Busy", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool connected = (streamWebSocket != null);
|
||||
VisualStateManager.GoToState(this, connected ? "Connected" : "Disconnected", false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBusy(bool value)
|
||||
{
|
||||
busy = value;
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
private async void OnStart()
|
||||
{
|
||||
SetBusy(true);
|
||||
await StartAsync();
|
||||
SetBusy(false);
|
||||
}
|
||||
|
||||
private async Task StartAsync()
|
||||
{
|
||||
// Validating the URI is required since it was received from an untrusted source (user input).
|
||||
// The URI is validated by calling TryGetUri() that will return 'false' for strings that are not
|
||||
// valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines on the intrAnet
|
||||
// or intErnet. In these cases the app requires the "Home or Work Networking" or
|
||||
// "Internet (Client)" capability respectively.
|
||||
Uri server;
|
||||
if (!rootPage.TryGetUri(ServerAddressField.Text, out server))
|
||||
Uri server = rootPage.TryGetUri(ServerAddressField.Text);
|
||||
if (server == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
streamWebSocket = new StreamWebSocket();
|
||||
streamWebSocket.Closed += OnClosed;
|
||||
|
||||
AppendOutputLine($"Connecting to {server}...");
|
||||
try
|
||||
{
|
||||
rootPage.NotifyUser("Connecting to: " + server, NotifyType.StatusMessage);
|
||||
|
||||
streamWebSocket = new StreamWebSocket();
|
||||
|
||||
// Dispatch close event on UI thread. This allows us to avoid synchronizing access to streamWebSocket.
|
||||
streamWebSocket.Closed += async (senderSocket, args) =>
|
||||
{
|
||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Closed(senderSocket, args));
|
||||
};
|
||||
|
||||
await streamWebSocket.ConnectAsync(server);
|
||||
|
||||
readBuffer = new byte[1000];
|
||||
|
||||
// Start a background task to continuously read for incoming data
|
||||
Task receiving = Task.Factory.StartNew(Scenario2ReceiveData,
|
||||
streamWebSocket.InputStream.AsStreamForRead(), TaskCreationOptions.LongRunning);
|
||||
|
||||
// Start a background task to continuously write outgoing data
|
||||
Task sending = Task.Factory.StartNew(Scenario2SendData,
|
||||
streamWebSocket.OutputStream, TaskCreationOptions.LongRunning);
|
||||
|
||||
rootPage.NotifyUser("Connected", NotifyType.StatusMessage);
|
||||
}
|
||||
catch (Exception ex) // For debugging
|
||||
{
|
||||
if (streamWebSocket != null)
|
||||
{
|
||||
streamWebSocket.Dispose();
|
||||
streamWebSocket = null;
|
||||
}
|
||||
streamWebSocket.Dispose();
|
||||
streamWebSocket = null;
|
||||
|
||||
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case WebErrorStatus.CannotConnect:
|
||||
case WebErrorStatus.NotFound:
|
||||
case WebErrorStatus.RequestTimeout:
|
||||
rootPage.NotifyUser("Cannot connect to the server. Please make sure " +
|
||||
"to run the server setup script before running the sample.", NotifyType.ErrorMessage);
|
||||
break;
|
||||
|
||||
case WebErrorStatus.Unknown:
|
||||
throw;
|
||||
|
||||
default:
|
||||
rootPage.NotifyUser("Error: " + status, NotifyType.ErrorMessage);
|
||||
break;
|
||||
}
|
||||
|
||||
OutputField.Text += ex.Message + "\r\n";
|
||||
AppendOutputLine(MainPage.BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex.Message);
|
||||
return;
|
||||
}
|
||||
rootPage.NotifyUser("Connected", NotifyType.StatusMessage);
|
||||
|
||||
// Start a task to continuously read for incoming data
|
||||
Task receiving = ReceiveDataAsync(streamWebSocket);
|
||||
|
||||
// Start a task to continuously write outgoing data
|
||||
Task sending = SendDataAsync(streamWebSocket);
|
||||
}
|
||||
|
||||
// Continuously write outgoing data. For writing data we'll show how to use data.AsBuffer() to get an
|
||||
// IBuffer for use with webSocket.OutputStream.WriteAsync. Alternatively you can call
|
||||
// webSocket.OutputStream.AsStreamForWrite() to use .NET streams.
|
||||
private async void Scenario2SendData(object state)
|
||||
// IBuffer for use with activeSocket.OutputStream.WriteAsync. Alternatively you can call
|
||||
// activeSocket.OutputStream.AsStreamForWrite() to use .NET streams.
|
||||
private async Task SendDataAsync(StreamWebSocket activeSocket)
|
||||
{
|
||||
int dataSent = 0;
|
||||
int bytesSent = 0;
|
||||
byte[] data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
|
||||
|
||||
MarshalText(OutputField, "Background sending data in " + data.Length
|
||||
+ " byte chunks each second.\r\n");
|
||||
AppendOutputLine($"Background sending data in {data.Length} byte chunks each second.");
|
||||
|
||||
try
|
||||
{
|
||||
IOutputStream writeStream = (IOutputStream)state;
|
||||
|
||||
// Send until the socket gets closed/stopped
|
||||
while (true)
|
||||
{
|
||||
// using System.Runtime.InteropServices.WindowsRuntime;
|
||||
await writeStream.WriteAsync(data.AsBuffer());
|
||||
if (streamWebSocket != activeSocket)
|
||||
{
|
||||
// Our socket is no longer active. Stop sending.
|
||||
AppendOutputLine("Background write stopped.");
|
||||
return;
|
||||
}
|
||||
|
||||
dataSent += data.Length;
|
||||
MarshalText(DataSentField, dataSent.ToString(), false);
|
||||
await activeSocket.OutputStream.WriteAsync(data.AsBuffer());
|
||||
|
||||
bytesSent += data.Length;
|
||||
DataSentField.Text = bytesSent.ToString();
|
||||
|
||||
// Delay so the user can watch what's going on.
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
MarshalText(OutputField, "Background write stopped.\r\n");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
|
||||
|
@ -168,44 +150,48 @@ namespace Microsoft.Samples.Networking.WebSocket
|
|||
switch (status)
|
||||
{
|
||||
case WebErrorStatus.OperationCanceled:
|
||||
MarshalText(OutputField, "Background write canceled.\r\n");
|
||||
AppendOutputLine("Background write canceled.");
|
||||
break;
|
||||
|
||||
case WebErrorStatus.Unknown:
|
||||
throw;
|
||||
|
||||
default:
|
||||
MarshalText(OutputField, "Error: " + status + "\r\n");
|
||||
MarshalText(OutputField, ex.Message + "\r\n");
|
||||
AppendOutputLine("Error: " + status);
|
||||
AppendOutputLine(ex.Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Continuously read incoming data. For reading data we'll show how to use webSocket.InputStream.AsStream()
|
||||
// Continuously read incoming data. For reading data we'll show how to use activeSocket.InputStream.AsStream()
|
||||
// to get a .NET stream. Alternatively you could call readBuffer.AsBuffer() to use IBuffer with
|
||||
// webSocket.InputStream.ReadAsync.
|
||||
private async void Scenario2ReceiveData(object state)
|
||||
// activeSocket.InputStream.ReadAsync.
|
||||
private async Task ReceiveDataAsync(StreamWebSocket activeSocket)
|
||||
{
|
||||
Stream readStream = streamWebSocket.InputStream.AsStreamForRead();
|
||||
int bytesReceived = 0;
|
||||
try
|
||||
{
|
||||
Stream readStream = (Stream)state;
|
||||
MarshalText(OutputField, "Background read starting.\r\n");
|
||||
AppendOutputLine("Background read starting.");
|
||||
|
||||
while (true) // Until closed and ReadAsync fails.
|
||||
byte[] readBuffer = new byte[1000];
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (streamWebSocket != activeSocket)
|
||||
{
|
||||
// Our socket is no longer active. Stop reading.
|
||||
AppendOutputLine("Background read stopped.");
|
||||
return;
|
||||
}
|
||||
|
||||
int read = await readStream.ReadAsync(readBuffer, 0, readBuffer.Length);
|
||||
bytesReceived += read;
|
||||
MarshalText(DataReceivedField, bytesReceived.ToString(), false);
|
||||
|
||||
// Do something with the data.
|
||||
// This sample merely reports that the data was received.
|
||||
|
||||
bytesReceived += read;
|
||||
DataReceivedField.Text = bytesReceived.ToString();
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
MarshalText(OutputField, "Background read stopped.\r\n");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
|
||||
|
@ -213,84 +199,61 @@ namespace Microsoft.Samples.Networking.WebSocket
|
|||
switch (status)
|
||||
{
|
||||
case WebErrorStatus.OperationCanceled:
|
||||
MarshalText(OutputField, "Background write canceled.\r\n");
|
||||
AppendOutputLine("Background read canceled.");
|
||||
break;
|
||||
|
||||
case WebErrorStatus.Unknown:
|
||||
throw;
|
||||
|
||||
default:
|
||||
MarshalText(OutputField, "Error: " + status + "\r\n");
|
||||
MarshalText(OutputField, ex.Message + "\r\n");
|
||||
AppendOutputLine("Error: " + status);
|
||||
AppendOutputLine(ex.Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Stop_Click(object sender, RoutedEventArgs e)
|
||||
private void OnStop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (streamWebSocket != null)
|
||||
{
|
||||
rootPage.NotifyUser("Stopping", NotifyType.StatusMessage);
|
||||
streamWebSocket.Close(1000, "Closed due to user request.");
|
||||
streamWebSocket = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("There is no active socket to stop.", NotifyType.StatusMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
|
||||
|
||||
if (status == WebErrorStatus.Unknown)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
// Normally we'd use the status to test for specific conditions we want to handle specially,
|
||||
// and only use ex.Message for display purposes. In this sample, we'll just output the
|
||||
// status for debugging here, but still use ex.Message below.
|
||||
rootPage.NotifyUser("Error: " + status, NotifyType.ErrorMessage);
|
||||
|
||||
OutputField.Text += ex.Message + "\r\n";
|
||||
}
|
||||
SetBusy(true);
|
||||
rootPage.NotifyUser("Stopping", NotifyType.StatusMessage);
|
||||
CloseSocket();
|
||||
SetBusy(false);
|
||||
}
|
||||
|
||||
// This may be triggered remotely by the server or locally by Close/Dispose()
|
||||
private void Closed(IWebSocket sender, WebSocketClosedEventArgs args)
|
||||
private async void OnClosed(IWebSocket sender, WebSocketClosedEventArgs args)
|
||||
{
|
||||
MarshalText(OutputField, "Closed; Code: " + args.Code + ", Reason: " + args.Reason + "\r\n");
|
||||
// Dispatch the event to the UI thread so we do not need to synchronize access to streamWebSocket.
|
||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
AppendOutputLine("Closed; Code: " + args.Code + ", Reason: " + args.Reason);
|
||||
|
||||
if (streamWebSocket == sender)
|
||||
{
|
||||
CloseSocket();
|
||||
UpdateVisualState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void CloseSocket()
|
||||
{
|
||||
if (streamWebSocket != null)
|
||||
{
|
||||
streamWebSocket.Dispose();
|
||||
try
|
||||
{
|
||||
streamWebSocket.Close(1000, "Closed due to user request.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendOutputLine(MainPage.BuildWebSocketError(ex));
|
||||
AppendOutputLine(ex.Message);
|
||||
}
|
||||
streamWebSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void MarshalText(TextBox output, string value)
|
||||
private void AppendOutputLine(string value)
|
||||
{
|
||||
MarshalText(output, value, true);
|
||||
}
|
||||
|
||||
// When operations happen on a background thread we have to marshal UI updates back to the UI thread.
|
||||
private void MarshalText(TextBox output, string value, bool append)
|
||||
{
|
||||
var ignore = output.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
output.Text += value;
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Text = value;
|
||||
}
|
||||
});
|
||||
OutputField.Text += value + "\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<ProjectGuid>{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>WebSocketSample</RootNamespace>
|
||||
<RootNamespace>SDKTemplate</RootNamespace>
|
||||
<AssemblyName>WebSocket</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
|
@ -126,11 +126,13 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario1_UTF8.xaml">
|
||||
<Page Include="..\shared\Scenario1_UTF8.xaml">
|
||||
<Link>Scenario1_UTF8.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario2_Binary.xaml">
|
||||
<Page Include="..\shared\Scenario2_Binary.xaml">
|
||||
<Link>Scenario2_Binary.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
|
|
|
@ -54,13 +54,11 @@
|
|||
<AppxManifest Include="package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
<Content Include="css\scenario1_Download.css" />
|
||||
<Content Include="css\scenario2_Upload.css" />
|
||||
<Content Include="..\..\..\SharedContent\js\default.html">
|
||||
<Link>default.html</Link>
|
||||
</Content>
|
||||
<Content Include="html\scenario1_Download.html" />
|
||||
<Content Include="html\scenario2_Upload.html" />
|
||||
<Content Include="html\scenario1-utf8.html" />
|
||||
<Content Include="html\scenario2-binary.html" />
|
||||
<Content Include="..\..\..\SharedContent\media\microsoft-sdk.png">
|
||||
<Link>images\microsoft-sdk.png</Link>
|
||||
</Content>
|
||||
|
@ -85,10 +83,9 @@
|
|||
<Content Include="..\..\..\SharedContent\js\css\default.css">
|
||||
<Link>css\default.css</Link>
|
||||
</Content>
|
||||
<Content Include="js\helpers.js" />
|
||||
<Content Include="js\sample-configuration.js" />
|
||||
<Content Include="js\scenario1_Download.js" />
|
||||
<Content Include="js\scenario2_Upload.js" />
|
||||
<Content Include="js\scenario1-utf8.js" />
|
||||
<Content Include="js\scenario2-binary.js" />
|
||||
<Content Include="..\..\..\SharedContent\js\Microsoft.WinJS\css\ui-dark.css">
|
||||
<Link>Microsoft.WinJS.4.0\css\ui-dark.css</Link>
|
||||
</Content>
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
/*
|
||||
Copyright (c) Microsoft Corporation. All rights reserved
|
||||
*/
|
||||
|
||||
#serverAddress {
|
||||
width: auto;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
/*
|
||||
Copyright (c) Microsoft Corporation. All rights reserved
|
||||
*/
|
||||
|
||||
#serverAddress {
|
||||
width: auto;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="/js/scenario1-utf8.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="win-type-body">
|
||||
<div>
|
||||
<h2 id="sampleHeader" class="win-type-subheader">Description:</h2>
|
||||
<div id="scenarioDescription">UTF-8 text messages</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
This scenario uses a MessageWebSocket to send UTF-8 strings.
|
||||
The sample server supports both plaintext WebSocket (ws://) and secure WebSocket (wss://) server endpoints.
|
||||
</p>
|
||||
<p>
|
||||
<label>Server address:
|
||||
<input type="text" id="serverAddressField" value="ws://localhost/WebSocketSample/EchoWebSocket.ashx" class="win-textbox" size="60" style="width: auto">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<button id="connectButton" class="win-button">Connect</button>
|
||||
<button id="disconnectButton" class="win-button">Disconnect</button>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Enter text to send to the server:
|
||||
<input type="text" id="inputField" value="Hello World" class="win-textbox">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<button id="sendButton" class="win-button">Send</button>
|
||||
</p>
|
||||
<p id="outputField">
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,46 +0,0 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="/css/scenario1_Download.css">
|
||||
<script src="/js/helpers.js"></script>
|
||||
<script src="/js/scenario1_Download.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="win-type-body">
|
||||
<div data-win-control="SdkSample.ScenarioInput">
|
||||
<p>This shows how to use a MessageWebSocket to send UTF-8 strings.</p>
|
||||
<p>
|
||||
<label for="serverAddress">Server address: </label>
|
||||
<input type="text" id="serverAddress" value="ws://localhost/WebSocketSample/EchoWebSocket.ashx" size="60" class="win-textbox">
|
||||
</p>
|
||||
<p>
|
||||
<label for="inputField">Enter text to send to the server: </label>
|
||||
<input type="text" id="inputField" value="Hello World" class="win-textbox">
|
||||
</p>
|
||||
<p>
|
||||
<button id="startButton" class="win-button">Start</button>
|
||||
<button id="closeButton" class="win-button">Close</button>
|
||||
</p>
|
||||
</div>
|
||||
<div data-win-control="SdkSample.ScenarioOutput">
|
||||
<div id="outputField">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,47 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="/js/scenario2-binary.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="win-type-body">
|
||||
<div>
|
||||
<h2 id="sampleHeader" class="win-type-subheader">Description:</h2>
|
||||
<div id="scenarioDescription">Binary data stream</div>
|
||||
</div>
|
||||
|
||||
<p>This shows how to use a StreamWebSocket to send and receive binary data.</p>
|
||||
<p>
|
||||
<label>
|
||||
Server address:
|
||||
<input type="text" id="serverAddressField" value="ws://localhost/WebSocketSample/EchoWebSocket.ashx" class="win-textbox" size="60" style="width: auto">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<button id="startButton" class="win-button">Start</button>
|
||||
<button id="stopButton" class="win-button">Stop</button>
|
||||
</p>
|
||||
<div>
|
||||
<p>Data Sent: <span id="dataSent"></span></p>
|
||||
<p>Data Received: <span id="dataReceived"></span></p>
|
||||
<p id="outputField">
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,50 +0,0 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="/css/scenario2_Upload.css">
|
||||
<script src="/js/helpers.js"></script>
|
||||
<script src="/js/scenario2_Upload.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="win-type-body">
|
||||
<div data-win-control="SdkSample.ScenarioInput">
|
||||
<p>This shows how to use a StreamWebSocket to send and receive binary data.</p>
|
||||
<p>
|
||||
<label for="serverAddress">Server address: </label>
|
||||
<input type="text" id="serverAddress" value="ws://localhost/WebSocketSample/EchoWebSocket.ashx" size="60" class="win-textbox">
|
||||
</p>
|
||||
<p>
|
||||
<button id="startButton" class="win-button">Start</button>
|
||||
<button id="closeButton" class="win-button">Close</button>
|
||||
</p>
|
||||
</div>
|
||||
<div data-win-control="SdkSample.ScenarioOutput">
|
||||
<p>
|
||||
<label for="dataSent">Data Sent:</label>
|
||||
<input type="text" id="dataSent" class="win-textbox">
|
||||
</p>
|
||||
<p>
|
||||
<label for="dataReceived">Data Received:</label>
|
||||
<input type="text" id="dataReceived" class="win-textbox">
|
||||
</p>
|
||||
<div id="outputField">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,98 +0,0 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
// Common helper methods
|
||||
|
||||
function getError(e) {
|
||||
"use strict";
|
||||
|
||||
var webSocketError = Windows.Networking.Sockets.WebSocketError.getStatus(e.number);
|
||||
switch (webSocketError) {
|
||||
case Windows.Web.WebErrorStatus.unknown: return e;
|
||||
case Windows.Web.WebErrorStatus.certificateCommonNameIsIncorrect: return "CertificateCommonNameIsIncorrect";
|
||||
case Windows.Web.WebErrorStatus.certificateExpired: return "CertificateExpired";
|
||||
case Windows.Web.WebErrorStatus.certificateContainsErrors: return "CertificateContainsErrors";
|
||||
case Windows.Web.WebErrorStatus.certificateRevoked: return "CertificateRevoked";
|
||||
case Windows.Web.WebErrorStatus.certificateIsInvalid: return "CertificateIsInvalid";
|
||||
case Windows.Web.WebErrorStatus.serverUnreachable: return "ServerUnreachable";
|
||||
case Windows.Web.WebErrorStatus.timeout: return "Timeout";
|
||||
case Windows.Web.WebErrorStatus.errorHttpInvalidServerResponse: return "ErrorHttpInvalidServerResponse";
|
||||
case Windows.Web.WebErrorStatus.connectionAborted: return "ConnectionAborted";
|
||||
case Windows.Web.WebErrorStatus.connectionReset: return "ConnectionReset";
|
||||
case Windows.Web.WebErrorStatus.disconnected: return "Disconnected";
|
||||
case Windows.Web.WebErrorStatus.httpToHttpsOnRedirection: return "HttpToHttpsOnRedirection";
|
||||
case Windows.Web.WebErrorStatus.httpsToHttpOnRedirection: return "HttpsToHttpOnRedirection";
|
||||
case Windows.Web.WebErrorStatus.cannotConnect: return "CannotConnect";
|
||||
case Windows.Web.WebErrorStatus.hostNameNotResolved: return "HostNameNotResolved";
|
||||
case Windows.Web.WebErrorStatus.operationCanceled: return "OperationCanceled";
|
||||
case Windows.Web.WebErrorStatus.redirectFailed: return "RedirectFailed";
|
||||
case Windows.Web.WebErrorStatus.unexpectedStatusCode: return "UnexpectedStatusCode";
|
||||
case Windows.Web.WebErrorStatus.unexpectedRedirection: return "UnexpectedRedirection";
|
||||
case Windows.Web.WebErrorStatus.unexpectedClientError: return "UnexpectedClientError";
|
||||
case Windows.Web.WebErrorStatus.unexpectedServerError: return "UnexpectedServerError";
|
||||
case Windows.Web.WebErrorStatus.multipleChoices: return "MultipleChoices (300)";
|
||||
case Windows.Web.WebErrorStatus.movedPermanently: return "MovedPermanently (301)";
|
||||
case Windows.Web.WebErrorStatus.found: return "Found (302)";
|
||||
case Windows.Web.WebErrorStatus.seeOther: return "SeeOther (303)";
|
||||
case Windows.Web.WebErrorStatus.notModified: return "NotModified (304)";
|
||||
case Windows.Web.WebErrorStatus.useProxy: return "UseProxy (305)";
|
||||
case Windows.Web.WebErrorStatus.temporaryRedirect: return "TemporaryRedirect (307)";
|
||||
case Windows.Web.WebErrorStatus.badRequest: return "BadRequest (400)";
|
||||
case Windows.Web.WebErrorStatus.unauthorized: return "Unauthorized (401)";
|
||||
case Windows.Web.WebErrorStatus.paymentRequired: return "PaymentRequired (402)";
|
||||
case Windows.Web.WebErrorStatus.forbidden: return "Forbidden (403)";
|
||||
case Windows.Web.WebErrorStatus.notFound: return "NotFound (404)";
|
||||
case Windows.Web.WebErrorStatus.methodNotAllowed: return "MethodNotAllowed (405)";
|
||||
case Windows.Web.WebErrorStatus.notAcceptable: return "NotAcceptable (406)";
|
||||
case Windows.Web.WebErrorStatus.proxyAuthenticationRequired: return "ProxyAuthenticationRequired (407)";
|
||||
case Windows.Web.WebErrorStatus.requestTimeout: return "RequestTimeout (408)";
|
||||
case Windows.Web.WebErrorStatus.conflict: return "Conflict (409)";
|
||||
case Windows.Web.WebErrorStatus.gone: return "Gone (410)";
|
||||
case Windows.Web.WebErrorStatus.lengthRequired: return "LengthRequired (411)";
|
||||
case Windows.Web.WebErrorStatus.preconditionFailed: return "PreconditionFailed (412)";
|
||||
case Windows.Web.WebErrorStatus.requestEntityTooLarge: return "RequestEntityTooLarge (413)";
|
||||
case Windows.Web.WebErrorStatus.requestUriTooLong: return "RequestUriTooLong (414)";
|
||||
case Windows.Web.WebErrorStatus.unsupportedMediaType: return "UnsupportedMediaType (415)";
|
||||
case Windows.Web.WebErrorStatus.requestedRangeNotSatisfiable: return "RequestedRangeNotSatisfiable (416)";
|
||||
case Windows.Web.WebErrorStatus.expectationFailed: return "ExpectationFailed (417)";
|
||||
case Windows.Web.WebErrorStatus.internalServerError: return "InternalServerError (500)";
|
||||
case Windows.Web.WebErrorStatus.notImplemented: return "NotImplemented (501)";
|
||||
case Windows.Web.WebErrorStatus.badGateway: return "BadGateway (502)";
|
||||
case Windows.Web.WebErrorStatus.serviceUnavailable: return "ServiceUnavailable (503)";
|
||||
case Windows.Web.WebErrorStatus.gatewayTimeout: return "GatewayTimeout (504)";
|
||||
case Windows.Web.WebErrorStatus.httpVersionNotSupported: return "HttpVersionNotSupported (505)";
|
||||
default: return e;
|
||||
}
|
||||
};
|
||||
|
||||
function validateAndCreateUri(uriString) {
|
||||
"use strict";
|
||||
|
||||
var webSocketUri;
|
||||
try {
|
||||
webSocketUri = new Windows.Foundation.Uri(uriString);
|
||||
} catch (error) {
|
||||
throw "Error: Invalid URI";
|
||||
}
|
||||
|
||||
if (webSocketUri.fragment !== "") {
|
||||
throw "Error: URI fragments not supported in WebSocket URIs.";
|
||||
}
|
||||
|
||||
// Uri.schemeName returns the canonicalized scheme name so we can use case-sensitive, ordinal string
|
||||
// comparison.
|
||||
var scheme = webSocketUri.schemeName;
|
||||
if ((scheme !== "ws") && (scheme !== "wss")) {
|
||||
throw "Error: WebSockets only support ws:// and wss:// schemes.";
|
||||
}
|
||||
|
||||
return webSocketUri;
|
||||
}
|
|
@ -12,15 +12,78 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
var sampleTitle = "WebSocket";
|
||||
var WebSocketError = Windows.Networking.Sockets.WebSocketError;
|
||||
var WebErrorStatus = Windows.Web.WebErrorStatus;
|
||||
|
||||
var sampleTitle = "WebSocket JS Sample";
|
||||
|
||||
var scenarios = [
|
||||
{ url: "/html/scenario1_Download.html", title: "UTF-8 text messages" },
|
||||
{ url: "/html/scenario2_Upload.html", title: "Binary data stream" }
|
||||
{ url: "/html/scenario1-utf8.html", title: "UTF-8 text messages" },
|
||||
{ url: "/html/scenario2-binary.html", title: "Binary data stream" }
|
||||
];
|
||||
|
||||
// Look up the name for an enumeration member.
|
||||
function lookupEnumName(e, value) {
|
||||
for (var name in e) {
|
||||
if (e[name] === value) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
// No name available; just use the number.
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
function validateAndCreateUri(uriString) {
|
||||
var webSocketUri;
|
||||
try {
|
||||
webSocketUri = new Windows.Foundation.Uri(uriString);
|
||||
} catch (error) {
|
||||
WinJS.log && WinJS.log("Error: Invalid URI", "sample", "error");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (webSocketUri.fragment !== "") {
|
||||
WinJS.log && WinJS.log("Error: URI fragments not supported in WebSocket URIs.", "sample", "error");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Uri.schemeName returns the canonicalized scheme name so we can use case-sensitive, ordinal string
|
||||
// comparison.
|
||||
var scheme = webSocketUri.schemeName;
|
||||
if ((scheme !== "ws") && (scheme !== "wss")) {
|
||||
WinJS.log && WinJS.log("Error: WebSockets only support ws:// and wss:// schemes.", "sample", "error");
|
||||
return null;
|
||||
}
|
||||
|
||||
return webSocketUri;
|
||||
}
|
||||
|
||||
function buildWebSocketError(error) {
|
||||
var hResult = error.number;
|
||||
var status = WebSocketError.getStatus(hResult);
|
||||
|
||||
// Normally we'd use the error number status to test for specific conditions we want to handle.
|
||||
// In this sample, we'll just output them for demonstration purposes.
|
||||
switch (status) {
|
||||
case WebErrorStatus.cannotConnect:
|
||||
case WebErrorStatus.notFound:
|
||||
case WebErrorStatus.requestTimeout:
|
||||
return "Cannot connect to the server. Please make sure " +
|
||||
"to run the server setup script before running the sample.";
|
||||
|
||||
case WebErrorStatus.unknown:
|
||||
return "COM error: " + hResult;
|
||||
|
||||
default:
|
||||
return "Error: " + lookupEnumName(WebErrorStatus, status);
|
||||
}
|
||||
}
|
||||
|
||||
WinJS.Namespace.define("SdkSample", {
|
||||
sampleTitle: sampleTitle,
|
||||
scenarios: new WinJS.Binding.List(scenarios)
|
||||
scenarios: new WinJS.Binding.List(scenarios),
|
||||
lookupEnumName: lookupEnumName,
|
||||
validateAndCreateUri: validateAndCreateUri,
|
||||
buildWebSocketError: buildWebSocketError
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var MessageWebSocket = Windows.Networking.Sockets.MessageWebSocket;
|
||||
var SocketMessageType = Windows.Networking.Sockets.SocketMessageType;
|
||||
var UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding;
|
||||
|
||||
// Local variables
|
||||
var messageWebSocket;
|
||||
var messageWriter;
|
||||
var busy;
|
||||
|
||||
// DOM elements
|
||||
var serverAddressField;
|
||||
var connectButton;
|
||||
var disconnectButton;
|
||||
var inputField;
|
||||
var sendButton;
|
||||
var outputField;
|
||||
|
||||
var page = WinJS.UI.Pages.define("/html/scenario1-utf8.html", {
|
||||
ready: function (element, options) {
|
||||
serverAddressField = document.getElementById("serverAddressField");
|
||||
connectButton = document.getElementById("connectButton");
|
||||
disconnectButton = document.getElementById("disconnectButton");
|
||||
inputField = document.getElementById("inputField");
|
||||
sendButton = document.getElementById("sendButton");
|
||||
outputField = document.getElementById("outputField");
|
||||
|
||||
connectButton.addEventListener("click", onConnect, false);
|
||||
disconnectButton.addEventListener("click", onDisconnect, false);
|
||||
sendButton.addEventListener("click", onSend, false);
|
||||
|
||||
updateVisualState();
|
||||
},
|
||||
unload: function (eventObject) {
|
||||
closeSocket();
|
||||
}
|
||||
});
|
||||
|
||||
function updateVisualState() {
|
||||
serverAddressField.disabled = busy || messageWebSocket;
|
||||
connectButton.disabled = busy || messageWebSocket;
|
||||
disconnectButton.disabled = busy || !messageWebSocket;
|
||||
inputField.disabled = busy || !messageWebSocket
|
||||
sendButton.disabled = busy || !messageWebSocket
|
||||
}
|
||||
|
||||
function setBusy(value) {
|
||||
busy = value;
|
||||
updateVisualState();
|
||||
}
|
||||
|
||||
function onConnect() {
|
||||
setBusy(true);
|
||||
connectAsync().done(function () {
|
||||
setBusy(false);
|
||||
});
|
||||
}
|
||||
|
||||
function connectAsync() {
|
||||
// Validating the URI is required since it was received from an untrusted source (user
|
||||
// input). The URI is validated by calling validateAndCreateUri() that will return null
|
||||
// for strings that are not valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines on the local network
|
||||
// or internet. In these cases the app requires the "Home or Work Networking" or
|
||||
// "Internet (Client)" capability respectively.
|
||||
var server = SdkSample.validateAndCreateUri(serverAddressField.value);
|
||||
if (!server)
|
||||
{
|
||||
return WinJS.Promise.wrap();
|
||||
}
|
||||
|
||||
// Set up the socket data format and callbacks
|
||||
messageWebSocket = new MessageWebSocket();
|
||||
messageWebSocket.control.messageType = SocketMessageType.utf8;
|
||||
messageWebSocket.addEventListener("messagereceived", onMessageReceived);
|
||||
messageWebSocket.addEventListener("closed", onClosed);
|
||||
|
||||
appendOutputLine("Connecting to: " + server.absoluteUri);
|
||||
|
||||
return messageWebSocket.connectAsync(server).then(function () {
|
||||
// The default DataWriter encoding is utf8.
|
||||
messageWriter = new Windows.Storage.Streams.DataWriter(messageWebSocket.outputStream);
|
||||
WinJS.log && WinJS.log("Connected", "sample", "status");
|
||||
}, function (error) {
|
||||
messageWebSocket.close();
|
||||
messageWebSocket = null;
|
||||
|
||||
appendOutputLine(SdkSample.buildWebSocketError(error));
|
||||
appendOutputLine(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function onSend() {
|
||||
setBusy(true);
|
||||
sendAsync().done(function () {
|
||||
setBusy(false);
|
||||
});
|
||||
}
|
||||
|
||||
function sendAsync() {
|
||||
var message = inputField.value;
|
||||
if (message === "") {
|
||||
WinJS.log && WinJS.log("Please specify text to send", "sample", "error");
|
||||
return WinJS.Promise.wrap();
|
||||
}
|
||||
|
||||
appendOutputLine("Sending Message: " + message);
|
||||
|
||||
// Buffer any data we want to send.
|
||||
messageWriter.writeString(message);
|
||||
|
||||
// Send the data as one complete message.
|
||||
return messageWriter.storeAsync().then(function() {
|
||||
WinJS.log && WinJS.log("Send Complete", "sample", "status");
|
||||
}, function(error) {
|
||||
appendOutputLine(SdkSample.buildWebSocketError(error));
|
||||
appendOutputLine(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function onMessageReceived(args) {
|
||||
appendOutputLine("Message Received; Type: " + SdkSample.lookupEnumName(SocketMessageType, args.messageType));
|
||||
|
||||
// The incoming message is already buffered.
|
||||
var reader = args.getDataReader();
|
||||
reader.unicodeEncoding = UnicodeEncoding.utf8;
|
||||
|
||||
try {
|
||||
appendOutputLine(reader.readString(reader.unconsumedBufferLength));
|
||||
} catch (error) {
|
||||
appendOutputLine(SdkSample.buildWebSocketError(error));
|
||||
appendOutputLine(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function onDisconnect() {
|
||||
setBusy(true);
|
||||
WinJS.log && WinJS.log("Closing", "sample", "status");
|
||||
closeSocket();
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
function onClosed(e) {
|
||||
appendOutputLine("Closed; Code: " + e.code + " Reason: " + e.reason);
|
||||
if (messageWebSocket) {
|
||||
closeSocket();
|
||||
updateVisualState();
|
||||
}
|
||||
}
|
||||
|
||||
function closeSocket() {
|
||||
if (messageWebSocket) {
|
||||
try {
|
||||
messageWebSocket.close(1000, "Closed due to user request.");
|
||||
} catch (error) {
|
||||
appendOutputLine(SdkSample.buildWebSocketError(error));
|
||||
appendOutputLine(error.message);
|
||||
}
|
||||
messageWebSocket = null;
|
||||
}
|
||||
if (messageWriter) {
|
||||
messageWriter.close();
|
||||
messageWriter = null;
|
||||
}
|
||||
}
|
||||
|
||||
function appendOutputLine(text) {
|
||||
outputField.innerText += "\r\n" + text;
|
||||
}
|
||||
|
||||
})();
|
|
@ -1,155 +0,0 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var messageWebSocket;
|
||||
var messageWriter;
|
||||
|
||||
var page = WinJS.UI.Pages.define("/html/scenario1_Download.html", {
|
||||
ready: function (element, options) {
|
||||
document.getElementById("startButton").addEventListener("click", startSend, false);
|
||||
document.getElementById("closeButton").addEventListener("click", closeSocket, false);
|
||||
},
|
||||
unload: function (eventObject) {
|
||||
closeSocket(eventObject);
|
||||
}
|
||||
});
|
||||
|
||||
function startSend(eventObject) {
|
||||
if (document.getElementById("inputField").value === "") {
|
||||
WinJS.log && WinJS.log("Please specify text to send", "sample", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!messageWebSocket) {
|
||||
// Set up the socket data format and callbacks
|
||||
var webSocket = new Windows.Networking.Sockets.MessageWebSocket();
|
||||
// Both utf8 and binary message types are supported. If utf8 is specified then the developer
|
||||
// promises to only send utf8 encoded data.
|
||||
webSocket.control.messageType = Windows.Networking.Sockets.SocketMessageType.utf8;
|
||||
webSocket.onmessagereceived = onMessageReceived;
|
||||
webSocket.onclosed = onClosed;
|
||||
|
||||
// Validating the URI is required since it was received from an untrusted source (user
|
||||
// input). The URI is validated by calling validateAndCreateUri() that will throw an exception for
|
||||
// strings that are not valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines in the LAN
|
||||
// or internet. In these cases the app requires the "Home or Work Networking" or
|
||||
// "Internet (Client)" capability respectively.
|
||||
var uri;
|
||||
try {
|
||||
uri = validateAndCreateUri(document.getElementById("serverAddress").value);
|
||||
} catch (error) {
|
||||
WinJS.log && WinJS.log(error, "sample", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
WinJS.log && WinJS.log("Connecting to: " + uri.absoluteUri, "sample", "status");
|
||||
|
||||
webSocket.connectAsync(uri).done(function () {
|
||||
|
||||
WinJS.log && WinJS.log("Connected", "sample", "status");
|
||||
|
||||
messageWebSocket = webSocket;
|
||||
// The default DataWriter encoding is utf8.
|
||||
messageWriter = new Windows.Storage.Streams.DataWriter(webSocket.outputStream);
|
||||
sendMessage();
|
||||
|
||||
}, function (error) {
|
||||
var errorStatus = Windows.Networking.Sockets.WebSocketError.getStatus(error.number);
|
||||
if (errorStatus === Windows.Web.WebErrorStatus.cannotConnect ||
|
||||
errorStatus === Windows.Web.WebErrorStatus.notFound ||
|
||||
errorStatus === Windows.Web.WebErrorStatus.requestTimeout) {
|
||||
WinJS.log && WinJS.log("Cannot connect to the server. Please make sure " +
|
||||
"to run the server setup script before running the sample.", "sample", "error");
|
||||
} else {
|
||||
WinJS.log && WinJS.log("Failed to connect: " + getError(error), "sample", "error");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
WinJS.log && WinJS.log("Already Connected", "sample", "status");
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
|
||||
function onMessageReceived(eventArguments) {
|
||||
// The incoming message is already buffered.
|
||||
var dataReader = eventArguments.getDataReader();
|
||||
log("Message Received; Type: " + getMessageTypeName(eventArguments.messageType)
|
||||
+ ", Bytes: " + dataReader.unconsumedBufferLength + ", Text: ");
|
||||
log(dataReader.readString(dataReader.unconsumedBufferLength));
|
||||
}
|
||||
|
||||
function getMessageTypeName(messageType) {
|
||||
switch (messageType) {
|
||||
case Windows.Networking.Sockets.SocketMessageType.utf8:
|
||||
return "UTF-8";
|
||||
case Windows.Networking.Sockets.SocketMessageType.binary:
|
||||
return "Binary";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
function sendMessage() {
|
||||
log("Sending message");
|
||||
messageWriter.writeString(document.getElementById("inputField").value);
|
||||
messageWriter.storeAsync().done("", sendError);
|
||||
}
|
||||
|
||||
function sendError(error) {
|
||||
log("Send error: " + getError(error));
|
||||
}
|
||||
|
||||
function onClosed(closedEventInfo) {
|
||||
log("Closed; Code: " + closedEventInfo.code + " Reason: " + closedEventInfo.reason);
|
||||
if (!messageWebSocket) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeSocketCore(Number(null), String(null));
|
||||
}
|
||||
|
||||
function closeSocket(eventObject) {
|
||||
if (!messageWebSocket) {
|
||||
WinJS.log && WinJS.log("Not connected", "sample", "status");
|
||||
return;
|
||||
}
|
||||
|
||||
WinJS.log && WinJS.log("Closing", "sample", "status");
|
||||
closeSocketCore(1000, "Closed due to user request.");
|
||||
}
|
||||
|
||||
function closeSocketCore(closeCode, closeStatus) {
|
||||
if (closeCode && closeStatus) {
|
||||
messageWebSocket.close(closeCode, closeStatus);
|
||||
} else {
|
||||
messageWebSocket.close();
|
||||
}
|
||||
|
||||
messageWebSocket = null;
|
||||
|
||||
if (messageWriter) {
|
||||
messageWriter.close();
|
||||
messageWriter = null;
|
||||
}
|
||||
}
|
||||
|
||||
function log(text) {
|
||||
var outputFiled = document.getElementById("outputField");
|
||||
if (outputFiled !== null) {
|
||||
outputFiled.innerHTML += text + "<br>";
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,194 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// Local variables
|
||||
var streamWebSocket;
|
||||
var busy;
|
||||
|
||||
// DOM elements
|
||||
var serverAddressField;
|
||||
var startButton;
|
||||
var stopButton;
|
||||
var outputField;
|
||||
|
||||
var page = WinJS.UI.Pages.define("/html/scenario2-binary.html", {
|
||||
ready: function (element, options) {
|
||||
serverAddressField = document.getElementById("serverAddressField");
|
||||
startButton = document.getElementById("startButton");
|
||||
stopButton = document.getElementById("stopButton");
|
||||
outputField = document.getElementById("outputField");
|
||||
|
||||
startButton.addEventListener("click", onStart, false);
|
||||
stopButton.addEventListener("click", onStop, false);
|
||||
|
||||
updateVisualState();
|
||||
},
|
||||
unload: function () {
|
||||
closeSocket();
|
||||
}
|
||||
});
|
||||
|
||||
function updateVisualState() {
|
||||
serverAddressField.disabled = busy || streamWebSocket;
|
||||
startButton.disabled = busy || streamWebSocket;
|
||||
stopButton.disabled = busy || !streamWebSocket;
|
||||
}
|
||||
|
||||
function setBusy(value) {
|
||||
busy = value;
|
||||
updateVisualState();
|
||||
}
|
||||
|
||||
function onStart() {
|
||||
setBusy(true);
|
||||
startAsync().done(function () {
|
||||
setBusy(false);
|
||||
});
|
||||
}
|
||||
|
||||
function startAsync() {
|
||||
// Validating the URI is required since it was received from an untrusted source (user
|
||||
// input). The URI is validated by calling validateAndCreateUri() that will return null
|
||||
// for strings that are not valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines on the local network
|
||||
// or internet. In these cases the app requires the "Home or Work Networking" or
|
||||
// "Internet (Client)" capability respectively.
|
||||
var server = SdkSample.validateAndCreateUri(serverAddressField.value);
|
||||
if (!server) {
|
||||
return WinJS.Promise.wrap();
|
||||
}
|
||||
|
||||
streamWebSocket = new Windows.Networking.Sockets.StreamWebSocket();
|
||||
streamWebSocket.addEventListener("closed", onClosed);
|
||||
|
||||
appendOutputLine("Connecting to: " + server.absoluteUri);
|
||||
|
||||
return streamWebSocket.connectAsync(server).then(function () {
|
||||
WinJS.log && WinJS.log("Connected", "sample", "status");
|
||||
|
||||
// Start a loop to continuously read incoming data.
|
||||
receiveData(streamWebSocket);
|
||||
|
||||
// Start a loop to continuously write outgoing data.
|
||||
sendData(streamWebSocket);
|
||||
}, function (error) {
|
||||
streamWebSocket.close();
|
||||
streamWebSocket = null;
|
||||
|
||||
appendOutputLine(SdkSample.buildWebSocketError(error));
|
||||
appendOutputLine(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function sendData(activeSocket) {
|
||||
var dataWriter = new Windows.Storage.Streams.DataWriter(activeSocket.outputStream);
|
||||
var bytesSent = 0;
|
||||
var data = "Hello World";
|
||||
|
||||
function loopAsync() {
|
||||
if (streamWebSocket != activeSocket) {
|
||||
// Our socket is no longer active. Stop sending.
|
||||
return;
|
||||
}
|
||||
var size = dataWriter.measureString(data);
|
||||
|
||||
dataWriter.writeString(data);
|
||||
return dataWriter.storeAsync().then(function () {
|
||||
bytesSent += size;
|
||||
dataSent.innerText = bytesSent;
|
||||
|
||||
// Add a 1 second delay so the user can see what's going on.
|
||||
return WinJS.Promise.timeout(1000);
|
||||
}).then(loopAsync);
|
||||
}
|
||||
|
||||
loopAsync().then(function () {
|
||||
}, function (error) {
|
||||
appendOutputLine("During write: " + SdkSample.buildWebSocketError(error));
|
||||
appendOutputLine(error.message);
|
||||
}).done(function () {
|
||||
dataWriter.close();
|
||||
});
|
||||
}
|
||||
|
||||
function receiveData(activeSocket) {
|
||||
var dataReader = new Windows.Storage.Streams.DataReader(activeSocket.inputStream);
|
||||
var bytesReceived = 0;
|
||||
|
||||
// When buffering, return as soon as any data is available.
|
||||
dataReader.inputStreamOptions = Windows.Storage.Streams.InputStreamOptions.partial;
|
||||
|
||||
function loopAsync() {
|
||||
if (streamWebSocket != activeSocket) {
|
||||
// Our socket is no longer active. Stop reading.
|
||||
appendOutputLine("Background read stopped.");
|
||||
return;
|
||||
}
|
||||
// Buffer as much data as you require for your protocol.
|
||||
return dataReader.loadAsync(100).then(function (sizeBytesRead) {
|
||||
bytesReceived += sizeBytesRead;
|
||||
dataReceived.innerText = bytesReceived;
|
||||
|
||||
var incomingBytes = new Array(sizeBytesRead);
|
||||
dataReader.readBytes(incomingBytes);
|
||||
|
||||
// Do something with the data.
|
||||
// Alternatively you can use DataReader to read out individual booleans,
|
||||
// ints, strings, etc.
|
||||
|
||||
// Start another read.
|
||||
return loopAsync();
|
||||
});
|
||||
}
|
||||
|
||||
loopAsync().then(function () {
|
||||
}, function (error) {
|
||||
appendOutputLine("During read: " + SdkSample.buildWebSocketError(error));
|
||||
appendOutputLine(error.message);
|
||||
}).done(function () {
|
||||
dataReader.close();
|
||||
});
|
||||
}
|
||||
|
||||
function onStop() {
|
||||
setBusy(true);
|
||||
WinJS.log && WinJS.log("Closing", "sample", "status");
|
||||
closeSocket();
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
function onClosed(e) {
|
||||
appendOutputLine("Closed; Code: " + e.code + " Reason: " + e.reason);
|
||||
if (streamWebSocket === e.target) {
|
||||
closeSocket();
|
||||
updateVisualState();
|
||||
}
|
||||
}
|
||||
|
||||
function closeSocket() {
|
||||
if (streamWebSocket) {
|
||||
try {
|
||||
streamWebSocket.close(1000, "Closed due to user request.");
|
||||
} catch (error) {
|
||||
WinJS.log && WinJS.log(SdkSample.buildWebSocketError(error), "sample", "error");
|
||||
appendOutputLine(error.message);
|
||||
}
|
||||
streamWebSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
function appendOutputLine(text) {
|
||||
outputField.innerText += "\r\n" + text;
|
||||
}
|
||||
})();
|
|
@ -1,180 +0,0 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var streamWebSocket;
|
||||
var dataWriter;
|
||||
var dataReader;
|
||||
var data = "Hello World";
|
||||
var countOfDataSent;
|
||||
var countOfDataReceived;
|
||||
|
||||
var page = WinJS.UI.Pages.define("/html/scenario2_Upload.html", {
|
||||
ready: function (element, options) {
|
||||
document.getElementById("startButton").addEventListener("click", start, false);
|
||||
document.getElementById("closeButton").addEventListener("click", closeSocket, false);
|
||||
},
|
||||
unload: function (eventObject) {
|
||||
closeSocket(eventObject);
|
||||
}
|
||||
});
|
||||
|
||||
function start(eventObject) {
|
||||
if (streamWebSocket) {
|
||||
WinJS.log && WinJS.log("Already started", "sample", "status");
|
||||
return;
|
||||
}
|
||||
|
||||
var webSocket = new Windows.Networking.Sockets.StreamWebSocket();
|
||||
webSocket.onclosed = onClosed;
|
||||
|
||||
// Validating the URI is required since it was received from an untrusted source (user
|
||||
// input). The URI is validated by calling validateAndCreateUri() that will throw an exception for
|
||||
// strings that are not valid WebSocket URIs.
|
||||
// Note that when enabling the text box users may provide URIs to machines in the LAN
|
||||
// or internet. In these cases the app requires the "Home or Work Networking" or
|
||||
// "Internet (Client)" capability respectively.
|
||||
var uri;
|
||||
try {
|
||||
uri = validateAndCreateUri(document.getElementById("serverAddress").value);
|
||||
} catch (error) {
|
||||
WinJS.log && WinJS.log(error, "sample", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
WinJS.log && WinJS.log("Connecting to: " + uri.absoluteUri, "sample", "status");
|
||||
|
||||
webSocket.connectAsync(uri).done(function () {
|
||||
WinJS.log && WinJS.log("Connected", "sample", "status");
|
||||
|
||||
streamWebSocket = webSocket;
|
||||
dataWriter = new Windows.Storage.Streams.DataWriter(webSocket.outputStream);
|
||||
dataReader = new Windows.Storage.Streams.DataReader(webSocket.inputStream);
|
||||
// When buffering, return as soon as any data is available.
|
||||
dataReader.inputStreamOptions = Windows.Storage.Streams.InputStreamOptions.partial;
|
||||
countOfDataSent = 0;
|
||||
countOfDataReceived = 0;
|
||||
|
||||
// Continuously send data to the server
|
||||
writeOutgoing();
|
||||
|
||||
// Continuously listen for a response
|
||||
readIncoming();
|
||||
|
||||
}, function (error) {
|
||||
var errorStatus = Windows.Networking.Sockets.WebSocketError.getStatus(error.number);
|
||||
if (errorStatus === Windows.Web.WebErrorStatus.cannotConnect ||
|
||||
errorStatus === Windows.Web.WebErrorStatus.notFound ||
|
||||
errorStatus === Windows.Web.WebErrorStatus.requestTimeout) {
|
||||
WinJS.log && WinJS.log("Cannot connect to the server. Please make sure " +
|
||||
"that you run the server setup script before running the sample.", "sample", "error");
|
||||
} else {
|
||||
WinJS.log && WinJS.log("Failed to connect: " + getError(error), "sample", "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeOutgoing() {
|
||||
try {
|
||||
var size = dataWriter.measureString(data);
|
||||
countOfDataSent += size;
|
||||
if (document.getElementById("dataSent") === null) {
|
||||
return; // We switched scenarios
|
||||
}
|
||||
document.getElementById("dataSent").value = countOfDataSent;
|
||||
|
||||
dataWriter.writeString(data);
|
||||
dataWriter.storeAsync().done(function () {
|
||||
// Add a 1 second delay so the user can see what's going on.
|
||||
setTimeout(writeOutgoing, 1000);
|
||||
}, writeError);
|
||||
}
|
||||
catch (error) {
|
||||
log("Sync write error: " + getError(error));
|
||||
}
|
||||
}
|
||||
|
||||
function writeError(error) {
|
||||
log("Write error: " + getError(error));
|
||||
}
|
||||
|
||||
function readIncoming() {
|
||||
// Buffer as much data as you require for your protocol.
|
||||
dataReader.loadAsync(100).done(function (sizeBytesRead) {
|
||||
countOfDataReceived += sizeBytesRead;
|
||||
if (document.getElementById("dataReceived") === null) {
|
||||
return; // We switched scenarios
|
||||
}
|
||||
document.getElementById("dataReceived").value = countOfDataReceived;
|
||||
|
||||
var incomingBytes = new Array(sizeBytesRead);
|
||||
dataReader.readBytes(incomingBytes);
|
||||
|
||||
// Do something with the data.
|
||||
// Alternatively you can use DataReader to read out individual booleans,
|
||||
// ints, strings, etc.
|
||||
|
||||
// Start another read.
|
||||
readIncoming();
|
||||
}, readError);
|
||||
}
|
||||
|
||||
function readError(error) {
|
||||
log("Read Error: " + getError(error));
|
||||
}
|
||||
|
||||
function onClosed(closedEventInfo) {
|
||||
log("Closed; Code: " + closedEventInfo.code + " Reason: " + closedEventInfo.reason);
|
||||
if (!streamWebSocket) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeSocketCore(Number(null), String(null));
|
||||
}
|
||||
|
||||
function closeSocket(eventObject) {
|
||||
if (!streamWebSocket) {
|
||||
WinJS.log && WinJS.log("Not connected", "sample", "status");
|
||||
return;
|
||||
}
|
||||
|
||||
WinJS.log && WinJS.log("Closing", "sample", "status");
|
||||
closeSocketCore(1000, "Closed due to user request.");
|
||||
}
|
||||
|
||||
function closeSocketCore(closeCode, closeStatus) {
|
||||
if (closeCode && closeStatus) {
|
||||
streamWebSocket.close(closeCode, closeStatus);
|
||||
} else {
|
||||
streamWebSocket.close();
|
||||
}
|
||||
|
||||
streamWebSocket = null;
|
||||
|
||||
if (dataWriter) {
|
||||
dataWriter.close();
|
||||
}
|
||||
|
||||
if (dataReader) {
|
||||
dataReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
function log(text) {
|
||||
var outputFiled = document.getElementById("outputField");
|
||||
if (outputFiled !== null) {
|
||||
outputFiled.innerHTML += text + "<br>";
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,72 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="SDKTemplate.Scenario1"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Padding="12,10,12,12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap" Text="UTF-8 text messages"/>
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,10,0,0">
|
||||
This scenario uses a MessageWebSocket to send UTF-8 strings.
|
||||
The sample server supports both plaintext WebSocket (ws://) and secure WebSocket (wss://) server endpoints.
|
||||
</TextBlock>
|
||||
<TextBlock Text="Server Address:" Margin="0,10,0,0"/>
|
||||
<TextBox x:Name="ServerAddressField" InputScope="Url" Text="ws://localhost/WebSocketSample/EchoWebSocket.ashx" Margin="0,10,0,0"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button x:Name="ConnectButton" Content="Connect" Margin="0,0,10,0" Click="{x:Bind OnConnect}"/>
|
||||
<Button x:Name="DisconnectButton" Content="Disconnect" Margin="0,0,10,0" Click="{x:Bind OnDisconnect}"/>
|
||||
</StackPanel>
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,10,0,0">Enter text to send to the server.</TextBlock>
|
||||
<TextBox x:Name="InputField" Text="Hello World" Margin="0,10,0,0"/>
|
||||
<Button x:Name="SendButton" Content="Send" Margin="0,10,0,0" Click="{x:Bind OnSend}"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto" Margin="0,10,0,0">
|
||||
<TextBox x:Name="OutputField" TextWrapping="Wrap" IsReadOnly="True"/>
|
||||
</ScrollViewer>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="Disconnected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="DisconnectButton.IsEnabled" Value="False"/>
|
||||
<Setter Target="InputField.IsEnabled" Value="False"/>
|
||||
<Setter Target="SendButton.IsEnabled" Value="False"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Connected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ServerAddressField.IsEnabled" Value="False"/>
|
||||
<Setter Target="ConnectButton.IsEnabled" Value="False"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Busy">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ServerAddressField.IsEnabled" Value="False"/>
|
||||
<Setter Target="ConnectButton.IsEnabled" Value="False"/>
|
||||
<Setter Target="DisconnectButton.IsEnabled" Value="False"/>
|
||||
<Setter Target="InputField.IsEnabled" Value="False"/>
|
||||
<Setter Target="SendButton.IsEnabled" Value="False"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,65 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="SDKTemplate.Scenario2"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Padding="12,10,12,12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap" Text="Binary data stream"/>
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,10,0,0">
|
||||
This scenario shows how to use a StreamWebSocket to send binary data.
|
||||
The sample server supports both plaintext WebSocket (ws://) and secure WebSocket (wss://) server endpoints.
|
||||
</TextBlock>
|
||||
<TextBlock Text="Server Address:" Margin="0,10,0,0"/>
|
||||
<TextBox x:Name="ServerAddressField" InputScope="Url" Text="ws://localhost/WebSocketSample/EchoWebSocket.ashx" Margin="0,10,0,0"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button x:Name="StartButton" Content="Start" Margin="0,0,10,0" Click="{x:Bind OnStart}"/>
|
||||
<Button x:Name="StopButton" Content="Stop" Margin="0,0,10,0" Click="{x:Bind OnStop}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Margin="0,10,0,0">Bytes sent: <Run x:Name="DataSentField"/></TextBlock>
|
||||
<TextBlock Margin="0,10,0,0">Bytes received: <Run x:Name="DataReceivedField"/></TextBlock>
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto" Margin="0,10,0,0">
|
||||
<TextBox Name="OutputField" HorizontalAlignment="Stretch" TextWrapping="Wrap" IsReadOnly="True"/>
|
||||
</ScrollViewer>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="Disconnected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="StopButton.IsEnabled" Value="False"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Connected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="StartButton.IsEnabled" Value="False"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Busy">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="StartButton.IsEnabled" Value="False"/>
|
||||
<Setter Target="StopButton.IsEnabled" Value="False"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</Page>
|
Загрузка…
Ссылка в новой задаче