Skip to content

Commit fdb4bbb

Browse files
authored
Merge pull request #3526 from MicrosoftDocs/drewbat/widget-convert-console-to-windows
Drewbat/widget convert console to windows
2 parents 8965ec2 + 8f463a4 commit fdb4bbb

File tree

4 files changed

+146
-9
lines changed

4 files changed

+146
-9
lines changed
59.5 KB
Loading
29.5 KB
Loading

hub/apps/develop/widgets/implement-widget-provider-cs.md

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ In Visual Studio, create a new project. In the **Create a new project** dialog,
3232

3333
When the project loads, in **Solution Explorer** right-click the project name and select **Properties**. On the **General** page, scroll down to **Target OS** and select "Windows". Under **Target OS Version**, select version 10.0.19041.0 or later.
3434

35+
Note that this walkthrough uses a console app that displays the console window when the widget is activated to enable easy debugging. When you are ready to publish your widget provider app, you can convert the console application to a Windows application by following the steps in [Convert your console app to a Windows app](#convert-your-console-app-to-a-windows-app).
36+
3537
## Add references to the Windows App SDK and Windows Implementation Library NuGet packages
3638

3739
This sample uses the latest stable Windows App SDK NuGet package. In **Solution Explorer**, right-click **Dependencies** and select **Manage NuGet packages...**. In the NuGet package manager, select the **Browse** tab and search for "Microsoft.WindowsAppSDK". Select the latest stable version in the **Version** drop-down and then click **Install**.
@@ -238,6 +240,23 @@ The widget host calls [DeleteWidget](/windows/windows-app-sdk/api/winrt/microsof
238240
public void DeleteWidget(string widgetId, string customState)
239241
{
240242
RunningWidgets.Remove(widgetId);
243+
244+
if(RunningWidgets.Count == 0)
245+
{
246+
emptyWidgetListEvent.Set();
247+
}
248+
}
249+
```
250+
251+
For this example, in addition to removing the widget with the specified from the list of enabled widgets, we also check to see if the list is now empty, and if so, we set an event that will be used later to allow the app to exit when there are no enabled widgets. Inside your class definition, add the declaration of the [ManualResetEvent](dotnet/api/system.threading.manualresetevent) and a public accessor function.
252+
253+
```csharp
254+
// WidgetProvider.cs
255+
static ManualResetEvent emptyWidgetListEvent = new ManualResetEvent(false);
256+
257+
public static ManualResetEvent GetEmptyWidgetListEvent()
258+
{
259+
return emptyWidgetListEvent;
241260
}
242261
```
243262

@@ -509,6 +528,9 @@ using ExampleWidgetProvider;
509528
using COM;
510529
using System;
511530

531+
[DllImport("kernel32.dll")]
532+
static extern IntPtr GetConsoleWindow();
533+
512534
[DllImport("ole32.dll")]
513535

514536
static extern int CoRegisterClassObject(
@@ -528,9 +550,24 @@ CoRegisterClassObject(CLSID_Factory, new WidgetProviderFactory<WidgetProvider>()
528550
Console.WriteLine("Registered successfully. Press ENTER to exit.");
529551
Console.ReadLine();
530552

531-
CoRevokeClassObject(cookie);
553+
if (GetConsoleWindow() != IntPtr.Zero)
554+
{
555+
Console.WriteLine("Registered successfully. Press ENTER to exit.");
556+
Console.ReadLine();
557+
}
558+
else
559+
{
560+
// Wait until the manager has disposed of the last widget provider.
561+
using (var emptyWidgetListEvent = WidgetProvider.GetEmptyWidgetListEvent())
562+
{
563+
emptyWidgetListEvent.WaitOne();
564+
}
565+
566+
CoRevokeClassObject(cookie);
567+
}
532568
```
533569

570+
Note that this code example imports the [GetConsoleWindow](/windows/console/getconsolewindow) function to determine if the app is running as a console application, the default behavior for this walkthrough. If function returns a valid pointer, we write debug information to the console. Otherwise, the app is running as a Windows app. In that case, we wait for the event that we set in [DeleteWidget](#deletewidget) method when the list of enabled widgets is empty, and the we exit the app. For information on converting the example console app to a Windows app, see [Convert your console app to a Windows app](#convert-your-console-app-to-a-windows-app).
534571

535572
## Package your widget provider app
536573

@@ -704,6 +741,29 @@ For information about the design requirements for screenshot images and the nami
704741

705742
Make sure you have selected the architecture that matches your development machine from the **Solution Platforms** drop-down, for example "x64". In **Solution Explorer**, right-click your solution and select **Build Solution**. Once this is done, right-click your **ExampleWidgetProviderPackage** and select **Deploy**. In the current release, the only supported widget host is the Widgets Board. To see the widgets you will need to open the Widgets Board and select **Add widgets** in the top right. Scroll to the bottom of the available widgets and you should see the mock **Weather Widget** and **Microsoft Counting Widget** that were created in this tutorial. Click on the widgets to pin them to your widgets board and test their functionality.
706743

744+
## Debugging your widget provider
745+
746+
After you have pinned your widgets, the Widget Platform will start your widget provider application in order to receive and send relevant information about the widget. To debug the running widget you can either attach a debugger to the running widget provider application or you can set up Visual Studio to automatically start debugging the widget provider process once it's started.
747+
748+
In order to attach to the running process:
749+
750+
1. In Visual Studio click **Debug -> Attach to process**.
751+
1. Filter the processes and find your desired widget provider application.
752+
1. Attach the debugger.
753+
754+
In order to automatically attach the debugger to the process when it's initially started:
755+
756+
1. In Visual Studio click **Debug -> Other Debug Targets -> Debug Installed App Package**.
757+
1. Filter the packages and find your desired widget provider package.
758+
1. Select it and check the box that says Do not launch, but debug my code when it starts.
759+
1. Click **Attach**.
760+
761+
## Convert your console app to a Windows app
762+
763+
To convert the console app created in this walkthrough to a Windows app, right-click the **ExampleWidgetProvider** project in **Solution Explorer** and select **Properties**. Under Application->General change the **Output type** from "Console Application" to "Windows Application".
764+
765+
:::image type="content" source="images/convert-to-windows-app-cs.png" alt-text="A screenshot showing the C# widget provider project properties with the output type set to Windows Application":::
766+
707767
## Publishing your widget
708768

709769
After you have developed and tested your widget you must publish your app on the Microsoft Store in order for users to install your widgets on their devices. For step by step guidance for publishing an app, see [Publish your app in the Microsoft Store](/windows/apps/publish/publish-your-app/overview?pivots=store-installer-msix).

hub/apps/develop/widgets/implement-widget-provider-win32.md

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,49 @@ In the precompiled header file, pch.h, add the following include directives.
4949
> [!NOTE]
5050
> You must include the wil/cppwinrt.h header first, before any WinRT headers.
5151
52+
In order to handle shutting down the widget provider app correctly, we need to a custom implementation of **winrt::get_module_lock**. We pre-declare the **SignalLocalServerShutdown** method which will be defined in our main.cpp file and will set an event that signals the app to exit. Add the following code to your pch.h file, just below the `#pragma once` directive, before the other includes.
53+
54+
```cpp
55+
//pch.h
56+
#include <stdint.h>
57+
#include <combaseapi.h>
58+
59+
// In .exe local servers the class object must not contribute to the module ref count, and use
60+
// winrt::no_module_lock, the other objects must and this is the hook into the C++ WinRT ref counting system
61+
// that enables this.
62+
void SignalLocalServerShutdown();
63+
64+
namespace winrt
65+
{
66+
inline auto get_module_lock() noexcept
67+
{
68+
struct service_lock
69+
{
70+
uint32_t operator++() noexcept
71+
{
72+
return ::CoAddRefServerProcess();
73+
}
74+
75+
uint32_t operator--() noexcept
76+
{
77+
const auto ref = ::CoReleaseServerProcess();
78+
79+
if (ref == 0)
80+
{
81+
SignalLocalServerShutdown();
82+
}
83+
return ref;
84+
}
85+
};
86+
87+
return service_lock{};
88+
}
89+
}
90+
91+
92+
#define WINRT_CUSTOM_MODULE_LOCK
93+
```
94+
5295
## Add a WidgetProvider class to handle widget operations
5396

5497
In Visual Studio, right-click the `ExampleWidgetProvider` project in **Solution Explorer** and select **Add->Class**. In the **Add class** dialog, name the class "WidgetProvider" and click **Add**.
@@ -471,6 +514,17 @@ Add the header that defines the **WidgetProvider** class to the includes at the
471514
#include <mutex>
472515
```
473516

517+
Declare the event that will trigger our app to exit and the **SignalLocalServerShutdown** function that will set the event. Paste the following code in main.cpp.
518+
519+
```cpp
520+
wil::unique_event g_shudownEvent(wil::EventOptions::None);
521+
522+
void SignalLocalServerShutdown()
523+
{
524+
g_shudownEvent.SetEvent();
525+
}
526+
```
527+
474528
Next, you will need to create a [CLSID](/windows/win32/com/com-class-objects-and-clsids) that will be used to identify your widget provider for COM activation. Generate a GUID in Visual Studio by going to **Tools->Create GUID**. Select the option "static const GUID =" and click **Copy** and then paste that into `main.cpp`. Update the GUID definition with the following C++/WinRT syntax, setting the GUID variable name widget_provider_clsid. Leave the commented version of the GUID because you will need this format later, when packaging your app.
475529
476530
```cpp
@@ -483,7 +537,7 @@ static constexpr GUID widget_provider_clsid
483537
};
484538
```
485539

486-
Add the following class factory definition to `main.cpp`. This is boilerplate code that is not specific to widget provider implementations.
540+
Add the following class factory definition to `main.cpp`. This is mostly boilerplate code that is not specific to widget provider implementations. Note that **CoWaitForMultipleObjects** waits for our shutdown event to be triggered before the app exits.
487541

488542
```cpp
489543
// main.cpp
@@ -535,12 +589,11 @@ int main()
535589
REGCLS_MULTIPLEUSE,
536590
widgetProviderFactory.put()));
537591

538-
MSG msg;
539-
while (GetMessage(&msg, nullptr, 0, 0))
540-
{
541-
TranslateMessage(&msg);
542-
DispatchMessage(&msg);
543-
}
592+
DWORD index{};
593+
HANDLE events[] = { g_shudownEvent.get() };
594+
winrt::check_hresult(CoWaitForMultipleObjects(CWMO_DISPATCH_CALLS | CWMO_DISPATCH_WINDOW_MESSAGES,
595+
INFINITE,
596+
static_cast<ULONG>(std::size(events)), events, &index));
544597

545598
return 0;
546599
}
@@ -719,6 +772,30 @@ For information about the design requirements for screenshot images and the nami
719772

720773
Make sure you have selected the architecture that matches your development machine from the **Solution Platforms** drop-down, for example "x64". In **Solution Explorer**, right-click your solution and select **Build Solution**. Once this is done, right-click your **ExampleWidgetProviderPackage** and select **Deploy**. In the current release, the only supported widget host is the Widgets Board. To see the widgets you will need to open the Widgets Board and select **Add widgets** in the top right. Scroll to the bottom of the available widgets and you should see the mock **Weather Widget** and **Microsoft Counting Widget** that were created in this tutorial. Click on the widgets to pin them to your widgets board and test their functionality.
721774

775+
## Debugging your widget provider
776+
777+
After you have pinned your widgets, the Widget Platform will start your widget provider application in order to receive and send relevant information about the widget. To debug the running widget you can either attach a debugger to the running widget provider application or you can set up Visual Studio to automatically start debugging the widget provider process once it's started.
778+
779+
In order to attach to the running process:
780+
781+
1. In Visual Studio click **Debug -> Attach to process**.
782+
1. Filter the processes and find your desired widget provider application.
783+
1. Attach the debugger.
784+
785+
In order to automatically attach the debugger to the process when it's initially started:
786+
787+
1. In Visual Studio click **Debug -> Other Debug Targets -> Debug Installed App Package**.
788+
1. Filter the packages and find your desired widget provider package.
789+
1. Select it and check the box that says Do not launch, but debug my code when it starts.
790+
1. Click **Attach**.
791+
792+
## Convert your console app to a Windows app
793+
794+
To convert the console app created in this walkthrough to a Windows app:
795+
1. Right-click on the ExampleWidgetProvider project in **Solution Explorer** and select **Properties**. Navigate to **Linker -> System** and change **SubSystem** from "Console" to "Windows". This can also be done by adding &lt;SubSystem&gt;Windows&lt;/SubSystem&gt; to the &lt;Link&gt;..&lt;/Link&gt; section of the .vcxproj.
796+
1. In main.cpp, change `int main()` to `int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ PWSTR pCmdLine, _In_ int /*nCmdShow*/)`.
797+
798+
:::image type="content" source="images/convert-to-windows-app-cpp.png" alt-text="A screenshot showing the C++ widget provider project properties with the output type set to Windows Application":::
722799

723800
## Publishing your widget
724801

@@ -728,4 +805,4 @@ After you have developed and tested your widget you must publish your app on the
728805

729806
After your app has been published on the Microsoft Store, you can request for your app to be included in the widgets Store Collection that helps users discover apps that feature Windows Widgets. To submit your request, see [Submit your Widget information for addition to the Store Collection](https://forms.office.com/pages/responsepage.aspx?id=v4j5cvGGr0GRqy180BHbRzIsoQuXjKhIoGxHt2iT41RUNjJJM09JSlFBOFJTTDJQT1dOODBEWlNYQy4u&wdLOR=c3CBC769A-D2E1-4558-8FAF-09B14B60351D).
730807

731-
:::image type="content" source="images/widgets-store-collection.png" alt-text="Screenshot of the Microsoft Store showing the widgets collection that allows users to discover apps that feature Windows Widgets.":::
808+
:::image type="content" source="images/widgets-store-collection.png" alt-text="Screenshot of the Microsoft Store showing the widgets collection that allows users to discover apps that feature Windows Widgets.":::

0 commit comments

Comments
 (0)