[
  {
    "path": ".github/workflows/ci.yml",
    "content": "# https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-nodejs\n\nname: ci\non: [push, pull_request]\njobs:\n  ci:\n    strategy:\n      fail-fast: false\n      matrix:\n        node-version: ['18.x', '20.x', '22.x', '24.x', '25.x']\n        os: [windows-latest]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n      - run: npm ci\n      - run: npm run build --if-present\n      # https://community.chocolatey.org/packages?q=Excel\n      - name: \"Installing Office 365 takes about 5 minutes...\"\n        run: choco install office365proplus\n      - run: |\n          npm install -g mocha\n          # mocha --expose-gc test  # Three tests will fail!\n      - run: npm test\n"
  },
  {
    "path": ".gitignore",
    "content": ".vscode\n.idea\nnode_modules\nbuild\nnpm-debug.log\ntest.js\n/data/PERSONS.DBF\n/debug*"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Yuri Dursin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "NodeWScript.js",
    "content": "#!/usr/bin/env node \nvar g_winax = require('./activex');\r\nvar path = require('path');\r\n\r\nif(process.argv.length<3)\r\n{\r\n    console.error(\"Missing argument. Usage:\\nnodewscript SomeJS.js <other arguments>\")\r\n}\r\n\r\n// For the case we want to use 'require' or 'module' - we need to make it available through global.\r\nglobal.require = require;\r\nglobal.exports = exports;\r\nglobal.module = module;\r\n\r\nglobal.SeSIncludeFile = function (includePath, isLocal, ctx) {\r\n\r\n    var vm = require(\"vm\");\r\n\r\n    var fs = require(\"fs\");\r\n    var path = require(\"path\");\r\n\r\n    var absolutePath = path.resolve(includePath);\r\n    var data = fs.readFileSync(absolutePath);\r\n    var script = new vm.Script(data, { filename: absolutePath, displayErrors: true });\r\n    if (isLocal && ctx) {\r\n        if (!ctx.__runctx__) ctx.__runctx__ = vm.createContext(ctx);\r\n        script.runInContext(ctx.__runctx__);\r\n    } else {\r\n        script.runInThisContext();\r\n    }\r\n\r\n    return '';\r\n};\n\nglobal.GetObject = function GetObject(strMoniker) {\n    return new g_winax.Object(strMoniker, {getobject:true});\n}\r\n\r\nvar WinAXActiveXObject = ActiveXObject;\r\n\r\nfunction ActiveXObjectCreate(progId, pfx)\r\n{\r\n    var res = new WinAXActiveXObject(progId);\r\n    if(progId==\"SeSHelper\")\r\n    {\r\n        // SeSHelper implements Include that we want to override\r\n        res = new Proxy({helper:res}, {\r\n            get(target, propKey, receiver) {\r\n                return function (...args) {\r\n                    var helper = target.helper;\r\n                    var ovr =\r\n                    {\r\n                        IncludeLocal(/**Object*/fName, ctx) /**Object*/\r\n                        {\r\n                            return this.Include(fName, true, ctx);\r\n                        },\r\n                \r\n                        Include(/**Object*/fName, local, ctx) /**Object*/\r\n                        {\r\n                            var includePath = helper.ResolvePath(fName);\r\n                            if(!includePath)\r\n                            {\r\n                                var err = \"Error trying to include file. File not found:\"+fName;\r\n                                return \"console.error(\"+JSON.stringify(err)+\")\";\r\n                            }\r\n                        \r\n                            if(!local) \r\n                            {\r\n                                helper.SetAlreadyIncluded(includePath);\r\n                            }\r\n                    \r\n                            return global.SeSIncludeFile(includePath, local, ctx);\r\n                    \r\n                        },\r\n                \r\n                        IncludeOnce(/**Object*/fName) /**Object*/\r\n                        {\r\n                            if(!helper.IsAlreadyIncluded(fName))\r\n                            {\r\n                                return this.Include(fName);\r\n                            }\r\n                            return \"\";\r\n                        }\r\n                    }\r\n\r\n                    if(propKey in ovr)\r\n                    {\r\n                        return ovr[propKey](...args);\r\n                    } else {\r\n                        let result = target.helper[propKey](...args);\r\n                        return result;    \r\n                    }\r\n\r\n                };\r\n            }\r\n          });\r\n    }\r\n\r\n    if(res&&pfx)\r\n    {\r\n        WScript.ConnectObject(res, pfx);\r\n    }\r\n    \r\n    return res;\r\n}\r\n\r\nglobal.ActiveXObject=new Proxy(ActiveXObject, {\r\n    construct(target,args)\r\n    {\r\n        //console.log(\"Creating ActiveXObject: \"+args);\r\n        return ActiveXObjectCreate(...args);\r\n    }\r\n});\r\n\r\nglobal.Enumerator = function (arr) {\r\n    this.arr = arr;\r\n    this.enum = arr._NewEnum;\r\n    this.nextItem = null;\r\n    this.atEnd = function () { return this.nextItem===undefined; }\r\n    this.moveNext = function () {  this.nextItem = this.enum.Next(1); return this.atEnd(); }\r\n    this.moveFirst = function () { this.enum.Reset(); this.nextItem=null; return this.moveNext(); }\r\n    this.item = function () { return this.nextItem; }\r\n    \r\n    this.moveNext();\r\n}\r\n\r\n// See interface here\r\n// https://www.devguru.com/content/technologies/wsh/objects-wscript.html\r\n\r\nvar WScript = {\r\n    // Properties\r\n    Application : null,\r\n    BuildVersion : \"6.0\",\r\n    FullName : null,\r\n    Interactive : true,\r\n    Name : \"Windows / Node Script Host\",\r\n    Path : __filename,\r\n    ScriptFullName : null,\r\n    ScriptName : null,\r\n    StdErr: {\r\n        Write(txt) { console.log(txt); }\r\n    },\r\n    StdIn: {\r\n        ReadLine() { \r\n            var readline = require('readline');\r\n            var gotLine = false;\r\n            var line = undefined;\r\n\r\n            var rl = readline.createInterface({\r\n              input: process.stdin,\r\n              output: process.stdout,\r\n              terminal: false\r\n            });\r\n            \r\n            rl.on('line', function (cmd) {\r\n                line = cmd;\r\n                rl.close();\r\n            });\r\n            while(!gotLine)\r\n            {\r\n                WScript.Sleep(10);\r\n            }\r\n            return line\r\n        }\r\n    },\r\n    StdOut: {\r\n        Write(txt) { console.err(txt); }\r\n    },\r\n    Timeout : -1,\r\n    Version : 'NODE.WIN32',\r\n\r\n    // Methods\r\n    \r\n    Echo : console.log,\r\n\r\n    __Connected : [],\r\n\r\n    ConnectObject(obj, pfx)\r\n    {\r\n        var connectionPoints = g_winax.getConnectionPoints(obj);\r\n        var connectionPoint = connectionPoints[0];\r\n        var allMethods = connectionPoint.getMethods();\r\n\r\n        var advobj={};\r\n        var found = false;\r\n        for(var i=0;i<allMethods.length;i++)\r\n        {\r\n            var a = allMethods[i];\r\n            \r\n            var cpFunc = null;\r\n            var fName = pfx + a;\r\n            \r\n            eval('if(typeof '+fName+'!=\"undefined\") cpFunc = '+fName+';')\r\n\r\n            if(cpFunc)\r\n            {\r\n                found = true;\r\n                advobj[a]=cpFunc;\r\n            }\r\n        }\r\n\r\n        if(found)\r\n        {\r\n            var dwCookie = connectionPoint.advise(advobj);\r\n            this.__Connected.push({obj,pfx,connectionPoint,dwCookie});\r\n        }\r\n\r\n    },\r\n\r\n    DisconnectObject(obj)\r\n    {\r\n        for(var i=0;i<this.__Connected.length;i++)\r\n        {\r\n            var o = this.__Connected[i];\r\n            if(o.obj==obj)\r\n            {\r\n                o.connectionPoint.unadvise(o.dwCookie);\r\n                // Remove connection from list\r\n                this.__Connected.splice(i,1);\r\n                break;\r\n            }\r\n        }\r\n    },\r\n    \r\n    CreateObject : ActiveXObjectCreate,\r\n\r\n    GetObject : ActiveXObjectCreate,\r\n\r\n    Quit (exitCode) { \r\n        // Gracefully disconnect and release all ActiveX objects, if needed\r\n        while(this.__Connected.length>0)\r\n        {\r\n            WScript.DisconnectObject(this.__Connected[0].obj);\r\n        }\r\n        WScript.Sleep(60);\r\n        process.exit(exitCode); \r\n    },\r\n\r\n    Sleep (ms) { \r\n        var begin = (new Date()).valueOf();\r\n        MessageLoop();\r\n        var dt = (new Date()).valueOf()-begin;\r\n\r\n        while(dt<ms)\r\n        {\r\n            MessageLoop();\r\n            g_winax.winaxsleep(1);\r\n            dt = (new Date()).valueOf()-begin;\r\n        }\r\n    },\r\n\r\n    __Args : process.argv,\r\n    __ShowLogo : true,\r\n    __Debug : false,\r\n\r\n    // Collections\r\n    Arguments : null,\r\n\r\n    __InitArgs(){\r\n        this.Application = this;\r\n        this.__Args=[];\r\n        for(let i=0;i<process.argv.length;i++)\r\n        {\r\n            if(i==0)\r\n            {\r\n                this.FullName = path.resolve(process.argv[0])\r\n            } else if(i==1) {\r\n                this.Name = process.argv[1];\r\n            } else if(i==2) {\r\n                this.ScriptFullName = path.resolve(process.argv[2]);\r\n                this.ScriptName = path.basename(this.ScriptFullName);\r\n            } else {\r\n                // All other args\r\n                var arg = process.argv[i];\r\n                if(arg.startsWith(\"//\"))\r\n                {\r\n/* \r\nParse all arguments and process those related to core WScript:\r\n //B         Batch mode: Suppresses script errors and prompts from displaying\r\n //D         Enable Active Debugging\r\n //E:engine  Use engine for executing script\r\n //H:CScript Changes the default script host to CScript.exe\r\n //H:WScript Changes the default script host to WScript.exe (default)\r\n //I         Interactive mode (default, opposite of //B)\r\n //Job:xxxx  Execute a WSF job\r\n //Logo      Display logo (default)\r\n //Nologo    Prevent logo display: No banner will be shown at execution time\r\n //S         Save current command line options for this user\r\n //T:nn      Time out in seconds:  Maximum time a script is permitted to run\r\n //X         Execute script in debugger\r\n //U         Use Unicode for redirected I/O from the console\r\n*/\r\n                    if(arg.toLowerCase()==\"//nologo\")\r\n                    {\r\n                        this.__ShowLogo = false;\r\n                    } else if(arg.toLowerCase().indexOf(\"//t:\")==0) {\r\n                        var timeOut = parseInt(arg.substr(\"//t:\".length));\r\n                        this.Timeout = timeOut;\r\n                        setTimeout(()=>{WScript.Quit(0);},timeOut*1000)\r\n                    } else if(arg.toLowerCase()==\"//x\"||arg.toLowerCase()==\"//d\") {\r\n                        this.__Debug = true;\r\n                    }\r\n                } else {\r\n                    this.__Args.push(arg);\r\n                }\r\n            }\r\n        }\r\n\r\n        // The trick starts here\r\n        // We want WScript.Arguments to be accessible in all of the following ways:\r\n        // WScript.Arguments(i)\r\n        // WScript.Arguments.item(i)\r\n        // WScript.Arguments.length\r\n        // Since it is not directly possible, we use a number of tricks.\r\n        // Function.length is a number of expected arguments\r\n        // And we make this 'fake' number\r\n        var argArr=[];\r\n        for(var ai=0;ai<WScript.__Args.length;ai++)argArr.push(\"arg\"+ai);\r\n        var ArgF=null;\r\n        eval(\"ArgF = function (\"+argArr.join(\",\")+\") { return WScript.__Args[arguments[0]];  }\");\r\n        ArgF.item = function(i) { return WScript.__Args[i] };\r\n        ArgF.toString = function() { return WScript.__Args.join(' '); }\n        ArgF.Count = function() { return WScript.__Args.length; };\r\n\r\n        this.Arguments = ArgF;\r\n    }\r\n}\r\n\r\nWScript.__InitArgs();\r\n\r\nglobal.WScript = WScript;\r\n\r\nif(WScript.__ShowLogo)\r\n{\r\n    WScript.Echo(WScript.Name+\" \"+WScript.BuildVersion);\r\n}\r\n\r\nfunction MessageLoop()\r\n{\r\n    g_winax.peekAndDispatchMessages(); // allows ActiveX event to be dispatched\r\n}\r\n\r\nconst __rapise_global_context__ = require(\"vm\").createContext(this);\r\nglobal.__rapise_global_context__ = __rapise_global_context__;\r\n\r\nif(WScript.ScriptFullName)\r\n{\r\n    SeSIncludeFile(WScript.ScriptFullName);\r\n} else {\r\n    WScript.StdErr.Write(\"File.js missing.\\nUsage wscriptnode <File.js>\")\r\n}\r\n"
  },
  {
    "path": "README.md",
    "content": "# Name\n\n\tWindows C++ Node.JS addon, that implements COM IDispatch object wrapper, analog ActiveXObject on cscript.exe\n\n# Features\n\n * Using *ITypeInfo* for conflict resolution between property and method \n (for example !rs.EOF not working without type information, becose need define EOF as an object) \n\n * Using optional parameters on constructor call    \n``` js \nvar con = new ActiveXObject(\"Object.Name\", {\n\tactivate: false, // Allow activate existance object instance (through CoGetObject), false by default\n\tgetobject: false, // Allow using name of the file in the ROT (through GetAccessibleObject), false by default\n\ttype: true\t// Allow using type information, true by default\n});\n```\n\n * Create COM object from JS object and may be send as argument (for example send to Excel procedure)\n``` js \nvar com_obj = new ActiveXObject({\n\ttext: test_value,\n\tobj: { params: test_value },\n\tarr: [ test_value, test_value, test_value ],\n\tfunc: function(v) { return v*2; }\n});\n```\n\n * Additional COM variant types support:\n\t- **int** - VT_INT\n\t- **uint** - VT_UINT\n\t- **int8**, **char** - VT_I1\n\t- **uint8**, **uchar**, **byte** - VT_UI1\n\t- **int16**, **short** - VT_I2\n\t- **uint16**, **ushort** - VT_UI2\n\t- **int32** - VT_I4\n\t- **uint32** - VT_UI4\n\t- **int64**, **long** - VT_I8\n\t- **uint64**, **ulong** - VT_UI8\n\t- **currency** - VT_CY\n\t- **float** - VT_R4\n\t- **double** - VT_R8\n\t- **string** - VT_BSTR\n\t- **date** - VT_DATE\n\t- **decimal** - VT_DECIMAL\n\t- **variant** - VT_VARIANT\n\t- **null** - VT_NULL\n\t- **empty** - VT_EMPTY\n\t- **byref** - VT_BYREF or use prefix **'p'** to indicate reference to the current type\n\n``` js \nvar winax = require('winax');\nvar Variant = winax.Variant;\n\n// create variant instance \nvar v_short = new Variant(17, 'short');\nvar v_short_byref = new Variant(17, 'pshort');\nvar v_int_byref = new Variant(17, 'byref');\nvar v_byref = new Variant(v_short, 'byref');\n\n// create variant arrays\nvar v_array_of_variant = new Variant([1,'2',3]);\nvar v_array_of_short = new Variant([1,'2',3], 'short');\nvar v_array_of_string = new Variant([1,'2',3], 'string');\t\n\n// change variant content\nvar v_test = new Variant();\nv_test.assign(17);\nv_test.cast('string');\nv_test.clear();\n\n// also may be used cast function\nvar v_short_from_cast = winax.cast(17, 'short');\n```\n\n * Additional dignostic propeties:\n\t- **__id** - dispatch identity, for exmplae: ADODB.Connection.@Execute.Fields\n\t- **__value** - value of dispatch object, equiles valueOf()\n\t- **__type** - array all member items with their properties\n\t- **__methods** - list member mathods by names (ITypeInfo::GetFuncDesc)\n\t- **__vars** - list member variables by names (ITypeInfo::GetVarDesc) \n\n * Full WScript Emulation Support through nodewscript\n\t- **ActiveXObject** type\n\t- **WScript.CreateObject**\n\t- **WScript.ConnectObject**\n\t- **WScript.DisconnectObject**\n\t- **WScript.Sleep**\n\t- **WScript.Arguments**\n\t- **WScript.Version**\n\t- **GetObject**\n\t- **Enumerator**\n\n# Usage example\n\nInstall package throw NPM (see below **Building** for details)\n```\nnpm install winax\nnpm install winax --msvs_version=2015\nnpm install winax --msvs_version=2017\n```\n\nCreate ADO Connection throw global function\n``` js\nrequire('winax');\nvar con = new ActiveXObject('ADODB.Connection');\n```\nOr using Object prototype\n``` js\nvar winax = require('winax');\nvar con = new winax.Object('ADODB.Connection');\n```\nOpen connection and create simple table\n``` js\ncon.Open('Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\\tmp;Extended Properties=\"DBASE IV;\"', '', '');\ncon.Execute(\"Create Table persons.dbf (Name char(50), City char(50), Phone char(20), Zip decimal(5))\");\ncon.Execute(\"Insert into persons.dbf Values('John', 'London','123-45-67','14589')\");\ncon.Execute(\"Insert into persons.dbf Values('Andrew', 'Paris','333-44-55','38215')\");\ncon.Execute(\"Insert into persons.dbf Values('Romeo', 'Rom','222-33-44','54323')\");\n```\nSelect query and return RecordSet object\n``` js\nvar rs = con.Execute(\"Select * from persons.dbf\"); \nvar reccnt = rs.RecordCount;\n```\nInspect RecordSet fields\n``` js\nvar fields = rs.Fields;\nvar fldcnt = fields.Count;\n```\nProcess records\n``` js\nrs.MoveFirst();\nwhile (!rs.EOF) {\n\tvar name = fields(\"Name\").value;\n\tvar town = fields(\"City\").value;\n\tvar phone = fields(\"Phone\").value;\n\tvar zip = fields(\"Zip\").value;   \n\tconsole.log(\"> Person: \" + name + \" from \" + town + \" phone: \" + phone + \" zip: \" + zip);    \n\trs.MoveNext();\n}\n```\nOr using fields by index\n``` js\nrs.MoveFirst();\nwhile (rs.EOF != false) {\n\tvar name = fields[0].value;\n\tvar town = fields[1].value;\n\tvar phone = fields[2].value;\n\tvar zip = fields[3].value;   \n\tconsole.log(\"> Person: \" + name + \" from \" + town + \" phone: \" + phone + \" zip: \" + zip);    \n\trs.MoveNext();\n}\n```\nRelease COM objects (but other temporary objects may be keep references too)\n``` js\nwinax.release(con, rs, fields)\n```\nWorking with Excel ranges using two dimension arrays (from 1.18.0 version)\n* The second dimension is only deduced from the first array.\n* If data is missing at the time of SafeArrayPutElement, VT_EMPTY is used.\n* Best way to explicitly pass VT_EMPTY is to use null\n* If the SAFEARRAY dims are smaller than those of the range, the missing cells are emptied.\n* Exception to 4! Excel may process the SAFEARRAY as it does when processing a copy/paste operation\n``` js\nvar excel = new winax.Object(\"Excel.Application\", { activate: true });\nvar wbk = excel.Workbooks.Add(template_filename);\nvar wsh = wbk.Worksheets.Item(1);\nwsh.Range(\"C3:E4\").Value = [ [\"C3\", \"D3\", \"E3\" ], [\"C4\", \"D4\", \"E4\" ] ];\nwsh.Range(\"C3:E4\").Value = [ [\"C3\", \"D3\", \"E3\" ], \"C4\" ]; // will let D4 and E4 empty\nwsh.Range(\"C3:E4\").Value = [ [null, \"D3\", \"E3\" ], \"C4\" ]; // will let C3, D4 and E4 empty\nwsh.Range(\"C3:F4\").Value = [ [100], [200] ]; // will duplicate the two rows in colums C, D, E, and F\nwsh.Range(\"C3:F4\").Value = [ [100, 200, 300, 400] ]; // will duplicate the for cols in rows 3 and 4\nwsh.Range(\"C3:F4\").Value = [ [100, 200] ]; // Will correctly duplicate the first two cols, but col E and F with contains \"#N/A\"\nconst data = wsh.Range(\"C3:E4\").Value.valueOf();\nconsole.log(\"Cell E4 value is\", data[1][2]);\n```\n\n## TypeScript\nYour `tsconfig.json` needs at least the following configuration:\n\n1. [`moduleResolution`](https://www.typescriptlang.org/tsconfig/#moduleResolution) needs to be something other than `classic`. Depending on your `target` and `module` this might be the default. For example:\n```json\n{\n\t\"compilerOptions\": {\n\t\t\"target\": \"esnext\",\n\t\t\"module\": \"nodenext\"\n\t}\n}\n```\n\n2. [`lib`](https://www.typescriptlang.org/tsconfig/#lib) needs to _**exclude**_ `ScriptHost`. It contains a conflicting global `ActiveXObject` type and is included by default. For example:\n```json\n{\n\t\"compilerOptions\": {\n\t\t\"lib\": [ \"ESNext\" ]\n\t}\n}\n```\n\n# Tutorial and Examples\n\n- [examples/ado.js](https://github.com/durs/node-activex/blob/master/examples/ado.js)\n\n# Usage as a library\n\nThe repo allows to re-use some of its code as a library for your own native node addon.\n\nTo include it, install it as a normal node-dependency and then add it\nto the `\"dependencies\"` section of your `binding.gyp` file like this:\n```\n\"dependencies\": [\n  \"<!(node -p \\\"require.resolve('winax/lib_binding.gyp')\\\"):lib_node_activex\"\n]\n```\n\nIn your code, you can then include it using:\n```cpp\n#include <node_activex.h>\n```\n\nThis makes functions like `Variant2Value` or `Value2Variant` that translate between COM VARIANT and node types\navailable in your code.\nNote that providing this library functionality is not the core target of this repo however,\nso importing it eg. currently declares\nall methods in the global namespace and opens the namespaces `v8` and `node`.\n\nCheck the source code [`src/disp.h`](src/disp.h) and [`src/utils.h`](src/utils.h) for details.\n\n# Building\n\nThis project uses Visual C++ 2013 (or later versions then support C++11 standard).\nBulding also requires node-gyp and python 2.6 (or later) to be installed. \nSupported NodeJS Versions (x86 or x64): 10, 12, 16, 18, 20, 22, 23 \nYou can do this with npm:\n```\nnpm install --global --production windows-build-tools\n```\nTo obtain and build use console commands:\n```\ngit clone git://github.com/durs/node-axtivex.git\ncd node-activex\nnpm install\n```\nor debug version\n```\nnpm install --debug\n```\nor using node-gyp directly\n```\nnpm install -g node_gyp\nnode-gyp configure\nnode-gyp build\nnode-gyp rebuild\n```\n\nFor Electron users, need rebuild with a different V8 version:\n```\nnpm rebuild winax --runtime=electron --target=41.1.0 --arch=x64 --dist-url=https://electronjs.org/headers\n```\nor using electron rebuild module\n```\nnpm install --save-dev @electron/rebuild\n.\\node_modules\\.bin\\electron-rebuild.cmd \n\n```\nChange --target value to your electron version.\nSee also Electron Documentation: [Using Native Node Modules](https://electron.atom.io/docs/tutorial/using-native-node-modules/).\n\n# WScript\n\n[WScript](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/wscript) is designed as a runtime, so WSH scripts should be executed from command line. WScript emulation mode allows one to use standard **WScript** features and mix them with nodejs and ES6 parts.\n\nThere is one significant difference between WScript and NodeJS in general. NodeJS is designed to be [non-blocking](https://nodejs.org/en/docs/guides/blocking-vs-non-blocking/) and execution is done when last statement is done AND all pending promises and callbacks are resolved. WScript runtime is linear. It is done when last statement is executed.\n\nSo NodeJS is more convenient for writing things like async web services. Also with NodeJS you get access to invaluable collection of **npm** modules.\n\nWScript has impressive collection of Windows-specific APIs available through ActiveX (such as `WScript.Shell`, `FileSystemObject` etc). Also it supports application events to implement two way communication with external apps and processes. \n\nThis module allows taking the best from the two worlds - you may now use **npm** modules in your WScript scripts. Or, if you have bunch of legacy JScript sources, you may now execute them using this **NodeJS**-based runtime.\n\nIf you install this package with -g key, you will have `nodewscript` command. \n\nUsage:\n```\nnodewscript [options] <Filename.js>\n```\n\nIts direct analog in the windows scripting host is `cscript.exe` (it it outputs to the console, and `WScript.Echo` writes a line instead of showing a popup window).\n\nWhere `Filename.js` is a file designed for windows scripting host.\n\n## WScript Limitations\n\n### Implicit Properties\n\nThis is a key drawback of v8 engine compared to MS. Consider an example:\n\n```javascript\n    var a = WScript.CreateObject(...)\n    if( a.Prop )\n    {\n        // If 'Prop' is a dynamic property (i.e. it is not defined in TypeInfo and\n        // not marked explicitly as a property, then execution never gets here. Even if a.Prop is null or false.\n    }\n```\n\n### Function Setters\n\n```javascript\nvar WshShell = new ActiveXObject(\"WScript.Shell\");\nvar processEnv = WshShell.Environment(\"PROCESS\");\nprocessEnv(\"ENV_VAR\") = \"CUSTOM_VALUE\";  // This syntax is valid for JScript, but will throw syntax error on v8\n```\n### Events & Sleep\n\nDefault WScript engine checks for events every time you do some sync operation or Sleep. In this implementation we check for events when `WScript.Sleep` is executed. \n\n### 32 vs 64 Bit\n\nIt is using the same bitness as installed version of the  nodejs. So if your JScript files are designed for 32 bit, make sure to have nodejs x86 version installed before installing the **winax**.\n\n\n# Tests\n\n[mocha](https://github.com/visionmedia/mocha) is required to run unit tests.\n```\nnpm install -g mocha\nmocha --expose-gc test\n```\n\n# Contributors\n\n* [durs](https://github.com/durs)\n* [somanuell](https://github.com/somanuell)\n* [Daniel-Userlane](https://github.com/Daniel-Userlane)\n* [alexeygrinevich](https://github.com/alexeygrinevich)\n"
  },
  {
    "path": "activex.js",
    "content": "var ActiveX = module.exports = require('./build/Release/node_activex.node');\n\nglobal.ActiveXObject = function(id, opt) {\n    return new ActiveX.Object(id, opt);\n};\n"
  },
  {
    "path": "binding.gyp",
    "content": "{\n  'targets': [\n    {\n      'target_name': 'node_activex',\n      'conditions': [\n        ['OS==\"win\"', {\n          'sources': [\n            'src/main.cpp',\n            'src/utils.cpp',\n            'src/disp.cpp'\n          ],\n          'libraries': [],\n          'dependencies': []\n        }]\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "examples/ado.js",
    "content": "//-------------------------------------------------------------------------------------------------------\n// Project: node-activex\n// Author: Yuri Dursin\n// Description: Example of using ActiveX addon with ADO\n//-------------------------------------------------------------------------------------------------------\n\n//require('winax');\nrequire('../activex');\n\nvar path = require('path'); \nvar data_path = path.join(__dirname, '../data/');\nvar filename = \"persons.dbf\";\nvar constr = \"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\" + data_path + \";Extended Properties=\\\"DBASE IV;\\\"\";\n\nconsole.log(\"==> Preapre directory and delete DBF file on exists\");\nvar fso = new ActiveXObject(\"Scripting.FileSystemObject\");\nif (!fso.FolderExists(data_path)) fso.CreateFolder(data_path);\nif (fso.FileExists(data_path + filename)) fso.DeleteFile(data_path + filename);\n\nconsole.log(\"==> Open connection\");\nvar con = new ActiveXObject(\"ADODB.Connection\");\nconsole.log(\"ADO version: \" + con.Version);\ncon.Open(constr, \"\", \"\");\n\nconsole.log(\"==> Create new DBF file\")\ncon.Execute(\"create Table \" + filename + \" (Name char(50), City char(50), Phone char(20), Zip decimal(5))\");\n\nconsole.log(\"==> Insert records to DBF\")\ncon.Execute(\"insert into \" + filename + \" values('John', 'London','123-45-67','14589')\");\ncon.Execute(\"insert into \" + filename + \" values('Andrew', 'Paris','333-44-55','38215')\");\ncon.Execute(\"insert into \" + filename + \" values('Romeo', 'Rom','222-33-44','54323')\");\n\nconsole.log(\"==> Select records from DBF\")\nvar rs = con.Execute(\"Select * from \" + filename); \nvar fields = rs.Fields;\nconsole.log(\"Result field count: \" + fields.Count);\nconsole.log(\"Result record count: \" + rs.RecordCount);\n\nrs.MoveFirst();\nwhile (!rs.EOF) {\n    // Access as property by string key\n    var name = fields[\"Name\"].Value;\n\n    // Access as method with string argument\n    var town = fields(\"City\").value;\n\n    // Access as indexed array\n    var phone = fields[2].value;\n    var zip = fields[3].value;    \n\n    console.log(\"> Person: \"+name+\" from \" + town + \" phone: \" + phone + \" zip: \" + zip);\n    rs.MoveNext();\n}\n\ncon.Close();\n"
  },
  {
    "path": "examples/event.js",
    "content": "var winax = require('..');\nvar interval = setInterval(function () {\n\twinax.peekAndDispatchMessages(); // allows ActiveX event to be dispatched\n}, 50);\n\nvar application = new ActiveXObject('Excel.Application');\n\nvar connectionPoints = winax.getConnectionPoints(application);\nvar connectionPoint = connectionPoints[0];\n\n// Excel Application events: https://docs.microsoft.com/en-us/office/vba/api/excel.application(object)#events\nconnectionPoint.advise({\n  WindowActivate: function (workbook, win) {\n    console.log('WindowActivate event triggerred');\n  },\n  WindowDeactivate: function (workbook, win) {\n    console.log('WindowDeactivate event triggerred');\n  },\n  WindowResize: function (workbook, win) {\n    console.log('WindowResize event triggerred');\n  },\n  NewWorkbook: function (workbook) {\n    console.log('NewWorkbook event triggerred');\n    var worksheet = workbook.Worksheets(1);\n    if (worksheet) {\n      var cell = worksheet.Cells(1, 1);\n      if (cell) {\n        cell.value = \"Hello world\";\n      }\n    }\n  },\n  SheetActivate: function (worksheet) {\n    console.log('SheetActivate event triggerred');\n  },\n  SheetDeactivate: function (worksheet) {\n    console.log('SheetDeactivate event triggerred');\n  },\n  SheetSelectionChange: function (worksheet, range) {\n    //var worksheet1 = range; // it's a bug now: args in reversal order\n    //var range = worksheet;\n    console.log('SheetSelectionChange event triggerred', range.Address);\n  },\n  SheetChange: function (worksheet, range) {\n    //var worksheet1 = range; // it's a bug now: args in reversal order\n    //var range = worksheet;\n    console.log('SheetChange event triggerred', range.Address, range.Value);\n  },\n});\n\napplication.Visible = true;\nconsole.log('Excel application has been open.');\nconsole.log('--> Please create a new workbook to trigger event \"NewWorkbook\"');\n\nsetTimeout(function () {\n  clearInterval(interval);\n\tapplication.Quit();\n\twinax.release(application);\n}, 1000 * 60);\n\n"
  },
  {
    "path": "examples/wbem.js",
    "content": "var ActiveX = require('../activex');\nvar conn = new ActiveX.Object('WbemScripting.SWbemLocator');\nvar svr = conn.ConnectServer('.', '\\\\root\\\\cimv2');\nconst resp = svr.ExecQuery('SELECT ProcessorId FROM Win32_Processor');\nfor (let i = 0; i < resp.Count; i += 1) {\n\tconst properties = resp.ItemIndex(i).Properties_;\n\tlet count = properties.Count;\n\tconst propEnum = properties._NewEnum;\n\twhile (count--) {\n\t\tconst prop = propEnum.Next();\n\t\tif (prop) console.log(prop.Name + '=' + prop.Value);\n\t}\n}"
  },
  {
    "path": "include/node_activex.h",
    "content": "#pragma once\n\n// Include all the relevant definitions for use of the library code in a dependency.\n// Note that this does use the namespaces v8 and node in the current configuration\n\n#include \"../src/stdafx.h\"\n#include \"../src/disp.h\"\n#include \"../src/utils.h\"\n"
  },
  {
    "path": "index.d.ts",
    "content": "export class Object {\n  constructor(id: string, options?: ActiveXObjectOptions);\n  \n  // Define properties typically found on COM objects\n  __id?: string;\n  __value?: any;\n  __type?: any[];\n  __methods?: string[];\n  __vars?: string[];\n\n  // Define general method signatures if any known\n  [key: string]: any;\n}\n\n/** @deprecated Use `ActiveXObjectOptions` instead. */\nexport interface ActiveXOptions extends ActiveXObjectOptions {}\n\nexport class Variant {\n  constructor(value?: any, type?: VariantType);\n  assign(value: any): void;\n  cast(type: VariantType): void;\n  clear(): void;\n  valueOf(): any;\n}\n\nexport type VariantType =\n  | 'int' | 'uint' | 'int8' | 'char' | 'uint8' | 'uchar' | 'byte'\n  | 'int16' | 'short' | 'uint16' | 'ushort'\n  | 'int32' | 'uint32'\n  | 'int64' | 'long' | 'uint64' | 'ulong'\n  | 'currency' | 'float' | 'double' | 'string'\n  | 'date' | 'decimal' | 'variant' | 'null' | 'empty'\n  | 'byref' | 'pbyref';\n\nexport function cast(value: any, type: VariantType): any;\n\n// Utility function to release COM objects\nexport function release(...objects: any[]): void;\n\ndeclare global {\n  function ActiveXObject(id: string, options?: ActiveXObjectOptions): any;\n  function ActiveXObject(obj: Record<string, any>): any;\n\n  interface ActiveXObjectOptions {\n    /** Allow activating existing object instance. */\n    activate?: boolean;\n    \n    /** Allow using the name of the file in the ROT. **/\n    getobject?: boolean;\n\n    /** Allow using type information. */\n    type?: boolean;\n  }\n}\n"
  },
  {
    "path": "index.js",
    "content": "module.exports = require('./activex');"
  },
  {
    "path": "lib_binding.gyp",
    "content": "{\n  'targets': [\n    {\n      'target_name': 'lib_node_activex',\n      'type': 'static_library',\n      'sources': [\n        'src/utils.cpp',\n        'src/disp.cpp'\n      ],\n      'defines': [\n        'BUILDING_NODE_EXTENSION',\n      ],\n      'direct_dependent_settings': {\n        'include_dirs': ['include']\n      },\n      'dependencies': [\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"winax\",\n  \"version\": \"3.6.9\",\n  \"description\": \"Windows COM bindings\",\n  \"homepage\": \"https://github.com/durs/node-activex\",\n  \"keywords\": [\n    \"OLE\",\n    \"COM\",\n    \"ActiveX\",\n    \"ActiveXObject\",\n    \"CreateObject\",\n    \"Variant\",\n    \"Dispatch\",\n    \"WSH\",\n    \"WMI\",\n    \"Excel\",\n    \"Word\",\n    \"WScript\",\n    \"ConnectObject\",\n    \"DisconnectObject\"\n  ],\n  \"author\": {\n    \"name\": \"Yuri Dursin\",\n    \"url\": \"https://github.com/durs\",\n    \"email\": \"yuri.dursin@gmail.com\"\n  },\n  \"bin\": {\n    \"nodewscript\": \"NodeWScript.js\"\n  },\n  \"contributors\": [\n    \"Yuri Dursin <yuri.dursin@gmail.com>\",\n    \"Somanuell\",\n    \"Daniel-Userlane\",\n    \"Alexey Grinevich <alexey.grinevich@inflectra.com>\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/durs/node-activex.git\"\n  },\n  \"dependencies\": {},\n  \"devDependencies\": {},\n  \"engines\": {\n    \"node\": \">= 10.0.0\"\n  },\n  \"scripts\": {\n    \"test\": \"mocha test\"\n  },\n  \"license\": \"MIT\",\n  \"main\": \"index.js\"\n}\n"
  },
  {
    "path": "src/disp.cpp",
    "content": "//-------------------------------------------------------------------------------------------------------\n// Project: NodeActiveX\n// Author: Yuri Dursin\n// Description: DispObject class implementations\n//-------------------------------------------------------------------------------------------------------\n\n#include \"stdafx.h\"\n#include \"disp.h\"\n\nPersistent<ObjectTemplate> DispObject::inst_template;\nPersistent<FunctionTemplate> DispObject::clazz_template;\nNodeMethods DispObject::clazz_methods;\n\nPersistent<ObjectTemplate> VariantObject::inst_template;\nPersistent<FunctionTemplate> VariantObject::clazz_template;\nNodeMethods VariantObject::clazz_methods;\n\nPersistent<ObjectTemplate> ConnectionPointObject::inst_template;\nPersistent<FunctionTemplate> ConnectionPointObject::clazz_template;\n\n//-------------------------------------------------------------------------------------------------------\n// DispObject implemetation\n\nDispObject::DispObject(const DispInfoPtr &ptr, const std::wstring &nm, DISPID id, LONG indx, int opt)\n    : disp(ptr), options((ptr->options & option_mask) | opt), name(nm), dispid(id), index(indx)\n{\t\n    if (dispid == DISPID_UNKNOWN) {\n        dispid = DISPID_VALUE;\n        options |= option_prepared;\n    }\n    else options |= option_owned;\n    NODE_DEBUG_FMT(\"DispObject '%S' constructor\", name.c_str());\n}\n\nDispObject::~DispObject() {\n    items.Reset();\n    methods.Reset();\n    vars.Reset();\n    NODE_DEBUG_FMT(\"DispObject '%S' destructor\", name.c_str());\n}\n\nHRESULT DispObject::prepare() {\n    CComVariant value;\n    HRESULT hrcode = disp ? disp->GetProperty(dispid, index, &value) : E_UNEXPECTED;\n\n    // Init dispatch interface\n    options |= option_prepared;\n    CComPtr<IDispatch> ptr;\n    if (VariantDispGet(&value, &ptr)) {\n        disp.reset(new DispInfo(ptr, name, options, &disp));\n        dispid = DISPID_VALUE;\n    }\n    else if ((value.vt & VT_ARRAY) != 0) {\n        \n    }\n    return hrcode;\n}\n\nbool DispObject::release() {\n    if (!disp) return false;\n    NODE_DEBUG_FMT(\"DispObject '%S' release\", name.c_str());\n    disp.reset();            \n    items.Reset();\n    methods.Reset();\n    vars.Reset();\n    return true;\n}\n\nbool DispObject::get(LPOLESTR tag, LONG index, const PropertyCallbackInfoGetter &args) {\n    Isolate *isolate = args.GetIsolate();\n    if (!is_prepared()) prepare();\n    if (!disp) {\n        isolate->ThrowException(DispErrorNull(isolate));\n        return false;\n    }\n\n    // Search dispid\n    HRESULT hrcode;\n    DISPID propid;\n    bool prop_by_key = false;\n    bool this_prop = false;\n    if (!tag || !*tag) {\n        tag = (LPOLESTR)name.c_str();\n        propid = dispid;\n        this_prop = true;\n    }\n    else {\n        hrcode = disp->FindProperty(tag, &propid);\n        if (SUCCEEDED(hrcode) && propid == DISPID_UNKNOWN) hrcode = E_INVALIDARG;\n        if FAILED(hrcode) {\n            prop_by_key = (options & option_property) != 0;\n            if (!prop_by_key) {\n                //isolate->ThrowException(DispError(isolate, hrcode, L\"DispPropertyFind\", tag));\n                args.GetReturnValue().SetUndefined();\n                return false;\n            }\n            propid = dispid;\n        }\n    }\n\n    // Check type info\n    int opt = 0;\n    bool is_property_simple = false;\n    if (prop_by_key) {\n        is_property_simple = true;\n        opt |= option_property;\n    }\n    else {\n        DispInfo::type_ptr disp_info;\n        if (disp->GetTypeInfo(propid, disp_info)) {\n            if (disp_info->is_function_simple()) opt |= option_function_simple;\n            else {\n                if (disp_info->is_property()) opt |= option_property;\n                is_property_simple = disp_info->is_property_simple();\n                \n                if (disp->bManaged && tag && *tag && wcscmp(tag, L\"ToString\")==0)\n                {\n                    // .NET ToString is reported as a property while we normally use it via .ToString() - i.e. as a method. \n                    is_property_simple = false;\n                }\n\n            }\n        }\n        else if ( disp->bManaged && tag && *tag && wcscmp(tag, L\"length\") == 0) {\n            DISPID lenprop;\n            if SUCCEEDED(disp->FindProperty((LPOLESTR)L\"length\", &lenprop)) {\n\n                // If we have 'IReflect' and '.length' - assume it is .NET JS Array or JS Object\n                is_property_simple = true;\n            }\n        }\n        else if (disp->bManaged && tag && *tag && index>=0 ) {\n            // jsarray[x]\n            is_property_simple = true;\n        }\n    }\n\n    // Return as property value\n    if (is_property_simple) {\n        CComException except;\n        CComVariant value;\n        VarArguments vargs;\n        if (prop_by_key) vargs.items.push_back(CComVariant(tag));\n        if (index >= 0) vargs.items.push_back(CComVariant(index));\n        LONG argcnt = (LONG)vargs.items.size();\n        VARIANT *pargs = (argcnt > 0) ? &vargs.items.front() : 0;\n        hrcode = disp->GetProperty(propid, argcnt, pargs, &value, &except);\n        if (FAILED(hrcode) && dispid != DISPID_VALUE){\n            isolate->ThrowException(DispError(isolate, hrcode, L\"DispPropertyGet\", tag, &except));\n            return false;\n        }\n        CComPtr<IDispatch> ptr;\n        if (VariantDispGet(&value, &ptr)) {\n            DispInfoPtr disp_result(new DispInfo(ptr, tag, options, &disp));\n            Local<Object> result = DispObject::NodeCreate(isolate, v8this(args), disp_result, tag, DISPID_UNKNOWN, -1, opt);\n            args.GetReturnValue().Set(result);\n        }\n        else {\n            args.GetReturnValue().Set(Variant2Value(isolate, value));\n        }\n    }\n\n    // Return as dispatch object \n    else {\n        Local<Object> result = DispObject::NodeCreate(isolate, v8this(args), disp, tag, propid, index, opt);\n        args.GetReturnValue().Set(result);\n    }\n    return true;\n}\n\nbool DispObject::set(LPOLESTR tag, LONG index, const Local<Value> &value, const PropertyCallbackInfoSetter &args) {\n    Isolate *isolate = args.GetIsolate();\n    if (!is_prepared()) prepare();\n    if (!disp) {\n        isolate->ThrowException(DispErrorNull(isolate));\n        return false;\n    }\n    \n    // Search dispid\n    HRESULT hrcode;\n    DISPID propid;\n    if (!tag || !*tag) {\n        tag = (LPOLESTR)name.c_str();\n        propid = dispid;\n    }\n    else {\n        hrcode = disp->FindProperty(tag, &propid);\n        if (SUCCEEDED(hrcode) && propid == DISPID_UNKNOWN) hrcode = E_INVALIDARG;\n        if FAILED(hrcode) {\n            isolate->ThrowException(DispError(isolate, hrcode, L\"DispPropertyFind\", tag));\n            return false;\n        }\n    }\n\n    // Set value using dispatch\n    CComException except;\n    CComVariant ret;\n    VarArguments vargs(isolate, value);\n    if (index >= 0) vargs.items.push_back(CComVariant(index));\n    LONG argcnt = (LONG)vargs.items.size();\n    VARIANT *pargs = (argcnt > 0) ? &vargs.items.front() : 0;\n    hrcode = disp->SetProperty(propid, argcnt, pargs, &ret, &except);\n    if FAILED(hrcode) {\n        isolate->ThrowException(DispError(isolate, hrcode, L\"DispPropertyPut\", tag, &except));\n        return false;\n    }\n\n    // Send result\n    // V8 deprecation: ReturnValue<void> setter is deprecated\n    /*\n    CComPtr<IDispatch> ptr;\n    if (VariantDispGet(&ret, &ptr)) {\n        std::wstring rtag;\n        rtag.reserve(32);\n        rtag += L\"@\";\n        rtag += tag;\n        DispInfoPtr disp_result(new DispInfo(ptr, tag, options, &disp));\n        Local<Object> result = DispObject::NodeCreate(isolate, args.This(), disp_result, rtag);\n        args.GetReturnValue().Set(result);\n    }\n    else {\n        args.GetReturnValue().Set(Variant2Value(isolate, ret));\n    }\n    */\n    return true;\n}\n\nvoid DispObject::call(Isolate *isolate, const FunctionCallbackInfo<Value> &args) {\n    if (!disp) {\n        isolate->ThrowException(DispErrorNull(isolate));\n        return;\n    }\n\n    CComException except;\n    CComVariant ret;\n    VarArguments vargs(isolate, args);\n    LONG argcnt = (LONG)vargs.items.size();\n    VARIANT* pargs = (argcnt > 0) ? &vargs.items.front() : 0;\n    HRESULT hrcode;\n\n    if (vargs.IsDefault()) {\n        hrcode = valueOf(isolate, ret, true);\n    }\n    else if ((options & option_property) == 0) {\n        hrcode = disp->ExecuteMethod(dispid, argcnt, pargs, &ret, &except);\n    }\n    else {\n        DispInfo::type_ptr disp_info;\n        disp->GetTypeInfo(dispid, disp_info);\n\n        if(disp_info->is_property_advanced() && argcnt > 1) {\n            hrcode = disp->SetProperty(dispid, argcnt, pargs, &ret, &except);\n        }\n        else {\n            hrcode = disp->GetProperty(dispid, argcnt, pargs, &ret, &except);\n        }\n    }\n    if FAILED(hrcode) {\n        isolate->ThrowException(DispError(isolate, hrcode, L\"DispInvoke\", name.c_str(), &except));\n        return;\n    }\n\n    // Prepare result\n    Local<Value> result;\n    CComPtr<IDispatch> ptr;\n    if (VariantDispGet(&ret, &ptr)) {\n        std::wstring tag;\n        tag.reserve(32);\n        tag += L\"@\";\n        tag += name;\n        DispInfoPtr disp_result(new DispInfo(ptr, tag, options, &disp));\n        result = DispObject::NodeCreate(isolate, args.This(), disp_result, tag);\n    }\n    else {\n        result = Variant2Value(isolate, ret, true);\n    }\n    args.GetReturnValue().Set(result);\n}\n\nHRESULT DispObject::valueOf(Isolate *isolate, VARIANT &value, bool simple) {\n    if (!is_prepared()) prepare();\n    HRESULT hrcode;\n    if (!disp) hrcode = E_UNEXPECTED;\n\n    // simple function without arguments\n    else if ((options & option_function_simple) != 0) {\n        hrcode = disp->ExecuteMethod(dispid, 0, 0, &value);\n    }\n\n    // property or array element\n    else if (dispid != DISPID_VALUE || index >= 0 || simple) {\n        hrcode = disp->GetProperty(dispid, index, &value);\n    }\n\n    // self dispatch object\n    else /*if (is_object())*/ {\n        value.vt = VT_DISPATCH;\n        value.pdispVal = (IDispatch*)disp->ptr;\n        if (value.pdispVal) value.pdispVal->AddRef();\n        hrcode = S_OK;\n    }\n    return hrcode;\n}\n\nHRESULT DispObject::valueOf(Isolate *isolate, const Local<Object> &self, Local<Value> &value) {\n    if (!is_prepared()) prepare();\n    HRESULT hrcode;\n    if (!disp) hrcode = E_UNEXPECTED;\n    else {\n        CComVariant val;\n\n        // simple function without arguments\n\n        if ((options & option_function_simple) != 0) {\n            hrcode = disp->ExecuteMethod(dispid, 0, 0, &val);\n        }\n\n        // self value, property or array element\n        else {\n            hrcode = disp->GetProperty(dispid, index, &val);\n            // Try to get some primitive value\n            if FAILED(hrcode) {\n                hrcode = disp->ExecuteMethod(dispid, 0, 0, &val);\n            }\n        }\n\n        // convert result to v8 value\n        if SUCCEEDED(hrcode) {\n            value = Variant2Value(isolate, val);\n        }\n\n        // or return self as object\n        else  {\n            value = self;\n            hrcode = S_OK;\n        }\n    }\n    return hrcode;\n}\n\nvoid DispObject::toString(const FunctionCallbackInfo<Value> &args) {\n    Isolate *isolate = args.GetIsolate();\n    CComVariant val;\n    HRESULT hrcode = valueOf(isolate, val, true);\n    if FAILED(hrcode) {\n        isolate->ThrowException(Win32Error(isolate, hrcode, L\"DispToString\"));\n        return;\n    }\n    args.GetReturnValue().Set(Variant2String(isolate, val));\n}\n\nLocal<Value> DispObject::getIdentity(Isolate *isolate) {\n    //wchar_t buf[64];\n    std::wstring id;\n    id.reserve(128);\n    id += name;\n    DispInfoPtr ptr = disp;\n    if (ptr && ptr->name == id)\n        ptr = ptr->parent.lock();\n    while (ptr) {\n        id.insert(0, L\".\");\n        id.insert(0, ptr->name);\n        /*\n        if (ptr->index >= 0) {\n            swprintf_s(buf, L\"[%ld]\", index);\n            id += buf;\n        }\n        */\n        ptr = ptr->parent.lock();\n    }\n    return v8str(isolate, id.c_str());\n}\n\nvoid DispObject::initTypeInfo(Isolate *isolate) {\n    if ((options & option_type) == 0 || !disp) {\n        return;\n    }\n    uint32_t index = 0;\n    Local<v8::Object> _items = v8::Array::New(isolate);\n    Local<v8::Object> _methods = v8::Object::New(isolate);\n    Local<v8::Object> _vars = v8::Object::New(isolate);\n    disp->Enumerate(1+2/*functions and variables*/, nullptr, [isolate, &_items, &_vars, &_methods, &index](ITypeInfo *info, FUNCDESC *func, VARDESC *var) {\n        Local<Context> ctx = isolate->GetCurrentContext();\n        CComBSTR name;\n        MEMBERID memid = func != nullptr ? func->memid : var->memid;\n        TypeInfoGetName(info, memid, &name);\n        Local<Object> item(Object::New(isolate));\n        Local<String> vname;\n        if (name) {\n            vname = v8str(isolate, (BSTR)name);\n            item->Set(ctx, v8str(isolate, \"name\"), vname);\n        }\n        item->Set(ctx, v8str(isolate, \"dispid\"), Int32::New(isolate, memid));\n        if (func != nullptr) {\n            item->Set(ctx, v8str(isolate, \"invkind\"), Int32::New(isolate, (int32_t)func->invkind));\n            item->Set(ctx, v8str(isolate, \"flags\"), Int32::New(isolate, (int32_t)func->wFuncFlags));\n            item->Set(ctx, v8str(isolate, \"argcnt\"), Int32::New(isolate, (int32_t)func->cParams));\n            _methods->Set(ctx, vname, item);\n        } else {\n            item->Set(ctx, v8str(isolate, \"varkind\"), Int32::New(isolate, (int32_t)var->varkind));\n            item->Set(ctx, v8str(isolate, \"flags\"), Int32::New(isolate, (int32_t)var->wVarFlags));\n            if (var->varkind == VAR_CONST && var->lpvarValue != nullptr) {\n                v8::Local<v8::Value> value = Variant2Value(isolate, *var->lpvarValue, false);\n                item->Set(ctx, v8str(isolate, \"value\"), value);\n            }\n            _vars->Set(ctx, vname, item);\n        }\n        _items->Set(ctx, index++, item);\n    });\n    items.Reset(isolate, _items);\n    methods.Reset(isolate, _methods);\n    vars.Reset(isolate, _vars);\n}\n\n//-----------------------------------------------------------------------------------\n// Static Node JS callbacks\n\nvoid DispObject::NodeInit(const Local<Object> &target, Isolate* isolate, Local<Context> &ctx) {\n\n    // Prepare constructor template\n    Local<FunctionTemplate> clazz = FunctionTemplate::New(isolate, NodeCreate);\n    clazz->SetClassName(v8str(isolate, \"Dispatch\"));\n\n    clazz_methods.add(isolate, clazz, \"toString\", NodeToString);\n    clazz_methods.add(isolate, clazz, \"valueOf\", NodeValueOf);\n\n    Local<ObjectTemplate> inst = clazz->InstanceTemplate();\n    inst->SetInternalFieldCount(1);\n\n#ifdef NODE_INTERCEPTED\n    inst->SetHandler(NamedPropertyHandlerConfiguration(InterceptedNodeGet, InterceptedNodeSet));\n    inst->SetHandler(IndexedPropertyHandlerConfiguration(InterceptedNodeGetByIndex, InterceptedNodeSetByIndex));\n#else\n    inst->SetHandler(NamedPropertyHandlerConfiguration(NodeGet, NodeSet));\n    inst->SetHandler(IndexedPropertyHandlerConfiguration(NodeGetByIndex, NodeSetByIndex));\n#endif\n\n    inst->SetCallAsFunctionHandler(NodeCall);\n    inst->SetNativeDataProperty(v8str(isolate, \"__id\"), NodeGet);\n    inst->SetNativeDataProperty(v8str(isolate, \"__value\"), NodeGet);\n    //inst->SetLazyDataProperty(v8str(isolate, \"__type\"), NodeGet, Local<Value>(), ReadOnly);\n    //inst->SetLazyDataProperty(v8str(isolate, \"__methods\"), NodeGet, Local<Value>(), ReadOnly);\n    //inst->SetLazyDataProperty(v8str(isolate, \"__vars\"), NodeGet, Local<Value>(), ReadOnly);\n    inst->SetNativeDataProperty(v8str(isolate, \"__type\"), NodeGet);\n    inst->SetNativeDataProperty(v8str(isolate, \"__methods\"), NodeGet);\n    inst->SetNativeDataProperty(v8str(isolate, \"__vars\"), NodeGet);\n\n    inst_template.Reset(isolate, inst);\n    clazz_template.Reset(isolate, clazz);\n    target->Set(ctx, v8str(isolate, \"Object\"), clazz->GetFunction(ctx).ToLocalChecked());\n    target->Set(ctx, v8str(isolate, \"cast\"), FunctionTemplate::New(isolate, NodeCast)->GetFunction(ctx).ToLocalChecked());\n    target->Set(ctx, v8str(isolate, \"release\"), FunctionTemplate::New(isolate, NodeRelease)->GetFunction(ctx).ToLocalChecked());\n\n    target->Set(ctx, v8str(isolate, \"getConnectionPoints\"), FunctionTemplate::New(isolate, NodeConnectionPoints)->GetFunction(ctx).ToLocalChecked());\n    target->Set(ctx, v8str(isolate, \"peekAndDispatchMessages\"), FunctionTemplate::New(isolate, PeakAndDispatchMessages)->GetFunction(ctx).ToLocalChecked());\n\n    //Context::GetCurrent()->Global()->Set(v8str(isolate, \"ActiveXObject\"), t->GetFunction());\n    NODE_DEBUG_MSG(\"DispObject initialized\");\n}\n\nLocal<Object> DispObject::NodeCreate(Isolate *isolate, const Local<Object> &parent, const DispInfoPtr &ptr, const std::wstring &name, DISPID id, LONG index, int opt) {\n    Local<Object> self;\n    if (!inst_template.IsEmpty()) {\n        if (inst_template.Get(isolate)->NewInstance(isolate->GetCurrentContext()).ToLocal(&self)) {\n            (new DispObject(ptr, name, id, index, opt))->Wrap(self);\n            //Local<String> prop_id(v8str(isolate, \"_identity\"));\n            //self->Set(prop_id, v8str(isolate, name.c_str()));\n        }\n    }\n    return self;\n}\n\nvoid DispObject::NodeCreate(const FunctionCallbackInfo<Value> &args) {\n    Isolate *isolate = args.GetIsolate();\n    Local<Context> context = isolate->GetCurrentContext();\n\n    // Invoked as plain function\n    if (!args.IsConstructCall()) {\n        Local<FunctionTemplate> clazz = clazz_template.Get(isolate);\n        if (clazz.IsEmpty()) {\n            isolate->ThrowException(TypeError(isolate, \"FunctionTemplateIsEmpty\"));\n            return;\n        }\n\n        int argc = args.Length();\n        std::vector<Local<Value>> argv(argc);\n        for (int i = 0; i < argc; ++i) {\n            argv[i] = args[i];\n        }\n\n        Local<Function> cons;\n        if (clazz->GetFunction(context).ToLocal(&cons)) {\n            Local<Object> self;\n            if (cons->NewInstance(context, argc, argv.data()).ToLocal(&self)) {\n                args.GetReturnValue().Set(self);\n            }\n        }\n        return;\n    }\n\n    // Check argument count\n    int argcnt = args.Length();\n    if (argcnt < 1) {\n        isolate->ThrowException(InvalidArgumentsError(isolate));\n        return;\n    }\n\n    // Process optional arguments\n    bool isGetObject = false;\n    bool isGetAccessibleObject = false;\n    int options = (option_async | option_type);\n    if (argcnt > 1) {\n        Local<Value> val, argopt = args[1];\n        bool isEmpty = argopt.IsEmpty();\n        bool isObject = argopt->IsObject();\n        if (!isEmpty && isObject) {\n            auto opt = Local<Object>::Cast(argopt);\n            if (opt->Get(context, v8str(isolate, \"async\")).ToLocal(&val)) {\n                if (!v8val2bool(isolate, val, true)) options &= ~option_async;\n            }\n            if (opt->Get(context, v8str(isolate, \"type\")).ToLocal(&val)) {\n                if (!v8val2bool(isolate, val, true)) options &= ~option_type;\n            }\n            if (opt->Get(context, v8str(isolate, \"activate\")).ToLocal(&val)) {\n                if (v8val2bool(isolate, val, false)) options |= option_activate;\n            }\n            if (opt->Get(context, v8str(isolate, \"getobject\")).ToLocal(&val)) {\n                if (v8val2bool(isolate, val, false)) isGetObject = true;\n            }\n            if (opt->Get(context, v8str(isolate, \"getaccessibleobject\")).ToLocal(&val)) {\n                if (v8val2bool(isolate, val, false)) isGetAccessibleObject = true;\n            }\n        }\n    }\n\n    // Create dispatch object from ProgId\n    HRESULT hrcode;\n    std::wstring name;\n    CComPtr<IDispatch> disp;\n    if (args[0]->IsString()) {\n\n        // Prepare arguments\n        String::Value vname(isolate, args[0]);\n        if (vname.length() <= 0) hrcode = E_INVALIDARG;\n        else {\n            name.assign((LPOLESTR)*vname, vname.length());\n\n            CComPtr<IUnknown> unk;\n            if (isGetObject)\n            {\n                hrcode = CoGetObject(name.c_str(), NULL, IID_IUnknown, (void**)&unk);\n                if SUCCEEDED(hrcode) hrcode = unk->QueryInterface(&disp);\n            } else {\n                if (isGetAccessibleObject)\n                {\n                    hrcode = GetAccessibleObject(name.c_str(), unk);\n                    if SUCCEEDED(hrcode) hrcode = unk->QueryInterface(&disp);\n                } else {\n                    CLSID clsid;\n                    hrcode = CLSIDFromProgID(name.c_str(), &clsid);\n                    if SUCCEEDED(hrcode) {\n                        if ((options & option_activate) == 0) hrcode = E_FAIL;\n                        else {\n                            hrcode = GetActiveObject(clsid, NULL, &unk);\n                            if SUCCEEDED(hrcode) hrcode = unk->QueryInterface(&disp);\n                        }\n                        if FAILED(hrcode) {\n                            hrcode = disp.CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    // Use supplied dispatch pointer\n    else if (args[0]->IsUint8Array()) {\n        size_t len = node::Buffer::Length(args[0]);\n        void *data = node::Buffer::Data(args[0]);\n        IDispatch *p = (len == sizeof(INT_PTR)) ? (IDispatch *) *(static_cast<INT_PTR*>(data)) : nullptr;\n        if (!p) {\n            isolate->ThrowException(InvalidArgumentsError(isolate));\n            return;\n        }\n        disp.Attach(p);\n        hrcode = S_OK;\n    }\n\n    // Create dispatch object from javascript object\n    else if (args[0]->IsObject()) {\n        name = L\"#\";\n        disp = new DispObjectImpl(Local<Object>::Cast(args[0]));\n        hrcode = S_OK;\n    }\n\n    // Other\n    else {\n        hrcode = E_INVALIDARG;\n    }\n\n    // Prepare result\n    if FAILED(hrcode) {\n        isolate->ThrowException(DispError(isolate, hrcode, L\"CreateInstance\", name.c_str()));\n    }\n    else {\n        Local<Object> self = args.This();\n        DispInfoPtr ptr(new DispInfo(disp, name, options));\n        (new DispObject(ptr, name))->Wrap(self);\n        args.GetReturnValue().Set(self);\n    }\n}\n\nvoid DispObject::NodeGet(Local<Name> name, const PropertyCallbackInfoGetter& args) {\n    Isolate *isolate = args.GetIsolate();\n    DispObject *self = DispObject::Unwrap<DispObject>(v8this(args));\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    String::Value vname(isolate, name);\n    LPOLESTR id = (vname.length() > 0) ? (LPOLESTR)*vname : (LPOLESTR)L\"valueOf\";\n    NODE_DEBUG_FMT2(\"DispObject '%S.%S' get\", self->name.c_str(), id);\n    if (_wcsicmp(id, L\"__value\") == 0) {\n        Local<Value> result;\n        HRESULT hrcode = self->valueOf(isolate, v8this(args), result);\n        if FAILED(hrcode) isolate->ThrowException(Win32Error(isolate, hrcode, L\"DispValueOf\"));\n        else args.GetReturnValue().Set(result);\n    }\n    else if (_wcsicmp(id, L\"__id\") == 0) {\n        args.GetReturnValue().Set(self->getIdentity(isolate));\n    }\n    else if (_wcsicmp(id, L\"__type\") == 0) {\n        if (self->items.IsEmpty()) {\n            self->initTypeInfo(isolate);\n        }\n        Local<Value> result = self->items.Get(isolate);\n        args.GetReturnValue().Set(result);\n    }\n    else if (_wcsicmp(id, L\"__methods\") == 0) {\n        if (self->methods.IsEmpty()) {\n            self->initTypeInfo(isolate);\n        }\n        Local<Value> result = self->methods.Get(isolate);\n        args.GetReturnValue().Set(result);\n    }\n    else if (_wcsicmp(id, L\"__vars\") == 0) {\n        if (self->vars.IsEmpty()) {\n            self->initTypeInfo(isolate);\n        }\n        Local<Value> result = self->vars.Get(isolate);\n        args.GetReturnValue().Set(result);\n    }\n    else if (_wcsicmp(id, L\"__proto__\") == 0) {\n        Local<Function> func;\n        Local<FunctionTemplate> clazz = clazz_template.Get(isolate);\n        Local<Context> ctx = isolate->GetCurrentContext();\n        if (!clazz.IsEmpty() && clazz->GetFunction(ctx).ToLocal(&func)) {\n            args.GetReturnValue().Set(func);\n        }\n        else {\n            args.GetReturnValue().SetNull();\n        }\n    }\n    else {\n        Local<Function> func;\n        if (clazz_methods.get(isolate, id, &func)) {\n            args.GetReturnValue().Set(func);\n        }\n\n        else if (!self->get(id, -1, args)) {\n            Local<Value> result;\n            HRESULT hrcode = self->valueOf(isolate, v8this(args), result);\n            if FAILED(hrcode) isolate->ThrowException(Win32Error(isolate, hrcode, L\"Unable to Get Value\"));\n            \n            Local<Context> ctx = isolate->GetCurrentContext();\n            MaybeLocal<Object> localObj = result->ToObject(ctx);\n            if (localObj.IsEmpty()) {\n                args.GetReturnValue().SetUndefined();\n                return;\n            }\n\n            Local<Object> obj = localObj.ToLocalChecked();\n            MaybeLocal<Value> realProp = obj->GetRealNamedPropertyInPrototypeChain(ctx, v8str(isolate, id));\n            if (realProp.IsEmpty()) {\n                // We may call non-existing property for an object to check its existence\n                // So we should return undefined in this case\n                args.GetReturnValue().SetUndefined();\n            }\n            else {\n                Local<Value> ownProp = realProp.ToLocalChecked();\n                if (ownProp->IsFunction()) {\n                    Local<Function> func = Local<Function>::Cast(ownProp);\n                    if (func.IsEmpty()) return;\n                    args.GetReturnValue().Set(func);\n                    return;\n                }\n            }\n        }\n    }\n}\n\nvoid DispObject::NodeGetByIndex(uint32_t index, const PropertyCallbackInfoGetter& args) {\n    Isolate *isolate = args.GetIsolate();\n    DispObject *self = DispObject::Unwrap<DispObject>(v8this(args));\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    NODE_DEBUG_FMT2(\"DispObject '%S[%u]' get\", self->name.c_str(), index);\n    self->get(0, index, args);\n}\n\nvoid DispObject::NodeSet(Local<Name> name, Local<Value> value, const PropertyCallbackInfoSetter& args) {\n    Isolate *isolate = args.GetIsolate();\n    DispObject *self = DispObject::Unwrap<DispObject>(v8this(args));\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    String::Value vname(isolate, name);\n    LPOLESTR id = (vname.length() > 0) ? (LPOLESTR)*vname : (LPOLESTR)L\"\";\n    NODE_DEBUG_FMT2(\"DispObject '%S.%S' set\", self->name.c_str(), id);\n    self->set(id, -1, value, args);\n}\n\nvoid DispObject::NodeSetByIndex(uint32_t index, Local<Value> value, const PropertyCallbackInfoSetter& args) {\n    Isolate *isolate = args.GetIsolate();\n    DispObject *self = DispObject::Unwrap<DispObject>(v8this(args));\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    NODE_DEBUG_FMT2(\"DispObject '%S[%u]' set\", self->name.c_str(), index);\n    self->set(0, index, value, args);\n}\n\nvoid DispObject::NodeCall(const FunctionCallbackInfo<Value> &args) {\n    Isolate *isolate = args.GetIsolate();\n    DispObject *self = DispObject::Unwrap<DispObject>(args.This());\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    NODE_DEBUG_FMT(\"DispObject '%S' call\", self->name.c_str());\n    self->call(isolate, args);\n}\n\nvoid DispObject::NodeValueOf(const FunctionCallbackInfo<Value>& args) {\n    Isolate *isolate = args.GetIsolate();\n    DispObject *self = DispObject::Unwrap<DispObject>(args.This());\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    Local<Value> result;\n    HRESULT hrcode = self->valueOf(isolate, args.This(), result);\n    if FAILED(hrcode) {\n        isolate->ThrowException(Win32Error(isolate, hrcode, L\"DispValueOf\"));\n        return;\n    }\n    args.GetReturnValue().Set(result);\n}\n\nvoid DispObject::NodeToString(const FunctionCallbackInfo<Value>& args) {\n    Isolate *isolate = args.GetIsolate();\n    DispObject *self = DispObject::Unwrap<DispObject>(args.This());\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    self->toString(args);\n}\n\nvoid DispObject::NodeRelease(const FunctionCallbackInfo<Value>& args) {\n    Isolate *isolate = args.GetIsolate();\n    int rcnt = 0, argcnt = args.Length();\n    for (int argi = 0; argi < argcnt; argi++) {\n        auto obj = args[argi];\n        if (obj->IsObject()) {\n            auto disp_obj = Local<Object>::Cast(obj);\n            DispObject *disp = DispObject::Unwrap<DispObject>(disp_obj);\n            if (disp && disp->release())\n                rcnt ++;\n        }\n    }\n    args.GetReturnValue().Set(rcnt);\n}\n\nvoid DispObject::NodeCast(const FunctionCallbackInfo<Value>& args) {\n    Local<Object> inst = VariantObject::NodeCreateInstance(args);\n    args.GetReturnValue().Set(inst);\n}\n\nvoid DispObject::NodeConnectionPoints(const FunctionCallbackInfo<Value>& args) {\n    Isolate *isolate = args.GetIsolate();\n    Local<Context> ctx = isolate->GetCurrentContext();\n    Local<Array> items = Array::New(isolate);\n    CComPtr<IDispatch> ptr;\n    CComPtr<IConnectionPointContainer> cp_cont;\n    CComPtr<IEnumConnectionPoints> cp_enum;\n    \n    // prepare connecton points from arguments\n    int argcnt = args.Length();\n    if (argcnt >= 1) {\n        auto arg = args[0];\n        if (Value2Unknown(isolate, arg, (IUnknown**)&ptr)) {\n            if SUCCEEDED(ptr->QueryInterface(&cp_cont)) {\n                cp_cont->EnumConnectionPoints(&cp_enum);\n            }\n        }\n    }\n\n    // enumerate connection points\n    if (cp_enum) {\n        ULONG cnt_fetched;\n        CComPtr<IConnectionPoint> cp_ptr;\n        uint32_t cnt = 0;\n        while (SUCCEEDED(cp_enum->Next(1, &cp_ptr, &cnt_fetched)) && cnt_fetched == 1) {\n            items->Set(ctx, cnt++, ConnectionPointObject::NodeCreateInstance(isolate, cp_ptr, ptr));\n            cp_ptr.Release();\n        }\n    }\n\n    // return array of connection points\n    args.GetReturnValue().Set(items);\n}\nvoid DispObject::PeakAndDispatchMessages(const FunctionCallbackInfo<Value>& args) {\n    MSG msg;\n    while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {\n        DispatchMessage(&msg);\n    }\n}\n\n//-------------------------------------------------------------------------------------------------------\n\nclass vtypes_t {\npublic:\n    inline vtypes_t(std::initializer_list<std::pair<std::wstring, VARTYPE>> recs) {\n        for (auto &rec : recs) {\n            str2vt.emplace(rec.first, rec.second);\n            vt2str.emplace(rec.second, rec.first);\n        }\n    }\n    inline bool find(VARTYPE vt, std::wstring &name) {\n        auto it = vt2str.find(vt);\n        if (it == vt2str.end()) return false;\n        name = it->second;\n        return true;\n    }\n    inline VARTYPE find(const std::wstring &name) {\n        auto it = str2vt.find(name);\n        if (it == str2vt.end()) return VT_EMPTY;\n        return it->second;\n    }\nprivate:\n    std::map<std::wstring, VARTYPE> str2vt;\n    std::map<VARTYPE, std::wstring> vt2str;\n};\n\nstatic vtypes_t vtypes({\n    { L\"char\", VT_I1 },\n    { L\"uchar\", VT_UI1 },\n    { L\"byte\", VT_UI1 },\n    { L\"short\", VT_I2 },\n    { L\"ushort\", VT_UI2 },\n    { L\"int\", VT_INT },\n    { L\"uint\", VT_UINT },\n    { L\"long\", VT_I8 },\n    { L\"ulong\", VT_UI8 },\n\n    { L\"int8\", VT_I1 },\n    { L\"uint8\", VT_UI1 },\n    { L\"int16\", VT_I2 },\n    { L\"uint16\", VT_UI2 },\n    { L\"int32\", VT_I4 },\n    { L\"uint32\", VT_UI4 },\n    { L\"int64\", VT_I8 },\n    { L\"uint64\", VT_UI8 },\n    { L\"currency\", VT_CY },\n\n    { L\"float\", VT_R4 },\n    { L\"double\", VT_R8 },\n    { L\"date\", VT_DATE },\n    { L\"decimal\", VT_DECIMAL },\n\n    { L\"string\", VT_BSTR },\n    { L\"empty\", VT_EMPTY },\n    { L\"variant\", VT_VARIANT },\n    { L\"null\", VT_NULL },\n    { L\"byref\", VT_BYREF }\n});\n\nbool VariantObject::assign(Isolate *isolate, Local<Value> &val, Local<Value> &type) {\n    VARTYPE vt = VT_EMPTY;\n    if (!type.IsEmpty()) {\n        if (type->IsString()) {\n            String::Value vtstr(isolate, type);\n            const wchar_t *pvtstr = (const wchar_t *)*vtstr;\n            int vtstr_len = vtstr.length();\n            if (vtstr_len > 1) {\n                if (pvtstr[0] == 'p') {\n                    vt |= VT_BYREF;\n                    vtstr_len--;\n                    pvtstr++;\n                }\n                else if (pvtstr[vtstr_len - 1] == '*') {\n                    vt |= VT_BYREF;\n                    vtstr_len--;\n                }\n                else if (pvtstr[vtstr_len - 2] == '[' || pvtstr[vtstr_len - 1] == ']') {\n                    vt |= VT_ARRAY;\n                    vtstr_len -= 2;\n                }\n            }\n            if (vtstr_len > 0) {\n                std::wstring type(pvtstr, vtstr_len);\n                vt |= vtypes.find(type);\n            }\n        }\n        else if (type->IsInt32()) {\n            vt |= type->Int32Value(isolate->GetCurrentContext()).FromMaybe(0);\n        }\n    }\n\n    if (val.IsEmpty()) {\n        if FAILED(value.ChangeType(vt)) return false;\n        if ((value.vt & VT_BYREF) == 0) pvalue.Clear();\n        return true;\n    }\n\n    value.Clear();\n    pvalue.Clear();\n    if ((vt & VT_ARRAY) != 0) {\n        Value2SafeArray(isolate, val, value, vt & ~VT_ARRAY);\n    }\n    else if ((vt & VT_BYREF) == 0) {\n        Value2Variant(isolate, val, value, vt);\n    }\n    else {\n        VARIANT *refvalue = nullptr;\n        VARTYPE vt_noref = vt & ~VT_BYREF;\n        VariantObject *ref = (!val.IsEmpty() && val->IsObject()) ? GetInstanceOf(isolate, Local<Object>::Cast(val)) : nullptr;\n        if (ref) {\n            if ((ref->value.vt & VT_BYREF) != 0) value = ref->value;\n            else refvalue = &ref->value;\n        }\n        else {\n            Value2Variant(isolate, val, pvalue, vt_noref);\n            refvalue = &pvalue;\n        }\n        if (refvalue) {\n            if (vt_noref == 0 || vt_noref == VT_VARIANT || refvalue->vt == VT_EMPTY) {\n                value.vt = VT_VARIANT | VT_BYREF;\n                value.pvarVal = refvalue;\n            }\n            else {\n                value.vt = refvalue->vt | VT_BYREF;\n                value.byref = &refvalue->intVal;\n            }\n        }\n    }\n    return true;\n}\n\nVariantObject::VariantObject(const FunctionCallbackInfo<Value> &args) {\n    Local<Value> val, type;\n    int argcnt = args.Length();\n    if (argcnt > 0) val = args[0];\n    if (argcnt > 1) type = args[1];\n    assign(args.GetIsolate(), val, type);\n}\n\nvoid VariantObject::NodeInit(const Local<Object> &target, Isolate* isolate, Local<Context> &ctx) {\n\n    // Prepare constructor template\n    Local<FunctionTemplate> clazz = FunctionTemplate::New(isolate, NodeCreate);\n    clazz->SetClassName(v8str(isolate, \"Variant\"));\n    clazz_methods.add(isolate, clazz, \"clear\", NodeClear);\n    clazz_methods.add(isolate, clazz, \"assign\", NodeAssign);\n    clazz_methods.add(isolate, clazz, \"cast\", NodeCast);\n    clazz_methods.add(isolate, clazz, \"toString\", NodeToString);\n    clazz_methods.add(isolate, clazz, \"valueOf\", NodeValueOf);\n\n    Local<ObjectTemplate> inst = clazz->InstanceTemplate();\n    inst->SetInternalFieldCount(1);\n\n#ifdef NODE_INTERCEPTED\n    inst->SetHandler(NamedPropertyHandlerConfiguration(InterceptedNodeGet, InterceptedNodeSet));\n    inst->SetHandler(IndexedPropertyHandlerConfiguration(InterceptedNodeGetByIndex, InterceptedNodeSetByIndex));\n#else\n    inst->SetHandler(NamedPropertyHandlerConfiguration(NodeGet, NodeSet));\n    inst->SetHandler(IndexedPropertyHandlerConfiguration(NodeGetByIndex, NodeSetByIndex));\n#endif\n    //inst->SetCallAsFunctionHandler(NodeCall);\n    //inst->SetNativeDataProperty(v8str(isolate, \"__id\"), NodeGet);\n\n    inst->SetNativeDataProperty(v8str(isolate, \"__value\"), NodeGet);\n    //inst->SetLazyDataProperty(v8str(isolate, \"__type\"), NodeGet, Local<Value>(), ReadOnly);\n    inst->SetNativeDataProperty(v8str(isolate, \"__type\"), NodeGet);\n\n    inst_template.Reset(isolate, inst);\n    clazz_template.Reset(isolate, clazz);\n    Local<Function> func;\n    if (clazz->GetFunction(ctx).ToLocal(&func)) {\n        target->Set(ctx, v8str(isolate, \"Variant\"), func);\n    }\n    NODE_DEBUG_MSG(\"VariantObject initialized\");\n}\n\nLocal<Object> VariantObject::NodeCreateInstance(const FunctionCallbackInfo<Value> &args) {\n    Local<Object> self;\n    Isolate *isolate = args.GetIsolate();\n    if (!inst_template.IsEmpty()) {\n        if (inst_template.Get(isolate)->NewInstance(isolate->GetCurrentContext()).ToLocal(&self)) {\n            (new VariantObject(args))->Wrap(self);\n        }\n    }\n    return self;\n}\n\nvoid VariantObject::NodeCreate(const FunctionCallbackInfo<Value> &args) {\n    Isolate *isolate = args.GetIsolate();\n    Local<Object> self = args.This();\n    (new VariantObject(args))->Wrap(self);\n    args.GetReturnValue().Set(self);\n}\n\nvoid VariantObject::NodeClear(const FunctionCallbackInfo<Value>& args) {\n    Isolate *isolate = args.GetIsolate();\n    VariantObject *self = VariantObject::Unwrap<VariantObject>(args.This());\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    self->value.Clear();\n    self->pvalue.Clear();\n}\n\nvoid VariantObject::NodeAssign(const FunctionCallbackInfo<Value>& args) {\n    Isolate *isolate = args.GetIsolate();\n    VariantObject *self = VariantObject::Unwrap<VariantObject>(args.This());\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    Local<Value> val, type;\n    int argcnt = args.Length();\n    if (argcnt > 0) val = args[0];\n    if (argcnt > 1) type = args[1];\n    self->assign(isolate, val, type);\n}\n\nvoid VariantObject::NodeCast(const FunctionCallbackInfo<Value>& args) {\n    Isolate *isolate = args.GetIsolate();\n    VariantObject *self = VariantObject::Unwrap<VariantObject>(args.This());\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    Local<Value> val, type;\n    int argcnt = args.Length();\n    if (argcnt > 0) type = args[0];\n    self->assign(isolate, val, type);\n}\n\nvoid VariantObject::NodeValueOf(const FunctionCallbackInfo<Value>& args) {\n    Isolate *isolate = args.GetIsolate();\n    VariantObject *self = VariantObject::Unwrap<VariantObject>(args.This());\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    // Last parameter false because valueOf should return primitive value\n    Local<Value> result = Variant2Value(isolate, self->value, false);\n    args.GetReturnValue().Set(result);\n}\n\nvoid VariantObject::NodeToString(const FunctionCallbackInfo<Value>& args) {\n    Isolate *isolate = args.GetIsolate();\n    VariantObject *self = VariantObject::Unwrap<VariantObject>(args.This());\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    Local<Value> result = Variant2String(isolate, self->value);\n    args.GetReturnValue().Set(result);\n}\n\nvoid VariantObject::NodeGet(Local<Name> name, const PropertyCallbackInfoGetter& args) {\n    Isolate *isolate = args.GetIsolate();\n    VariantObject *self = VariantObject::Unwrap<VariantObject>(v8this(args));\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n\n    String::Value vname(isolate, name);\n    LPOLESTR id = (vname.length() > 0) ? (LPOLESTR)*vname : (LPOLESTR)L\"valueOf\";\n    if (_wcsicmp(id, L\"__value\") == 0) {\n        Local<Value> result = Variant2Value(isolate, self->value);\n        args.GetReturnValue().Set(result);\n    }\n    else if (_wcsicmp(id, L\"__type\") == 0) {\n        std::wstring type, name;\n        if (self->value.vt & VT_BYREF) type += L\"byref:\";\n        if (self->value.vt & VT_ARRAY) type = L\"array:\";\n        if (vtypes.find(self->value.vt & VT_TYPEMASK, name)) {\n            type += name;\n        }\n        else if (self->value.vt & VT_UNKNOWN) {\n            type += L\"IUnknown\";\n        }\n        else {\n            type += std::to_wstring(self->value.vt & VT_TYPEMASK);\n        }\n        Local<String> text = v8str(isolate, type.c_str());\n        // Debugger was crashing on IUnknown due to missing return value here\n        args.GetReturnValue().Set(text);\n    }\n    else if (_wcsicmp(id, L\"__proto__\") == 0) {\n        Local<Function> func;\n        Local<FunctionTemplate> clazz = clazz_template.Get(isolate);\n        Local<Context> ctx = isolate->GetCurrentContext();\n        if (!clazz.IsEmpty() && clazz_template.Get(isolate)->GetFunction(ctx).ToLocal(&func)) {\n            args.GetReturnValue().Set(func);\n        }\n        else {\n            args.GetReturnValue().SetNull();\n        }\n    }\n    else if (_wcsicmp(id, L\"length\") == 0) {\n        if ((self->value.vt & VT_ARRAY) != 0) {\n            args.GetReturnValue().Set((uint32_t)self->value.ArrayLength());\n        }\n        else {\n            args.GetReturnValue().SetUndefined();\n        }\n    }\n    else {\n        Local<Function> func;\n        if (clazz_methods.get(isolate, id, &func)) {\n            args.GetReturnValue().Set(func);\n        }\n        else {\n            args.GetReturnValue().SetUndefined();\n        }\n    }\n}\n\nvoid VariantObject::NodeGetByIndex(uint32_t index, const PropertyCallbackInfoGetter& args) {\n    Isolate *isolate = args.GetIsolate();\n    VariantObject *self = VariantObject::Unwrap<VariantObject>(v8this(args));\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    Local<Value> result;\n    if ((self->value.vt & VT_ARRAY) == 0) {\n         result = Variant2Value(isolate, self->value);\n    }\n    else {\n        CComVariant value;\n        if SUCCEEDED(self->value.ArrayGet((LONG)index, value)) {\n            result = Variant2Value(isolate, value);\n        }\n    }\n    args.GetReturnValue().Set(result);\n}\n\nvoid VariantObject::NodeSet(Local<Name> name, Local<Value> val, const PropertyCallbackInfoSetter& args) {\n    Isolate *isolate = args.GetIsolate();\n    VariantObject *self = VariantObject::Unwrap<VariantObject>(v8this(args));\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    isolate->ThrowException(DispError(isolate, E_NOTIMPL));\n}\n\nvoid VariantObject::NodeSetByIndex(uint32_t index, Local<Value> value, const PropertyCallbackInfoSetter& args) {\n    Isolate *isolate = args.GetIsolate();\n    VariantObject *self = VariantObject::Unwrap<VariantObject>(v8this(args));\n    if (!self) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    isolate->ThrowException(DispError(isolate, E_NOTIMPL));\n}\n\nLocal<Object> VariantObject::NodeCreate(Isolate* isolate, const VARIANT& var) {\n    Local<Object> self;\n    if (!inst_template.IsEmpty()) {\n        if (inst_template.Get(isolate)->NewInstance(isolate->GetCurrentContext()).ToLocal(&self)) {\n            (new VariantObject(var))->Wrap(self);\n        }\n    }\n    return self;\n}\n\n//-------------------------------------------------------------------------------------------------------\n\nConnectionPointObject::ConnectionPointObject(IConnectionPoint *p, IDispatch *d)\n  : ptr(p), disp(d) {\n    InitIndex();\n}\n\nbool ConnectionPointObject::InitIndex() {\n  if (!ptr || !disp) {\n    return false;\n  }\n  UINT typeindex = 0;\n  CComPtr<ITypeInfo> typeinfo;\n  if FAILED(disp->GetTypeInfo(typeindex, LOCALE_USER_DEFAULT, &typeinfo)) {\n    return false;\n  }\n\n  CComPtr<ITypeLib> typelib;\n  if FAILED(typeinfo->GetContainingTypeLib(&typelib, &typeindex)) {\n    return false;\n  }\n\n  IID conniid;\n  if FAILED(ptr->GetConnectionInterface(&conniid)) {\n    return false;\n  }\n\n  CComPtr<ITypeInfo> conninfo;\n  if FAILED(typelib->GetTypeInfoOfGuid(conniid, &conninfo)) {\n    return false;\n  }\n\n  TYPEATTR *typeattr = nullptr;\n  if FAILED(conninfo->GetTypeAttr(&typeattr)) {\n    return false;\n  }\n\n  if (typeattr->typekind != TKIND_DISPATCH) {\n    conninfo->ReleaseTypeAttr(typeattr);\n    return false;\n  }\n\n  for (UINT fd = 0; fd < typeattr->cFuncs; ++fd) {\n    FUNCDESC *funcdesc;\n    if FAILED(conninfo->GetFuncDesc(fd, &funcdesc)) {\n      continue;\n    }\n    if (!funcdesc) {\n      break;\n    }\n\n    if (funcdesc->invkind != INVOKE_FUNC || funcdesc->funckind != FUNC_DISPATCH) {\n      conninfo->ReleaseFuncDesc(funcdesc);\n      continue;\n    }\n\n    const size_t nameSize = 2;\n    BSTR bstrNames[nameSize];\n    UINT maxNames = 1; // only event function name required\n    UINT maxNamesOut = 0;\n    if SUCCEEDED(conninfo->GetNames(funcdesc->memid, reinterpret_cast<BSTR *>(&bstrNames), maxNames, &maxNamesOut)) {\n      DISPID id = funcdesc->memid;\n      std::wstring funcname(bstrNames[0]);\n      index.insert(std::pair<DISPID, DispObjectImpl::name_ptr>(id, new DispObjectImpl::name_t(id, funcname)));\n\n      for (size_t i = 0; i < maxNamesOut; i++) {\n        SysFreeString(bstrNames[i]);\n      }\n    }\n\n    conninfo->ReleaseFuncDesc(funcdesc);\n  }\n\n  conninfo->ReleaseTypeAttr(typeattr);\n\n  return true;\n}\n\nLocal<Object> ConnectionPointObject::NodeCreateInstance(Isolate *isolate, IConnectionPoint *p, IDispatch* d) {\n    Local<Object> self;\n    if (!inst_template.IsEmpty()) {\n        if (inst_template.Get(isolate)->NewInstance(isolate->GetCurrentContext()).ToLocal(&self)) {\n            (new ConnectionPointObject(p, d))->Wrap(self);\n        }\n    }\n    return self;\n}\n\nvoid ConnectionPointObject::NodeInit(const Local<Object> &target, Isolate* isolate, Local<Context> &ctx) {\n\n    // Prepare constructor template\n    Local<FunctionTemplate> clazz = FunctionTemplate::New(isolate, NodeCreate);\n    clazz->SetClassName(v8str(isolate, \"ConnectionPoint\"));\n\n    NODE_SET_PROTOTYPE_METHOD(clazz, \"advise\", NodeAdvise);\n    NODE_SET_PROTOTYPE_METHOD(clazz, \"unadvise\", NodeUnadvise);\n    NODE_SET_PROTOTYPE_METHOD(clazz, \"getMethods\", NodeConnectionPointMethods);\n\n    Local<ObjectTemplate> inst = clazz->InstanceTemplate();\n    inst->SetInternalFieldCount(1);\n\n    inst_template.Reset(isolate, inst);\n    clazz_template.Reset(isolate, clazz);\n    //target->Set(v8str(isolate, \"ConnectionPoint\"), clazz->GetFunction());\n    NODE_DEBUG_MSG(\"ConnectionPointObject initialized\");\n}\n\nvoid ConnectionPointObject::NodeCreate(const FunctionCallbackInfo<Value> &args) {\n    Isolate *isolate = args.GetIsolate();\n    Local<Object> self = args.This();\n    (new ConnectionPointObject(args))->Wrap(self);\n    args.GetReturnValue().Set(self);\n}\n\nvoid ConnectionPointObject::NodeAdvise(const FunctionCallbackInfo<Value> &args) {\n    Isolate *isolate = args.GetIsolate();\n    ConnectionPointObject *self = ConnectionPointObject::Unwrap<ConnectionPointObject>(args.This());\n    if (!self || !self->ptr) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n    CComPtr<IUnknown> unk;\n    int argcnt = args.Length();\n    if (argcnt > 0) {\n        Local<Value> val = args[0];\n        if (!Value2Unknown(isolate, val, &unk)) {\n            Local<Object> obj;\n            if (!val.IsEmpty() && val->IsObject() && val->ToObject(isolate->GetCurrentContext()).ToLocal(&obj)) {\n\n                // .NET Connection Points require to implement specific interface\n                // So we need to remember its IID for the case when Container does QueryInterface for it\n                IID connif;\n                self->ptr->GetConnectionInterface(&connif);\n                DispObjectImpl *impl = new DispObjectImpl(obj, false, connif);\n                // It requires reversed arguments\n                impl->reverse_arguments = true;\n                impl->index = self->index;\n                if (self->index.size()) {\n                    impl->dispid_next = self->index.rbegin()->first + 1;\n                }\n                unk.Attach(impl);\n            }\n        }\n    }\n    if (!unk) {\n        isolate->ThrowException(InvalidArgumentsError(isolate));\n        return;\n    }\n    DWORD dwCookie;\n    HRESULT hrcode = self->ptr->Advise(unk, &dwCookie);\n    if FAILED(hrcode) {\n        isolate->ThrowException(DispError(isolate, hrcode));\n        return;\n    }\n    self->cookies.insert(dwCookie);\n    args.GetReturnValue().Set(v8::Integer::New(isolate, (uint32_t)dwCookie));\n}\n\nvoid ConnectionPointObject::NodeUnadvise(const FunctionCallbackInfo<Value> &args) {\n    Isolate *isolate = args.GetIsolate();\n    Local<Context> ctx = isolate->GetCurrentContext();\n    ConnectionPointObject *self = ConnectionPointObject::Unwrap<ConnectionPointObject>(args.This());\n    if (!self || !self->ptr) {\n        isolate->ThrowException(DispErrorInvalid(isolate));\n        return;\n    }\n\n    if (args.Length()==0 || !args[0]->IsUint32()) {\n        isolate->ThrowException(InvalidArgumentsError(isolate));\n        return;\n    }\n    DWORD dwCookie = (args[0]->Uint32Value(ctx)).FromMaybe(0);\n    if (dwCookie == 0 || self->cookies.find(dwCookie) == self->cookies.end()) {\n        isolate->ThrowException(InvalidArgumentsError(isolate));\n    return;\n    }\n    self->cookies.erase(dwCookie);\n    HRESULT hrcode = self->ptr->Unadvise(dwCookie);\n    if FAILED(hrcode) {\n        isolate->ThrowException(DispError(isolate, hrcode));\n        return;\n    }\n}\n\nvoid ConnectionPointObject::NodeConnectionPointMethods(const FunctionCallbackInfo<Value>& args) {\n    Isolate* isolate = args.GetIsolate();\n    Local<Context> ctx = isolate->GetCurrentContext();\n    Local<Array> items = Array::New(isolate);\n\n    ConnectionPointObject* self = ConnectionPointObject::Unwrap<ConnectionPointObject>(args.This());\n\n    DispObjectImpl::index_t::iterator it;\n    uint32_t cnt = 0;\n\n    for (it = self->index.begin(); it != self->index.end(); it++)\n    {\n        items->Set(ctx, cnt++, v8str(isolate, it->second->name.c_str()));\n    }\n\n    args.GetReturnValue().Set(items);\n}\n\n//-------------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "src/disp.h",
    "content": "//-------------------------------------------------------------------------------------------------------\n// Project: NodeActiveX\n// Author: Yuri Dursin\n// Description: DispObject class declarations. This class incapsulates COM IDispatch interface to Node JS Object\n//-------------------------------------------------------------------------------------------------------\n\n#pragma once\n\n#include \"utils.h\"\n\nenum options_t { \n    option_none = 0, \n    option_async = 0x0001, \n    option_type = 0x0002,\n    option_activate = 0x0004,\n    option_prepared = 0x0100,\n    option_owned = 0x0200,\n    option_property = 0x0400,\n    option_function_simple = 0x0800,\n    option_mask = 0x00FF,\n    option_auto = (option_async | option_type)\n};\n\ninline bool TypeInfoGetName(ITypeInfo *info, DISPID dispid, BSTR *name) {\n    HRESULT hrcode = info->GetDocumentation(dispid, name, NULL, NULL, NULL);\n    if SUCCEEDED(hrcode) return true;\n    UINT cnt_ret;\n    return info->GetNames(dispid, name, 1, &cnt_ret) == S_OK && cnt_ret > 0;\n}\n\ntemplate<typename T>\nbool TypeInfoPrepareFunc(ITypeInfo *info, UINT n, T process) {\n    FUNCDESC *desc;\n    if (info->GetFuncDesc(n, &desc) != S_OK) return false;\n    process(info, desc, nullptr);\n    info->ReleaseFuncDesc(desc);\n    return true;\n}\n\ntemplate<typename T>\nbool TypeInfoPrepareVar(ITypeInfo *info, UINT n, T process) {\n    VARDESC *desc;\n    if (info->GetVarDesc(n, &desc) != S_OK) return false;\n    process(info, nullptr, desc);\n    info->ReleaseVarDesc(desc);\n    return true;\n}\n\ntemplate<typename T>\nvoid TypeInfoPrepare(ITypeInfo *info, int mode, T process) {\n    UINT cFuncs = 0, cVars = 0;\n    TYPEATTR *pattr = NULL;\n    if (info->GetTypeAttr(&pattr) == S_OK) {\n        cFuncs = pattr->cFuncs;\n        cVars = pattr->cVars;\n        info->ReleaseTypeAttr(pattr);\n    }\n    if ((mode & 1) != 0) {\n        for (UINT n = 0; n < cFuncs; n++) {\n            TypeInfoPrepareFunc<T>(info, n, process);\n        }\n    }\n    if ((mode & 2) != 0) {\n        for (UINT n = 0; n < cVars; n++) {\n            TypeInfoPrepareVar<T>(info, n, process);\n        }\n    }\n}\n\ntemplate<typename T>\nbool TypeLibEnumerate(ITypeLib *typelib, int mode, T process) {\n    UINT i, cnt = typelib ? typelib->GetTypeInfoCount() : 0;\n    for (i = 0; i < cnt; i++) {\n        CComPtr<ITypeInfo> info;\n        if (typelib->GetTypeInfo(i, &info) != S_OK) continue;\n        TypeInfoPrepare<T>(info, mode, process);\n    }\n    return cnt > 0;\n}\n\nclass DispInfo {\npublic:\n    std::weak_ptr<DispInfo> parent;\n    CComPtr<IDispatch> ptr;\n    std::wstring name;\n    int options;\n    bool bManaged;\n\n    struct type_t { \n        DISPID dispid; \n        int kind; \n        int argcnt_get; \n        inline type_t(DISPID dispid_, int kind_) : dispid(dispid_), kind(kind_), argcnt_get(0) {}\n        inline bool is_property() const { return ((kind & INVOKE_FUNC) == 0); }\n        inline bool is_property_simple() const { return (((kind & (INVOKE_PROPERTYGET | INVOKE_FUNC))) == INVOKE_PROPERTYGET) && (argcnt_get == 0); }\n        inline bool is_function_simple() const { return (((kind & (INVOKE_PROPERTYGET | INVOKE_FUNC))) == INVOKE_FUNC) && (argcnt_get == 0); }\n        inline bool is_property_advanced() const { return kind == (INVOKE_PROPERTYGET | INVOKE_PROPERTYPUT) && (argcnt_get == 1); }\n    };\n    typedef std::shared_ptr<type_t> type_ptr;\n    typedef std::map<DISPID, type_ptr> types_by_dispid_t;\n    types_by_dispid_t types_by_dispid;\n\n    inline DispInfo(IDispatch *disp, const std::wstring &nm, int opt, std::shared_ptr<DispInfo> *parnt = nullptr)\n        : ptr(disp), options(opt), name(nm), bManaged(false)\n    { \n        if (parnt) parent = *parnt;\n        if ((options & option_type) != 0)\n            Prepare(disp);\n    }\n\n    void Prepare(IDispatch *disp) {\n        Enumerate(1/*functions only*/, &bManaged, [this](ITypeInfo *info, FUNCDESC *func, VARDESC *var) {\n            type_ptr &ptr = this->types_by_dispid[func->memid];\n            if (!ptr) ptr.reset(new type_t(func->memid, func->invkind));\n            else ptr->kind |= func->invkind;\n            if ((func->invkind & INVOKE_PROPERTYGET) != 0) {\n                if (func->cParams > ptr->argcnt_get)\n                    ptr->argcnt_get = func->cParams;\n            }\n        });\n        bool prepared = types_by_dispid.size() > 3; // QueryInterface, AddRef, Release\n        if (prepared) options |= option_prepared;\n    }\n\n    inline bool GetTypeInfo(const DISPID dispid, type_ptr &info) {\n        if ((options & option_prepared) == 0) return false;\n        types_by_dispid_t::const_iterator it = types_by_dispid.find(dispid);\n        if (it == types_by_dispid.end()) return false;\n        info = it->second;\n        return true;\n    }\n\n    HRESULT FindProperty(LPOLESTR name, DISPID *dispid) {\n        return DispFind(ptr, name, dispid);\n    }\n\n    HRESULT GetProperty(DISPID dispid, LONG argcnt, VARIANT *args, VARIANT *value, EXCEPINFO *except = 0) {\n        HRESULT hrcode = DispInvoke(ptr, dispid, argcnt, args, value, DISPATCH_PROPERTYGET, except);\n        return hrcode;\n    }\n\n    HRESULT GetProperty(DISPID dispid, LONG index, VARIANT *value, EXCEPINFO *except = 0) {\n        CComVariant arg(index);\n        LONG argcnt = (index >= 0) ? 1 : 0;\n        return DispInvoke(ptr, dispid, argcnt, &arg, value, DISPATCH_PROPERTYGET, except);\n    }\n\n    HRESULT SetProperty(DISPID dispid, LONG argcnt, VARIANT *args, VARIANT *value, EXCEPINFO *except = 0) {\n        HRESULT hrcode = DispInvoke(ptr, dispid, argcnt, args, value, DISPATCH_PROPERTYPUT, except);\n        if FAILED(hrcode) value->vt = VT_EMPTY;\n        return hrcode;\n    }\n\n    HRESULT ExecuteMethod(DISPID dispid, LONG argcnt, VARIANT *args, VARIANT *value, EXCEPINFO *except = 0) {\n        HRESULT hrcode = DispInvoke(ptr, dispid, argcnt, args, value, DISPATCH_METHOD, except);\n        return hrcode;\n    }\n\n    template<typename T>\n    bool Enumerate(int mode, bool* checkManaged, T process = nullptr) {\n        UINT i, cnt;\n        CComPtr<ITypeLib> prevtypelib;\n        if (!ptr || FAILED(ptr->GetTypeInfoCount(&cnt))) cnt = 0;\n        else for (i = 0; i < cnt; i++) {\n            CComPtr<ITypeInfo> info;\n            if (ptr->GetTypeInfo(i, 0, &info) != S_OK) continue;\n\n            // Query type library\n            UINT typeindex;\n            CComPtr<ITypeLib> typelib;\n            if (info->GetContainingTypeLib(&typelib, &typeindex) == S_OK) {\n\n                // Check if typelib is managed\n                if (checkManaged != nullptr && !*checkManaged) {\n                    CComPtr<ITypeLib2> typeLib2;\n                    if (SUCCEEDED(typelib->QueryInterface(IID_ITypeLib2, (void**)&typeLib2))) {\n\n                        // {90883F05-3D28-11D2-8F17-00A0C9A6186D}\n                        static const GUID GUID_ExportedFromComPlus = { 0x90883F05, 0x3D28, 0x11D2, { 0x8F, 0x17, 0x00, 0xA0, 0xC9, 0xA6, 0x18, 0x6D } };\n\n                        CComVariant cv;\n                        if (SUCCEEDED(typeLib2->GetCustData(GUID_ExportedFromComPlus, &cv))) {\n                            *checkManaged = true;\n                        }\n                    }\n                }\n\n                // Enumerate all types in library types\n                // May be very slow! need a special method\n                // if (typelib != prevtypelib) {\n                //     TypeLibEnumerate<T>(typelib, mode, process);\n                //\t   prevtypelib.Attach(typelib.Detach());\n                // }\n\n                CComPtr<ITypeInfo> tinfo;\n                if (typelib->GetTypeInfo(typeindex, &tinfo) == S_OK) {\n                    TypeInfoPrepare<T>(tinfo, mode, process);\n                }\n            }\n\n            // Process types\n            else {\n                TypeInfoPrepare<T>(info, mode, process);\n            }\n        }\n        return cnt > 0;\n    }\n};\n\ntypedef std::shared_ptr<DispInfo> DispInfoPtr;\n\nclass DispObject: public NodeObject\n{\npublic:\n    DispObject(const DispInfoPtr &ptr, const std::wstring &name, DISPID id = DISPID_UNKNOWN, LONG indx = -1, int opt = 0);\n    ~DispObject();\n\n    static Persistent<ObjectTemplate> inst_template;\n    static Persistent<FunctionTemplate> clazz_template;\n    static NodeMethods clazz_methods;\n\n    static void NodeInit(const Local<Object> &target, Isolate* isolate, Local<Context> &ctx);\n    static bool HasInstance(Isolate *isolate, const Local<Value> &obj) {\n        Local<FunctionTemplate> clazz = clazz_template.Get(isolate);\n        return !clazz.IsEmpty() && clazz->HasInstance(obj);\n    }\n    static DispObject *GetPtr(Isolate *isolate, const Local<Object> &obj) {\n        Local<FunctionTemplate> clazz = clazz_template.Get(isolate);\n        if (clazz.IsEmpty() || !clazz->HasInstance(obj)) return nullptr;\n        return Unwrap<DispObject>(obj);\n    }\n    static IDispatch *GetDispPtr(Isolate *isolate, const Local<Object> &obj) {\n        DispObject *self = GetPtr(isolate, obj);\n        return (self && self->disp) ? self->disp->ptr : nullptr;\n    }\n    static bool GetValueOf(Isolate *isolate, const Local<Object> &obj, VARIANT &value) {\n        DispObject *self = GetPtr(isolate, obj);\n        return self && SUCCEEDED(self->valueOf(isolate, value, false));\n    }\n    static Local<Object> NodeCreate(Isolate *isolate, IDispatch *disp, const std::wstring &name, int opt) {\n        Local<Object> parent;\n        DispInfoPtr ptr(new DispInfo(disp, name, opt));\n        return DispObject::NodeCreate(isolate, parent, ptr, name);\n    }\n\nprivate:\n    static Local<Object> NodeCreate(Isolate *isolate, const Local<Object> &parent, const DispInfoPtr &ptr, const std::wstring &name, DISPID id = DISPID_UNKNOWN, LONG indx = -1, int opt = 0);\n\n    static void NodeCreate(const FunctionCallbackInfo<Value> &args);\n    static void NodeValueOf(const FunctionCallbackInfo<Value> &args);\n    static void NodeToString(const FunctionCallbackInfo<Value> &args);\n    static void NodeRelease(const FunctionCallbackInfo<Value> &args);\n    static void NodeCast(const FunctionCallbackInfo<Value> &args);\n    static void NodeGet(Local<Name> name, const PropertyCallbackInfoGetter &args);\n    static void NodeSet(Local<Name> name, Local<Value> value, const PropertyCallbackInfoSetter &args);\n    static void NodeGetByIndex(uint32_t index, const PropertyCallbackInfoGetter&args);\n    static void NodeSetByIndex(uint32_t index, Local<Value> value, const PropertyCallbackInfoSetter &args);\n    static void NodeCall(const FunctionCallbackInfo<Value> &args);\n\n#ifdef NODE_INTERCEPTED\n    static inline Intercepted InterceptedNodeGet(Local<Name> name, const PropertyCallbackInfoGetter& args) {\n        NodeGet(name, args);\n        return Intercepted::kYes;\n    }\n    static inline Intercepted InterceptedNodeSet(Local<Name> name, Local<Value> value, const PropertyCallbackInfoSetter& args) {\n        NodeSet(name, value, args);\n        return Intercepted::kYes;\n    }\n    static inline Intercepted InterceptedNodeGetByIndex(uint32_t index, const PropertyCallbackInfoGetter& args) {\n        NodeGetByIndex(index, args);\n        return Intercepted::kYes;\n    }\n    static inline Intercepted InterceptedNodeSetByIndex(uint32_t index, Local<Value> value, const PropertyCallbackInfoSetter& args) {\n        NodeSetByIndex(index, value, args);\n        return Intercepted::kYes;\n    }\n#endif\n\n    static void NodeConnectionPoints(const FunctionCallbackInfo<Value> &args);\n    static void PeakAndDispatchMessages(const FunctionCallbackInfo<Value> &args);\n\nprotected:\n    bool release();\n    bool get(LPOLESTR tag, LONG index, const PropertyCallbackInfoGetter &args);\n    bool set(LPOLESTR tag, LONG index, const Local<Value> &value, const PropertyCallbackInfoSetter &args);\n    void call(Isolate *isolate, const FunctionCallbackInfo<Value> &args);\n\n    HRESULT valueOf(Isolate *isolate, VARIANT &value, bool simple);\n    HRESULT valueOf(Isolate *isolate, const Local<Object> &self, Local<Value> &value);\n    void toString(const FunctionCallbackInfo<Value> &args);\n    Local<Value> getIdentity(Isolate *isolate);\n\nprivate:\n    int options;\n    inline bool is_null() { return !disp; }\n    inline bool is_prepared() { return (options & option_prepared) != 0; }\n    inline bool is_object() { return dispid == DISPID_VALUE /*&& index < 0*/; }\n    inline bool is_owned() { return (options & option_owned) != 0; }\n\n    Persistent<Value> items, methods, vars;\n    void initTypeInfo(Isolate *isolate);\n\n    DispInfoPtr disp;\n    std::wstring name;\n    DISPID dispid;\n    LONG index;\n\n    HRESULT prepare();\n};\n\nclass VariantObject : public NodeObject\n{\npublic:\n    VariantObject() {};\n    VariantObject(const VARIANT &v) : value(v) {};\n    VariantObject(const FunctionCallbackInfo<Value> &args);\n\n    static Persistent<ObjectTemplate> inst_template;\n    static Persistent<FunctionTemplate> clazz_template;\n    static NodeMethods clazz_methods;\n\n    static void NodeInit(const Local<Object> &target, Isolate* isolate, Local<Context> &ctx);\n    static bool HasInstance(Isolate *isolate, const Local<Value> &obj) {\n        Local<FunctionTemplate> clazz = clazz_template.Get(isolate);\n        return !clazz.IsEmpty() && clazz->HasInstance(obj);\n    }\n    static VariantObject *GetInstanceOf(Isolate *isolate, const Local<Object> &obj) {\n        Local<FunctionTemplate> clazz = clazz_template.Get(isolate);\n        if (clazz.IsEmpty() || !clazz->HasInstance(obj)) return nullptr;\n        return Unwrap<VariantObject>(obj);\n    }\n    static bool GetValueOf(Isolate *isolate, const Local<Object> &obj, VARIANT &value) {\n        Local<FunctionTemplate> clazz = clazz_template.Get(isolate);\n        if (clazz.IsEmpty() || !clazz->HasInstance(obj)) return false;\n        VariantObject *self = Unwrap<VariantObject>(obj);\n        return self && SUCCEEDED(self->value.CopyTo(&value));\n    }\n\n    static Local<Object> NodeCreateInstance(const FunctionCallbackInfo<Value> &args);\n    static void NodeCreate(const FunctionCallbackInfo<Value> &args);\n    static Local<Object> NodeCreate(Isolate* isolate, const VARIANT& var);\n\n    static void NodeClear(const FunctionCallbackInfo<Value> &args);\n    static void NodeAssign(const FunctionCallbackInfo<Value> &args);\n    static void NodeCast(const FunctionCallbackInfo<Value> &args);\n    static void NodeValueOf(const FunctionCallbackInfo<Value> &args);\n    static void NodeToString(const FunctionCallbackInfo<Value> &args);\n    static void NodeGet(Local<Name> name, const PropertyCallbackInfoGetter &args);\n    static void NodeSet(Local<Name> name, Local<Value> value, const PropertyCallbackInfoSetter&args);\n    static void NodeGetByIndex(uint32_t index, const PropertyCallbackInfoGetter&args);\n    static void NodeSetByIndex(uint32_t index, Local<Value> value, const PropertyCallbackInfoSetter&args);\n\n#ifdef NODE_INTERCEPTED\n    static inline Intercepted InterceptedNodeGet(Local<Name> name, const PropertyCallbackInfoGetter& args) {\n        NodeGet(name, args);\n        return Intercepted::kYes;\n    }\n    static inline Intercepted InterceptedNodeSet(Local<Name> name, Local<Value> value, const PropertyCallbackInfoSetter& args) {\n        NodeSet(name, value, args);\n        return Intercepted::kYes;\n    }\n    static inline Intercepted InterceptedNodeGetByIndex(uint32_t index, const PropertyCallbackInfoGetter& args) {\n        NodeGetByIndex(index, args);\n        return Intercepted::kYes;\n    }\n    static inline Intercepted InterceptedNodeSetByIndex(uint32_t index, Local<Value> value, const PropertyCallbackInfoSetter& args) {\n        NodeSetByIndex(index, value, args);\n        return Intercepted::kYes;\n    }\n#endif\n\nprivate:\n    CComVariant value, pvalue;\n    bool assign(Isolate *isolate, Local<Value> &val, Local<Value> &type);\n};\n\nclass ConnectionPointObject : public NodeObject\n{\npublic:\n    ConnectionPointObject(IConnectionPoint *p, IDispatch* d);\n    ConnectionPointObject(const FunctionCallbackInfo<Value> &args) {}\n    static Persistent<ObjectTemplate> inst_template;\n    static Persistent<FunctionTemplate> clazz_template;\n    static Local<Object> NodeCreateInstance(Isolate *isolate, IConnectionPoint *p, IDispatch* d);\n    static void NodeInit(const Local<Object> &target, Isolate* isolate, Local<Context> &ctx);\n    static void NodeCreate(const FunctionCallbackInfo<Value> &args);\n    static void NodeAdvise(const FunctionCallbackInfo<Value> &args);\n    static void NodeUnadvise(const FunctionCallbackInfo<Value> &args);\n    static void NodeConnectionPointMethods(const FunctionCallbackInfo<Value> &args);\n\nprivate:\n    bool InitIndex();\n\n    CComPtr<IConnectionPoint> ptr;\n    CComPtr<IDispatch> disp;\n    DispObjectImpl::index_t index;\n\n    std::unordered_set<DWORD> cookies;\n\n};\n"
  },
  {
    "path": "src/main.cpp",
    "content": "//-------------------------------------------------------------------------------------------------------\n// Project: node-activex\n// Author: Yuri Dursin\n// Description: Defines the entry point for the NodeJS addon\n//-------------------------------------------------------------------------------------------------------\n\n#include \"stdafx.h\"\n#include \"disp.h\"\n\n//----------------------------------------------------------------------------------\n\n// Initialize this addon\nNODE_MODULE_INIT(/*exports, module, context*/) {\n  Isolate* isolate = Isolate::GetCurrent();\n\n  DispObject::NodeInit(exports, isolate, context);\n  VariantObject::NodeInit(exports, isolate, context);\n  ConnectionPointObject::NodeInit(exports, isolate, context);\n\n  // Sleep is essential to have proper WScript emulation\n  exports->Set(context,\n      String::NewFromUtf8(isolate, \"winaxsleep\", NewStringType::kNormal)\n      .ToLocalChecked(),\n      FunctionTemplate::New(isolate, WinaxSleep)\n      ->GetFunction(context).ToLocalChecked()).FromJust();\n\n  /* Example implementation of a context-aware addon:\n  // Create a new instance of `AddonData` for this instance of the addon and\n  // tie its life cycle to that of the Node.js environment.\n  AddonData* data = new AddonData(isolate);\n\n  // Wrap the data in a `v8::External` so we can pass it to the method we\n  // expose.\n  Local<External> external = External::New(isolate, data);\n\n  // Expose the method `Method` to JavaScript, and make sure it receives the\n  // per-addon-instance data we created above by passing `external` as the\n  // third parameter to the `FunctionTemplate` constructor.\n  exports->Set(context,\n               String::NewFromUtf8(isolate, \"method\", NewStringType::kNormal)\n                  .ToLocalChecked(),\n               FunctionTemplate::New(isolate, Method, external)\n                  ->GetFunction(context).ToLocalChecked()).FromJust();\n  */\n}\n\n//----------------------------------------------------------------------------------\n\nBOOL APIENTRY DllMain(HMODULE hModule, DWORD  ulReason, LPVOID lpReserved) {\n    \n    switch (ulReason) {\n    case DLL_PROCESS_ATTACH:\n        CoInitialize(0);\n        break;\n    case DLL_PROCESS_DETACH:\n        CoUninitialize();\n        break;\n    case DLL_THREAD_ATTACH:\n        break;\n    case DLL_THREAD_DETACH:\n        break;\n    }\n    return TRUE;\n}\n\n//----------------------------------------------------------------------------------\n"
  },
  {
    "path": "src/stdafx.h",
    "content": "#pragma once\n\n// To remove conflicts with recent v8 code std::numeric_limits<int>::max()\n#ifndef NOMINMAX\n#define NOMINMAX \n#endif\n\n#include <SDKDDKVer.h>\n\n// Windows Header Files:\n#define WIN32_LEAN_AND_MEAN                     // Exclude rarely-used stuff from Windows headers\n#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // some CString constructors will be explicit\n#include <windows.h>\n\n// ATL headers\n//#define USE_ATL\n#ifdef USE_ATL\n#include <atlbase.h>\n#include <atlstr.h>\n#else\n#include <ole2.h>\n#include <ocidl.h>\n#endif\n\n// STD headers\n#include <iostream>\n#include <stdio.h>\n#include <string>\n#include <vector>\n#include <map>\n#include <unordered_set>\n#include <memory>\n#include <initializer_list>\n\n// Node JS headers\n#include <v8.h>\n#include <node.h>\n#include <node_version.h>\n#include <node_object_wrap.h>\n#include <node_buffer.h>\nusing namespace v8;\nusing namespace node;\n\n// Application defines\n//#define TEST_ADVISE"
  },
  {
    "path": "src/utils.cpp",
    "content": "//-------------------------------------------------------------------------------------------------------\n// Project: NodeActiveX\n// Author: Yuri Dursin\n// Description:  Common utilities implementations\n//-------------------------------------------------------------------------------------------------------\n\n#include \"stdafx.h\"\n#include \"disp.h\"\n#include <OleCtl.h>\n#include <oleacc.h>                  // for AccessibleObjectFromWindow\n#pragma comment(lib, \"OleAcc.lib\")   // for AccessibleObjectFromWindow\n\nconst GUID CLSID_DispObjectImpl = { 0x9dce8520, 0x2efe, 0x48c0,{ 0xa0, 0xdc, 0x95, 0x1b, 0x29, 0x18, 0x72, 0xc0 } };\n\nconst IID IID_IReflect = { 0xAFBF15E5, 0xC37C, 0x11D2,{ 0xB8, 0x8E, 0x00, 0xA0, 0xC9, 0xB4, 0x71, 0xB8} };\n\n//-------------------------------------------------------------------------------------------------------\n\n#define ERROR_MESSAGE_WIDE_MAXSIZE 1024\n#define ERROR_MESSAGE_UTF8_MAXSIZE 2048\n\nvoid GetScodeString(HRESULT hr, wchar_t* buf, int bufSize)\n{\n    struct HRESULT_ENTRY\n    {\n        HRESULT hr;\n        LPCWSTR lpszName;\n    };\n#define MAKE_HRESULT_ENTRY(hr)    { hr, L#hr }\n    static const HRESULT_ENTRY hrNameTable[] =\n    {\n        MAKE_HRESULT_ENTRY(S_OK),\n        MAKE_HRESULT_ENTRY(S_FALSE),\n\n        MAKE_HRESULT_ENTRY(CACHE_S_FORMATETC_NOTSUPPORTED),\n        MAKE_HRESULT_ENTRY(CACHE_S_SAMECACHE),\n        MAKE_HRESULT_ENTRY(CACHE_S_SOMECACHES_NOTUPDATED),\n        MAKE_HRESULT_ENTRY(CONVERT10_S_NO_PRESENTATION),\n        MAKE_HRESULT_ENTRY(DATA_S_SAMEFORMATETC),\n        MAKE_HRESULT_ENTRY(DRAGDROP_S_CANCEL),\n        MAKE_HRESULT_ENTRY(DRAGDROP_S_DROP),\n        MAKE_HRESULT_ENTRY(DRAGDROP_S_USEDEFAULTCURSORS),\n        MAKE_HRESULT_ENTRY(INPLACE_S_TRUNCATED),\n        MAKE_HRESULT_ENTRY(MK_S_HIM),\n        MAKE_HRESULT_ENTRY(MK_S_ME),\n        MAKE_HRESULT_ENTRY(MK_S_MONIKERALREADYREGISTERED),\n        MAKE_HRESULT_ENTRY(MK_S_REDUCED_TO_SELF),\n        MAKE_HRESULT_ENTRY(MK_S_US),\n        MAKE_HRESULT_ENTRY(OLE_S_MAC_CLIPFORMAT),\n        MAKE_HRESULT_ENTRY(OLE_S_STATIC),\n        MAKE_HRESULT_ENTRY(OLE_S_USEREG),\n        MAKE_HRESULT_ENTRY(OLEOBJ_S_CANNOT_DOVERB_NOW),\n        MAKE_HRESULT_ENTRY(OLEOBJ_S_INVALIDHWND),\n        MAKE_HRESULT_ENTRY(OLEOBJ_S_INVALIDVERB),\n        MAKE_HRESULT_ENTRY(OLEOBJ_S_LAST),\n        MAKE_HRESULT_ENTRY(STG_S_CONVERTED),\n        MAKE_HRESULT_ENTRY(VIEW_S_ALREADY_FROZEN),\n\n        MAKE_HRESULT_ENTRY(E_UNEXPECTED),\n        MAKE_HRESULT_ENTRY(E_NOTIMPL),\n        MAKE_HRESULT_ENTRY(E_OUTOFMEMORY),\n        MAKE_HRESULT_ENTRY(E_INVALIDARG),\n        MAKE_HRESULT_ENTRY(E_NOINTERFACE),\n        MAKE_HRESULT_ENTRY(E_POINTER),\n        MAKE_HRESULT_ENTRY(E_HANDLE),\n        MAKE_HRESULT_ENTRY(E_ABORT),\n        MAKE_HRESULT_ENTRY(E_FAIL),\n        MAKE_HRESULT_ENTRY(E_ACCESSDENIED),\n\n        MAKE_HRESULT_ENTRY(CACHE_E_NOCACHE_UPDATED),\n        MAKE_HRESULT_ENTRY(CLASS_E_CLASSNOTAVAILABLE),\n        MAKE_HRESULT_ENTRY(CLASS_E_NOAGGREGATION),\n        MAKE_HRESULT_ENTRY(CLIPBRD_E_BAD_DATA),\n        MAKE_HRESULT_ENTRY(CLIPBRD_E_CANT_CLOSE),\n        MAKE_HRESULT_ENTRY(CLIPBRD_E_CANT_EMPTY),\n        MAKE_HRESULT_ENTRY(CLIPBRD_E_CANT_OPEN),\n        MAKE_HRESULT_ENTRY(CLIPBRD_E_CANT_SET),\n        MAKE_HRESULT_ENTRY(CO_E_ALREADYINITIALIZED),\n        MAKE_HRESULT_ENTRY(CO_E_APPDIDNTREG),\n        MAKE_HRESULT_ENTRY(CO_E_APPNOTFOUND),\n        MAKE_HRESULT_ENTRY(CO_E_APPSINGLEUSE),\n        MAKE_HRESULT_ENTRY(CO_E_BAD_PATH),\n        MAKE_HRESULT_ENTRY(CO_E_CANTDETERMINECLASS),\n        MAKE_HRESULT_ENTRY(CO_E_CLASS_CREATE_FAILED),\n        MAKE_HRESULT_ENTRY(CO_E_CLASSSTRING),\n        MAKE_HRESULT_ENTRY(CO_E_DLLNOTFOUND),\n        MAKE_HRESULT_ENTRY(CO_E_ERRORINAPP),\n        MAKE_HRESULT_ENTRY(CO_E_ERRORINDLL),\n        MAKE_HRESULT_ENTRY(CO_E_IIDSTRING),\n        MAKE_HRESULT_ENTRY(CO_E_NOTINITIALIZED),\n        MAKE_HRESULT_ENTRY(CO_E_OBJISREG),\n        MAKE_HRESULT_ENTRY(CO_E_OBJNOTCONNECTED),\n        MAKE_HRESULT_ENTRY(CO_E_OBJNOTREG),\n        MAKE_HRESULT_ENTRY(CO_E_OBJSRV_RPC_FAILURE),\n        MAKE_HRESULT_ENTRY(CO_E_SCM_ERROR),\n        MAKE_HRESULT_ENTRY(CO_E_SCM_RPC_FAILURE),\n        MAKE_HRESULT_ENTRY(CO_E_SERVER_EXEC_FAILURE),\n        MAKE_HRESULT_ENTRY(CO_E_SERVER_STOPPING),\n        MAKE_HRESULT_ENTRY(CO_E_WRONGOSFORAPP),\n        MAKE_HRESULT_ENTRY(CONVERT10_E_OLESTREAM_BITMAP_TO_DIB),\n        MAKE_HRESULT_ENTRY(CONVERT10_E_OLESTREAM_FMT),\n        MAKE_HRESULT_ENTRY(CONVERT10_E_OLESTREAM_GET),\n        MAKE_HRESULT_ENTRY(CONVERT10_E_OLESTREAM_PUT),\n        MAKE_HRESULT_ENTRY(CONVERT10_E_STG_DIB_TO_BITMAP),\n        MAKE_HRESULT_ENTRY(CONVERT10_E_STG_FMT),\n        MAKE_HRESULT_ENTRY(CONVERT10_E_STG_NO_STD_STREAM),\n        MAKE_HRESULT_ENTRY(DISP_E_ARRAYISLOCKED),\n        MAKE_HRESULT_ENTRY(DISP_E_BADCALLEE),\n        MAKE_HRESULT_ENTRY(DISP_E_BADINDEX),\n        MAKE_HRESULT_ENTRY(DISP_E_BADPARAMCOUNT),\n        MAKE_HRESULT_ENTRY(DISP_E_BADVARTYPE),\n        MAKE_HRESULT_ENTRY(DISP_E_EXCEPTION),\n        MAKE_HRESULT_ENTRY(DISP_E_MEMBERNOTFOUND),\n        MAKE_HRESULT_ENTRY(DISP_E_NONAMEDARGS),\n        MAKE_HRESULT_ENTRY(DISP_E_NOTACOLLECTION),\n        MAKE_HRESULT_ENTRY(DISP_E_OVERFLOW),\n        MAKE_HRESULT_ENTRY(DISP_E_PARAMNOTFOUND),\n        MAKE_HRESULT_ENTRY(DISP_E_PARAMNOTOPTIONAL),\n        MAKE_HRESULT_ENTRY(DISP_E_TYPEMISMATCH),\n        MAKE_HRESULT_ENTRY(DISP_E_UNKNOWNINTERFACE),\n        MAKE_HRESULT_ENTRY(DISP_E_UNKNOWNLCID),\n        MAKE_HRESULT_ENTRY(DISP_E_UNKNOWNNAME),\n        MAKE_HRESULT_ENTRY(DRAGDROP_E_ALREADYREGISTERED),\n        MAKE_HRESULT_ENTRY(DRAGDROP_E_INVALIDHWND),\n        MAKE_HRESULT_ENTRY(DRAGDROP_E_NOTREGISTERED),\n        MAKE_HRESULT_ENTRY(DV_E_CLIPFORMAT),\n        MAKE_HRESULT_ENTRY(DV_E_DVASPECT),\n        MAKE_HRESULT_ENTRY(DV_E_DVTARGETDEVICE),\n        MAKE_HRESULT_ENTRY(DV_E_DVTARGETDEVICE_SIZE),\n        MAKE_HRESULT_ENTRY(DV_E_FORMATETC),\n        MAKE_HRESULT_ENTRY(DV_E_LINDEX),\n        MAKE_HRESULT_ENTRY(DV_E_NOIVIEWOBJECT),\n        MAKE_HRESULT_ENTRY(DV_E_STATDATA),\n        MAKE_HRESULT_ENTRY(DV_E_STGMEDIUM),\n        MAKE_HRESULT_ENTRY(DV_E_TYMED),\n        MAKE_HRESULT_ENTRY(INPLACE_E_NOTOOLSPACE),\n        MAKE_HRESULT_ENTRY(INPLACE_E_NOTUNDOABLE),\n        MAKE_HRESULT_ENTRY(MEM_E_INVALID_LINK),\n        MAKE_HRESULT_ENTRY(MEM_E_INVALID_ROOT),\n        MAKE_HRESULT_ENTRY(MEM_E_INVALID_SIZE),\n        MAKE_HRESULT_ENTRY(MK_E_CANTOPENFILE),\n        MAKE_HRESULT_ENTRY(MK_E_CONNECTMANUALLY),\n        MAKE_HRESULT_ENTRY(MK_E_ENUMERATION_FAILED),\n        MAKE_HRESULT_ENTRY(MK_E_EXCEEDEDDEADLINE),\n        MAKE_HRESULT_ENTRY(MK_E_INTERMEDIATEINTERFACENOTSUPPORTED),\n        MAKE_HRESULT_ENTRY(MK_E_INVALIDEXTENSION),\n        MAKE_HRESULT_ENTRY(MK_E_MUSTBOTHERUSER),\n        MAKE_HRESULT_ENTRY(MK_E_NEEDGENERIC),\n        MAKE_HRESULT_ENTRY(MK_E_NO_NORMALIZED),\n        MAKE_HRESULT_ENTRY(MK_E_NOINVERSE),\n        MAKE_HRESULT_ENTRY(MK_E_NOOBJECT),\n        MAKE_HRESULT_ENTRY(MK_E_NOPREFIX),\n        MAKE_HRESULT_ENTRY(MK_E_NOSTORAGE),\n        MAKE_HRESULT_ENTRY(MK_E_NOTBINDABLE),\n        MAKE_HRESULT_ENTRY(MK_E_NOTBOUND),\n        MAKE_HRESULT_ENTRY(MK_E_SYNTAX),\n        MAKE_HRESULT_ENTRY(MK_E_UNAVAILABLE),\n        MAKE_HRESULT_ENTRY(OLE_E_ADVF),\n        MAKE_HRESULT_ENTRY(OLE_E_ADVISENOTSUPPORTED),\n        MAKE_HRESULT_ENTRY(OLE_E_BLANK),\n        MAKE_HRESULT_ENTRY(OLE_E_CANT_BINDTOSOURCE),\n        MAKE_HRESULT_ENTRY(OLE_E_CANT_GETMONIKER),\n        MAKE_HRESULT_ENTRY(OLE_E_CANTCONVERT),\n        MAKE_HRESULT_ENTRY(OLE_E_CLASSDIFF),\n        MAKE_HRESULT_ENTRY(OLE_E_ENUM_NOMORE),\n        MAKE_HRESULT_ENTRY(OLE_E_INVALIDHWND),\n        MAKE_HRESULT_ENTRY(OLE_E_INVALIDRECT),\n        MAKE_HRESULT_ENTRY(OLE_E_NOCACHE),\n        MAKE_HRESULT_ENTRY(OLE_E_NOCONNECTION),\n        MAKE_HRESULT_ENTRY(OLE_E_NOSTORAGE),\n        MAKE_HRESULT_ENTRY(OLE_E_NOT_INPLACEACTIVE),\n        MAKE_HRESULT_ENTRY(OLE_E_NOTRUNNING),\n        MAKE_HRESULT_ENTRY(OLE_E_OLEVERB),\n        MAKE_HRESULT_ENTRY(OLE_E_PROMPTSAVECANCELLED),\n        MAKE_HRESULT_ENTRY(OLE_E_STATIC),\n        MAKE_HRESULT_ENTRY(OLE_E_WRONGCOMPOBJ),\n        MAKE_HRESULT_ENTRY(OLEOBJ_E_INVALIDVERB),\n        MAKE_HRESULT_ENTRY(OLEOBJ_E_NOVERBS),\n        MAKE_HRESULT_ENTRY(REGDB_E_CLASSNOTREG),\n        MAKE_HRESULT_ENTRY(REGDB_E_IIDNOTREG),\n        MAKE_HRESULT_ENTRY(REGDB_E_INVALIDVALUE),\n        MAKE_HRESULT_ENTRY(REGDB_E_KEYMISSING),\n        MAKE_HRESULT_ENTRY(REGDB_E_READREGDB),\n        MAKE_HRESULT_ENTRY(REGDB_E_WRITEREGDB),\n        MAKE_HRESULT_ENTRY(RPC_E_ATTEMPTED_MULTITHREAD),\n        MAKE_HRESULT_ENTRY(RPC_E_CALL_CANCELED),\n        MAKE_HRESULT_ENTRY(RPC_E_CALL_REJECTED),\n        MAKE_HRESULT_ENTRY(RPC_E_CANTCALLOUT_AGAIN),\n        MAKE_HRESULT_ENTRY(RPC_E_CANTCALLOUT_INASYNCCALL),\n        MAKE_HRESULT_ENTRY(RPC_E_CANTCALLOUT_INEXTERNALCALL),\n        MAKE_HRESULT_ENTRY(RPC_E_CANTCALLOUT_ININPUTSYNCCALL),\n        MAKE_HRESULT_ENTRY(RPC_E_CANTPOST_INSENDCALL),\n        MAKE_HRESULT_ENTRY(RPC_E_CANTTRANSMIT_CALL),\n        MAKE_HRESULT_ENTRY(RPC_E_CHANGED_MODE),\n        MAKE_HRESULT_ENTRY(RPC_E_CLIENT_CANTMARSHAL_DATA),\n        MAKE_HRESULT_ENTRY(RPC_E_CLIENT_CANTUNMARSHAL_DATA),\n        MAKE_HRESULT_ENTRY(RPC_E_CLIENT_DIED),\n        MAKE_HRESULT_ENTRY(RPC_E_CONNECTION_TERMINATED),\n        MAKE_HRESULT_ENTRY(RPC_E_DISCONNECTED),\n        MAKE_HRESULT_ENTRY(RPC_E_FAULT),\n        MAKE_HRESULT_ENTRY(RPC_E_INVALID_CALLDATA),\n        MAKE_HRESULT_ENTRY(RPC_E_INVALID_DATA),\n        MAKE_HRESULT_ENTRY(RPC_E_INVALID_DATAPACKET),\n        MAKE_HRESULT_ENTRY(RPC_E_INVALID_PARAMETER),\n        MAKE_HRESULT_ENTRY(RPC_E_INVALIDMETHOD),\n        MAKE_HRESULT_ENTRY(RPC_E_NOT_REGISTERED),\n        MAKE_HRESULT_ENTRY(RPC_E_OUT_OF_RESOURCES),\n        MAKE_HRESULT_ENTRY(RPC_E_RETRY),\n        MAKE_HRESULT_ENTRY(RPC_E_SERVER_CANTMARSHAL_DATA),\n        MAKE_HRESULT_ENTRY(RPC_E_SERVER_CANTUNMARSHAL_DATA),\n        MAKE_HRESULT_ENTRY(RPC_E_SERVER_DIED),\n        MAKE_HRESULT_ENTRY(RPC_E_SERVER_DIED_DNE),\n        MAKE_HRESULT_ENTRY(RPC_E_SERVERCALL_REJECTED),\n        MAKE_HRESULT_ENTRY(RPC_E_SERVERCALL_RETRYLATER),\n        MAKE_HRESULT_ENTRY(RPC_E_SERVERFAULT),\n        MAKE_HRESULT_ENTRY(RPC_E_SYS_CALL_FAILED),\n        MAKE_HRESULT_ENTRY(RPC_E_THREAD_NOT_INIT),\n        MAKE_HRESULT_ENTRY(RPC_E_UNEXPECTED),\n        MAKE_HRESULT_ENTRY(RPC_E_WRONG_THREAD),\n        MAKE_HRESULT_ENTRY(STG_E_ABNORMALAPIEXIT),\n        MAKE_HRESULT_ENTRY(STG_E_ACCESSDENIED),\n        MAKE_HRESULT_ENTRY(STG_E_CANTSAVE),\n        MAKE_HRESULT_ENTRY(STG_E_DISKISWRITEPROTECTED),\n        MAKE_HRESULT_ENTRY(STG_E_EXTANTMARSHALLINGS),\n        MAKE_HRESULT_ENTRY(STG_E_FILEALREADYEXISTS),\n        MAKE_HRESULT_ENTRY(STG_E_FILENOTFOUND),\n        MAKE_HRESULT_ENTRY(STG_E_INSUFFICIENTMEMORY),\n        MAKE_HRESULT_ENTRY(STG_E_INUSE),\n        MAKE_HRESULT_ENTRY(STG_E_INVALIDFLAG),\n        MAKE_HRESULT_ENTRY(STG_E_INVALIDFUNCTION),\n        MAKE_HRESULT_ENTRY(STG_E_INVALIDHANDLE),\n        MAKE_HRESULT_ENTRY(STG_E_INVALIDHEADER),\n        MAKE_HRESULT_ENTRY(STG_E_INVALIDNAME),\n        MAKE_HRESULT_ENTRY(STG_E_INVALIDPARAMETER),\n        MAKE_HRESULT_ENTRY(STG_E_INVALIDPOINTER),\n        MAKE_HRESULT_ENTRY(STG_E_LOCKVIOLATION),\n        MAKE_HRESULT_ENTRY(STG_E_MEDIUMFULL),\n        MAKE_HRESULT_ENTRY(STG_E_NOMOREFILES),\n        MAKE_HRESULT_ENTRY(STG_E_NOTCURRENT),\n        MAKE_HRESULT_ENTRY(STG_E_NOTFILEBASEDSTORAGE),\n        MAKE_HRESULT_ENTRY(STG_E_OLDDLL),\n        MAKE_HRESULT_ENTRY(STG_E_OLDFORMAT),\n        MAKE_HRESULT_ENTRY(STG_E_PATHNOTFOUND),\n        MAKE_HRESULT_ENTRY(STG_E_READFAULT),\n        MAKE_HRESULT_ENTRY(STG_E_REVERTED),\n        MAKE_HRESULT_ENTRY(STG_E_SEEKERROR),\n        MAKE_HRESULT_ENTRY(STG_E_SHAREREQUIRED),\n        MAKE_HRESULT_ENTRY(STG_E_SHAREVIOLATION),\n        MAKE_HRESULT_ENTRY(STG_E_TOOMANYOPENFILES),\n        MAKE_HRESULT_ENTRY(STG_E_UNIMPLEMENTEDFUNCTION),\n        MAKE_HRESULT_ENTRY(STG_E_UNKNOWN),\n        MAKE_HRESULT_ENTRY(STG_E_WRITEFAULT),\n        MAKE_HRESULT_ENTRY(TYPE_E_AMBIGUOUSNAME),\n        MAKE_HRESULT_ENTRY(TYPE_E_BADMODULEKIND),\n        MAKE_HRESULT_ENTRY(TYPE_E_BUFFERTOOSMALL),\n        MAKE_HRESULT_ENTRY(TYPE_E_CANTCREATETMPFILE),\n        MAKE_HRESULT_ENTRY(TYPE_E_CANTLOADLIBRARY),\n        MAKE_HRESULT_ENTRY(TYPE_E_CIRCULARTYPE),\n        MAKE_HRESULT_ENTRY(TYPE_E_DLLFUNCTIONNOTFOUND),\n        MAKE_HRESULT_ENTRY(TYPE_E_DUPLICATEID),\n        MAKE_HRESULT_ENTRY(TYPE_E_ELEMENTNOTFOUND),\n        MAKE_HRESULT_ENTRY(TYPE_E_INCONSISTENTPROPFUNCS),\n        MAKE_HRESULT_ENTRY(TYPE_E_INVALIDSTATE),\n        MAKE_HRESULT_ENTRY(TYPE_E_INVDATAREAD),\n        MAKE_HRESULT_ENTRY(TYPE_E_IOERROR),\n        MAKE_HRESULT_ENTRY(TYPE_E_LIBNOTREGISTERED),\n        MAKE_HRESULT_ENTRY(TYPE_E_NAMECONFLICT),\n        MAKE_HRESULT_ENTRY(TYPE_E_OUTOFBOUNDS),\n        MAKE_HRESULT_ENTRY(TYPE_E_QUALIFIEDNAMEDISALLOWED),\n        MAKE_HRESULT_ENTRY(TYPE_E_REGISTRYACCESS),\n        MAKE_HRESULT_ENTRY(TYPE_E_SIZETOOBIG),\n        MAKE_HRESULT_ENTRY(TYPE_E_TYPEMISMATCH),\n        MAKE_HRESULT_ENTRY(TYPE_E_UNDEFINEDTYPE),\n        MAKE_HRESULT_ENTRY(TYPE_E_UNKNOWNLCID),\n        MAKE_HRESULT_ENTRY(TYPE_E_UNSUPFORMAT),\n        MAKE_HRESULT_ENTRY(TYPE_E_WRONGTYPEKIND),\n        MAKE_HRESULT_ENTRY(VIEW_E_DRAW),\n\n        MAKE_HRESULT_ENTRY(CONNECT_E_NOCONNECTION),\n        MAKE_HRESULT_ENTRY(CONNECT_E_ADVISELIMIT),\n        MAKE_HRESULT_ENTRY(CONNECT_E_CANNOTCONNECT),\n        MAKE_HRESULT_ENTRY(CONNECT_E_OVERRIDDEN),\n\n        MAKE_HRESULT_ENTRY(CLASS_E_NOTLICENSED),\n        MAKE_HRESULT_ENTRY(CLASS_E_NOAGGREGATION),\n        MAKE_HRESULT_ENTRY(CLASS_E_CLASSNOTAVAILABLE),\n\n        MAKE_HRESULT_ENTRY(CTL_E_ILLEGALFUNCTIONCALL),\n        MAKE_HRESULT_ENTRY(CTL_E_OVERFLOW),\n        MAKE_HRESULT_ENTRY(CTL_E_OUTOFMEMORY),\n        MAKE_HRESULT_ENTRY(CTL_E_DIVISIONBYZERO),\n        MAKE_HRESULT_ENTRY(CTL_E_OUTOFSTRINGSPACE),\n        MAKE_HRESULT_ENTRY(CTL_E_OUTOFSTACKSPACE),\n        MAKE_HRESULT_ENTRY(CTL_E_BADFILENAMEORNUMBER),\n        MAKE_HRESULT_ENTRY(CTL_E_FILENOTFOUND),\n        MAKE_HRESULT_ENTRY(CTL_E_BADFILEMODE),\n        MAKE_HRESULT_ENTRY(CTL_E_FILEALREADYOPEN),\n        MAKE_HRESULT_ENTRY(CTL_E_DEVICEIOERROR),\n        MAKE_HRESULT_ENTRY(CTL_E_FILEALREADYEXISTS),\n        MAKE_HRESULT_ENTRY(CTL_E_BADRECORDLENGTH),\n        MAKE_HRESULT_ENTRY(CTL_E_DISKFULL),\n        MAKE_HRESULT_ENTRY(CTL_E_BADRECORDNUMBER),\n        MAKE_HRESULT_ENTRY(CTL_E_BADFILENAME),\n        MAKE_HRESULT_ENTRY(CTL_E_TOOMANYFILES),\n        MAKE_HRESULT_ENTRY(CTL_E_DEVICEUNAVAILABLE),\n        MAKE_HRESULT_ENTRY(CTL_E_PERMISSIONDENIED),\n        MAKE_HRESULT_ENTRY(CTL_E_DISKNOTREADY),\n        MAKE_HRESULT_ENTRY(CTL_E_PATHFILEACCESSERROR),\n        MAKE_HRESULT_ENTRY(CTL_E_PATHNOTFOUND),\n        MAKE_HRESULT_ENTRY(CTL_E_INVALIDPATTERNSTRING),\n        MAKE_HRESULT_ENTRY(CTL_E_INVALIDUSEOFNULL),\n        MAKE_HRESULT_ENTRY(CTL_E_INVALIDFILEFORMAT),\n        MAKE_HRESULT_ENTRY(CTL_E_INVALIDPROPERTYVALUE),\n        MAKE_HRESULT_ENTRY(CTL_E_INVALIDPROPERTYARRAYINDEX),\n        MAKE_HRESULT_ENTRY(CTL_E_SETNOTSUPPORTEDATRUNTIME),\n        MAKE_HRESULT_ENTRY(CTL_E_SETNOTSUPPORTED),\n        MAKE_HRESULT_ENTRY(CTL_E_NEEDPROPERTYARRAYINDEX),\n        MAKE_HRESULT_ENTRY(CTL_E_SETNOTPERMITTED),\n        MAKE_HRESULT_ENTRY(CTL_E_GETNOTSUPPORTEDATRUNTIME),\n        MAKE_HRESULT_ENTRY(CTL_E_GETNOTSUPPORTED),\n        MAKE_HRESULT_ENTRY(CTL_E_PROPERTYNOTFOUND),\n        MAKE_HRESULT_ENTRY(CTL_E_INVALIDCLIPBOARDFORMAT),\n        MAKE_HRESULT_ENTRY(CTL_E_INVALIDPICTURE),\n        MAKE_HRESULT_ENTRY(CTL_E_PRINTERERROR),\n        MAKE_HRESULT_ENTRY(CTL_E_CANTSAVEFILETOTEMP),\n        MAKE_HRESULT_ENTRY(CTL_E_SEARCHTEXTNOTFOUND),\n        MAKE_HRESULT_ENTRY(CTL_E_REPLACEMENTSTOOLONG),\n    };\n\n    // look for scode in the table\n    for (int i = 0; i < sizeof(hrNameTable) / sizeof(hrNameTable[0]); i++)\n    {\n        if (hr == hrNameTable[i].hr) {\n            swprintf_s(buf, (size_t)(bufSize - 1), hrNameTable[i].lpszName);\n            return;\n        }\n    }\n    // not found - make one up\n    swprintf_s(buf, (size_t)(bufSize - 1), L\"OLE error 0x%08x\", hr);\n}\n\n\nuint16_t *GetWin32ErrorMessage(uint16_t *buf, size_t buflen, Isolate *isolate, HRESULT hrcode, LPCOLESTR msg, LPCOLESTR msg2, LPCOLESTR desc) {\n    uint16_t *bufptr = buf;\n    size_t len;\n    if (msg) {\n        len = wcslen(msg);\n        if (len >= buflen) len = buflen - 1;\n        if (len > 0) memcpy(bufptr, msg, len * sizeof(uint16_t));\n        buflen -= len;\n        bufptr += len;\n        if (buflen > 2) {\n            bufptr[0] = ':';\n            bufptr[1] = ' ';\n            buflen -= 2;\n            bufptr += 2;\n        }\n    }\n    if (msg2) {\n        len = wcslen(msg2);\n        if (len >= buflen) len = buflen - 1;\n        if (len > 0) memcpy(bufptr, msg2, len * sizeof(uint16_t));\n        buflen -= len;\n        bufptr += len;\n        if (buflen > 1) {\n            bufptr[0] = ' ';\n            buflen -= 1;\n            bufptr += 1;\n        }\n    }\n    if (buflen > 1) {\n        len = desc ? wcslen(desc) : 0;\n        if (len > 0) {\n            if (len >= buflen) len = buflen - 1;\n            memcpy(bufptr, desc, len * sizeof(OLECHAR));\n        }\n        else {\n            len = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, hrcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPOLESTR)bufptr, (DWORD)buflen - 1, 0);\n            if (len == 0) len = swprintf_s((LPOLESTR)bufptr, buflen - 1, L\"Error 0x%08X\", hrcode);\n        }\n        buflen -= len;\n        bufptr += len;\n    }\n    if (buflen > 0) bufptr[0] = 0;\n    return buf;\n}\n\nchar *GetWin32ErrorMessage(char *buf, size_t buflen, Isolate *isolate, HRESULT hrcode, LPCOLESTR msg, LPCOLESTR msg2, LPCOLESTR desc) {\n    uint16_t buf_wide[ERROR_MESSAGE_WIDE_MAXSIZE];\n    GetWin32ErrorMessage(buf_wide, ERROR_MESSAGE_WIDE_MAXSIZE, isolate, hrcode, msg, msg2, desc);\n    int rcode = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)buf_wide, -1, buf, buflen, NULL, NULL);\n    if (rcode < 0) rcode = 0;\n    buf[rcode] = 0;\n    return buf;\n}\n\nLocal<String> GetWin32ErrorMessage(Isolate *isolate, HRESULT hrcode, LPCOLESTR msg, LPCOLESTR msg2, LPCOLESTR desc) {\n    uint16_t buf_wide[ERROR_MESSAGE_WIDE_MAXSIZE];\n    return v8str(isolate, GetWin32ErrorMessage(buf_wide, ERROR_MESSAGE_WIDE_MAXSIZE, isolate, hrcode, msg, msg2, desc));\n}\n\n//-------------------------------------------------------------------------------------------------------\n\nLocal<Value> Variant2Array(Isolate *isolate, const VARIANT &v) {\n    if ((v.vt & VT_ARRAY) == 0) return Null(isolate);\n    SAFEARRAY *varr = (v.vt & VT_BYREF) != 0 ? *v.pparray : v.parray;\n    if (!varr || varr->cDims > 2 || varr->cDims == 0) return Null(isolate);\n    else if ( varr->cDims == 2 ) return Variant2Array2( isolate, v );\n    Local<Context> ctx = isolate->GetCurrentContext();\n    VARTYPE vt = v.vt & VT_TYPEMASK;\n    LONG cnt = (LONG)varr->rgsabound[0].cElements;\n    Local<Array> arr = Array::New(isolate, cnt);\n    for (LONG i = varr->rgsabound[0].lLbound; i < varr->rgsabound[0].lLbound + cnt; i++) {\n        CComVariant vi;\n        if SUCCEEDED(SafeArrayGetElement(varr, &i, (vt == VT_VARIANT) ? (void*)&vi : (void*)&vi.byref)) {\n            if (vt != VT_VARIANT) vi.vt = vt;\n            uint32_t jsi = i - varr->rgsabound[ 0 ].lLbound;\n            arr->Set(ctx, jsi, Variant2Value(isolate, vi, true));\n        }\n    }\n    return arr;\n}\n\nstatic Local<Value> Variant2Array2(Isolate *isolate, const VARIANT &v) {\n    if ((v.vt & VT_ARRAY) == 0) return Null(isolate);\n    SAFEARRAY *varr = (v.vt & VT_BYREF) != 0 ? *v.pparray : v.parray;\n    if (!varr || varr->cDims != 2) return Null(isolate);\n    Local<Context> ctx = isolate->GetCurrentContext();\n    VARTYPE vt = v.vt & VT_TYPEMASK;\n    LONG cnt1 = (LONG)varr->rgsabound[0].cElements;\n    LONG cnt2 = (LONG)varr->rgsabound[1].cElements;\n    Local<Array> arr1 = Array::New(isolate, cnt2);\n    LONG rgIndices[ 2 ];\n    for (LONG i2 = varr->rgsabound[1].lLbound; i2 < varr->rgsabound[1].lLbound + cnt2; i2++) {\n        rgIndices[ 0 ] = i2;\n        Local<Array> arr2 = Array::New(isolate, cnt1);\n        for (LONG i1 = varr->rgsabound[0].lLbound; i1 < varr->rgsabound[0].lLbound + cnt1; i1++) {\n            CComVariant vi;\n            rgIndices[ 1 ] = i1;\n            if SUCCEEDED(SafeArrayGetElement(varr, &rgIndices[0], (vt == VT_VARIANT) ? (void*)&vi : (void*)&vi.byref)) {\n                if (vt != VT_VARIANT) vi.vt = vt;\n                uint32_t jsi = (uint32_t)i1 - varr->rgsabound[ 0 ].lLbound;\n                arr2->Set(ctx, jsi, Variant2Value(isolate, vi, true));\n            }\n        }\n        uint32_t jsi = i2 - varr->rgsabound[ 1 ].lLbound;\n        arr1->Set(ctx, jsi, arr2);\n    }\n    return arr1;\n}\n\nLocal<Value> Variant2Value(Isolate *isolate, const VARIANT &v, bool allow_disp) {\n    if ((v.vt & VT_ARRAY) != 0) return Variant2Array(isolate, v);\n    VARTYPE vt = (v.vt & VT_TYPEMASK);\n    bool by_ref = (v.vt & VT_BYREF) != 0;\n    switch (vt) {\n    case VT_NULL:\n        return Null(isolate);\n    case VT_I1:\n        return Int32::New(isolate, (int32_t)(by_ref ? *v.pcVal : v.cVal));\n    case VT_I2:\n        return Int32::New(isolate, (int32_t)(by_ref ? *v.piVal : v.iVal));\n    case VT_I4:\n        return Int32::New(isolate, (int32_t)(by_ref ? *v.plVal : v.lVal));\n    case VT_INT:\n        return Int32::New(isolate, (int32_t)(by_ref ? *v.pintVal : v.intVal));\n    case VT_UI1:\n        return Int32::New(isolate, (uint32_t)(by_ref ? *v.pbVal : v.bVal));\n    case VT_UI2:\n        return Int32::New(isolate, (uint32_t)(by_ref ? *v.puiVal : v.uiVal));\n    case VT_UI4:\n        return Int32::New(isolate, (uint32_t)(by_ref ? *v.pulVal : v.ulVal));\n    case VT_UINT:\n        return Int32::New(isolate, (uint32_t)(by_ref ? *v.puintVal : v.uintVal));\n    case VT_I8:\n        return Number::New(isolate, (double)(by_ref ? *v.pllVal : v.llVal));\n    case VT_UI8:\n        return Number::New(isolate, (double)(by_ref ? *v.pullVal : v.ullVal));\n    case VT_CY:\n        return Number::New(isolate, (double)(by_ref ? v.pcyVal : &v.cyVal)->int64 / 10000.);\n    case VT_R4:\n        return Number::New(isolate, by_ref ? *v.pfltVal : v.fltVal);\n    case VT_R8:\n        return Number::New(isolate, by_ref ? *v.pdblVal : v.dblVal);\n    case VT_DATE: {\n        Local<Value> ret;\n        if (!Date::New(isolate->GetCurrentContext(), FromOleDate(by_ref ? *v.pdate : v.date)).ToLocal(&ret))\n            ret = Undefined(isolate);\n        return ret;\n    }\n    case VT_DECIMAL: {\n        DOUBLE dblval;\n        if FAILED(VarR8FromDec(by_ref ? v.pdecVal : &v.decVal, &dblval)) return Undefined(isolate);\n        return Number::New(isolate, dblval);\t\t\n    }\n    case VT_BOOL:\n        return Boolean::New(isolate, (by_ref ? *v.pboolVal : v.boolVal) != VARIANT_FALSE);\n    case VT_DISPATCH: {\n        IDispatch *disp = (by_ref ? *v.ppdispVal : v.pdispVal);\n        if (!disp) return Null(isolate);\n        if (allow_disp) {\n            DispObjectImpl *impl;\n            if (disp->QueryInterface(CLSID_DispObjectImpl, (void**)&impl) == S_OK) {\n                return impl->obj.Get(isolate);\n            }\n            return DispObject::NodeCreate(isolate, disp, L\"Dispatch\", option_auto);\n        }\n        return v8str(isolate, \"[Dispatch]\");\n    }\n    case VT_UNKNOWN: {\n        if (allow_disp)\n        {\n            CComPtr<IDispatch> disp;\n            if (UnknownDispGet(by_ref ? *v.ppunkVal : v.punkVal, &disp)) {\n                return DispObject::NodeCreate(isolate, disp, L\"Unknown\", option_auto);\n            }\n        }\n        // Check for NULL value\n        if ((by_ref && *v.ppunkVal) || v.punkVal)\n        {\n            if (allow_disp) {\n                return VariantObject::NodeCreate(isolate, v);\n            }\n            return v8str(isolate, \"[Unknown]\");\n        }\n        return Null(isolate);\n    }\n    case VT_BSTR: {\n        BSTR bstr = by_ref ? (v.pbstrVal ? *v.pbstrVal : nullptr) : v.bstrVal;\n        //if (!bstr) return String::Empty(isolate);\n        // Sometimes we need to distinguish between NULL and empty string\n        if (!bstr) return Null(isolate);\n        return v8str(isolate, bstr);\n    }\n    case VT_VARIANT: \n        if (v.pvarVal) return Variant2Value(isolate, *v.pvarVal, allow_disp);\n    }\n    return Undefined(isolate);\n}\n\nLocal<Value> Variant2String(Isolate *isolate, const VARIANT &v) {\n    char buf[256] = {};\n    VARTYPE vt = (v.vt & VT_TYPEMASK);\n    bool by_ref = (v.vt & VT_BYREF) != 0;\n    switch (vt) {\n    case VT_EMPTY:\n        strcpy(buf, \"EMPTY\");\n        break;\n    case VT_NULL:\n        strcpy(buf, \"NULL\");\n        break;\n    case VT_I1:\n        sprintf_s(buf, \"%i\", (int)(by_ref ? *v.pcVal : v.cVal));\n        break;\n    case VT_I2:\n        sprintf_s(buf, \"%i\", (int)(by_ref ? *v.piVal : v.iVal));\n        break;\n    case VT_I4:\n        sprintf_s(buf, \"%i\", (int)(by_ref ? *v.plVal : v.lVal));\n        break;\n    case VT_INT:\n        sprintf_s(buf, \"%i\", (int)(by_ref ? *v.pintVal : v.intVal));\n        break;\n    case VT_UI1:\n        sprintf_s(buf, \"%u\", (unsigned int)(by_ref ? *v.pbVal : v.bVal));\n        break;\n    case VT_UI2:\n        sprintf_s(buf, \"%u\", (unsigned int)(by_ref ? *v.puiVal : v.uiVal));\n        break;\n    case VT_UI4:\n        sprintf_s(buf, \"%u\", (unsigned int)(by_ref ? *v.pulVal : v.ulVal));\n        break;\n    case VT_UINT:\n        sprintf_s(buf, \"%u\", (unsigned int)(by_ref ? *v.puintVal : v.uintVal));\n        break;\n    case VT_CY:\n    case VT_I8:\n        sprintf_s(buf, \"%lld\", (by_ref ? *v.pllVal : v.llVal));\n        break;\n    case VT_UI8:\n        sprintf_s(buf, \"%llu\", (by_ref ? *v.pullVal : v.ullVal));\n        break;\n    case VT_R4:\n        sprintf_s(buf, \"%f\", (double)(by_ref ? *v.pfltVal : v.fltVal));\n        break;\n    case VT_R8:\n        sprintf_s(buf, \"%f\", (double)(by_ref ? *v.pdblVal : v.dblVal));\n        break;\n    case VT_DATE: {\n        Local<Value> ret;\n        if (!Date::New(isolate->GetCurrentContext(), FromOleDate(by_ref ? *v.pdate : v.date)).ToLocal(&ret))\n            ret = Undefined(isolate);\n        return ret;\n    }\n    case VT_DECIMAL: {\n        DOUBLE dblval;\n        if FAILED(VarR8FromDec(by_ref ? v.pdecVal : &v.decVal, &dblval)) return Undefined(isolate); \n        sprintf_s(buf, \"%f\", (double)dblval);\n        break;\t\t\n    }\n    case VT_BOOL:\n        strcpy(buf, ((by_ref ? *v.pboolVal : v.boolVal) == VARIANT_FALSE) ? \"false\" : \"true\");\n        break;\n    case VT_DISPATCH:\n        strcpy(buf, \"[Dispatch]\");\n        break;\n    case VT_UNKNOWN: \n        strcpy(buf, \"[Unknown]\");\n        break;\n    case VT_VARIANT:\n        if (v.pvarVal) return Variant2String(isolate, *v.pvarVal);\n        break;\n    default:\n        CComVariant tmp;\n        if (SUCCEEDED(VariantChangeType(&tmp, &v, 0, VT_BSTR)) && tmp.vt == VT_BSTR && v.bstrVal != nullptr) {\n            return v8str(isolate, v.bstrVal);\n        }\n    }\n    return v8str(isolate, buf);\n}\n\nvoid Value2Variant(Isolate *isolate, Local<Value> &val, VARIANT &var, VARTYPE vt) {\n    Local<Context> ctx = isolate->GetCurrentContext();\n    if (val.IsEmpty() || val->IsUndefined()) {\n        var.vt = VT_EMPTY;\n    }\n    else if (val->IsNull()) {\n        var.vt = VT_NULL;\n    }\n    else if (val->IsInt32()) {\n        //var.lVal = val->Int32Value();\n        var.lVal = val->Int32Value(ctx).FromMaybe(0);\n        var.vt = VT_I4;\n    }\n    else if (val->IsUint32()) {\n        //var.ulVal = val->Uint32Value();\n        var.ulVal = val->Uint32Value(ctx).FromMaybe(0);\n        var.vt = (var.ulVal <= 0x7FFFFFFF) ? VT_I4 : VT_UI4;\n    }\n    else if (val->IsNumber()) {\n        //var.dblVal = val->NumberValue();\n        var.dblVal = val->NumberValue(ctx).FromMaybe(0);\n        var.vt = VT_R8;\n    }\n    else if (val->IsDate()) {\n        //var.date = ToOleDate(val->NumberValue());\n        var.date = ToOleDate(val->NumberValue(ctx).FromMaybe(0));\n        var.vt = VT_DATE;\n    }\n    else if (val->IsBoolean()) {\n        var.boolVal = NODE_BOOL(isolate, val) ? VARIANT_TRUE : VARIANT_FALSE;\n        var.vt = VT_BOOL;\n    }\n    else if (val->IsArray() && (vt != VT_NULL)) {\n        Local<Array> arr = v8::Local<Array>::Cast(val);\n        uint32_t len = arr->Length();\n        if (vt == VT_EMPTY) vt = VT_VARIANT;\n        var.vt = VT_ARRAY | vt;\n        // if array of arrays, create a 2 dim array, choose the 2nd bound\n        uint32_t second_len = 0;\n        if (len) {\n            Local<Value> first_value;\n            if (arr->Get(ctx, 0).ToLocal(&first_value)) {\n                if (first_value->IsArray()) second_len = v8::Local<Array>::Cast(first_value)->Length();\n            }\n        }\n        if ( second_len == 0 ) {\n            var.parray = SafeArrayCreateVector(vt, 0, len);\n            for (uint32_t i = 0; i < len; i++) {\n                CComVariant v;\n                Local<Value> val;\n                if (!arr->Get(ctx, i).ToLocal(&val)) val = Undefined(isolate);\n                Value2Variant(isolate, val, v, vt);\n                void *pv;\n                if (vt == VT_VARIANT) pv = (void*)&v;\n                else if (vt == VT_DISPATCH || vt == VT_UNKNOWN || vt == VT_BSTR) pv = v.byref;\n                else pv = (void*)&v.byref;\n                SafeArrayPutElement(var.parray, (LONG*)&i, pv);\n            }\n        }\n        else {\n            SAFEARRAYBOUND rgsabounds[ 2 ];\n            rgsabounds[ 0 ].lLbound = rgsabounds[ 1 ].lLbound = 0;\n            rgsabounds[ 0 ].cElements = len;\n            rgsabounds[ 1 ].cElements = second_len;\n            var.parray = SafeArrayCreate(vt, 2, rgsabounds);\n            LONG rgIndices[ 2 ];\n            for (uint32_t i = 0; i < len; i++) {\n                rgIndices[ 0 ] = i;\n                Local<Value> maybearray;\n                Local<Array> arr2;\n                bool bGotArray = false;\n                if (arr->Get( ctx, i ).ToLocal( &maybearray ) && maybearray->IsArray()) {\n                    bGotArray = true;\n                    arr2 = v8::Local<Array>::Cast(maybearray);\n                }\n                for (uint32_t j = 0; j < second_len; j++) {\n                    rgIndices[ 1 ] = j;\n                    Local<Value> val;\n                    if ( bGotArray ) {\n                        if (!arr2->Get(ctx, j).ToLocal(&val)) val = Undefined(isolate);\n                    }\n                    else {\n                        // if no arrays for a \"row\", the value is put only in first \"col\"\n                        if ( j == 0 ) val = maybearray;\n                        else val = Undefined(isolate);\n                    }\n                    CComVariant v;\n                    Value2Variant(isolate, val, v, vt);\n                    void *pv;\n                    if (vt == VT_VARIANT) pv = (void*)&v;\n                    else if (vt == VT_DISPATCH || vt == VT_UNKNOWN || vt == VT_BSTR) pv = v.byref;\n                    else pv = (void*)&v.byref;\n                    SafeArrayPutElement(var.parray, rgIndices, pv);\n                }\n            }\n        }\n        vt = VT_EMPTY;\n    }\n    else if (val->IsObject()) {\n        auto obj = Local<Object>::Cast(val);\n        if (!DispObject::GetValueOf(isolate, obj, var) && !VariantObject::GetValueOf(isolate, obj, var)) {\n            var.vt = VT_DISPATCH;\n            var.pdispVal = new DispObjectImpl(obj);\n            var.pdispVal->AddRef();\n        }\n    }\n    else {\n        String::Value str(isolate, val);\n        var.vt = VT_BSTR;\n        // For some apps there is still a difference between \"\" and NULL string, so we should support it here gracefully\n        if (*str)\n        {\n            var.bstrVal = SysAllocString((LPOLESTR)*str);\n        }\n        else {\n            var.bstrVal = 0;\n        }\n        //var.bstrVal = (str.length() > 0) ? SysAllocString((LPOLESTR)*str) : 0;\n    }\n    if (vt != VT_EMPTY && vt != VT_NULL && vt != VT_VARIANT) {\n        if FAILED(VariantChangeType(&var, &var, 0, vt))\n            VariantClear(&var);\n    }\n}\n\nvoid Value2SafeArray(Isolate *isolate, Local<Value> &val, VARIANT &var, VARTYPE vt) {\n    Local<Context> ctx = isolate->GetCurrentContext();\n    if (val.IsEmpty() || val->IsUndefined()) {\n        var.vt = VT_EMPTY;\n    }\n    else if (val->IsNull()) {\n        var.vt = VT_NULL;\n    }\n    else if (val->IsObject()) {\n        // Test conversion dispatch pointer to Uint8Array\n        if (vt == VT_UI1) {\n            auto obj = Local<Object>::Cast(val);\n            auto ptr = DispObject::GetDispPtr(isolate, obj);\n            size_t len = sizeof(UINT_PTR);\n            var.vt = VT_ARRAY | vt;\n            var.parray = SafeArrayCreateVector(vt, 0, len);\n            if (var.parray != nullptr) memcpy(var.parray->pvData, &ptr, len);\n        }\n    }\n    else {\n        var.vt = VT_EMPTY;\n    }\n}\n\nbool Value2Unknown(Isolate *isolate, Local<Value> &val, IUnknown **unk) {\n    if (val.IsEmpty() || !val->IsObject()) return false;\n    auto obj = Local<Object>::Cast(val);\n    CComVariant var;\n    if (!DispObject::GetValueOf(isolate, obj, var) && !VariantObject::GetValueOf(isolate, obj, var)) return false;\n    return VariantUnkGet(&var, unk);\n}\n\nbool UnknownDispGet(IUnknown *unk, IDispatch **disp) {\n    if (!unk) return false;\n    if SUCCEEDED(unk->QueryInterface(__uuidof(IDispatch), (void**)disp)) {\n        return true;\n    }\n    CComPtr<IEnumVARIANT> enum_ptr;\n    if SUCCEEDED(unk->QueryInterface(__uuidof(IEnumVARIANT), (void**)&enum_ptr)) {\n        *disp = new DispEnumImpl(enum_ptr);\n        (*disp)->AddRef();\n        return true;\n    }\n    return false;\n}\n\nbool VariantUnkGet(VARIANT *v, IUnknown **punk) {\n    IUnknown *unk = NULL;\n    if ((v->vt & VT_TYPEMASK) == VT_DISPATCH) {\n        unk = ((v->vt & VT_BYREF) != 0) ? *v->ppdispVal : v->pdispVal;\n    }\n    else if ((v->vt & VT_TYPEMASK) == VT_UNKNOWN) {\n        unk = ((v->vt & VT_BYREF) != 0) ? *v->ppunkVal : v->punkVal;\n    }\n    if (!unk) return false;\n    unk->AddRef();\n    *punk = unk;\n    return true;\n}\n\nbool VariantDispGet(VARIANT *v, IDispatch **pdisp) {\n    /*\n    if ((v->vt & VT_ARRAY) != 0) {\n        *disp = new DispArrayImpl(*v);\n        (*disp)->AddRef();\n        return true;\n    }\n    */\n    if ((v->vt & VT_TYPEMASK) == VT_DISPATCH) {\n        IDispatch *disp = ((v->vt & VT_BYREF) != 0) ? *v->ppdispVal : v->pdispVal;\n        if (!disp) return false;\n        disp->AddRef();\n        *pdisp = disp;\n        return true;\n    }\n    if ((v->vt & VT_TYPEMASK) == VT_UNKNOWN) {\n        return UnknownDispGet(((v->vt & VT_BYREF) != 0) ? *v->ppunkVal : v->punkVal, pdisp);\n    }\n    return false;\n}\n\n//-------------------------------------------------------------------------------------------------------\n// DispArrayImpl implemetation\n\nHRESULT STDMETHODCALLTYPE DispArrayImpl::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {\n    if (cNames != 1 || !rgszNames[0]) return DISP_E_UNKNOWNNAME;\n    LPOLESTR name = rgszNames[0];\n    if (wcscmp(name, L\"length\") == 0) *rgDispId = 1;\n    else return DISP_E_UNKNOWNNAME;\n    return S_OK;\n}\n\nHRESULT STDMETHODCALLTYPE DispArrayImpl::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {\n    HRESULT hrcode = S_OK;\n    UINT argcnt = pDispParams->cArgs;\n    VARIANT *args = pDispParams->rgvarg;\n\n    if ((var.vt & VT_ARRAY) == 0) return E_NOTIMPL;\n    SAFEARRAY *arr = ((var.vt & VT_BYREF) != 0) ? *var.pparray : var.parray;\n\n    switch (dispIdMember) {\n    case 1: {\n        if (pVarResult) {\n            pVarResult->vt = VT_INT;\n            pVarResult->intVal = (INT)(arr ? arr->rgsabound[0].cElements : 0);\n        }\n        return hrcode; }\n    }\n    return E_NOTIMPL;\n}\n\n//-------------------------------------------------------------------------------------------------------\n// DispEnumImpl implemetation\n\nHRESULT STDMETHODCALLTYPE DispEnumImpl::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {\n    if (cNames != 1 || !rgszNames[0]) return DISP_E_UNKNOWNNAME;\n    LPOLESTR name = rgszNames[0];\n    if (wcscmp(name, L\"Next\") == 0) *rgDispId = 1;\n    else if (wcscmp(name, L\"Skip\") == 0) *rgDispId = 2;\n    else if (wcscmp(name, L\"Reset\") == 0) *rgDispId = 3;\n    else if (wcscmp(name, L\"Clone\") == 0) *rgDispId = 4;\n    else return DISP_E_UNKNOWNNAME;\n    return S_OK;\n}\n\nHRESULT STDMETHODCALLTYPE DispEnumImpl::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {\n    HRESULT hrcode = S_OK;\n    UINT argcnt = pDispParams->cArgs;\n    VARIANT *args = pDispParams->rgvarg;\n    switch (dispIdMember) {\n    case 1: {\n        CComVariant arr;\n        ULONG fetched, celt = (argcnt > 0) ? Variant2Int(args[argcnt - 1], (ULONG)1) : 1;\n        if (!pVarResult || celt == 0) hrcode = E_INVALIDARG;\n        if SUCCEEDED(hrcode) hrcode = arr.ArrayCreate(VT_VARIANT, celt);\n        if SUCCEEDED(hrcode) hrcode = ptr->Next(celt, arr.ArrayGet<VARIANT>(0), &fetched);\n        if SUCCEEDED(hrcode) {\n            if (fetched == 0) pVarResult->vt = VT_EMPTY;\n            else if (fetched == 1) {\n                VARIANT *v = arr.ArrayGet<VARIANT>(0);\n                *pVarResult = *v;\n                v->vt = VT_EMPTY;\n            }\n            else {\n                if (fetched < celt) hrcode = arr.ArrayResize(fetched);\n                if SUCCEEDED(hrcode) arr.Detach(pVarResult);\n            }\n        }\n        return hrcode; \n        }\n    case 2: {\n        if (pVarResult) pVarResult->vt = VT_EMPTY;\n        ULONG celt = (argcnt > 0) ? Variant2Int(args[argcnt - 1], (ULONG)1) : 1;\n        return ptr->Skip(celt); \n        }\n    case 3: {\n        if (pVarResult) pVarResult->vt = VT_EMPTY;\n        return ptr->Reset(); \n        }\n    case 4: {\n        std::unique_ptr<DispEnumImpl> disp;\n        hrcode = pVarResult ? ptr->Clone(&disp->ptr) : E_INVALIDARG;\n        if SUCCEEDED(hrcode) {\n            disp->AddRef();\n            pVarResult->vt = VT_DISPATCH;\n            pVarResult->pdispVal = disp.release();\n        }\n        return hrcode; }\n    }\n    return E_NOTIMPL;\n}\n\n//-------------------------------------------------------------------------------------------------------\n// DispObjectImpl implemetation\n\nHRESULT STDMETHODCALLTYPE DispObjectImpl::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {\n    if (cNames != 1 || !rgszNames[0]) return DISP_E_UNKNOWNNAME;\n    std::wstring name(rgszNames[0]);\n    name_ptr &ptr = names[name];\n    if (!ptr) {\n        ptr.reset(new name_t(dispid_next++, name));\n        index.insert(index_t::value_type(ptr->dispid, ptr));\n    }\n    *rgDispId = ptr->dispid;\n    return S_OK;\n}\n\nHRESULT STDMETHODCALLTYPE DispObjectImpl::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {\n    Isolate *isolate = Isolate::GetCurrent();\n    Local<Context> ctx = isolate->GetCurrentContext();\n    Local<Object> self = obj.Get(isolate);\n    Local<Value> name, val, ret;\n\n    // Prepare name by member id\n    if (!index.empty()) {\n        index_t::const_iterator p = index.find(dispIdMember);\n        if (p == index.end())\n        {\n            // DispID may be 0 for  regular member or for DISPID_VALUE.\n            // Since regular method not found assume DISPID_VALUE if it is 0.\n            if(dispIdMember != DISPID_VALUE) return DISP_E_MEMBERNOTFOUND;\n        } else {\n            name_t& info = *p->second;\n            name = v8str(isolate, info.name.c_str());\n        }\n    }\n    // Set property value\n    if ((wFlags & DISPATCH_PROPERTYPUT) != 0) {\n        UINT argcnt = pDispParams->cArgs;\n        VARIANT *key = (argcnt > 1) ? &pDispParams->rgvarg[--argcnt] : nullptr;\n        if (argcnt > 0) val = Variant2Value(isolate, pDispParams->rgvarg[--argcnt], true);\n        else val = Undefined(isolate);\n        bool rcode;\n\n        // Set simple object property value\n        if (!key) {\n            if (name.IsEmpty()) return DISP_E_MEMBERNOTFOUND;\n            rcode = self->Set(ctx, name, val).FromMaybe(false);\n        }\n\n        // Set object/array item value\n        else {\n            Local<Object> target;\n            if (name.IsEmpty()) target = self;\n            else {\n                Local<Value> obj;\n                if (self->Get(ctx, name).ToLocal(&obj) && !obj.IsEmpty()) target = Local<Object>::Cast(obj);\n                if (target.IsEmpty()) return DISP_E_BADCALLEE;\n            }\n\n            LONG index = Variant2Int<LONG>(*key, -1);\n            if (index >= 0) rcode = target->Set(ctx, (uint32_t)index, val).FromMaybe(false);\n            else rcode = target->Set(ctx, Variant2Value(isolate, *key, false), val).FromMaybe(false);\n        }\n\n        // Store result\n        if (pVarResult) {\n            pVarResult->vt = VT_BOOL;\n            pVarResult->boolVal = rcode ? VARIANT_TRUE : VARIANT_FALSE;\n        }\n        return S_OK;\n    }\n\n    // Prepare property item\n    if (name.IsEmpty()) val = self;\n    else self->Get(ctx, name).ToLocal(&val);\n\n    // Call property as method\n    if ((wFlags & DISPATCH_METHOD) != 0) {\n        wFlags = 0;\n        NodeArguments args(isolate, pDispParams, true, reverse_arguments);\n        int argcnt = (int)args.items.size();\n        Local<Value> *argptr = (argcnt > 0) ? &args.items[0] : nullptr;\n        if (val->IsFunction()) {\n            Local<Function> func = Local<Function>::Cast(val);\n            if (func.IsEmpty()) return DISP_E_BADCALLEE;\n            func->Call(isolate->GetCurrentContext(), self, argcnt, argptr).ToLocal(&ret);\n        }\n        else if (val->IsObject()) {\n            wFlags = DISPATCH_PROPERTYGET;\n            //Local<Object> target = val->ToObject();\n            //target->CallAsFunction(isolate->GetCurrentContext(), target, args.items.size(), &args.items[0]).ToLocal(&ret);\n        }\n        else {\n            ret = val;\n        }\n    }\n\n    // Get property value\n    if ((wFlags & DISPATCH_PROPERTYGET) != 0) {\n        if (pDispParams->cArgs == 1) {\n            Local<Object> target;\n            if (!val.IsEmpty()) target = Local<Object>::Cast(val);\n            if (target.IsEmpty()) return DISP_E_BADCALLEE;\n            VARIANT &key = pDispParams->rgvarg[0];\n            LONG index = Variant2Int<LONG>(key, -1);\n            if (index >= 0) target->Get(ctx, (uint32_t)index).ToLocal(&ret);\n            else target->Get(ctx, Variant2Value(isolate, key, false)).ToLocal(&ret);\n        }\n        else {\n            ret = val;\n        }\n    }\n\n    // Store result\n    if (pVarResult) {\n        Value2Variant(isolate, ret, *pVarResult, VT_NULL);\n    }\n    return S_OK;\n}\n\n/*\n* Microsoft OLE Date type:\n* https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/82ab7w69(v=vs.90)\n*/\n\ndouble FromOleDate(double oleDate) {\n    double posixDate = oleDate - 25569; // days from 1899 dec 30\n    posixDate *= 24 * 60 * 60 * 1000;   // days to milliseconds\n    return posixDate;\n}\n\ndouble ToOleDate(double posixDate) {\n    double oleDate = posixDate / (24 * 60 * 60 * 1000); // milliseconds to days\n    oleDate += 25569;                                   // days from 1899 dec 30\n    return oleDate;\n}\n\n//-------------------------------------------------------------------------------------------------------\n\nbool NodeMethods::get(Isolate* isolate, const std::wstring &name, Local<Function>* value) {\n    auto ptr = items.find(name);\n    if (ptr == items.end()) return false;\n    Local<FunctionTemplate> ft = ptr->second->Get(isolate);\n    return ft->GetFunction(isolate->GetCurrentContext()).ToLocal(value);\n}\n\nvoid NodeMethods::add(Isolate* isolate, Local<FunctionTemplate>& clazz, const char* name, FunctionCallback callback) {\n    // see NODE_SET_PROTOTYPE_METHOD\n    HandleScope handle_scope(isolate);\n    Local<Signature> s = Signature::New(isolate, clazz);\n    Local<FunctionTemplate> t = FunctionTemplate::New(isolate, callback, Local<Value>(), s);\n    Local<String> fn_name = String::NewFromUtf8(isolate, name, NewStringType::kInternalized).ToLocalChecked();\n    t->SetClassName(fn_name);\n    \n    clazz->PrototypeTemplate()->Set(fn_name, t);\n\n    String::Value vname(isolate, fn_name);\n    item_type item(new Persistent<FunctionTemplate>(isolate, t));\n    items.emplace(std::wstring((const wchar_t *)*vname), item);\n}\n\n//-------------------------------------------------------------------------------------------------------\n\n/* Message loop. Just like with WScript it is executed while the script is waiting. \n   So if you have something showing, for example, a popup message, then you will only\n   see it when you do WScript.Sleep.\n*/\n\nvoid DoEvents()\n{\n    MSG msg;\n    BOOL result;\n\n    if (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))\n    {\n        result = ::GetMessage(&msg, NULL, 0, 0);\n        if (result == 0) // WM_QUIT\n        {\n            ::PostQuitMessage(msg.wParam);\n        }\n        else if (result == -1)\n        {\n            // Handle errors/exit application, etc.\n        }\n        else\n        {\n            ::TranslateMessage(&msg);\n            ::DispatchMessage(&msg);\n        }\n    }\n}\n\n/* Returns the amount of milliseconds elapsed since the UNIX epoch. Works on both\n * windows and linux. */\n\nlong long GetTimeMs64()\n{\n    /* Windows */\n    FILETIME ft;\n    LARGE_INTEGER li;\n\n    /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it\n     * to a LARGE_INTEGER structure. */\n    GetSystemTimeAsFileTime(&ft);\n    li.LowPart = ft.dwLowDateTime;\n    li.HighPart = ft.dwHighDateTime;\n\n    long long ret = li.QuadPart;\n    ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */\n    ret /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */\n\n    return ret;\n\n}\n\n// Sleep is essential to have proper WScript emulation\nvoid WinaxSleep(const FunctionCallbackInfo<Value>& args) {\n    Isolate* isolate = args.GetIsolate();\n    Local<Context> ctx = isolate->GetCurrentContext();\n\n    if (args.Length() == 0 && !args[0]->IsUint32()) {\n        isolate->ThrowException(InvalidArgumentsError(isolate));\n        return;\n    }\n    uint32_t ms = (args[0]->Uint32Value(ctx)).FromMaybe(0);\n    long long start = GetTimeMs64();\n    do\n    {\n        DoEvents();\n        Sleep(1);\n    } while (GetTimeMs64() - start < ms);\n    args.GetReturnValue().SetUndefined();\n}\n\n// Get a COM pointer from a window found by it's text\nHRESULT GetAccessibleObject(const wchar_t* pszWindowText, CComPtr<IUnknown>& spIUnknown) {\n    struct ew {\n        static BOOL CALLBACK ecp(HWND hWnd, LPARAM lParam) {\n            wchar_t szWindowText[128];\n            if (GetWindowTextW(hWnd, szWindowText, _countof(szWindowText))) {\n                ewp* pparams = reinterpret_cast<ewp*>(lParam);\n                if (!wcscmp(szWindowText, pparams->pszWindowText)) {\n                    pparams->hWnd = hWnd;\n                    return FALSE;\n                }\n            }\n            return TRUE;\n        }\n        struct ewp {\n            const wchar_t* pszWindowText;\n            HWND hWnd;\n        };\n    };\n    ew::ewp params{ pszWindowText, nullptr };\n    EnumChildWindows(GetDesktopWindow(), ew::ecp, reinterpret_cast<LPARAM>(&params));\n    if (params.hWnd == nullptr) return _HRESULT_TYPEDEF_(0x80070057L); // ERROR_INVALID_PARAMETER\n    return AccessibleObjectFromWindow(params.hWnd, OBJID_NATIVEOM, IID_IUnknown,\n                                      reinterpret_cast<void**>(&spIUnknown));\n}"
  },
  {
    "path": "src/utils.h",
    "content": "//-------------------------------------------------------------------------------------------------------\n// Project: NodeActiveX\n// Author: Yuri Dursin\n// Description: Common utilities for translation COM - NodeJS\n//-------------------------------------------------------------------------------------------------------\n\n#pragma once\n\n//-------------------------------------------------------------------------------------------------------\n\n#ifdef _DEBUG\n#define NODE_DEBUG\n#endif\n\n#if (V8_MAJOR_VERSION > 7) || (V8_MAJOR_VERSION == 7 && V8_MINOR_VERSION >= 1)\n    #define NODE_BOOL_ISOLATE\n#endif\n\n#ifdef NODE_BOOL_ISOLATE\n    #define NODE_BOOL(isolate, v) v->BooleanValue(isolate)\n#else\n    #define NODE_BOOL(isolate, v) v->BooleanValue(isolate->GetCurrentContext()).FromMaybe(false)\n#endif\n\n#if (V8_MAJOR_VERSION >= 12)\n#define NODE_INTERCEPTED\ntypedef PropertyCallbackInfo<Value> PropertyCallbackInfoGetter;\ntypedef PropertyCallbackInfo<void> PropertyCallbackInfoSetter;\n#else\ntypedef PropertyCallbackInfo<Value> PropertyCallbackInfoGetter;\ntypedef PropertyCallbackInfo<Value> PropertyCallbackInfoSetter;\n#endif\n\n#ifdef NODE_DEBUG\n    #define NODE_DEBUG_PREFIX \"### \"\n    #define NODE_DEBUG_MSG(msg) { printf(NODE_DEBUG_PREFIX\"%s\", msg); std::cout << std::endl; }\n    #define NODE_DEBUG_FMT(msg, arg) { std::cout << NODE_DEBUG_PREFIX; printf(msg, arg); std::cout << std::endl; }\n    #define NODE_DEBUG_FMT2(msg, arg, arg2) { std::cout << NODE_DEBUG_PREFIX; printf(msg, arg, arg2); std::cout << std::endl; }\n#else\n    #define NODE_DEBUG_MSG(msg)\n    #define NODE_DEBUG_FMT(msg, arg)\n    #define NODE_DEBUG_FMT2(msg, arg, arg2)\n#endif\n\ninline Local<String> v8str(Isolate *isolate, const char *text) {\n    Local<String> str;\n    if (!text || !String::NewFromUtf8(isolate, text, NewStringType::kNormal).ToLocal(&str)) {\n        str = String::Empty(isolate);\n    }\n    return str;\n}\n\ninline Local<String> v8str(Isolate *isolate, const uint16_t *text) {\n    Local<String> str;\n    if (!text || !String::NewFromTwoByte(isolate, (const uint16_t*)text, NewStringType::kNormal).ToLocal(&str)) {\n        str = String::Empty(isolate);\n    }\n    return str;\n}\n\ninline Local<String> v8str(Isolate *isolate, const wchar_t *text) {\n    return v8str(isolate, (const uint16_t *)text);\n}\n\ntemplate <class T>\nusing is_holder_v2 = std::is_member_function_pointer<decltype(&T::HolderV2)>;\n \ntemplate<class T>\ninline std::enable_if<!is_holder_v2<T>::value, Local<Object>>::type\nv8this(const T& args) { return args.This(); }\n\ntemplate<class T>\ninline std::enable_if<is_holder_v2<T>::value, Local<Object>>::type\nv8this(const T& args) { return args.HolderV2();  }\n\n//-------------------------------------------------------------------------------------------------------\n#ifndef USE_ATL\n\nclass CComVariant : public VARIANT {\npublic:\n    inline CComVariant() { \n        memset((VARIANT*)this, 0, sizeof(VARIANT));\n    }\n    inline CComVariant(const CComVariant &src) { \n        memset((VARIANT*)this, 0, sizeof(VARIANT));\n        VariantCopyInd(this, &src);\n    }\n    inline CComVariant(const VARIANT &src) { \n        memset((VARIANT*)this, 0, sizeof(VARIANT));\n        VariantCopyInd(this, &src);\n    }\n    inline CComVariant(LONG v) { \n        memset((VARIANT*)this, 0, sizeof(VARIANT));\n        vt = VT_I4;\n        lVal = v; \n    }\n    inline CComVariant(LPOLESTR v) {\n        memset((VARIANT*)this, 0, sizeof(VARIANT));\n        vt = VT_BSTR;\n        bstrVal = SysAllocString(v);\n    }\n    inline ~CComVariant() {\n        Clear(); \n    }\n    inline void Clear() {\n        if (vt != VT_EMPTY) {\n            VariantClear(this);\n        }\n    }\n    inline void Detach(VARIANT *dst) {\n        *dst = *this;\n        vt = VT_EMPTY;\n    }\n    inline HRESULT CopyTo(VARIANT *dst) {\n        return VariantCopy(dst, this);\n    }\n\n    inline HRESULT ChangeType(VARTYPE vtNew, const VARIANT* pSrc = NULL) {\n        return VariantChangeType(this, pSrc ? pSrc : this, 0, vtNew);\n    }\n\n    inline ULONG ArrayLength() {\n        if ((vt & VT_ARRAY) == 0) return 0;\n        SAFEARRAY *varr = (vt & VT_BYREF) != 0 ? *pparray : parray;\n        return varr ? varr->rgsabound[0].cElements : 0;\n    }\n\n    inline HRESULT ArrayGet(LONG index, CComVariant &var) {\n        if ((vt & VT_ARRAY) == 0) return E_NOTIMPL;\n        SAFEARRAY *varr = (vt & VT_BYREF) != 0 ? *pparray : parray;\n        if (!varr) return E_FAIL;\n        index += varr->rgsabound[0].lLbound;\n        VARTYPE vart = vt & VT_TYPEMASK;\n        HRESULT hr = SafeArrayGetElement(varr, &index, (vart == VT_VARIANT) ? (void*)&var : (void*)&var.byref);\n        if (SUCCEEDED(hr) && vart != VT_VARIANT) var.vt = vart;\n        return hr;\n    }\n    template<typename T>\n    inline T* ArrayGet(ULONG index = 0) {\n        return ((T*)parray->pvData) + index;\n    }\n    inline HRESULT ArrayCreate(VARTYPE avt, ULONG cnt) {\n        Clear();\n        parray = SafeArrayCreateVector(avt, 0, cnt);\n        if (!parray) return E_UNEXPECTED;\n        vt = VT_ARRAY | avt;\n        return S_OK;\n    }\n    inline HRESULT ArrayResize(ULONG cnt) {\n        SAFEARRAYBOUND bnds = { cnt, 0 };\n        return SafeArrayRedim(parray, &bnds);\n    }\n};\n\nclass CComBSTR {\npublic:\n    BSTR p;\n    inline CComBSTR() : p(0) {}\n    inline CComBSTR(const CComBSTR &src) : p(0) {}\n    inline ~CComBSTR() { Free(); }\n    inline void Attach(BSTR _p) { Free(); p = _p; }\n    inline BSTR Detach() { BSTR pp = p; p = 0; return pp; }\n    inline void Free() { if (p) { SysFreeString(p); p = 0; } }\n\n    inline operator BSTR () const { return p; }\n    inline BSTR* operator&() { return &p; }\n    inline bool operator!() const { return (p == 0); }\n    inline bool operator!=(BSTR _p) const { return !operator==(_p); }\n    inline bool operator==(BSTR _p) const { return p == _p; }\n    inline BSTR operator = (BSTR _p) {\n        if (p != _p) Attach(_p ? SysAllocString(_p) : 0);\n        return p;\n    }\n};\n\nclass CComException: public EXCEPINFO {\npublic:\n    inline CComException() {\n        memset((EXCEPINFO*)this, 0, sizeof(EXCEPINFO));\n    }\n    inline ~CComException() {\n        Clear(true);\n    }\n    inline void Clear(bool internal = false) {\n        if (bstrSource) SysFreeString(bstrSource);\n        if (bstrDescription) SysFreeString(bstrDescription);\n        if (bstrHelpFile) SysFreeString(bstrHelpFile);\n        if (!internal) memset((EXCEPINFO*)this, 0, sizeof(EXCEPINFO));\n    }\n};\n\ntemplate <typename T = IUnknown>\nclass CComPtr {\npublic:\n    T *p;\n    inline CComPtr() : p(0) {}\n    inline CComPtr(T *_p) : p(0) { Attach(_p); }\n    inline CComPtr(const CComPtr<T> &ptr) : p(0) { if (ptr.p) Attach(ptr.p); }\n    inline ~CComPtr() { Release(); }\n\n    inline void Attach(T *_p) { Release(); p = _p; if (p) p->AddRef(); }\n    inline T *Detach() { T *pp = p; p = 0; return pp; }\n    inline void Release() { if (p) { p->Release(); p = 0; } }\n\n    inline operator T*() const { return p; }\n    inline T* operator->() const { return p; }\n    inline T& operator*() const { return *p; }\n    inline T** operator&() { return &p; }\n    inline bool operator!() const { return (p == 0); }\n    inline bool operator!=(T* _p) const { return !operator==(_p); }\n    inline bool operator==(T* _p) const { return p == _p; }\n    inline T* operator = (T* _p) {\n        if (p != _p) Attach(_p);\n        return p;\n    }\n\n    inline HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) {\n        Release();\n        return ::CoCreateInstance(rclsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&p);\n    }\n\n    inline HRESULT CoCreateInstance(LPCOLESTR szProgID, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) {\n        Release();\n        CLSID clsid;\n        HRESULT hr = CLSIDFromProgID(szProgID, &clsid);\n        if FAILED(hr) return hr;\n        return ::CoCreateInstance(clsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&p);\n    }\n};\n\n#endif\n//-------------------------------------------------------------------------------------------------------\n\nLocal<String> GetWin32ErrorMessage(Isolate *isolate, HRESULT hrcode, LPCOLESTR msg, LPCOLESTR msg2 = 0, LPCOLESTR desc = 0);\n\ninline Local<Value> Win32Error(Isolate *isolate, HRESULT hrcode, LPCOLESTR id = 0, LPCOLESTR msg = 0) {\n    auto err = Exception::Error(GetWin32ErrorMessage(isolate, hrcode, id, msg));\n    auto obj = Local<Object>::Cast(err);\n    obj->Set(isolate->GetCurrentContext(), v8str(isolate, \"errno\"), Integer::New(isolate, hrcode));\n    return err;\n}\n\nvoid GetScodeString(HRESULT hr, wchar_t* buf, int bufSize);\n\ninline Local<Value> DispError(Isolate *isolate, HRESULT hrcode, LPCOLESTR id = 0, LPCOLESTR msg = 0, EXCEPINFO *except = 0) {\n    Local<Context> ctx = isolate->GetCurrentContext();\n    CComBSTR desc;\n    CComPtr<IErrorInfo> errinfo;\n\n    std::wstring emsg;\n    emsg.reserve(1024);\n    if (except && except->scode != 0) {\n        wchar_t wc[1024];\n        GetScodeString(except->scode, wc, sizeof(wc) / sizeof(wc[0]));\n        emsg += msg;\n        emsg += L\": \";\n        emsg += wc;\n    }\n    else {\n        emsg = std::wstring(msg);\n    }\n\n    HRESULT hr = GetErrorInfo(0, &errinfo);\n    if (hr == S_OK) errinfo->GetDescription(&desc);\n    auto err = Exception::Error(GetWin32ErrorMessage(isolate, hrcode, id, emsg.c_str(), desc));\n    auto obj = Local<Object>::Cast(err);\n    obj->Set(ctx, v8str(isolate, \"errno\"), Integer::New(isolate, hrcode));\n    if (except) {\n        if (except->scode != 0) obj->Set(ctx, v8str(isolate, \"code\"), Integer::New(isolate, except->scode));\n        else if (except->wCode != 0) obj->Set(ctx, v8str(isolate, \"code\"), Integer::New(isolate, except->wCode));\n        if (except->bstrSource != 0) obj->Set(ctx, v8str(isolate, \"source\"), v8str(isolate, except->bstrSource));\n        if (except->bstrDescription != 0) {\n            obj->Set(ctx, v8str(isolate, \"message\"), v8str(isolate, except->bstrDescription));\n            obj->Set(ctx, v8str(isolate, \"description\"), v8str(isolate, except->bstrDescription));\n        }\n    }\n    return err;\n}\n\ninline Local<Value> DispErrorNull(Isolate *isolate) {\n    return Exception::TypeError(v8str(isolate, \"DispNull\"));\n}\n\ninline Local<Value> DispErrorInvalid(Isolate *isolate) {\n    return Exception::TypeError(v8str(isolate, \"DispInvalid\"));\n}\n\ninline Local<Value> TypeError(Isolate *isolate, const char *msg) {\n    return Exception::TypeError(v8str(isolate, msg));\n}\n\ninline Local<Value> InvalidArgumentsError(Isolate *isolate) {\n    return Exception::TypeError(v8str(isolate, \"Invalid arguments\"));\n}\n\ninline Local<Value> Error(Isolate *isolate, const char *msg) {\n    return Exception::Error(v8str(isolate, msg));\n}\n\n//-------------------------------------------------------------------------------------------------------\n\nclass NodeObject : public ObjectWrap\n{\npublic:\n    template<typename T>\n    static inline T *Unwrap(Local<Object> handle) {\n        if (handle.IsEmpty() || handle->InternalFieldCount() == 0) {\n            return nullptr;\n        }\n        void *ptr = nullptr;\n\n#if (V8_MAJOR_VERSION > 14 || (V8_MAJOR_VERSION == 14 && V8_MINOR_VERSION >= 3))\n        ptr = handle->GetAlignedPointerFromInternalField(0, v8::kEmbedderDataTypeTagDefault);\n#else\n        ptr = handle->GetAlignedPointerFromInternalField(0);\n#endif\n        NodeObject *obj = static_cast<NodeObject*>(ptr);\n        return static_cast<T*>(obj);\n    }\n};\n\n//-------------------------------------------------------------------------------------------------------\n\ninline HRESULT DispFind(IDispatch *disp, LPOLESTR name, DISPID *dispid) {\n    LPOLESTR names[] = { name };\n    return disp->GetIDsOfNames(GUID_NULL, names, 1, 0, dispid);\n}\n\ninline HRESULT DispInvoke(IDispatch *disp, DISPID dispid, UINT argcnt = 0, VARIANT *args = 0, VARIANT *ret = 0, WORD  flags = DISPATCH_METHOD, EXCEPINFO *except = 0) {\n    DISPPARAMS params = { args, 0, argcnt, 0 };\n    DISPID dispidNamed = DISPID_PROPERTYPUT;\n    if (flags == DISPATCH_PROPERTYPUT) { // It`s a magic\n        params.cNamedArgs = 1;\n        params.rgdispidNamedArgs = &dispidNamed;\n        if (params.rgvarg && params.rgvarg->vt == VT_DISPATCH) flags = DISPATCH_PROPERTYPUTREF;\n    }\n    return disp->Invoke(dispid, IID_NULL, 0, flags, &params, ret, except, 0);\n}\n\ninline HRESULT DispInvoke(IDispatch *disp, LPOLESTR name, UINT argcnt = 0, VARIANT *args = 0, VARIANT *ret = 0, WORD  flags = DISPATCH_METHOD, DISPID *dispid = 0, EXCEPINFO *except = 0) {\n    LPOLESTR names[] = { name };\n    DISPID dispids[] = { 0 };\n    HRESULT hrcode = disp->GetIDsOfNames(GUID_NULL, names, 1, 0, dispids);\n    if SUCCEEDED(hrcode) hrcode = DispInvoke(disp, dispids[0], argcnt, args, ret, flags, except);\n    if (dispid) *dispid = dispids[0];\n    return hrcode;\n}\n\n//-------------------------------------------------------------------------------------------------------\n\ntemplate<typename INTTYPE>\ninline INTTYPE Variant2Int(const VARIANT &v, const INTTYPE def) {\n    VARTYPE vt = (v.vt & VT_TYPEMASK);\n    bool by_ref = (v.vt & VT_BYREF) != 0;\n    switch (vt) {\n    case VT_EMPTY:\n    case VT_NULL:\n        return def;\n    case VT_I1:\n    case VT_I2:\n    case VT_I4:\n    case VT_INT:\n        return (INTTYPE)(by_ref ? *v.plVal : v.lVal);\n    case VT_UI1:\n    case VT_UI2:\n    case VT_UI4:\n    case VT_UINT:\n        return (INTTYPE)(by_ref ? *v.pulVal : v.ulVal);\n    case VT_CY:\n        return (INTTYPE)((by_ref ? v.pcyVal : &v.cyVal)->int64 / 10000);\n    case VT_R4:\n        return (INTTYPE)(by_ref ? *v.pfltVal : v.fltVal);\n    case VT_R8:\n        return (INTTYPE)(by_ref ? *v.pdblVal : v.dblVal);\n    case VT_DATE:\n        return (INTTYPE)(by_ref ? *v.pdate : v.date);\n    case VT_DECIMAL: {\n        LONG64 int64val;\n        return SUCCEEDED(VarI8FromDec(by_ref ? v.pdecVal : &v.decVal, &int64val)) ? (INTTYPE)int64val : def; \n    }\n    case VT_BOOL:\n        return (v.boolVal == VARIANT_TRUE) ? 1 : 0;\n    case VT_VARIANT:\n        if (v.pvarVal) return Variant2Int<INTTYPE>(*v.pvarVal, def);\n    }\n    CComVariant dst;\n    if SUCCEEDED(VariantChangeType(&dst, &v, 0, VT_INT)) {\n        return (INTTYPE)dst.intVal;\n    }\n    return def;\n}\n\nLocal<Value> Variant2Array(Isolate *isolate, const VARIANT &v);\nLocal<Value> Variant2Array2(Isolate *isolate, const VARIANT &v);\nLocal<Value> Variant2Value(Isolate *isolate, const VARIANT &v, bool allow_disp = false);\nLocal<Value> Variant2String(Isolate *isolate, const VARIANT &v);\nvoid Value2Variant(Isolate *isolate, Local<Value> &val, VARIANT &var, VARTYPE vt = VT_EMPTY);\nvoid Value2SafeArray(Isolate *isolate, Local<Value> &val, VARIANT &var, VARTYPE vt);\nbool Value2Unknown(Isolate *isolate, Local<Value> &val, IUnknown **unk);\nbool VariantUnkGet(VARIANT *v, IUnknown **unk);\nbool VariantDispGet(VARIANT *v, IDispatch **disp);\nbool UnknownDispGet(IUnknown *unk, IDispatch **disp);\n\n//-------------------------------------------------------------------------------------------------------\n\ninline bool v8val2bool(Isolate *isolate, const Local<Value> &v, bool def) {\n    Local<Context> ctx = isolate->GetCurrentContext();\n    if (v.IsEmpty()) return def;\n    if (v->IsBoolean()) return NODE_BOOL(isolate, v);\n    if (v->IsInt32()) return v->Int32Value(ctx).FromMaybe(def ? 1 : 0) != 0;\n    if (v->IsUint32()) return v->Uint32Value(ctx).FromMaybe(def ? 1 : 0) != 0;\n    return def;\n}\n\n//-------------------------------------------------------------------------------------------------------\n\nclass VarArguments {\npublic:\n    std::vector<CComVariant> items;\n    VarArguments() {}\n    VarArguments(Isolate *isolate, Local<Value> value) {\n        items.resize(1);\n        Value2Variant(isolate, value, items[0]);\n    }\n    VarArguments(Isolate *isolate, const FunctionCallbackInfo<Value> &args) {\n        int argcnt = args.Length();\n        items.resize(argcnt);\n        for (int i = 0; i < argcnt; i++) {\n            auto arg = args[argcnt - i - 1];\n            Value2Variant(isolate, arg, items[i]);\n        }\n    }\n    inline bool IsDefault() {\n        if (items.size() != 1) return false;\n        auto &arg = items[0];\n        if (arg.vt != VT_BSTR || arg.bstrVal == nullptr) return false;\n        return wcscmp(arg.bstrVal, L\"default\") == 0;\n    }\n};\n\nclass NodeArguments {\npublic:\n    std::vector<Local<Value>> items;\n    NodeArguments(Isolate *isolate, DISPPARAMS *pDispParams, bool allow_disp, bool reverse_arguments = true) {\n        UINT argcnt = pDispParams->cArgs;\n        items.resize(argcnt);\n        for (UINT i = 0; i < argcnt; i++) {\n            items[i] = Variant2Value(isolate, pDispParams->rgvarg[reverse_arguments ? argcnt - i - 1 : i], allow_disp);\n        }\n    }\n};\n\n//-------------------------------------------------------------------------------------------------------\n\ntemplate<typename IBASE = IUnknown>\nclass UnknownImpl : public IBASE {\npublic:\n    inline UnknownImpl() : refcnt(0) {}\n    virtual ~UnknownImpl() {}\n\n    // IUnknown interface\n    virtual HRESULT __stdcall QueryInterface(REFIID qiid, void **ppvObject) {\n        if ((qiid == IID_IUnknown) || (qiid == __uuidof(IBASE))) {\n            *ppvObject = this;\n            AddRef();\n            return S_OK;\n        }\n        return E_NOINTERFACE;\n    }\n\n    virtual ULONG __stdcall AddRef() {\n        return InterlockedIncrement(&refcnt);\n    }\n\n    virtual ULONG __stdcall Release() {\n        if (InterlockedDecrement(&refcnt) != 0) return refcnt;\n        delete this;\n        return 0;\n    }\n\nprotected:\n    LONG refcnt;\n\n};\n\nclass DispArrayImpl : public UnknownImpl<IDispatch> {\npublic:\n    CComVariant var;\n    DispArrayImpl(const VARIANT &v): var(v) {}\n\n    // IDispatch interface\n    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) { *pctinfo = 0; return S_OK; }\n    virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { return E_NOTIMPL; }\n    virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);\n    virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);\n};\n\nclass DispEnumImpl : public UnknownImpl<IDispatch> {\npublic:\n    CComPtr<IEnumVARIANT> ptr;\n    DispEnumImpl() {}\n    DispEnumImpl(IEnumVARIANT *p) : ptr(p) {}\n\n    // IDispatch interface\n    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) { *pctinfo = 0; return S_OK; }\n    virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { return E_NOTIMPL; }\n    virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);\n    virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);\n};\n\n\n// {9DCE8520-2EFE-48C0-A0DC-951B291872C0}\nextern const GUID CLSID_DispObjectImpl;\n\n// {AFBF15E5-C37C-11D2-B88E-00A0C9B471B8}\nextern const IID IID_IReflect;\n\nclass DispObjectImpl : public UnknownImpl<IDispatch> {\npublic:\n    Persistent<Object> obj;\n\n    struct name_t { \n        DISPID dispid;\n        std::wstring name;\n        inline name_t(DISPID id, const std::wstring &nm): dispid(id), name(nm) {}\n    };\n    typedef std::shared_ptr<name_t> name_ptr;\n    typedef std::map<std::wstring, name_ptr> names_t;\n    typedef std::map<DISPID, name_ptr> index_t;\n    DISPID dispid_next;\n    names_t names;\n    index_t index;\n    bool reverse_arguments;\n    IID connIfIID;\n\n    inline DispObjectImpl(const Local<Object> &_obj, bool revargs = true, IID connIfIID=IID_NULL) : obj(Isolate::GetCurrent(), _obj), dispid_next(1), reverse_arguments(revargs), connIfIID(connIfIID){}\n    virtual ~DispObjectImpl() { obj.Reset(); }\n\n    // IUnknown interface\n    virtual HRESULT __stdcall QueryInterface(REFIID qiid, void **ppvObject) {\n        if( qiid==this->connIfIID) { return UnknownImpl<IDispatch>::QueryInterface(IID_IDispatch, ppvObject); }\n        //if (qiid == this->connIfIID) { *ppvObject = this; return S_OK; }\n        if (qiid == CLSID_DispObjectImpl) { *ppvObject = this; return S_OK; }\n        HRESULT res = UnknownImpl<IDispatch>::QueryInterface(qiid, ppvObject);\n        NODE_DEBUG_MSG(\"RES\")\n        return res;\n    }\n\n    // IDispatch interface\n    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) { *pctinfo = 0; return S_OK; }\n    virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { return E_NOTIMPL; }\n    virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);\n    virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);\n};\n\ndouble FromOleDate(double);\ndouble ToOleDate(double);\n\n//-------------------------------------------------------------------------------------------------------\n\nclass NodeMethods : public std::map<std::wstring, Persistent<FunctionTemplate> > {\npublic:\n    typedef std::shared_ptr<Persistent<FunctionTemplate>> item_type;\n    typedef std::map<std::wstring, item_type> map_type;\n    map_type items;\n\n    bool get(Isolate* isolate, const std::wstring& name, Local<Function>* value);\n    void add(Isolate* isolate, Local<FunctionTemplate>& clazz, const char* name, FunctionCallback callback);\n};\n\n//-------------------------------------------------------------------------------------------------------\n// Sleep is essential to have proper WScript emulation\n\nvoid WinaxSleep(const FunctionCallbackInfo<Value>& args);\n\n//-------------------------------------------------------------------------------------------------------\nHRESULT GetAccessibleObject(const wchar_t* pszWindowText, CComPtr<IUnknown>& spIUnknown);\n"
  },
  {
    "path": "test/ado.js",
    "content": "var winax = require('../activex');\n\nvar path = require('path');\nconst assert = require('assert');\n\nvar data_path = path.join(__dirname, '../data/');\nvar filename = \"persons.dbf\";\nvar provider = \"Microsoft.ACE.OLEDB.12.0\";\nvar constr = \"Provider=\" + provider + \";Data Source=\" + data_path + \";Extended Properties=\\\"DBASE IV;\\\"\";\nvar fso, con, rs, fields, reccnt;\n\ndescribe(\"Scripting.FileSystemObject\", function() {\n\n    it(\"create\", function() {\n        fso = new ActiveXObject(\"Scripting.FileSystemObject\");\n    });\n\n    it(\"create data folder if not exists\", function() {\n        if (fso) {\n            if (!fso.FolderExists(data_path))\n                fso.CreateFolder(data_path);\n        }\n    });\n\n    it(\"delete DBF file if exists\", function() {\n        if (fso) {\n            if (fso.FileExists(data_path + filename))\n                fso.DeleteFile(data_path + filename);\n        }\n    });\n\n});\n\ndescribe(\"ADODB.Connection\", function() {\n\n    it(\"create and open\", function() {\n        this.timeout(5000);\n        con = new ActiveXObject(\"ADODB.Connection\");\n        con.Open(constr, \"\", \"\");\n        this.test.title += ': ver=' + con.Version;\n    });\n\n    it(\"create and fill table\", function() {\n        if (con) {\n            con.Execute(\"create Table \" + filename + \" (Name char(50), City char(50), Phone char(20), Zip decimal(5))\");\n            con.Execute(\"insert into \" + filename + \" values('John', 'London','123-45-67','14589')\");\n            con.Execute(\"insert into \" + filename + \" values('Andrew', 'Paris','333-44-55','38215')\");\n            con.Execute(\"insert into \" + filename + \" values('Romeo', 'Rom','222-33-44','54323')\");\n            reccnt = 3;\n        }\n    });\n\n    it(\"select records from table\", function() {\n        if (con) {\n            var rs = con.Execute(\"Select * from \" + filename);\n            var fields = rs.Fields;\n        }\n    });\n\n    it(\"loop by records\", function() {\n        if (rs && fields) {\n            var cnt = 0;\n            rs.MoveFirst();\n            while (!rs.EOF) {\n                cnt++;\n                var name = fields(\"Name\").Value;\n                var town = fields[\"City\"].value;\n                var phone = fields[2].value;\n                var zip = fields[3].value;\n                rs.MoveNext();\n            }\n            assert.equal(cnt, reccnt);\n        }\n    });\n\n});\n\ndescribe(\"Release objects\", function() {\n\n    it(\"try call\", function() {\n        if (con) try { this.test.title += ': SUCCESS (' + con.Version + ')'; }\n            catch (e) { this.test.title += ': FAILED (' + e.message + ')'; }\n    });\n\n    it(\"release\", function() {\n        this.test.title += ': ' + winax.release(fso, con, rs, fields);\n    });\n\n    it(\"double release\", function() {\n        this.test.title += ': ' + winax.release(fso, con, rs, fields);\n    });\n\n    if (typeof global.gc === 'function') {\n        global.gc();\n        const mem_usage = process.memoryUsage().heapUsed / 1024;\n        it(\"check memory\", function() {\n            global.gc();\n            const mem = process.memoryUsage().heapUsed / 1024;\n            if (mem > mem_usage) throw new Error(`used memory increased from ${mem_usage.toFixed(2)}Kb to ${mem.toFixed(2)}Kb`);\n        });\n    }\n});"
  },
  {
    "path": "test/excel.js",
    "content": "require('../activex');\n\nvar path = require('path');\nconst assert = require('assert');\n\nvar template_filename = path.join(__dirname, '../data/test.xltm');\nvar excel, wbk;\n\nvar test_value = 'value';\nvar test_value2 = 'value2';\nvar test_value3 = 'value3';\nvar test_func_arg = 10;\nvar com_obj, js_obj = {\n    text: test_value,\n    obj: { params: test_value },\n    arr: [test_value, test_value, test_value],\n    func: function(v) { return v * 2; },\n    func2: function(obj) { return obj.text; }\n};\n\ndescribe(\"COM from JS object\", function() {\n\n    it(\"create\", function() {\n        com_obj = new ActiveXObject(js_obj);\n    });\n\n    it(\"read simple property\", function() {\n        if (com_obj) assert.equal(com_obj.text, js_obj.text);\n    });\n\n    it(\"read object property\", function() {\n        if (com_obj) assert.equal(com_obj.obj.params, js_obj.obj.params);\n    });\n\n    it(\"read array property\", function() {\n        if (com_obj) {\n            assert.equal(com_obj.arr.length, js_obj.arr.length);\n            assert.equal(com_obj.arr[0], js_obj.arr[0]);\n        }\n    });\n\n    it(\"change simple property\", function() {\n        if (com_obj) {\n            com_obj.text = test_value2;\n            assert.equal(com_obj.text, test_value2);\n            assert.equal(js_obj.text, test_value2);\n        }\n    });\n\n    it(\"change object property\", function() {\n        if (com_obj) {\n            com_obj.obj.params = test_value2;\n            assert.equal(com_obj.obj.params, test_value2);\n            assert.equal(js_obj.obj.params, test_value2);\n        }\n    });\n\n    it(\"change array property\", function() {\n        if (com_obj) {\n            com_obj.arr[0] = test_value2;\n            assert.equal(com_obj.arr[0], test_value2);\n            assert.equal(js_obj.arr[0], test_value2);\n        }\n    });\n\n    it(\"call method\", function() {\n        if (com_obj) assert.equal(com_obj.func(test_func_arg), js_obj.func(test_func_arg));\n    });\n\n    it(\"call method with object argument\", function() {\n        if (com_obj) assert.equal(com_obj.func2(com_obj), js_obj.text);\n    });\n    \n    it(\"COM error message\", function() {\n        var fso = new ActiveXObject(\"Scripting.FileSystemObject\"); \n        try {\n            fso.DeleteFile(\"c:\\\\noexist.txt\");\n        } catch(e) {\n            // assert.equal(e.message, \"DispInvoke: DeleteFile: CTL_E_FILENOTFOUND Exception occurred.\\r\\n\");\n            assert(e.message.startsWith(\"DispInvoke: DeleteFile: CTL_E_FILENOTFOUND\"));\n        }\n    });\n});\n\ndescribe(\"Excel with JS object\", function() {\n\n    it(\"create\", function() {\n        this.timeout(10000);\n        excel = new ActiveXObject(\"Excel.Application\", { activate: true });\n    });\n\n    it(\"create workbook from test template\", function() {\n        this.timeout(10000);\n        if (excel) wbk = excel.Workbooks.Add(template_filename);\n    });\n\n    it(\"worksheet access\", function() {\n        var wsh1 = wbk.Worksheets.Item(1);\n        var wsh2 = wbk.Worksheets.Item[1];\n        assert.equal(wsh1.Name, 'Sheet1');\n        assert.equal(wsh2.Name, 'Sheet1');\n    });\n\n    it(\"cell access\", function() {\n        var wsh = wbk.Worksheets.Item(1);\n        wsh.Cells(1, 1).Value = 'test';\n        var val = wsh.Cells(1, 1).Value;\n        assert.equal(val, 'test');\n    });\n\n    it(\"invoke test simple property\", function() {\n        if (wbk && com_obj) assert.equal(test_value3, wbk.Test(com_obj, 'text', 0, test_value3));\n    });\n\n    it(\"invoke test object property\", function() {\n        if (wbk && com_obj) assert.equal(test_value3, wbk.Test(com_obj, 'obj', 0, test_value3));\n    });\n\n    it(\"invoke test array property\", function() {\n        if (wbk && com_obj) assert.equal(test_value3, wbk.Test(com_obj, 'arr', 0, test_value3));\n    });\n\n    it(\"invoke test method\", function() {\n        if (wbk && com_obj) assert.equal(js_obj.func(test_func_arg, 1), wbk.Test(com_obj, 'func', 0, test_func_arg));\n    });\n\n    it(\"range read, write with two dimension arrays\", function() {\n        if (wbk) {\n            var wsh = wbk.Worksheets.Item(1);\n            wsh.Range(\"A1:B3\").Value = [[\"A1\", \"B1\"], [\"A2\", \"B2\"], [\"A3\", \"B3\"]];\n            const data = wsh.Range(\"A1:B3\").Value.valueOf();\n            assert(data instanceof Array);\n            assert.equal(data.length, 3);\n            assert(data[0] instanceof Array);\n            assert.equal(data[0].length, 2);\n            assert.equal(data[0][0], \"A1\");\n            assert.equal(data[2][1], \"B3\");\n        }\n    });\n\n    it(\"quit\", function() {\n        if (wbk) wbk.Close(false);\n        if (excel) excel.Quit();\n    });\n\n    if (typeof global.gc === 'function') {\n        global.gc();\n        const mem_usage = process.memoryUsage().heapUsed / 1024;\n        it(\"check memory\", function() {\n            global.gc();\n            const mem = process.memoryUsage().heapUsed / 1024;\n            if (mem > mem_usage) throw new Error(`used memory increased from ${mem_usage.toFixed(2)}Kb to ${mem.toFixed(2)}Kb`);\n        });\n    }\n});"
  },
  {
    "path": "test/variant.js",
    "content": "const winax = require('../activex');\n\nconst path = require('path');\nconst assert = require('assert');\nconst x64 = process.arch.indexOf('64') >= 0;\n\nconst js_arr = ['1', 2, 3];\n\ndescribe(\"Variants\", function() {\n\n    it(\"Short Array\", function() {\n        const arr = new winax.Variant(js_arr, 'short');\n        assert.equal(arr.length, js_arr.length);\n        assert.strictEqual(arr[0], 1);\n    });\n\n    it(\"String Array\", function() {\n        const arr = new winax.Variant(js_arr, 'string');\n        assert.equal(arr.length, js_arr.length);\n        assert.strictEqual(arr[1], '2');\n    });\n\n    it(\"Variant Array\", function() {\n        const arr = new winax.Variant(js_arr, 'variant');\n        assert.equal(arr.length, js_arr.length);\n        assert.strictEqual(arr[0], js_arr[0]);\n        assert.strictEqual(arr[1], js_arr[1]);\n    });\n\n    it(\"References\", function() {\n        const v = new winax.Variant();\n        const ref = new winax.Variant(v, 'byref');\n\n        v.assign(1, 'string');\n        assert.strictEqual(v.valueOf(), '1');\n        assert.strictEqual(ref.valueOf(), '1');\n\n        v.cast('int');\n        assert.strictEqual(v.valueOf(), 1);\n        assert.strictEqual(ref.valueOf(), 1);\n\n        v.clear();\n        assert.strictEqual(v.valueOf(), undefined);\n        assert.strictEqual(ref.valueOf(), undefined);\n    });\n\n    it(\"Dispatch pointer as Uint8Array\", function() {\n\n        // create simple dispatch object\n        const obj = new winax.Object({\n            test: function(v) { return v * 2; }\n        });\n\n        // convert dispatch pointer to bytes array\n        const varr = new winax.Variant(obj, 'uint8[]');\n\n        // convert to javascript Uint8Array and check length\n        const arr = new Uint8Array(varr.valueOf());\n        assert.strictEqual(arr.length, x64 ? 8 : 4);\n\n        // create dispatch object from array pointer and test\n        const obj2 = new winax.Object(arr);\n        assert.strictEqual(obj2.test(2), 4);\n    });\n\n    if (typeof global.gc === 'function') {\n        global.gc();\n        const mem_usage = process.memoryUsage().heapUsed / 1024;\n        it(\"Check memory\", function() {\n            global.gc();\n            const mem = process.memoryUsage().heapUsed / 1024;\n            if (mem > mem_usage) throw new Error(`used memory increased from ${mem_usage.toFixed(2)}Kb to ${mem.toFixed(2)}Kb`);\n        });\n    }\n});\n"
  },
  {
    "path": "test/wscript/README.md",
    "content": "# WScript JScript samples\n\nHere we have a number of .js files taken from available tutorials and samples just as is. Our goal is to demonstrante that `nodewscript` may be used to launch them without any modification.\n\n`wmi.js` https://www.activexperts.com/admin/scripts/wmi/jscript/0383/\n\n`enumerator.js` https://gist.github.com/mlhaufe/1569247\n\n"
  },
  {
    "path": "test/wscript/arguments.js",
    "content": "WScript.Echo(\"Same count: \" + (WScript.Arguments.length==WScript.Arguments.Count()) );\n\nobjArgs = WScript.Arguments\nWScript.Echo(WScript.Arguments.Count());\nfor (i=0; i<objArgs.length; i++)\n{\n    WScript.Echo(objArgs(i))\n}"
  },
  {
    "path": "test/wscript/enumerator.js",
    "content": "//Reference: <https://groups.google.com/group/comp.lang.javascript/browse_thread/thread/684ad16518c837a2/67d00aa5dbe854c2?show_docid=67d00aa5dbe854c2>\nvar listFileTypes = (function(){\n    var fso = new ActiveXObject(\"Scripting.FileSystemObject\");\n    var shell = new ActiveXObject(\"WScript.Shell\");\n\n    function isKnown(file){\n        var fName = file.Name;\n        //the rules of capitalization are strange in windows...\n        //there are rare cases where this can fail\n        //for example: .HKEY_CLASSES_ROOT\\.HeartsSave-ms\n        var ext = fName.slice(fName.lastIndexOf(\".\")).toLowerCase();\n\n        try{\n            shell.RegRead(\"HKCR\\\\\"+ext+\"\\\\\");\n            return \"Yes\"\n        } catch(e){\n            return \"No\"\n        }\n    }\n\n    return function(folder){\n        var files = new Enumerator(fso.GetFolder(folder).Files);\n        for(;!files.atEnd();files.moveNext()){\n            var file = files.item();\n            WScript.Echo(file.Name + \"\\t\" + file.Type + \"\\t\" + isKnown(file));\n        }\n    }\n})()\n\nlistFileTypes(\"./\")"
  },
  {
    "path": "test/wscript/mixed.js",
    "content": "// This test should have both node and WScript mixed\n\n// First, we get count of files using FSO\n\nvar fso = new ActiveXObject(\"Scripting.FileSystemObject\");\n\nvar filesCount = fso.GetFolder(\".\").Files.Count;\n\nWScript.Echo(\"FSO Path: \"+fso.GetFolder(\".\").Path+\" Files count: \"+filesCount);\n\n// This if makes sure you still may run this file using cscript\nif(WScript.Version=='NODE.WIN32')\n{\n    // And then use node fs\n    console.log(\"path.resolve(.): \"+require('path').resolve('.'));\n\n    var fs = require('fs');\n    var files = fs.readdirSync(\".\");\n\n    var nodeFilesCount = 0;\n    for(var f in files) \n    {\n        if( !fs.lstatSync(files[f]).isDirectory() )\n        {\n            nodeFilesCount++;\n        }\n    }\n\n    if(nodeFilesCount!=filesCount)\n    {\n        console.log(\"FSO files: \", filesCount, \"fs files: \", nodeFilesCount);\n        WScript.Quit(-5);\n    } else {\n        console.log(\"Same number of files: \"+nodeFilesCount);\n    }\n}\n"
  },
  {
    "path": "test/wscript/nodewscript_test_cli.cmd",
    "content": "@ECHO off\nSETLOCAL\nCALL :find_dp0\n\nIF EXIST \"%dp0%\\node.exe\" (\n  SET \"_prog=%dp0%\\node.exe\"\n) ELSE (\n  SET \"_prog=node\"\n  SET PATHEXT=%PATHEXT:;.JS;=;%\n)\n\n\"%_prog%\"  \"%dp0%\\..\\..\\NodeWScript.js\" %*\nENDLOCAL\nEXIT /b %errorlevel%\n:find_dp0\nSET dp0=%~dp0\nEXIT /b\n"
  },
  {
    "path": "test/wscript/wmi.js",
    "content": "  var wbemFlagReturnImmediately = 0x10;\nvar wbemFlagForwardOnly = 0x20;\n\nvar arrComputers = new Array(\".\");\nfor (i = 0; i < arrComputers.length; i++) {\n   WScript.Echo();\n   WScript.Echo(\"==========================================\");\n   WScript.Echo(\"Computer: \" + arrComputers[i]);\n   WScript.Echo(\"==========================================\");\n\n   var objWMIService = GetObject(\"winmgmts:\\\\\\\\\" + arrComputers[i] + \"\\\\root\\\\CIMV2\");\n   var colItems = objWMIService.ExecQuery(\"SELECT * FROM Win32_ComputerSystem\", \"WQL\",\n                                          wbemFlagReturnImmediately | wbemFlagForwardOnly);\n\n   var enumItems = new Enumerator(colItems);\n   for (; !enumItems.atEnd(); enumItems.moveNext()) {\n      var objItem = enumItems.item();\n\n      WScript.Echo(\"AdminPasswordStatus: \" + objItem.AdminPasswordStatus);\n      WScript.Echo(\"AutomaticResetBootOption: \" + objItem.AutomaticResetBootOption);\n      WScript.Echo(\"AutomaticResetCapability: \" + objItem.AutomaticResetCapability);\n      WScript.Echo(\"BootOptionOnLimit: \" + objItem.BootOptionOnLimit);\n      WScript.Echo(\"BootOptionOnWatchDog: \" + objItem.BootOptionOnWatchDog);\n      WScript.Echo(\"BootROMSupported: \" + objItem.BootROMSupported);\n      WScript.Echo(\"BootupState: \" + objItem.BootupState);\n      WScript.Echo(\"Caption: \" + objItem.Caption);\n      WScript.Echo(\"ChassisBootupState: \" + objItem.ChassisBootupState);\n      WScript.Echo(\"CreationClassName: \" + objItem.CreationClassName);\n      WScript.Echo(\"CurrentTimeZone: \" + objItem.CurrentTimeZone);\n      WScript.Echo(\"DaylightInEffect: \" + objItem.DaylightInEffect);\n      WScript.Echo(\"Description: \" + objItem.Description);\n      WScript.Echo(\"DNSHostName: \" + objItem.DNSHostName);\n      WScript.Echo(\"Domain: \" + objItem.Domain);\n      WScript.Echo(\"DomainRole: \" + objItem.DomainRole);\n      WScript.Echo(\"EnableDaylightSavingsTime: \" + objItem.EnableDaylightSavingsTime);\n      WScript.Echo(\"FrontPanelResetStatus: \" + objItem.FrontPanelResetStatus);\n      WScript.Echo(\"InfraredSupported: \" + objItem.InfraredSupported);\n      try { WScript.Echo(\"InitialLoadInfo: \" + (objItem.InitialLoadInfo.toArray()).join(\",\")); }\n         catch(e) { WScript.Echo(\"InitialLoadInfo: null\"); }\n      WScript.Echo(\"InstallDate: \" + WMIDateStringToDate(\"\"+objItem.InstallDate));\n      WScript.Echo(\"KeyboardPasswordStatus: \" + objItem.KeyboardPasswordStatus);\n      WScript.Echo(\"LastLoadInfo: \" + objItem.LastLoadInfo);\n      WScript.Echo(\"Manufacturer: \" + objItem.Manufacturer);\n      WScript.Echo(\"Model: \" + objItem.Model);\n      WScript.Echo(\"Name: \" + objItem.Name);\n      WScript.Echo(\"NameFormat: \" + objItem.NameFormat);\n      WScript.Echo(\"NetworkServerModeEnabled: \" + objItem.NetworkServerModeEnabled);\n      WScript.Echo(\"NumberOfProcessors: \" + objItem.NumberOfProcessors);\n      try { WScript.Echo(\"OEMLogoBitmap: \" + (objItem.OEMLogoBitmap.toArray()).join(\",\")); }\n         catch(e) { WScript.Echo(\"OEMLogoBitmap: null\"); }\n      try { WScript.Echo(\"OEMStringArray: \" + (objItem.OEMStringArray.toArray()).join(\",\")); }\n         catch(e) { WScript.Echo(\"OEMStringArray: null\"); }\n      WScript.Echo(\"PartOfDomain: \" + objItem.PartOfDomain);\n      WScript.Echo(\"PauseAfterReset: \" + objItem.PauseAfterReset);\n      try { WScript.Echo(\"PowerManagementCapabilities: \" + (objItem.PowerManagementCapabilities.toArray()).join(\",\")); }\n         catch(e) { WScript.Echo(\"PowerManagementCapabilities: null\"); }\n      WScript.Echo(\"PowerManagementSupported: \" + objItem.PowerManagementSupported);\n      WScript.Echo(\"PowerOnPasswordStatus: \" + objItem.PowerOnPasswordStatus);\n      WScript.Echo(\"PowerState: \" + objItem.PowerState);\n      WScript.Echo(\"PowerSupplyState: \" + objItem.PowerSupplyState);\n      WScript.Echo(\"PrimaryOwnerContact: \" + objItem.PrimaryOwnerContact);\n      WScript.Echo(\"PrimaryOwnerName: \" + objItem.PrimaryOwnerName);\n      WScript.Echo(\"ResetCapability: \" + objItem.ResetCapability);\n      WScript.Echo(\"ResetCount: \" + objItem.ResetCount);\n      WScript.Echo(\"ResetLimit: \" + objItem.ResetLimit);\n      try { WScript.Echo(\"Roles: \" + (objItem.Roles.toArray()).join(\",\")); }\n         catch(e) { WScript.Echo(\"Roles: null\"); }\n      WScript.Echo(\"Status: \" + objItem.Status);\n      try { WScript.Echo(\"SupportContactDescription: \" + (objItem.SupportContactDescription.toArray()).join(\",\")); }\n         catch(e) { WScript.Echo(\"SupportContactDescription: null\"); }\n      WScript.Echo(\"SystemStartupDelay: \" + objItem.SystemStartupDelay);\n      try { WScript.Echo(\"SystemStartupOptions: \" + (objItem.SystemStartupOptions.toArray()).join(\",\")); }\n         catch(e) { WScript.Echo(\"SystemStartupOptions: null\"); }\n      WScript.Echo(\"SystemStartupSetting: \" + objItem.SystemStartupSetting);\n      WScript.Echo(\"SystemType: \" + objItem.SystemType);\n      WScript.Echo(\"ThermalState: \" + objItem.ThermalState);\n      WScript.Echo(\"TotalPhysicalMemory: \" + objItem.TotalPhysicalMemory);\n      WScript.Echo(\"UserName: \" + objItem.UserName);\n      WScript.Echo(\"WakeUpType: \" + objItem.WakeUpType);\n      WScript.Echo(\"Workgroup: \" + objItem.Workgroup);\n   }\n}\n\nfunction WMIDateStringToDate(dtmDate)\n{\n   if (dtmDate == null || dtmDate==\"null\")\n   {\n      return \"null date\";\n   }\n   var strDateTime;\n   if (dtmDate.substr(4, 1) == 0)\n   {\n      strDateTime = dtmDate.substr(5, 1) + \"/\";\n   }\n   else\n   {\n      strDateTime = dtmDate.substr(4, 2) + \"/\";\n   }\n   if (dtmDate.substr(6, 1) == 0)\n   {\n      strDateTime = strDateTime + dtmDate.substr(7, 1) + \"/\";\n   }\n   else\n   {\n      strDateTime = strDateTime + dtmDate.substr(6, 2) + \"/\";\n   }\n   strDateTime = strDateTime + dtmDate.substr(0, 4) + \" \" +\n   dtmDate.substr(8, 2) + \":\" +\n   dtmDate.substr(10, 2) + \":\" +\n   dtmDate.substr(12, 2);\n   return(strDateTime);\n}"
  },
  {
    "path": "test/wscript.js",
    "content": "const assert = require('assert');\n\nfunction r(name) {\n    const {execSync} = require('child_process')\n    execSync(__dirname+'/wscript/nodewscript_test_cli.cmd '+__dirname+'/wscript/'+name+'.js', {timeout: 10000})\n}\n\ndescribe(\"Execute WScript Tests\", function() {\n\n    it('WScript Arguments', function (done) {\n        r('arguments');\n        done()\n    })\n\n    it('WScript Enumerator', function (done) {\n        r('enumerator');\n        done()\n    })\n\n\n    it('WScript Mixed', function (done) {\n        r('mixed');\n        done()\n    })\n\n    it('WScript WMI', function (done) {\n        r('wmi');\n        done()\n    })\n\n\n});\n\n"
  }
]