用nodejs访问ActiveX对象,以操作Access数据库为例。


Posted in NodeJs onDecember 15, 2011

起因
有人提问“如果用nodejs访问sql server?”
找了找资料,发现有两类解决方法,使用第三方nodejs插件:https://github.com/orenmazor/node-tds、使用ADODB.ConnectionActiveX对象。
参考:
http://stackoverflow.com/questions/857670/how-to-connect-to-sql-server-database-from-javascript
http://stackoverflow.com/questions/4728385/connecting-to-a-remote-microsoft-sql-server-from-node-js
如果用ActiveX那么在Windows下nodejs将会无所不能,类似写asp。那它们怎么通信?得动手试试
经过
思路
用nodejs通过cscript.exe(windows脚本进程)间接访问ActiveX
cscript能解析jscript和vbscript两种脚本,无疑为方便维护选jscript开发。
参考:http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cscript_overview.mspx?mfr=true
需解决的问题
1、跨进程通信
新版的nodejs里增加了对子进程的操作,跨进程通信不是问题。
http://nodejs.org/docs/latest/api/all.html#child_Processes

var util = require('util'), 
exec = require('child_process').exec, 
child; 
child = exec('cat *.js bad_file | wc -l', 
function (error, stdout, stderr) { 
console.log('stdout: ' + stdout); 
console.log('stderr: ' + stderr); 
if (error !== null) { 
console.log('exec error: ' + error); 
} 
});

如例我们可以拿到控制台的输出内容stdout!

2、数据库访问相关ActiveX,ADODB.Connection
参考:http://msdn.microsoft.com/en-us/library/windows/desktop/aa746471%28v=vs.85%29.aspx

var connection = new ActiveXObject("ADODB.Connection"); 
var result = 'ok'; 
try{ 
connection.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + params.accessfile); 
connection.Execute(params.sql); 
} catch(ex){ 
result = ex.message; 
} 
return { 
result: result 
};

connection.Open(connectionString),链接字符串参数可以设置访问sql server。
参考:http://www.connectionstrings.com/sql-server-2005
3、为方便维护,特别将cscript和nodejs的脚本合并,用typeof exports判断当前运行环境。
4、字符编码cscript代码使用ascii编码
非ascii码字符进行“\uHHHH”Unicode编码。
5、命令行字符需转义,双引号、百分号在命令行有特殊意义。
参数传递使用base64编码,避免冲突
cscript环境MSXML2.DOMDocument可以做base64编解码
function base64Decode(base64){ 
var xmldom = new ActiveXObject("MSXML2.DOMDocument"); 
var adostream = new ActiveXObject("ADODB.Stream"); 
var temp = xmldom.createElement("temp"); 
temp.dataType = "bin.base64"; 
temp.text = base64; 
adostream.Charset = "utf-8"; 
adostream.Type = 1; // 1=adTypeBinary 2=adTypeText 
adostream.Open(); 
adostream.Write(temp.nodeTypedValue); 
adostream.Position = 0; 
adostream.Type = 2; // 1=adTypeBinary 2=adTypeText 
var result = adostream.ReadText(-1); // -1=adReadAll 
adostream.Close(); 
adostream = null; 
xmldom = null; 
return result; 
}

总结
调用流程
1、创建子进程,传递经过编码的参数;
2、子进程处理完毕将数据JSON格式化输出到控制台;(子进程自动结束)
3、读取控制台的数据,执行回调函数。

优势
1、使nodejs拥有访问ActiveX对象的能力;
2、实现简单,开发维护方便。

劣势
1、只能运行在Windows平台;
2、数据编解码会消耗更多cpu;
3、每次调用需要创建一个子进程重新连接。(可改进)
总结
1、具有一定实用性;
2、跨进程通信性能可继续探索。
模块代码:

var Access = { 
create: function(params){ 
var fso = new ActiveXObject("Scripting.FileSystemObject"); 
var result = 'ok'; 
if (!fso.FileExists(params.accessfile)){ 
var adoxcatalog = new ActiveXObject("ADOX.Catalog"); 
try { 
adoxcatalog.Create("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + params.accessfile); 
} catch(ex) { 
result = ex.message; 
return; 
} 
adoxcatalog = null; 
} else { 
result = 'exists'; 
} 
return { 
result: result 
}; 
}, 
existsTable: function(params){ 
var connection = new ActiveXObject("ADODB.Connection"); 
var result = 'ok', exists = false; 
try{ 
connection.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + params.accessfile); 
var recordset = connection.OpenSchema(20/*adSchemaTables*/); 
recordset.MoveFirst(); 
while (!recordset.EOF){ 
if (recordset("TABLE_TYPE") == "TABLE" && recordset("TABLE_NAME") == params.tablename){ 
exists = true; 
break; 
} 
recordset.MoveNext(); 
} 
recordset.Close(); 
recordset = null; 
} catch(ex){ 
result = ex.message; 
} 
return { 
"result": result, 
"exists": exists 
}; 
}, 
execute: function(params){ 
var connection = new ActiveXObject("ADODB.Connection"); 
var result = 'ok'; 
try{ 
connection.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + params.accessfile); 
connection.Execute(params.sql); 
} catch(ex){ 
result = ex.message; 
} 
return { 
result: result 
}; 
}, 
query: function(params){ 
var connection = new ActiveXObject("ADODB.Connection"); 
var result = 'ok', records = []; 
try{ 
connection.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + params.accessfile); 
var recordset = new ActiveXObject("ADODB.Recordset"); 
recordset.Open(params.sql, connection); 
var fields = []; 
var enumer = new Enumerator(recordset.Fields); 
for (; !enumer.atEnd(); enumer.moveNext()){ 
fields.push(enumer.item().name); 
} 
recordset.MoveFirst(); 
while (!recordset.EOF) { 
var item = {}; 
for (var i = 0; i < fields.length; i++){ 
var fieldname = fields[i]; 
item[fieldname] = recordset(fieldname).value; 
} 
records.push(item); 
recordset.MoveNext(); 
} 
recordset.Close(); 
recordset = null; 
} catch(ex){ 
result = ex.message; 
} 
return { 
result: result, 
records: records 
}; 
} 
}; 
if (/^u/.test(typeof exports)){ // cscript 
void function(){ 
//from http://tangram.baidu.com/api.html#baidu.json 
var JSON = { 
stringify: (function () { 
/** 
* 字符串处理时需要转义的字符表 
* @private 
*/ 
var escapeMap = { 
"\b": '\\b', 
"\t": '\\t', 
"\n": '\\n', 
"\f": '\\f', 
"\r": '\\r', 
'"' : '\\"', 
"\\": '\\\\' 
}; 
/** 
* 字符串序列化 
* @private 
*/ 
function encodeString(source) { 
if (/["\\\x00-\x1f]/.test(source)) { 
source = source.replace( 
/["\\\x00-\x1f]/g, 
function (match) { 
var c = escapeMap[match]; 
if (c) { 
return c; 
} 
c = match.charCodeAt(); 
return "\\u00" 
+ Math.floor(c / 16).toString(16) 
+ (c % 16).toString(16); 
}); 
} 
return '"' + source + '"'; 
} 
/** 
* 数组序列化 
* @private 
*/ 
function encodeArray(source) { 
var result = ["["], 
l = source.length, 
preComma, i, item; 
for (i = 0; i < l; i++) { 
item = source[i]; 
switch (typeof item) { 
case "undefined": 
case "function": 
case "unknown": 
break; 
default: 
if(preComma) { 
result.push(','); 
} 
result.push(JSON.stringify(item)); 
preComma = 1; 
} 
} 
result.push("]"); 
return result.join(""); 
} 
/** 
* 处理日期序列化时的补零 
* @private 
*/ 
function pad(source) { 
return source < 10 ? '0' + source : source; 
} 
/** 
* 日期序列化 
* @private 
*/ 
function encodeDate(source){ 
return '"' + source.getFullYear() + "-" 
+ pad(source.getMonth() + 1) + "-" 
+ pad(source.getDate()) + "T" 
+ pad(source.getHours()) + ":" 
+ pad(source.getMinutes()) + ":" 
+ pad(source.getSeconds()) + '"'; 
} 
return function (value) { 
switch (typeof value) { 
case 'undefined': 
return 'undefined'; 
case 'number': 
return isFinite(value) ? String(value) : "null"; 
case 'string': 
return encodeString(value).replace(/[^\x00-\xff]/g, function(all) { 
return "\\u" + (0x10000 + all.charCodeAt(0)).toString(16).substring(1); 
}); 
case 'boolean': 
return String(value); 
default: 
if (value === null) { 
return 'null'; 
} 
if (value instanceof Array) { 
return encodeArray(value); 
} 
if (value instanceof Date) { 
return encodeDate(value); 
} 
var result = ['{'], 
encode = JSON.stringify, 
preComma, 
item; 
for (var key in value) { 
if (Object.prototype.hasOwnProperty.call(value, key)) { 
item = value[key]; 
switch (typeof item) { 
case 'undefined': 
case 'unknown': 
case 'function': 
break; 
default: 
if (preComma) { 
result.push(','); 
} 
preComma = 1; 
result.push(encode(key) + ':' + encode(item)); 
} 
} 
} 
result.push('}'); 
return result.join(''); 
} 
}; 
})(), 
parse: function (data) { 
return (new Function("return (" + data + ")"))(); 
} 
} 
//http://blog.csdn.net/cuixiping/article/details/409468 
function base64Decode(base64){ 
var xmldom = new ActiveXObject("MSXML2.DOMDocument"); 
var adostream = new ActiveXObject("ADODB.Stream"); 
var temp = xmldom.createElement("temp"); 
temp.dataType = "bin.base64"; 
temp.text = base64; 
adostream.Charset = "utf-8"; 
adostream.Type = 1; // 1=adTypeBinary 2=adTypeText 
adostream.Open(); 
adostream.Write(temp.nodeTypedValue); 
adostream.Position = 0; 
adostream.Type = 2; // 1=adTypeBinary 2=adTypeText 
var result = adostream.ReadText(-1); // -1=adReadAll 
adostream.Close(); 
adostream = null; 
xmldom = null; 
return result; 
} 
WScript.StdOut.Write('<json>'); 
var method = Access[WScript.Arguments(0)]; 
var result = null; 
if (method){ 
result = method(JSON.parse(base64Decode(WScript.Arguments(1)))); 
} 
WScript.StdOut.Write(JSON.stringify(result)); 
WScript.StdOut.Write('</json>'); 
}(); 
} else { // nodejs 
void function(){ 
function json4stdout(stdout){ 
if (!stdout) return; 
var result = null; 
String(stdout).replace(/<json>([\s\S]+)<\/json>/, function(){ 
result = JSON.parse(arguments[1]); 
}); 
return result; 
} 
var util = require('util'), exec = require('child_process').exec; 
for (var name in Access){ 
exports[name] = (function(funcname){ 
return function(params, callback){ 
console.log([funcname, params]); 
exec( 
util.format( 
'cscript.exe /e:jscript "%s" %s "%s"', __filename, 
funcname, 
(new Buffer(JSON.stringify(params))).toString('base64') 
), 
function (error, stdout, stderr) { 
if (error != null) { 
console.log('exec error: ' + error); 
return; 
} 
console.log('stdout: ' + stdout); 
callback && callback(json4stdout(stdout)); 
} 
); 
} 
})(name); 
} 
}(); 
}

调用代码:
var access = require('./access.js'); 
var util = require('util'); 
var accessfile = 'demo.mdb'; 
access.create({ accessfile: accessfile }, function(data){ 
console.log(data); 
}); 
access.existsTable({ accessfile: accessfile, tablename: 'demo' }, function(data){ 
if (data.result == 'ok' && !data.exists){ 
access.execute({ 
accessfile: 'demo.mdb', 
sql: "CREATE TABLE demo(id Counter Primary key, data Text(100))" 
}); 
} 
}); 
access.execute({ 
accessfile: 'demo.mdb', 
sql: util.format("INSERT INTO demo(data) VALUES('zswang 路过!%s')", +new Date) 
}, function(data){ 
console.log(data); 
}); 
access.query({ 
accessfile: 'demo.mdb', 
sql: "SELECT * FROM demo" 
}, function(data){ 
console.log(data); 
});

最新代码:http://code.google.com/p/nodejs-demo/source/browse/#svn%2Ftrunk%2Fdatabase
NodeJs 相关文章推荐
nodejs 后缀名判断限制代码
Mar 31 NodeJs
跟我学Nodejs(一)--- Node.js简介及安装开发环境
May 20 NodeJs
nodejs下打包模块archiver详解
Dec 03 NodeJs
NodeJS学习笔记之Http模块
Jan 13 NodeJs
NodeJs基本语法和类型
Feb 13 NodeJs
NodeJS使用jQuery选择器操作DOM
Feb 13 NodeJs
nodejs 的 session 简单使用
Jun 06 NodeJs
NodeJs下的测试框架Mocha的简单介绍
Feb 22 NodeJs
基于nodejs+express4.X实现文件下载的实例代码
Jul 13 NodeJs
Nodejs实现文件上传的示例代码
Sep 26 NodeJs
独立部署小程序基于nodejs的服务器过程详解
Jun 24 NodeJs
Nodejs 微信小程序消息推送的实现
Jan 20 NodeJs
golang、python、php、c++、c、java、Nodejs性能对比
Mar 12 #NodeJs
PHPStorm 2020.1 调试 Nodejs的多种方法详解
Sep 17 #NodeJs
nodejs实用示例 缩址还原
Dec 28 #NodeJs
nodejs 后缀名判断限制代码
Mar 31 #NodeJs
NodeJS框架Express的模板视图机制分析
Jul 19 #NodeJs
NodeJS 模块开发及发布详解分享
Mar 07 #NodeJs
nodejs入门详解(多篇文章结合)
Mar 07 #NodeJs
You might like
比file_get_contents稳定的curl_get_contents分享
2012/01/11 PHP
详谈PHP程序Laravel 5框架的优化技巧
2016/07/18 PHP
php编程实现简单的网页版计算器功能示例
2017/04/26 PHP
Javascript Math ceil()、floor()、round()三个函数的区别
2010/03/09 Javascript
关于jQuery中的end()使用方法
2011/07/10 Javascript
JS中产生标识符方式的演变
2015/06/12 Javascript
理解javascript封装
2016/02/23 Javascript
不间断循环滚动效果的实例代码(必看篇)
2016/10/08 Javascript
jQuery将表单序列化成一个Object对象的实例
2016/11/29 Javascript
jquery实现输入框实时输入触发事件代码
2016/12/21 Javascript
jQuery设置和获取select、checkbox、radio的选中值方法
2017/01/01 Javascript
详解Node项目部署到云服务器上
2017/07/12 Javascript
ReactNative之FlatList的具体使用方法
2017/11/29 Javascript
在SSM框架下用laypage和ajax实现分页和数据交互的方法
2019/09/27 Javascript
JavaScript中的null和undefined用法解析
2019/09/30 Javascript
element实现合并单元格通用方法
2019/11/13 Javascript
全面解析js中的原型,原型对象,原型链
2021/01/25 Javascript
[01:05:40]VG vs Newbee 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
python爬虫入门教程之点点美女图片爬虫代码分享
2014/09/02 Python
Python基于PycURL实现POST的方法
2015/07/25 Python
Python中文件I/O高效操作处理的技巧分享
2017/02/04 Python
Python简单操作sqlite3的方法示例
2017/03/22 Python
浅谈Python里面小数点精度的控制
2018/07/16 Python
详解Python进阶之切片的误区与高级用法
2018/12/24 Python
为什么称python为胶水语言
2020/06/16 Python
CSS3对图片照片进行边缘模糊处理的实现
2018/08/08 HTML / CSS
数据库专业英语
2012/11/30 面试题
中科软笔试题和面试题
2014/10/07 面试题
农村婚庆司仪主持词
2014/03/15 职场文书
电子商务系毕业生自荐信
2014/05/29 职场文书
企业法人授权委托书范本
2014/09/23 职场文书
餐厅服务员岗位职责
2015/02/09 职场文书
2015年度内部审计工作总结
2015/05/20 职场文书
写一个Python脚本自动爬取Bilibili小视频
2021/04/24 Python
写一个Python脚本下载哔哩哔哩舞蹈区的所有视频
2021/05/31 Python
MySQL Shell import_table数据导入的实现
2021/08/07 MySQL