wintoast 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3a5ffc4e1ef7ddebe12e6096aa293b1d70f96a8bb8c7fdba4e6c807dc80578f6
4
+ data.tar.gz: d7d1357b903a3582b4e51b5616a83e13f4a1c0dbbc94c273c9bf3d8234c1daef
5
+ SHA512:
6
+ metadata.gz: 1f0271faa0e9e3ee302a7e54d74eff648c75538432ae821b58fc338b2eb25c5da1f0cff9e034da31ad94ad44c515967975b05d420ee9784e1cdd7f106c90e14c
7
+ data.tar.gz: 48714a218686fdfb01849299231162881f70302e8ae9278ce1dc3f6858ab8505da5a88fdcc87aa4acff32ab287effe9844d0f1234f5c5ff32411ad1282adfa91
data/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-06-27
10
+
11
+ Initial release.
12
+
13
+ - `Wintoast.toast(title, body = nil, ...)` — fire-and-forget toast notification
14
+ via the inbox WinRT API (no App SDK, no packaging, no COM server). Supports
15
+ `aumid:`, `attribution:`, `image:`/`hero:` (local absolute paths) with
16
+ `circle:`, `audio:` (system sounds or `false` for silent), `duration:`,
17
+ `scenario:` (`:alarm`/`:incoming_call`/`:urgent`), `expires_at:`/`expires_in:`,
18
+ and `tag:`/`group:` replacement keys. Returns `nil` (accepted, not necessarily
19
+ displayed — silent suppression is undetectable by design of the platform).
20
+ - `Wintoast.register!(aumid:, display_name:, icon: nil)` /
21
+ `Wintoast.unregister!(aumid:)` — opt-in, reversible, per-user branding that
22
+ writes only `HKCU\Software\Classes\AppUserModelId\<aumid>`. No elevation, no
23
+ Start-Menu shortcut, no HKLM, no COM activator. `unregister!` is idempotent
24
+ (`false` when the key was absent).
25
+ - `Wintoast.progress(value = nil, of: 100, state: nil)` /
26
+ `Wintoast.progress_clear` — taskbar (`ITaskbarList3`) and Windows Terminal tab
27
+ (OSC 9;4) progress on every call; exactly one renders per host. Returns
28
+ `true`/`false` (accepted, not necessarily visible); environmental failure is a
29
+ `false`, never an exception. Never writes OSC bytes to a redirected stdout.
30
+ - `Wintoast::Payload.build(title, body = nil, ...)` — the pure-Ruby
31
+ `ToastGeneric` XML builder, exposed for debugging and as the escaping /
32
+ audio-mapping test seam.
33
+ - `Wintoast::POWERSHELL_AUMID` — the registered-everywhere default AUMID, so
34
+ `Wintoast.toast("hi")` works on a stock machine (branded "Windows PowerShell").
35
+ - `Wintoast::Error` / `Wintoast::OSError` (with `#code`) error hierarchy.
36
+
37
+ Windows MSVC (mswin) Ruby only; Windows 10 1809+ / Windows 11, x64.
38
+
39
+ [Unreleased]: https://github.com/main-path/wintoast/compare/v0.1.0...HEAD
40
+ [0.1.0]: https://github.com/main-path/wintoast/releases/tag/v0.1.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ned
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,335 @@
1
+ # wintoast
2
+
3
+ **Fire-and-forget Windows toast notifications and taskbar/terminal progress for Ruby — inbox WinRT and shell APIs only, no App SDK, no packaging, no COM server.**
4
+
5
+ Scripts deserve native notifications. When a backup finishes, a build breaks, or
6
+ a long job crosses 50%, a plain `ruby.exe` should be able to pop a real Windows
7
+ toast and light up the taskbar — without bundling a runtime or registering a COM
8
+ activation server.
9
+
10
+ That gap is real. PowerShell's [BurntToast] exists, but there is nothing
11
+ native-and-thin for Ruby; the [Windows App SDK] notification path drags in a
12
+ NuGet runtime, an MSIX singleton package, and a bootstrapper — against this
13
+ suite's OS-libraries-only rule. `wintoast` uses the **inbox** WinRT notification
14
+ API (`Windows.UI.Notifications`, shipped with Windows since 10240) via
15
+ `RoGetActivationFactory`, plus `ITaskbarList3` and the terminal OSC 9;4 progress
16
+ sequence. Nothing to install but the gem.
17
+
18
+ > Not to be confused with [mohabouje/WinToast], a C++ library — different
19
+ > ecosystem, same idea.
20
+
21
+ ```ruby
22
+ require "wintoast"
23
+
24
+ Wintoast.toast("Backup finished", "1,204 files in 38 s")
25
+ # => nil (a banner pops; it persists in the Notification Center)
26
+ ```
27
+
28
+ | What | API |
29
+ |---|---|
30
+ | Pop a toast | `Wintoast.toast(title, body, ...)` |
31
+ | Brand it (opt-in) | `Wintoast.register!` / `Wintoast.unregister!` |
32
+ | Taskbar + tab progress | `Wintoast.progress` / `Wintoast.progress_clear` |
33
+ | Inspect the XML | `Wintoast::Payload.build` |
34
+
35
+ ## Requirements
36
+
37
+ - **Windows 10 1809+ or Windows 11** with a native **MSVC (mswin)** Ruby
38
+ (`x64-mswin64`). On a MinGW/UCRT Ruby this gem is not supported — its
39
+ `extconf.rb` will say so.
40
+ - Visual Studio 2017+ or the Build Tools with the **Desktop development with
41
+ C++** workload (for `cl.exe` + the Windows SDK headers/libs).
42
+ - x64. arm64-mswin is expected to work (all code is arch-neutral) but is
43
+ untested and unsupported until an arm64-mswin Ruby distribution exists.
44
+
45
+ > Building uses [`vcvars`](https://rubygems.org/gems/vcvars) to load the MSVC
46
+ > toolchain automatically — no "Developer Command Prompt" needed. If a build
47
+ > fails, run `vcvars doctor`.
48
+
49
+ ## Install
50
+
51
+ ```sh
52
+ gem install wintoast
53
+ ```
54
+
55
+ ## Quick start
56
+
57
+ **Zero setup — works on a stock machine (branded "Windows PowerShell"):**
58
+
59
+ ```ruby
60
+ require "wintoast"
61
+
62
+ Wintoast.toast("Backup finished", "1,204 files in 38 s")
63
+ # => nil (a banner pops; it persists in the Notification Center)
64
+
65
+ # Soften the borrowed branding with an attribution line:
66
+ Wintoast.toast("Backup finished", "1,204 files in 38 s", attribution: "via backup.rb")
67
+ ```
68
+
69
+ **Own branding — one-time, per-user, reversible, no admin:**
70
+
71
+ ```ruby
72
+ AUMID = Wintoast.register!(aumid: "Acme.BackupTool", display_name: "Acme Backup",
73
+ icon: "C:/Acme/backup.png") # => "Acme.BackupTool"
74
+
75
+ Wintoast.toast("Backup finished", aumid: AUMID,
76
+ image: "C:/Acme/backup.png", circle: true,
77
+ audio: :mail, duration: :long,
78
+ expires_in: 3600, tag: "backup", group: "acme")
79
+ # A later toast with tag: "backup", group: "acme" REPLACES this one in Action Center.
80
+
81
+ Wintoast.unregister!(aumid: "Acme.BackupTool") # => true
82
+ ```
83
+
84
+ **Progress around a work loop (taskbar under conhost, tab ring under Windows Terminal):**
85
+
86
+ ```ruby
87
+ begin
88
+ files.each_with_index do |f, i|
89
+ process(f)
90
+ Wintoast.progress(i + 1, of: files.size)
91
+ end
92
+ Wintoast.toast("Done", "#{files.size} files processed")
93
+ rescue => e
94
+ Wintoast.progress(100, state: :error) # red bar
95
+ Wintoast.toast("Failed", e.message, audio: :reminder)
96
+ raise
97
+ ensure
98
+ Wintoast.progress_clear
99
+ end
100
+ ```
101
+
102
+ **Failure paths, demonstrated:**
103
+
104
+ ```ruby
105
+ Wintoast.toast("hi", image: "logo.png")
106
+ # => ArgumentError: wintoast: image: must be an absolute path to an existing file
107
+
108
+ Wintoast.toast("hi", aumid: "Not.Registered.Anywhere")
109
+ # => nil — and NOTHING is displayed. Unregistered AUMIDs are silently dropped by
110
+ # Windows (no error, no Action Center entry). Use register! or the default AUMID.
111
+
112
+ Wintoast.progress(50, state: :indeterminate)
113
+ # => ArgumentError: wintoast: state: :indeterminate ignores value — pass value nil
114
+ ```
115
+
116
+ ## The one trap you must know about
117
+
118
+ **An unregistered AUMID is silently dropped by Windows.** `Show()` returns
119
+ success, but no banner pops, nothing lands in the Notification Center, and there
120
+ is no error, no event-log breadcrumb — nothing. This is the platform's behavior,
121
+ not the gem's, and it is structurally undetectable.
122
+
123
+ What "registered" means — one of:
124
+
125
+ - a **Start-Menu shortcut** carrying an `AppUserModelID` (what most installed
126
+ apps have), **or**
127
+ - a per-user **registry key** `HKCU\Software\Classes\AppUserModelId\<aumid>`
128
+ with a `DisplayName` — exactly what `Wintoast.register!` writes, **or**
129
+ - **package identity** (MSIX) — out of scope here.
130
+
131
+ `SetCurrentProcessExplicitAppUserModelID` is **not** registration: it stamps the
132
+ process AUMID for taskbar grouping / Jump Lists, and does nothing for
133
+ notifications. (A future `winshell` concern, not this gem's.)
134
+
135
+ That is why `Wintoast.toast` defaults to **Windows PowerShell's** AUMID
136
+ (`Wintoast::POWERSHELL_AUMID`): it is registered on every Windows box via
137
+ PowerShell's Start-Menu shortcut, so `Wintoast.toast("hi")` visibly works on a
138
+ stock machine. The trade-offs of borrowing it:
139
+
140
+ - the toast header reads **"Windows PowerShell"** (soften it with
141
+ `attribution:`, or switch to your own AUMID via `register!`);
142
+ - the per-app Settings toggle is **shared** — turning off "Windows PowerShell"
143
+ notifications kills every tool borrowing the default. `register!` gives you a
144
+ private toggle.
145
+
146
+ `Wintoast.toast` **never touches the registry.** Registration is an explicit,
147
+ consented, reversible act. There is deliberately **no `registered?`** query: it
148
+ could only check the registry route and would lie `false` for the common
149
+ shortcut-registered case (including the default AUMID).
150
+
151
+ ## Toasts
152
+
153
+ ```ruby
154
+ Wintoast.toast(title, body = nil,
155
+ aumid: Wintoast::POWERSHELL_AUMID,
156
+ attribution: nil, # small "via ..." line
157
+ image: nil, # absolute path -> appLogoOverride
158
+ hero: nil, # absolute path -> hero (364x180 @100%)
159
+ circle: false, # hint-crop="circle" on image:
160
+ audio: :default, # see table; false => silent
161
+ duration: :short, # :short | :long
162
+ scenario: nil, # nil | :alarm | :incoming_call | :urgent
163
+ expires_at: nil, # Time -> ExpirationTime (OS caps at 3 days)
164
+ expires_in: nil, # Numeric seconds from now (mutually exclusive)
165
+ tag: nil, # String <= 64 chars (dedup/replace key)
166
+ group: nil) -> nil # String <= 64 chars
167
+ ```
168
+
169
+ **A normal return (`nil`) means the OS ACCEPTED the toast — NOT that it was
170
+ displayed.** Banners can be suppressed undetectably by Focus Assist /
171
+ Do-Not-Disturb (the toast still lands in the Notification Center), the per-app
172
+ toggle, or the `NoToastApplicationNotification` group policy. The return value is
173
+ `nil`, not `true`/`self`, precisely because the gem cannot honestly promise
174
+ display.
175
+
176
+ **Audio** (`audio:`) — only the system sounds Windows honors for unpackaged
177
+ apps; arbitrary file paths silently fall back to the default sound and are not
178
+ accepted:
179
+
180
+ | value | effect |
181
+ |---|---|
182
+ | `:default` | OS default sound (no `<audio>` element) |
183
+ | `false` | silent |
184
+ | `:im` `:mail` `:reminder` `:sms` | the matching `ms-winsoundevent` sound |
185
+ | `:alarm`, `:alarm2`..`:alarm10` | looping alarm sounds |
186
+ | `:call`, `:call2`..`:call10` | looping call sounds |
187
+
188
+ Looping sounds only *audibly* loop when the toast is pinned by
189
+ `scenario: :alarm` / `:incoming_call` (a short alarm sound is still valid).
190
+
191
+ **Scenario** (`scenario:`) — `:alarm` and `:incoming_call` pin the toast and
192
+ loop audio; `:urgent` breaks through Do-Not-Disturb (Windows 11 build 22546+;
193
+ older builds ignore the attribute and show a normal toast). `:reminder` is
194
+ deliberately **not** accepted — without a button it is ignored by Windows, i.e.
195
+ useless for fire-and-forget.
196
+
197
+ **Expiration** (`expires_at:` Time / `expires_in:` seconds, mutually exclusive)
198
+ maps to `ExpirationTime` (removal from the Notification Center). Local toasts are
199
+ retained **at most 3 days** regardless; larger values pass through and are
200
+ clamped by the OS. `expires_in <= 0` raises; a past `expires_at` passes through
201
+ (the OS treats it as already expired).
202
+
203
+ **Tag / group** (`tag:`, `group:`, each ≤ 64 chars) map to
204
+ `IToastNotification2.Tag/Group`: a later toast with the same tag (+ group) under
205
+ the same AUMID **replaces** the earlier one in the Action Center. Pure
206
+ pass-through; there is no history/removal API in v1.
207
+
208
+ **Images** (`image:`, `hero:`) are **local absolute paths only** (http(s) images
209
+ need package identity and are rejected by the absolute-path check). A
210
+ relative/missing path raises `ArgumentError` so a typo fails loudly instead of
211
+ rendering an imageless toast. Paths are emitted as plain absolute paths (no
212
+ `file://` URI), sidestepping percent-encoding of spaces and Unicode.
213
+
214
+ Desktop apps **cannot schedule** toasts (`ScheduledToastNotification` needs
215
+ package identity) — out of scope.
216
+
217
+ ## Progress
218
+
219
+ ```ruby
220
+ Wintoast.progress(value = nil, of: 100, state: nil) -> true | false
221
+ Wintoast.progress_clear -> true | false
222
+ ```
223
+
224
+ Every call drives **both** progress surfaces, unconditionally — exactly one
225
+ takes effect per host, the other no-ops (the strategy blessed in
226
+ [microsoft/terminal#14268]):
227
+
228
+ 1. **OSC 9;4** written to the console — the Windows Terminal tab ring + WT-window
229
+ taskbar progress (also ConEmu and a growing set of terminals). Emitted **only
230
+ when STDOUT is a real console** (never pollutes redirected/piped output).
231
+ 2. **`ITaskbarList3`** on `GetConsoleWindow()` — classic-conhost taskbar
232
+ progress.
233
+
234
+ | call | meaning |
235
+ |---|---|
236
+ | `progress(50)` / `progress(7, of: 23)` | determinate, green |
237
+ | `progress(50, state: :error)` | determinate, red |
238
+ | `progress(50, state: :paused)` | determinate, yellow |
239
+ | `progress(state: :indeterminate)` | marquee / ring (value must be nil) |
240
+ | `progress(nil)` / `progress(:clear)` / `progress_clear` | remove |
241
+
242
+ Host matrix:
243
+
244
+ | host | taskbar (ITaskbarList3) | tab/taskbar (OSC 9;4) |
245
+ |---|---|---|
246
+ | classic conhost | ✓ | swallowed |
247
+ | Windows Terminal | accepts, invisible (hidden ConPTY window) | ✓ |
248
+ | Windows Terminal + redirected stdout | accepts, invisible | ✗ (no OSC to a pipe) |
249
+ | ConEmu | — | ✓ |
250
+ | no console (rubyw / detached) | — | — → returns `false` |
251
+
252
+ **The return value means accepted, not visible** — the same honesty rule as
253
+ `toast`'s `nil`. `true` means at least one OS surface accepted the update (OSC
254
+ bytes reached a real console, or every `ITaskbarList3` call succeeded against a
255
+ non-NULL console window). Under a ConPTY host the taskbar leg accepts against a
256
+ hidden window that can never render, so e.g. running under Windows Terminal with
257
+ stdout redirected returns `true` with nothing visible. `false` is guaranteed
258
+ only when no surface accepted at all.
259
+
260
+ **Environmental failure is a `false`, never an exception** — a progress bar must
261
+ not be able to crash the app. Only argument misuse raises `ArgumentError`
262
+ (`of: 0`, a missing value for a determinate state, a value for `:indeterminate`,
263
+ an unknown state, a non-Numeric value). Overshoot like `progress(101)` is
264
+ **clamped**, not raised.
265
+
266
+ **Clearing on exit is the caller's job** — put `Wintoast.progress_clear` in an
267
+ `ensure`. v1 installs no `at_exit` hook.
268
+
269
+ Two console side effects, both standard CLI behavior: progress enables
270
+ `ENABLE_VIRTUAL_TERMINAL_PROCESSING` once when missing and leaves it on
271
+ (per-call restore would race other writers); and under a **classic conhost in
272
+ select mode** (the user is holding a text selection) the OSC write can block.
273
+ The GVL is released so every *other* Ruby thread keeps running, but the parked
274
+ write itself is **not interruptible** (`Thread#kill` / `Timeout` will not break
275
+ it) — exact parity with `Kernel#puts`, whose console write is equally stuck.
276
+
277
+ ## Library API
278
+
279
+ ```ruby
280
+ Wintoast.toast(title, body = nil, **opts) # => nil (accepted, not necessarily shown)
281
+ Wintoast.register!(aumid:, display_name:, icon: nil) # => aumid String (HKCU branding)
282
+ Wintoast.unregister!(aumid:) # => true | false (false = wasn't there)
283
+ Wintoast.progress(value = nil, of: 100, state: nil) # => true | false (accepted, not visible)
284
+ Wintoast.progress_clear # => true | false
285
+ Wintoast::Payload.build(title, body = nil, **opts) # => String (debugging: the exact XML sent)
286
+ Wintoast::VERSION # => "0.1.0"
287
+ Wintoast::POWERSHELL_AUMID # the registered-everywhere default AUMID
288
+ ```
289
+
290
+ ## How it works
291
+
292
+ Toasts are sent through the **inbox** WinRT API activated directly via
293
+ `RoGetActivationFactory` — no Windows App SDK, no C++/WinRT codegen, no NuGet.
294
+ The C++ extension assembles the `ToastGeneric` XML in Ruby, hands it to
295
+ `Windows.Data.Xml.Dom.XmlDocument.LoadXml`, builds a `ToastNotification`, and
296
+ calls `ToastNotifier.Show` — all synchronous, milliseconds.
297
+
298
+ `register!` writes **only** `HKCU\Software\Classes\AppUserModelId\<aumid>`
299
+ (`DisplayName`, optional `IconUri`) — per-user, no elevation, no Start-Menu
300
+ shortcut, no HKLM, no COM activator. `unregister!` deletes that key tree.
301
+
302
+ Progress drives both `ITaskbarList3` and OSC 9;4 every call.
303
+
304
+ Every native operation is **stateless and complete within one call** — WinRT/COM
305
+ is initialized and uninitialized per call, owning no resources across calls. That
306
+ makes every API **idempotent and thread-safe**: call them from any thread,
307
+ concurrently, with no locks.
308
+
309
+ ## Errors
310
+
311
+ ```
312
+ StandardError
313
+ └─ Wintoast::Error misuse / non-OS failures (e.g. invalid UTF-8)
314
+ └─ Wintoast::OSError OS API failures; #code => Integer
315
+ (HRESULT for COM/WinRT, Win32 code for registry)
316
+ ```
317
+
318
+ Plain argument-shape problems raise Ruby's own `ArgumentError` / `TypeError`.
319
+
320
+ These are **not** errors (by design — the platform cannot report them):
321
+
322
+ - a toast sent to an unregistered AUMID is silently dropped;
323
+ - Focus Assist / Do-Not-Disturb suppresses the banner (the toast still reaches
324
+ the Notification Center);
325
+ - a per-app or policy toggle disables toasts;
326
+ - `progress` returns `false` when no console surface accepted.
327
+
328
+ ## License
329
+
330
+ [MIT](LICENSE.txt).
331
+
332
+ [BurntToast]: https://github.com/Windos/BurntToast
333
+ [Windows App SDK]: https://learn.microsoft.com/windows/apps/windows-app-sdk/
334
+ [mohabouje/WinToast]: https://github.com/mohabouje/WinToast
335
+ [microsoft/terminal#14268]: https://github.com/microsoft/terminal/discussions/14268
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # extconf.rb for the wintoast C++ extension (WinRT toasts + ITaskbarList3 +
4
+ # OSC 9;4 progress + HKCU AppUserModelId registration).
5
+ #
6
+ # wintoast is Windows-MSVC-only: it activates the inbox WinRT notification API
7
+ # via RoGetActivationFactory, drives COM (ITaskbarList3), and writes the registry
8
+ # — all with cl.exe. mkmf auto-discovers the .cpp source and compiles it as C++
9
+ # (its CXX_EXT list includes "cpp"). The guard is arch-neutral (/mswin/ only), so
10
+ # it passes unchanged on a future arm64-mswin Ruby (target_os stays "mswin64").
11
+
12
+ require "mkmf"
13
+
14
+ unless RbConfig::CONFIG["target_os"] =~ /mswin/
15
+ abort <<~MSG
16
+ wintoast requires a native Windows MSVC (mswin) Ruby — it binds the inbox
17
+ WinRT toast notification API (RoGetActivationFactory), ITaskbarList3, the
18
+ console VT/OSC path, and the registry, and is built with cl.exe. Your Ruby
19
+ is "#{RbConfig::CONFIG['arch']}". On a MinGW/UCRT Ruby this gem is not supported.
20
+ MSG
21
+ end
22
+
23
+ # C++ (WinRT ABI interfaces, std::wstring, COM patterns); enable exceptions.
24
+ $CXXFLAGS << " -EHsc"
25
+
26
+ # System import libs (bare "NAME.lib" tokens on mswin; no -l prefix):
27
+ # runtimeobject - WinRT (RoInitialize / RoGetActivationFactory /
28
+ # WindowsCreateStringReference / WindowsCreateString)
29
+ # ole32 - COM (CoInitializeEx / CoCreateInstance for ITaskbarList3)
30
+ # uuid - CLSID_TaskbarList / interface IIDs
31
+ # advapi32 - registry (RegCreateKeyExW / RegSetValueExW / RegDeleteTreeW)
32
+ # kernel32 (console APIs, GetConsoleWindow) is linked by default and not listed.
33
+ $libs = [$libs, "runtimeobject.lib", "ole32.lib", "uuid.lib", "advapi32.lib"].join(" ")
34
+
35
+ # Produces lib/wintoast/wintoast.so, required as "wintoast/wintoast".
36
+ create_makefile("wintoast/wintoast")