Initial commit

This commit is contained in:
João Moreno 2022-05-18 09:28:30 +02:00 committed by GitHub
parent a36186ecb3
commit c42e9ee946
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 2579 additions and 22 deletions

20
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: CI
on:
push:
branches: ["**"]
tags: [v*]
pull_request:
branches: [main]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/node_modules
/build

19
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/node_modules/node-addon-api",
"${default}"
],
"defines": ["_DEBUG"],
"windowsSdkVersion": "10.0.18362.0",
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}

72
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,72 @@
{
"files.associations": {
"system_error": "cpp",
"thread": "cpp",
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"exception": "cpp",
"format": "cpp",
"forward_list": "cpp",
"functional": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"optional": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"utility": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp"
}
}

24
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,24 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build",
"group": "build",
"problemMatcher": [],
"label": "npm: build",
"detail": "node-gyp build"
},
{
"type": "npm",
"script": "rebuild",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"label": "npm: rebuild",
"detail": "node-gyp rebuild"
}
]
}

View File

@ -1,18 +1,23 @@
# Project
# vscode-policy-watcher
> This repo has been populated by an initial template to help get you started. Please
> make sure to update the content to build a great experience for community-building.
Example usage:
As the maintainer of this project, please make a few updates:
```js
const createWatcher = require("vscode-policy-watcher");
- Improving this README.MD file to provide a great experience
- Updating SUPPORT.MD with content about this project's support experience
- Understanding the security reporting process in SECURITY.MD
- Remove this section from the README
createWatcher(
"CodeOSS",
{
UpdateMode: { type: "string" },
SCMInputFontSize: { type: "number" },
},
(update) => console.log(update)
);
```
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
@ -26,8 +31,8 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
## Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
trademarks or logos is subject to and must follow
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
trademarks or logos is subject to and must follow
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Any use of third-party trademarks or logos are subject to those third-party's policies.

View File

@ -1,13 +1,3 @@
# TODO: The maintainer of this repo has not yet edited this file
**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project?
- **No CSS support:** Fill out this template with information about how to file issues and get help.
- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps.
- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide.
*Then remove this first heading from this SUPPORT.MD file before publishing your repo.*
# Support
## How to file issues and get help
@ -22,4 +12,4 @@ CHANNEL. WHERE WILL YOU HELP PEOPLE?**.
## Microsoft Support Policy
Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
Support for this project is limited to the resources listed above.

44
binding.gyp Normal file
View File

@ -0,0 +1,44 @@
{
"targets": [
{
"target_name": "vscode-policy-watcher",
"sources": [
"src/main.cc",
"src/StringPolicy.cc",
"src/NumberPolicy.cc",
"src/PolicyWatcher.cc"
],
"include_dirs": [
"<!(node -p \"require('node-addon-api').include_dir\")"
],
'cflags!': ['-fno-exceptions'],
'cflags_cc!': ['-fno-exceptions'],
'conditions': [
["OS=='win'", {
"defines": [
"_HAS_EXCEPTIONS=1"
],
"libraries": [
"userenv.lib"
],
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1,
'AdditionalOptions': [
'/W3',
'/Qspectre',
'/guard:cf',
'-std:c++17'
]
},
'VCLinkerTool': {
'AdditionalOptions': [
'/guard:cf'
]
}
},
}],
],
}
]
}

31
index.d.ts vendored Normal file
View File

@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
interface Watcher {
dispose(): void;
}
type StringPolicy = { type: "string" };
type NumberPolicy = { type: "number" };
export interface Policies {
[policyName: string]: StringPolicy | NumberPolicy;
}
export type PolicyUpdate<T extends Policies> = {
[K in keyof T]:
| undefined
| (T[K] extends StringPolicy
? string
: T[K] extends NumberPolicy
? number
: never);
};
export function createWatcher<T extends Policies>(
productName: string,
policies: T,
onDidChange: (update: PolicyUpdate<T>) => void
): Watcher;

17
index.js Normal file
View File

@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
exports.createWatcher = require('bindings')('vscode-policy-watcher');
if (require.main === module) {
exports.createWatcher(
'CodeOSS',
{
UpdateMode: { type: 'string' },
SCMInputFontSize: { type: 'number' },
},
msg => console.log(msg)
);
}

1904
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "vscode-policy-watcher",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "node-gyp build",
"rebuild": "node-gyp rebuild"
},
"author": "João Moreno",
"license": "MIT",
"os": [
"win32"
],
"types": "index.d.ts",
"dependencies": {
"bindings": "^1.5.0",
"node-addon-api": "*"
},
"devDependencies": {
"node-gyp": "^9.0.0"
}
}

21
src/NumberPolicy.cc Normal file
View File

@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#include "NumberPolicy.hh"
using namespace Napi;
NumberPolicy::NumberPolicy(const std::string name, const std::string &productName)
: RegistryPolicy(name, productName, REG_QWORD) {}
long long NumberPolicy::parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const
{
return *reinterpret_cast<long long *>(buffer);
}
Value NumberPolicy::getJSValue(Env env, long long value) const
{
return Number::New(env, value);
}

25
src/NumberPolicy.hh Normal file
View File

@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef NUMBER_POLICY_H
#define NUMBER_POLICY_H
#include <napi.h>
#include <windows.h>
#include "RegistryPolicy.hh"
using namespace Napi;
class NumberPolicy : public RegistryPolicy<long long>
{
public:
NumberPolicy(const std::string name, const std::string &productName);
protected:
long long parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const;
Value getJSValue(Env env, long long value) const;
};
#endif

24
src/Policy.hh Normal file
View File

@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef POLICY_H
#define POLICY_H
#include <napi.h>
using namespace Napi;
class Policy
{
public:
virtual bool refresh() = 0;
virtual Value getValue(Env env) const = 0;
const std::string name;
Policy(const std::string name)
: name(name) {}
};
#endif

89
src/PolicyWatcher.cc Normal file
View File

@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#include "PolicyWatcher.hh"
using namespace Napi;
PolicyWatcher::PolicyWatcher(Function &okCallback, std::vector<std::unique_ptr<Policy>> _policies)
: AsyncProgressQueueWorker(okCallback),
policies(std::move(_policies))
{
}
PolicyWatcher::~PolicyWatcher()
{
UnregisterGPNotification(handles[2]);
UnregisterGPNotification(handles[3]);
CloseHandle(handles[0]);
CloseHandle(handles[1]);
CloseHandle(handles[2]);
CloseHandle(handles[3]);
}
void PolicyWatcher::OnExecute(Napi::Env env)
{
if ((handles[0] = CreateEvent(NULL, false, false, NULL)) == NULL)
return SetError("Failed to create exit event");
if ((handles[1] = CreateEvent(NULL, false, false, NULL)) == NULL)
return SetError("Failed to create dispose event");
if ((handles[2] = CreateEvent(NULL, false, false, NULL)) == NULL)
return SetError("Failed to create machine GP event");
if ((handles[3] = CreateEvent(NULL, false, false, NULL)) == NULL)
return SetError("Failed to create user GP event");
if (!RegisterGPNotification(handles[2], TRUE))
return SetError("Failed to register machine GP event");
if (!RegisterGPNotification(handles[3], FALSE))
return SetError("Failed to register user GP event");
AsyncProgressQueueWorker::OnExecute(env);
}
void PolicyWatcher::Execute(const ExecutionProgress &progress)
{
bool first = true;
while (TRUE)
{
std::vector<const Policy *> updatedPolicies;
for (auto &policy : policies)
{
if (policy->refresh())
{
updatedPolicies.push_back(policy.get());
}
}
if (first || updatedPolicies.size() > 0)
progress.Send(&updatedPolicies[0], updatedPolicies.size());
first = false;
auto dwResult = WaitForMultipleObjects(4, handles, FALSE, INFINITE);
if (dwResult == WAIT_FAILED || (dwResult - WAIT_OBJECT_0) == 0 || (dwResult - WAIT_OBJECT_0) == 1 /* someone called dispose() */)
break;
}
}
void PolicyWatcher::OnOK() {}
void PolicyWatcher::OnError() {}
void PolicyWatcher::OnProgress(const Policy *const *policies, size_t count)
{
HandleScope scope(Env());
auto result = Object::New(Env());
for (size_t i = 0; i < count; i++)
result.Set(policies[i]->name, policies[i]->getValue(Env()));
Callback().Call(Receiver().Value(), {result});
}
void PolicyWatcher::Dispose()
{
SetEvent(handles[1]);
}

35
src/PolicyWatcher.hh Normal file
View File

@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef POLICY_WATCHER_H
#define POLICY_WATCHER_H
#include <napi.h>
#include <windows.h>
#include <userenv.h>
#include <vector>
#include "Policy.hh"
using namespace Napi;
class PolicyWatcher : public AsyncProgressQueueWorker<const Policy *>
{
public:
PolicyWatcher(Function &okCallback, std::vector<std::unique_ptr<Policy>> _policies);
~PolicyWatcher();
void OnExecute(Napi::Env env);
void Execute(const ExecutionProgress &progress);
void OnOK();
void OnError();
void OnProgress(const Policy *const *policies, size_t count);
void Dispose();
private:
std::vector<std::unique_ptr<Policy>> policies;
HANDLE handles[4];
};
#endif

89
src/RegistryPolicy.hh Normal file
View File

@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef REGISTRY_POLICY_H
#define REGISTRY_POLICY_H
#include <napi.h>
#include <windows.h>
#include <optional>
#include "Policy.hh"
using namespace Napi;
template <typename T>
class RegistryPolicy : public Policy
{
public:
RegistryPolicy(const std::string name, const std::string &productName, const DWORD regType)
: Policy(name),
registryKey("Software\\Policies\\Microsoft\\" + productName),
regType(regType) {}
bool refresh()
{
auto machine = read(HKEY_LOCAL_MACHINE);
if (machine.has_value())
{
if (value != machine)
{
value = machine;
return true;
}
return false;
}
auto user = read(HKEY_CURRENT_USER);
if (value != user)
{
value = user;
return true;
}
return false;
}
Value getValue(Env env) const
{
if (!value.has_value())
return env.Undefined();
return getJSValue(env, value.value());
}
protected:
virtual T parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const = 0;
virtual Value getJSValue(Env env, T value) const = 0;
private:
const std::string registryKey;
const DWORD regType;
std::optional<T> value;
std::optional<T> read(HKEY root)
{
HKEY hKey;
if (ERROR_SUCCESS != RegOpenKeyEx(root, registryKey.c_str(), 0, KEY_READ, &hKey))
return std::nullopt;
BYTE buffer[1024];
DWORD bufferSize = sizeof(buffer);
DWORD type;
auto readResult = RegQueryValueEx(hKey, name.c_str(), 0, &type, buffer, &bufferSize);
RegCloseKey(hKey);
if (ERROR_SUCCESS != readResult || type != regType)
return std::nullopt;
return std::optional<T>{parseRegistryValue(buffer, bufferSize)};
}
};
#endif

21
src/StringPolicy.cc Normal file
View File

@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#include "StringPolicy.hh"
using namespace Napi;
StringPolicy::StringPolicy(const std::string name, const std::string &productName)
: RegistryPolicy(name, productName, REG_SZ) {}
std::string StringPolicy::parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const
{
return std::string(reinterpret_cast<char *>(buffer), bufferSize - 1);
}
Value StringPolicy::getJSValue(Env env, std::string value) const
{
return String::New(env, value);
}

25
src/StringPolicy.hh Normal file
View File

@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#ifndef STRING_POLICY_H
#define STRING_POLICY_H
#include <napi.h>
#include <windows.h>
#include "RegistryPolicy.hh"
using namespace Napi;
class StringPolicy : public RegistryPolicy<std::string>
{
public:
StringPolicy(const std::string name, const std::string &productName);
protected:
std::string parseRegistryValue(LPBYTE buffer, DWORD bufferSize) const;
Value getJSValue(Env env, std::string value) const;
};
#endif

77
src/main.cc Normal file
View File

@ -0,0 +1,77 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
#include <napi.h>
#include <vector>
#include "Policy.hh"
#include "StringPolicy.hh"
#include "NumberPolicy.hh"
#include "PolicyWatcher.hh"
using namespace Napi;
Value DisposeWatcher(const CallbackInfo &info)
{
auto watcher = (PolicyWatcher *)info.Data();
watcher->Dispose();
return info.Env().Null();
}
Value CreateWatcher(const CallbackInfo &info)
{
auto env = info.Env();
if (info.Length() < 3)
throw TypeError::New(env, "Expected 3 arguments");
else if (!info[0].IsString())
throw TypeError::New(env, "Expected first arg to be string");
else if (!info[1].IsObject())
throw TypeError::New(env, "Expected second arg to be object");
else if (!info[2].IsFunction())
throw TypeError::New(env, "Expected third arg to be function");
auto productName = info[0].As<String>();
auto rawPolicies = info[1].As<Object>();
auto policies = std::vector<std::unique_ptr<Policy>>();
for (auto const &item : rawPolicies)
{
auto rawPolicyName = item.first.As<String>();
auto rawPolicyValue = static_cast<Value>(item.second);
if (!rawPolicyValue.IsObject())
throw TypeError::New(env, "Expected policy to be object");
auto rawPolicy = rawPolicyValue.As<Object>();
auto rawPolicyType = rawPolicy.Get("type");
if (!rawPolicyType.IsString())
throw TypeError::New(env, "Expected policy type to be string");
auto policyType = std::string(rawPolicyType.As<String>());
if (policyType == "string")
policies.push_back(std::make_unique<StringPolicy>(rawPolicyName.As<String>(), productName));
else if (policyType == "number")
policies.push_back(std::make_unique<NumberPolicy>(rawPolicyName.As<String>(), productName));
else
throw TypeError::New(env, "Unknown policy type '" + policyType + "'");
}
auto watcher = new PolicyWatcher(info[2].As<Function>(), std::move(policies));
watcher->Queue();
auto result = Object::New(env);
result.Set(String::New(env, "dispose"), Function::New(env, DisposeWatcher, "disposeWatcher", watcher));
return result;
}
Object Init(Env env, Object exports)
{
return Function::New(env, CreateWatcher, "createWatcher");
}
NODE_API_MODULE(vscodepolicy, Init)