Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update repo #2

Merged
merged 22 commits into from
Jul 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
``` js
var con = new ActiveXObject("ADODB.Connection", {
activate: false, // Allow activate existance object instance, false by default
async: true, // Allow asynchronius calls, true by default (for future usage)
async: true, // Allow asynchronius calls, true by default (not implemented, for future usage)
type: true // Allow using type information, true by default
});
```
Expand Down Expand Up @@ -76,7 +76,9 @@ var v_short_from_cast = winax.cast(17, 'short');
* Additional dignostic propeties:
- **__id** - dispatch identity, for exmplae: [email protected]
- **__value** - value of dispatch object, equiles valueOf()
- **__type** - list member names with their properties
- **__type** - array all member items with their properties
- **__methods** - list member mathods by names (ITypeInfo::GetFuncDesc)
- **__vars** - list member variables by names (ITypeInfo::GetVarDesc)

# Usage example

Expand Down Expand Up @@ -143,6 +145,25 @@ Release COM objects (but other temporary objects may be keep references too)
``` js
winax.release(con, rs, fields)
```
Working with Excel ranges using two dimension arrays (from 1.18.0 version)
* The second dimension is only deduced from the first array.
* If data is missing at the time of SafeArrayPutElement, VT_EMPTY is used.
* Best way to explicitly pass VT_EMPTY is to use null
* If the SAFEARRAY dims are smaller than those of the range, the missing cells are emptied.
* Exception to 4! Excel may process the SAFEARRAY as it does when processing a copy/paste operation
``` js
var excel = new winax.Object("Excel.Application", { activate: true });
var wbk = excel.Workbooks.Add(template_filename);
var wsh = wbk.Worksheets.Item(1);
wsh.Range("C3:E4").Value = [ ["C3", "D3", "E3" ], ["C4", "D4", "E4" ] ];
wsh.Range("C3:E4").Value = [ ["C3", "D3", "E3" ], "C4" ]; // will let D4 and E4 empty
wsh.Range("C3:E4").Value = [ [null, "D3", "E3" ], "C4" ]; // will let C3, D4 and E4 empty
wsh.Range("C3:F4").Value = [ [100], [200] ]; // will duplicate the two rows in colums C, D, E, and F
wsh.Range("C3:F4").Value = [ [100, 200, 300, 400] ]; // will duplicate the for cols in rows 3 and 4
wsh.Range("C3:F4").Value = [ [100, 200] ]; // Will correctly duplicate the first two cols, but col E and F with contains "#N/A"
const data = wsh.Range("C3:E4").Value.valueOf();
console.log("Cell E4 value is", data[1][2]);
```

# Tutorial and Examples

Expand All @@ -160,6 +181,14 @@ To obtain and build use console commands:
```
git clone git://github.com/durs/node-axtivex.git
cd node-activex
npm install
```
or debug version
```
npm install --debug
```
or using node-gyp directly
```
node-gyp configure
node-gyp build
```
Expand All @@ -182,4 +211,5 @@ mocha test
# Contributors

* [durs](https://github.com/durs)
* [somanuell](https://github.com/somanuell)

5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "winax",
"version": "1.12.0",
"version": "1.19.0",
"description": "Windows COM bindings",
"homepage": "https://github.com/durs/node-activex",
"keywords": [
Expand Down Expand Up @@ -37,7 +37,7 @@
"test": "mocha test"
},
"license": "MIT",
"main": "./lib/activex",
"main": "index.js",
"os": [
"win32"
]
Expand Down
116 changes: 88 additions & 28 deletions src/disp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ DispObject::DispObject(const DispInfoPtr &ptr, const std::wstring &nm, DISPID id
}

DispObject::~DispObject() {
items.Reset();
methods.Reset();
vars.Reset();
NODE_DEBUG_FMT("DispObject '%S' destructor", name.c_str());
}

Expand All @@ -57,7 +60,10 @@ bool DispObject::release() {
if (!disp) return false;
NODE_DEBUG_FMT("DispObject '%S' release", name.c_str());
disp.reset();
return true;
items.Reset();
methods.Reset();
vars.Reset();
return true;
}

bool DispObject::get(LPOLESTR tag, LONG index, const PropertyCallbackInfo<Value> &args) {
Expand Down Expand Up @@ -215,7 +221,15 @@ void DispObject::call(Isolate *isolate, const FunctionCallbackInfo<Value> &args)
hrcode = disp->ExecuteMethod(dispid, argcnt, pargs, &ret, &except);
}
else {
hrcode = disp->GetProperty(dispid, argcnt, pargs, &ret, &except);
DispInfo::type_ptr disp_info;
disp->GetTypeInfo(dispid, disp_info);

if(disp_info->is_property_advanced() && argcnt > 1) {
hrcode = disp->SetProperty(dispid, argcnt, pargs, &ret, &except);
}
else {
hrcode = disp->GetProperty(dispid, argcnt, pargs, &ret, &except);
}
}
if FAILED(hrcode) {
isolate->ThrowException(DispError(isolate, hrcode, L"DispInvoke", name.c_str(), &except));
Expand Down Expand Up @@ -328,24 +342,45 @@ Local<Value> DispObject::getIdentity(Isolate *isolate) {
return v8str(isolate, id.c_str());
}

Local<Value> DispObject::getTypeInfo(Isolate *isolate) {
void DispObject::initTypeInfo(Isolate *isolate) {
if ((options & option_type) == 0 || !disp) {
return Undefined(isolate);
return;
}
uint32_t index = 0;
Local<v8::Array> items(v8::Array::New(isolate));
disp->Enumerate([isolate, this, &items, &index](ITypeInfo *info, FUNCDESC *desc) {
Local<v8::Object> _items = v8::Array::New(isolate);
Local<v8::Object> _methods = v8::Object::New(isolate);
Local<v8::Object> _vars = v8::Object::New(isolate);
disp->Enumerate(3, [isolate, &_items, &_vars, &_methods, &index](ITypeInfo *info, FUNCDESC *func, VARDESC *var) {
Local<Context> ctx = isolate->GetCurrentContext();
CComBSTR name;
this->disp->GetItemName(info, desc->memid, &name);
MEMBERID memid = func != nullptr ? func->memid : var->memid;
TypeInfoGetName(info, memid, &name);
Local<Object> item(Object::New(isolate));
if (name) item->Set(ctx, v8str(isolate, "name"), v8str(isolate, (BSTR)name));
item->Set(ctx, v8str(isolate, "dispid"), Int32::New(isolate, desc->memid));
item->Set(ctx, v8str(isolate, "invkind"), Int32::New(isolate, desc->invkind));
item->Set(ctx, v8str(isolate, "argcnt"), Int32::New(isolate, desc->cParams));
items->Set(ctx, index++, item);
Local<String> vname;
if (name) {
vname = v8str(isolate, (BSTR)name);
item->Set(ctx, v8str(isolate, "name"), vname);
}
item->Set(ctx, v8str(isolate, "dispid"), Int32::New(isolate, memid));
if (func != nullptr) {
item->Set(ctx, v8str(isolate, "invkind"), Int32::New(isolate, (int32_t)func->invkind));
item->Set(ctx, v8str(isolate, "flags"), Int32::New(isolate, (int32_t)func->wFuncFlags));
item->Set(ctx, v8str(isolate, "argcnt"), Int32::New(isolate, (int32_t)func->cParams));
_methods->Set(ctx, vname, item);
} else {
item->Set(ctx, v8str(isolate, "varkind"), Int32::New(isolate, (int32_t)var->varkind));
item->Set(ctx, v8str(isolate, "flags"), Int32::New(isolate, (int32_t)var->wVarFlags));
if (var->varkind == VAR_CONST && var->lpvarValue != nullptr) {
v8::Local<v8::Value> value = Variant2Value(isolate, *var->lpvarValue, false);
item->Set(ctx, v8str(isolate, "value"), value);
}
_vars->Set(ctx, vname, item);
}
_items->Set(ctx, index++, item);
});
return items;
items.Reset(isolate, _items);
methods.Reset(isolate, _methods);
vars.Reset(isolate, _vars);
}

//-----------------------------------------------------------------------------------
Expand All @@ -369,17 +404,22 @@ void DispObject::NodeInit(const Local<Object> &target) {
inst->SetCallAsFunctionHandler(NodeCall);
inst->SetNativeDataProperty(v8str(isolate, "__id"), NodeGet);
inst->SetNativeDataProperty(v8str(isolate, "__value"), NodeGet);
inst->SetNativeDataProperty(v8str(isolate, "__type"), NodeGet);
//inst->SetLazyDataProperty(v8str(isolate, "__type"), NodeGet, Local<Value>(), ReadOnly);
//inst->SetLazyDataProperty(v8str(isolate, "__methods"), NodeGet, Local<Value>(), ReadOnly);
//inst->SetLazyDataProperty(v8str(isolate, "__vars"), NodeGet, Local<Value>(), ReadOnly);
inst->SetNativeDataProperty(v8str(isolate, "__type"), NodeGet);
inst->SetNativeDataProperty(v8str(isolate, "__methods"), NodeGet);
inst->SetNativeDataProperty(v8str(isolate, "__vars"), NodeGet);

inst_template.Reset(isolate, inst);
clazz_template.Reset(isolate, clazz);
target->Set(ctx, v8str(isolate, "Object"), clazz->GetFunction(ctx).ToLocalChecked());
target->Set(ctx, v8str(isolate, "cast"), FunctionTemplate::New(isolate, NodeCast, target)->GetFunction(ctx).ToLocalChecked());
target->Set(ctx, v8str(isolate, "release"), FunctionTemplate::New(isolate, NodeRelease, target)->GetFunction(ctx).ToLocalChecked());
target->Set(ctx, v8str(isolate, "cast"), FunctionTemplate::New(isolate, NodeCast)->GetFunction(ctx).ToLocalChecked());
target->Set(ctx, v8str(isolate, "release"), FunctionTemplate::New(isolate, NodeRelease)->GetFunction(ctx).ToLocalChecked());

#ifdef TEST_ADVISE
target->Set(ctx, v8str(isolate, "getConnectionPoints"), FunctionTemplate::New(isolate, NodeConnectionPoints, target)->GetFunction(ctx).ToLocalChecked());
target->Set(ctx, v8str(isolate, "peekAndDispatchMessages"), FunctionTemplate::New(isolate, PeakAndDispatchMessages, target)->GetFunction(ctx).ToLocalChecked());
target->Set(ctx, v8str(isolate, "getConnectionPoints"), FunctionTemplate::New(isolate, NodeConnectionPoints)->GetFunction(ctx).ToLocalChecked());
target->Set(ctx, v8str(isolate, "peekAndDispatchMessages"), FunctionTemplate::New(isolate, PeakAndDispatchMessages)->GetFunction(ctx).ToLocalChecked());
#endif

//Context::GetCurrent()->Global()->Set(v8str(isolate, "ActiveXObject"), t->GetFunction());
Expand Down Expand Up @@ -514,9 +554,27 @@ void DispObject::NodeGet(Local<Name> name, const PropertyCallbackInfo<Value>& ar
else if (_wcsicmp(id, L"__id") == 0) {
args.GetReturnValue().Set(self->getIdentity(isolate));
}
else if (_wcsicmp(id, L"__type") == 0) {
args.GetReturnValue().Set(self->getTypeInfo(isolate));
}
else if (_wcsicmp(id, L"__type") == 0) {
if (self->items.IsEmpty()) {
self->initTypeInfo(isolate);
}
Local<Value> result = self->items.Get(isolate);
args.GetReturnValue().Set(result);
}
else if (_wcsicmp(id, L"__methods") == 0) {
if (self->methods.IsEmpty()) {
self->initTypeInfo(isolate);
}
Local<Value> result = self->methods.Get(isolate);
args.GetReturnValue().Set(result);
}
else if (_wcsicmp(id, L"__vars") == 0) {
if (self->vars.IsEmpty()) {
self->initTypeInfo(isolate);
}
Local<Value> result = self->vars.Get(isolate);
args.GetReturnValue().Set(result);
}
else if (_wcsicmp(id, L"__proto__") == 0) {
Local<Function> func;
Local<FunctionTemplate> clazz = clazz_template.Get(isolate);
Expand All @@ -525,13 +583,13 @@ void DispObject::NodeGet(Local<Name> name, const PropertyCallbackInfo<Value>& ar
}
else if (_wcsicmp(id, L"valueOf") == 0 || !*id) {
Local<Function> func;
if (FunctionTemplate::New(isolate, NodeValueOf, args.This())->GetFunction(ctx).ToLocal(&func)) {
if (FunctionTemplate::New(isolate, NodeValueOf)->GetFunction(ctx).ToLocal(&func)) {
args.GetReturnValue().Set(func);
}
}
else if (_wcsicmp(id, L"toString") == 0) {
Local<Function> func;
if (FunctionTemplate::New(isolate, NodeToString, args.This())->GetFunction(ctx).ToLocal(&func)) {
if (FunctionTemplate::New(isolate, NodeToString)->GetFunction(ctx).ToLocal(&func)) {
args.GetReturnValue().Set(func);
}
}
Expand Down Expand Up @@ -819,7 +877,9 @@ void VariantObject::NodeInit(const Local<Object> &target) {
inst->SetHandler(IndexedPropertyHandlerConfiguration(NodeGetByIndex, NodeSetByIndex));
//inst->SetCallAsFunctionHandler(NodeCall);
//inst->SetNativeDataProperty(v8str(isolate, "__id"), NodeGet);

inst->SetNativeDataProperty(v8str(isolate, "__value"), NodeGet);
//inst->SetLazyDataProperty(v8str(isolate, "__type"), NodeGet, Local<Value>(), ReadOnly);
inst->SetNativeDataProperty(v8str(isolate, "__type"), NodeGet);

inst_template.Reset(isolate, inst);
Expand Down Expand Up @@ -939,31 +999,31 @@ void VariantObject::NodeGet(Local<Name> name, const PropertyCallbackInfo<Value>&
}
else if (_wcsicmp(id, L"clear") == 0) {
Local<Function> func;
if (FunctionTemplate::New(isolate, NodeClear, args.This())->GetFunction(ctx).ToLocal(&func)) {
if (FunctionTemplate::New(isolate, NodeClear)->GetFunction(ctx).ToLocal(&func)) {
args.GetReturnValue().Set(func);
}
}
else if (_wcsicmp(id, L"assign") == 0) {
Local<Function> func;
if (FunctionTemplate::New(isolate, NodeAssign, args.This())->GetFunction(ctx).ToLocal(&func)) {
if (FunctionTemplate::New(isolate, NodeAssign)->GetFunction(ctx).ToLocal(&func)) {
args.GetReturnValue().Set(func);
}
}
else if (_wcsicmp(id, L"cast") == 0) {
Local<Function> func;
if (FunctionTemplate::New(isolate, NodeCast, args.This())->GetFunction(ctx).ToLocal(&func)) {
if (FunctionTemplate::New(isolate, NodeCast)->GetFunction(ctx).ToLocal(&func)) {
args.GetReturnValue().Set(func);
}
}
else if (_wcsicmp(id, L"valueOf") == 0 || !*id) {
Local<Function> func;
if (FunctionTemplate::New(isolate, NodeValueOf, args.This())->GetFunction(ctx).ToLocal(&func)) {
if (FunctionTemplate::New(isolate, NodeValueOf)->GetFunction(ctx).ToLocal(&func)) {
args.GetReturnValue().Set(func);
}
}
else if (_wcsicmp(id, L"toString") == 0) {
Local<Function> func;
if (FunctionTemplate::New(isolate, NodeToString, args.This())->GetFunction(ctx).ToLocal(&func)) {
if (FunctionTemplate::New(isolate, NodeToString)->GetFunction(ctx).ToLocal(&func)) {
args.GetReturnValue().Set(func);
}
}
Expand Down
Loading