├── .gitignore ├── binding.gyp ├── package.json ├── LICENSE ├── index.js ├── test └── index.js ├── src ├── uwp.h ├── node-async.h └── uwp.cc ├── README.md └── SECURITY.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [{ 3 | 'target_name': 'uwp', 4 | "include_dirs" : [ 5 | " target); 45 | static NAN_METHOD(ProjectNamespace); 46 | static NAN_METHOD(Close); 47 | }; 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-uwp 2 | ========== 3 | 4 | Enables Universal Windows Platform (UWP) API access for Node.js (Chakra build) 5 | on Windows 10. 6 | 7 | Example 8 | ------- 9 | 10 | ```javascript 11 | const uwp = require('uwp'); 12 | const Windows = uwp.projectNamespace('Windows'); 13 | 14 | Windows.Storage.KnownFolders.documentsLibrary.createFileAsync( 15 | 'sample.dat', Windows.Storage.CreationCollisionOption.replaceExisting) 16 | .done( 17 | function (file) { 18 | console.log('ok'); 19 | uwp.close(); // all async operations are completed, release uwp 20 | }, 21 | function (error) { 22 | console.error('error', error); 23 | uwp.close(); // all async operations are completed, release uwp 24 | } 25 | ); 26 | ``` 27 | 28 | Installation 29 | ------------ 30 | 31 | ### Prerequisites 32 | 33 | * Windows 10 [1511 or above](http://windows.microsoft.com/en-us/windows-10/windows-update-faq) 34 | * [Visual Studio](https://www.visualstudio.com/vs-2015-product-editions) 35 | * [Node.js (Chakra)](http://aka.ms/node-chakra-installer) 36 | 37 | Run under Node.js (Chakra) command prompt: 38 | 39 | ```sh 40 | npm install uwp 41 | ``` 42 | 43 | APIs 44 | ---- 45 | 46 | This package exports 2 functions. 47 | 48 | ### projectNamespace(name) 49 | 50 | Project a UWP namespace of given name. 51 | 52 | * **Note**: This function will keep Node process alive so that your app can 53 | continue to run and handle UWP async callbacks. You need to call 54 | [close()](#close) when UWP usage is completed. 55 | 56 | 57 | ### close() 58 | 59 | Close all UWP handles used by this package. Call this when all UWP usage is 60 | completed. 61 | 62 | --------------------------------------------------------------------------- 63 | Checkout our OSS effort with 64 | [Node-ChakraCore](https://github.com/nodejs/node-chakracore). It supports the 65 | most recent version of node.js and will also be useful if you are on Windows 7 66 | or Windows 8.1. Note: It does not support UWP. 67 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/node-async.h: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files(the "Software"), to 5 | // deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | // sell copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions : 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | // IN THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | // 24 | // This file is derived from https://github.com/saary/node-async-helper 25 | // 26 | 27 | #include "v8.h" 28 | #include "nan.h" 29 | #include 30 | 31 | namespace NodeUtils { 32 | 33 | extern std::thread::id g_mainThreadId; 34 | 35 | class Async { 36 | private: 37 | class TokenData { 38 | private: 39 | std::function func; 40 | friend Async; 41 | 42 | public: 43 | static uv_async_t* NewAsyncToken() { 44 | uv_async_t* asyncHandle = new uv_async_t; 45 | uv_async_init(uv_default_loop(), asyncHandle, AsyncCb); 46 | asyncHandle->data = new TokenData(); 47 | 48 | return asyncHandle; 49 | } 50 | }; 51 | 52 | public: 53 | static uv_async_t* GetAsyncToken() { 54 | return TokenData::NewAsyncToken(); 55 | } 56 | 57 | static void ReleaseAsyncToken(uv_async_t* handle) { 58 | TokenData* tokenData = static_cast(handle->data); 59 | uv_close(reinterpret_cast(handle), AyncCloseCb); 60 | delete tokenData; 61 | } 62 | 63 | static void RunOnMain(uv_async_t* async, std::function func) { 64 | TokenData* tokenData = static_cast(async->data); 65 | tokenData->func = func; 66 | uv_async_send(async); 67 | } 68 | 69 | static void RunOnMain(std::function func) { 70 | if (std::this_thread::get_id() == g_mainThreadId) { 71 | func(); 72 | } else { 73 | uv_async_t *async = GetAsyncToken(); 74 | RunOnMain(async, func); 75 | } 76 | } 77 | 78 | private: 79 | // called after the async handle is closed in order to free it's memory 80 | static void AyncCloseCb(uv_handle_t* handle) { 81 | if (handle != nullptr) { 82 | uv_async_t* async = reinterpret_cast(handle); 83 | delete async; 84 | } 85 | } 86 | 87 | // Called by run on main in case we are not running on the main thread 88 | static void AsyncCb(uv_async_t* handle) { 89 | TokenData* tokenData = static_cast(handle->data); 90 | tokenData->func(); 91 | ReleaseAsyncToken(handle); 92 | } 93 | }; 94 | 95 | } // namespace NodeUtils 96 | -------------------------------------------------------------------------------- /src/uwp.cc: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files(the "Software"), to 5 | // deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and / or 7 | // sell copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions : 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | // IN THE SOFTWARE. 20 | 21 | #include "uwp.h" 22 | 23 | std::thread::id NodeUtils::g_mainThreadId; 24 | UWPAddOn UWPAddOn::s_instance; 25 | 26 | bool UWPAddOn::EnsureCoInitialized() { 27 | if (!_coInitialized) { 28 | HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); 29 | _coInitialized = SUCCEEDED(hr); 30 | 31 | if (!_coInitialized) { 32 | Nan::ThrowError("CoInitializeEx failed"); 33 | } 34 | } 35 | 36 | return _coInitialized; 37 | } 38 | 39 | void UWPAddOn::EnsureCoUninitialized() { 40 | if (_coInitialized) { 41 | CoUninitialize(); 42 | _coInitialized = false; 43 | } 44 | } 45 | 46 | bool UWPAddOn::EnsureKeepAlive() { 47 | if (_keepAliveToken == nullptr) { 48 | // Open an active async handler to keep Node alive 49 | _keepAliveToken = Async::GetAsyncToken(); 50 | } 51 | 52 | return _keepAliveToken != nullptr; 53 | } 54 | 55 | void UWPAddOn::ReleaseKeepAlive() { 56 | if (_keepAliveToken != nullptr) { 57 | auto token = _keepAliveToken; 58 | _keepAliveToken = nullptr; 59 | 60 | Async::ReleaseAsyncToken(token); 61 | } 62 | } 63 | 64 | void UWPAddOn::Init(Handle target) { 65 | Nan::HandleScope scope; 66 | 67 | NodeUtils::g_mainThreadId = 68 | std::this_thread::get_id(); // Capture entering thread id 69 | 70 | if (!s_instance.EnsureCoInitialized()) { 71 | return; 72 | } 73 | 74 | JsErrorCode err = JsSetProjectionEnqueueCallback( 75 | [](JsProjectionCallback jsCallback, 76 | JsProjectionCallbackContext jsContext, void *context) { 77 | Async::RunOnMain([jsCallback, jsContext]() { 78 | jsCallback(jsContext); 79 | }); 80 | }, 81 | /*projectionEnqueueContext*/nullptr); 82 | if (err != JsNoError) { 83 | Nan::ThrowError("JsSetProjectionEnqueueCallback failed"); 84 | return; 85 | } 86 | 87 | Nan::SetMethod(target, "projectNamespace", ProjectNamespace); 88 | Nan::SetMethod(target, "close", Close); 89 | } 90 | 91 | NAN_METHOD(UWPAddOn::ProjectNamespace) { 92 | Nan::HandleScope scope; 93 | 94 | if (!info[0]->IsString()) { 95 | Nan::ThrowTypeError("Argument must be a string"); 96 | return; 97 | } 98 | 99 | if (!s_instance.EnsureCoInitialized()) { 100 | return; 101 | } 102 | 103 | String::Value name(info[0]); 104 | if (JsProjectWinRTNamespace(reinterpret_cast(*name)) != JsNoError) { 105 | Nan::ThrowError("JsProjectWinRTNamespace failed"); 106 | return; 107 | } 108 | 109 | // Keep Node alive once successfully projected a UWP namespace 110 | s_instance.EnsureKeepAlive(); 111 | return; 112 | } 113 | 114 | NAN_METHOD(UWPAddOn::Close) { 115 | Nan::HandleScope scope; 116 | 117 | s_instance.ReleaseKeepAlive(); 118 | s_instance.EnsureCoUninitialized(); 119 | return; 120 | } 121 | 122 | NODE_MODULE(uwp, UWPAddOn::Init); 123 | --------------------------------------------------------------------------------