├── src ├── win │ ├── enumser.cpp │ ├── stdstring.h │ ├── stdafx.h │ ├── AutoHandle.h │ ├── AutoHModule.h │ ├── AutoHeapAlloc.h │ ├── enumser.h │ ├── disphelper.h │ └── disphelper.c ├── serialport_poller.h ├── serialport.h ├── serialport_poller.cpp ├── serialport.cpp ├── serialport_win.cpp └── serialport_unix.cpp ├── .npmignore ├── .gitignore ├── tests ├── test_relaxedBaudRate.js ├── arduinioEcho │ └── arduinioEcho.ino ├── test_write.js ├── test_zwave.js ├── test_read.js ├── arduino-ldr-read.pde ├── arduino-ldr-read.js └── echoTest.js ├── bin ├── serialportList.js └── serialportTerminal.js ├── AUTHORS ├── binding.gyp ├── examples ├── readdata.js └── logger.js ├── LICENSE ├── package.json ├── changelog.md ├── README.md └── serialport.js /src/win/enumser.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/em/node-serialport/master/src/win/enumser.cpp -------------------------------------------------------------------------------- /src/win/stdstring.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/em/node-serialport/master/src/win/stdstring.h -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | serialport_native/build 2 | serialport_native.node 3 | serialport_native/.lock-wscript 4 | *.node 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .lock-wscript 2 | *.node 3 | .DS_Store 4 | build/ 5 | node_modules 6 | npm-debug.log 7 | Makefile.gyp 8 | *.Makefile 9 | *.target.gyp.mk 10 | out 11 | gyp-mac-tool 12 | /.idea 13 | -------------------------------------------------------------------------------- /tests/test_relaxedBaudRate.js: -------------------------------------------------------------------------------- 1 | var SerialPort = require("../serialport").SerialPort; 2 | var util = require('util'); 3 | 4 | var serial_port = new SerialPort("/dev/slave", {baudrate: 5}); 5 | serial_port.write("This is a test"); 6 | util.puts("wrote buffer"); 7 | serial_port.close(); 8 | -------------------------------------------------------------------------------- /tests/arduinioEcho/arduinioEcho.ino: -------------------------------------------------------------------------------- 1 | 2 | void setup() 3 | { 4 | Serial.begin(9600); 5 | Serial.write("READY"); 6 | } 7 | 8 | void loop() 9 | { 10 | if (Serial.available() > 0) { 11 | char inByte = Serial.read(); 12 | Serial.write(toupper(inByte)); 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /tests/test_write.js: -------------------------------------------------------------------------------- 1 | // Test with the epic VirtualSerialPortApp - http://code.google.com/p/macosxvirtualserialport/ 2 | 3 | var SerialPort = require("../serialport").SerialPort; 4 | 5 | var serial_port = new SerialPort("/dev/master", {baudrate: 9600}); 6 | 7 | serial_port.write("It worked!\r"); 8 | serial_port.close(); 9 | -------------------------------------------------------------------------------- /bin/serialportList.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var serialport = require('../'); 4 | var sf = require('sf'); 5 | 6 | serialport.list(function (err, results) { 7 | if (err) { 8 | throw err; 9 | } 10 | 11 | for (var i = 0; i < results.length; i++) { 12 | var item = results[i]; 13 | console.log(sf('{comName,-15} {pnpId,-20} {manufacturer}', item)); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # The main authors of this library 2 | 3 | Chris Williams 4 | Joe Ferner 5 | 6 | # Other contributors ordered by first contribution. 7 | 8 | Esa-Matti Suuronen 9 | Nathan Rajlich 10 | Rick Waldron 11 | 12 | Georges-Etienne Legendre 13 | Duane Johnson 14 | Jay Beavers -------------------------------------------------------------------------------- /tests/test_zwave.js: -------------------------------------------------------------------------------- 1 | // To test with a z-wave device, I recommend: http://www.aeon-labs.com/site/products/view/2/ 2 | 3 | var SerialPort = require("../serialport").SerialPort; 4 | var util = require('util'); 5 | 6 | var serial_port = new SerialPort("/dev/ttyUSB0"); 7 | serial_port.write(new Buffer([0x01, 0x03, 0x00, 0x20,220])); 8 | util.puts("write"); 9 | serial_port.read(); 10 | /* 11 | serial_port.on("data", function(d){ 12 | util.puts("here"); 13 | util.puts(d); 14 | serial_port.close(); 15 | }); 16 | */ -------------------------------------------------------------------------------- /tests/test_read.js: -------------------------------------------------------------------------------- 1 | // Test with the epic VirtualSerialPortApp - http://code.google.com/p/macosxvirtualserialport/ 2 | 3 | var SerialPort = require("../serialport").SerialPort; 4 | var util = require("util"), repl = require("repl"); 5 | 6 | var serial_port = new SerialPort("/dev/master", {baudrate: 9600}); 7 | 8 | serial_port.on("data", function (data) { 9 | util.puts("here: "+data); 10 | }) 11 | serial_port.on("error", function (msg) { 12 | util.puts("error: "+msg); 13 | }) 14 | repl.start("=>") 15 | 16 | //serial_port.close(); 17 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'serialport', 5 | 'sources': [ 6 | 'src/serialport.cpp', 7 | ], 8 | 'conditions': [ 9 | ['OS=="win"', 10 | { 11 | 'sources': [ 12 | "src/serialport_win.cpp", 13 | 'src/win/disphelper.c', 14 | 'src/win/enumser.cpp', 15 | ], 16 | } 17 | ], 18 | ['OS!="win"', 19 | { 20 | 'sources': [ 21 | 'src/serialport_unix.cpp', 22 | 'src/serialport_poller.cpp', 23 | ], 24 | } 25 | ], 26 | ], 27 | }, 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /examples/readdata.js: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////// 2 | // Use the cool library // 3 | // git://github.com/voodootikigod/node-serialport.git // 4 | // to read the serial port where arduino is sitting. // 5 | //////////////////////////////////////////////////////// 6 | var com = require("serialport"); 7 | 8 | var serialPort = new com.SerialPort("/dev/cu.usbmodemfd121", { 9 | baudrate: 9600, 10 | parser: com.parsers.readline('\r\n') 11 | }); 12 | 13 | serialPort.on('open',function() { 14 | console.log('Port open'); 15 | }); 16 | 17 | serialPort.on('data', function(data) { 18 | console.log(data); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/arduino-ldr-read.pde: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | */ 4 | 5 | int sensorPin = A0; // select the input pin for the potentiometer 6 | int ledPin = 13; // select the pin for the LED 7 | int sensorValue = 0; // variable to store the value coming from the sensor 8 | 9 | void setup() { 10 | // declare the ledPin as an OUTPUT: 11 | pinMode(ledPin, OUTPUT); 12 | 13 | Serial.begin(9600); 14 | } 15 | 16 | void loop() { 17 | // read the value from the sensor: 18 | sensorValue = analogRead(sensorPin); 19 | 20 | Serial.println(sensorValue); 21 | // turn the ledPin on 22 | digitalWrite(ledPin, HIGH); 23 | // stop the program for milliseconds: 24 | delay(sensorValue); 25 | // turn the ledPin off: 26 | digitalWrite(ledPin, LOW); 27 | // stop the program for for milliseconds: 28 | delay(sensorValue); 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/serialport_poller.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013 Robert Giseburt 2 | // serialport_poller.h Written as a part of https://github.com/voodootikigod/node-serialport 3 | // License to use this is the same as that of node-serialport. 4 | 5 | #ifndef SERIALPORT_POLLER_H 6 | #define SERIALPORT_POLLER_H 7 | 8 | #include 9 | 10 | class SerialportPoller : public node::ObjectWrap { 11 | public: 12 | static void Init(v8::Handle target); 13 | 14 | void callCallback(); 15 | 16 | void _start(); 17 | void _stop(); 18 | 19 | private: 20 | SerialportPoller(); 21 | ~SerialportPoller(); 22 | 23 | static v8::Handle New(const v8::Arguments& args); 24 | static v8::Handle Close(const v8::Arguments& args); 25 | static v8::Handle Start(const v8::Arguments& args); 26 | 27 | uv_poll_t poll_handle_; 28 | int fd_; 29 | 30 | v8::Persistent callback_; 31 | }; 32 | 33 | #endif -------------------------------------------------------------------------------- /tests/arduino-ldr-read.js: -------------------------------------------------------------------------------- 1 | 2 | // For use with Arduino LDR 3 | // Upload arduino-ldr-read.pde to Arduino apparatus via Arduino IDE 4 | 5 | var sys = require("sys"), 6 | repl = require("repl"), 7 | serialPort = require("serialport").SerialPort, 8 | 9 | // Required 10 | defaults = { 11 | baudrate: 9600 12 | }, 13 | // Create new serialport pointer 14 | serial = new serialPort("/dev/ttyACM0" , defaults); 15 | 16 | 17 | // Add data read event listener 18 | serial.on( "data", function( data ) { 19 | 20 | var output; 21 | 22 | // Coerce data into a number 23 | data = +data; 24 | 25 | // If data is worth reading and processing 26 | if ( data && data > 1 ) { 27 | 28 | // Create a new Array() whose length equals data 29 | // then join to create output visualization 30 | output = ( new Array(data) ).join( ">" ); 31 | 32 | // Print the data value along with visualization of data 33 | sys.puts( data + " " + output ); 34 | } 35 | 36 | }); 37 | 38 | 39 | serial.on( "error", function( msg ) { 40 | sys.puts("error: " + msg ); 41 | }); 42 | 43 | 44 | repl.start( "=>" ); 45 | 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2011, 2012 Christopher Williams. All rights reserved. 2 | Permission is hereby granted, free of charge, to any person obtaining a copy 3 | of this software and associated documentation files (the "Software"), to 4 | deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 6 | sell copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in 10 | all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 17 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 18 | IN THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serialport", 3 | "version": "1.2.1", 4 | "description": "Welcome your robotic javascript overlords. Better yet, program them!", 5 | "author": { 6 | "name": "Chris Williams", 7 | "email": "voodootikigod@gmail.com", 8 | "url": "http://www.voodootikigod.com" 9 | }, 10 | "main": "./serialport", 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/voodootikigod/node-serialport.git" 14 | }, 15 | "maintainers": [ 16 | { 17 | "name": "Chris Williams", 18 | "email": "voodootikigod@gmail.com" 19 | }, 20 | { 21 | "name": "Joe Ferner", 22 | "email": "joe.ferner@nearinfinity.com" 23 | }, 24 | { 25 | "name": "Jay Beavers", 26 | "email": "jay@hikinghomeschoolers.org" 27 | }, 28 | { 29 | "name": "Rob Giseburt", 30 | "email": "giseburt@gmail.com" 31 | } 32 | ], 33 | "dependencies": { 34 | "bindings": "1.1.1", 35 | "async": "0.1.18", 36 | "sf": "0.1.6", 37 | "optimist": "~0.3.4" 38 | }, 39 | "devDependencies": { 40 | "mocha": "*", 41 | "chai": "*" 42 | }, 43 | "engines": { 44 | "node": ">= 0.10.0" 45 | }, 46 | "bin": { 47 | "serialportlist": "./bin/serialportList.js", 48 | "serialportterm": "./bin/serialportTerminal.js" 49 | }, 50 | "scripts": { 51 | "test": "mocha -R spec -t 60s -s 60s" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/win/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define CENUMERATESERIAL_USE_STL //Uncomment this line if you want to test the STL support in CEnumerateSerial 4 | 5 | #ifndef _SECURE_ATL 6 | #define _SECURE_ATL 1 //Use the Secure C Runtime in ATL 7 | #endif 8 | 9 | #ifndef VC_EXTRALEAN 10 | #define VC_EXTRALEAN 11 | #endif 12 | 13 | #ifndef WINVER 14 | #define WINVER 0x0500 15 | #endif 16 | 17 | #ifndef _WIN32_WINNT 18 | #define _WIN32_WINNT 0x0500 19 | #endif 20 | 21 | #ifndef _WIN32_WINDOWS 22 | #define _WIN32_WINDOWS 0x0500 23 | #endif 24 | 25 | #ifndef _WIN32_IE 26 | #define _WIN32_IE 0x0500 27 | #endif 28 | 29 | #ifndef CENUMERATESERIAL_USE_STL 30 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit 31 | 32 | #define _AFX_ALL_WARNINGS // turns off MFC's hiding of some common and often safely ignored warning messages 33 | 34 | #include 35 | #include 36 | #include 37 | #else 38 | #include "stdstring.h" 39 | #define NO_ENUMSERIAL_USING_WMI 40 | #endif 41 | 42 | #include 43 | 44 | #define NO_ENUMSERIAL_USING_ENUMPORTS 45 | #define NO_ENUMSERIAL_USING_SETUPAPI1 46 | #define NO_ENUMSERIAL_USING_SETUPAPI2 47 | #define NO_ENUMSERIAL_USING_WMI 48 | #define NO_ENUMSERIAL_USING_COMDB 49 | #define NO_ENUMSERIAL_USING_CREATEFILE 50 | #define NO_ENUMSERIAL_USING_GETDEFAULTCOMMCONFIG 51 | #define NO_ENUMSERIAL_USING_REGISTRY -------------------------------------------------------------------------------- /tests/echoTest.js: -------------------------------------------------------------------------------- 1 | // Test with the epic VirtualSerialPortApp - http://code.google.com/p/macosxvirtualserialport/ 2 | 3 | var SerialPort = require("../serialport").SerialPort; 4 | var assert = require('assert'); 5 | 6 | var keepAlive = setTimeout(function () { 7 | console.log('timeout'); 8 | process.exit(); 9 | }, 10000); 10 | 11 | var portName; 12 | 13 | if (process.platform == 'win32') { 14 | portName = 'COM4'; 15 | } else if (process.platform == 'darwin') { 16 | portName = '/dev/cu.usbserial-A800eFN5'; 17 | } else { 18 | portName = '/dev/ttyUSB0'; 19 | } 20 | 21 | var readData = ''; 22 | var sp = new SerialPort(portName, { 23 | baudRate: 9600, 24 | dataBits: 8, 25 | parity: 'none', 26 | stopBits: 1, 27 | flowControl: false 28 | }); 29 | sp.on('data', function (data) { 30 | readData += data.toString(); 31 | console.log('read buffer:', readData); 32 | if (readData.indexOf('READY') >= 0) { 33 | readData = ''; 34 | sp.write("hello world", function (err, bytesWritten) { 35 | console.log('bytes written:', bytesWritten); 36 | }); 37 | } 38 | if (readData === "HELLO WORLD") { 39 | sp.close(); 40 | clearTimeout(keepAlive); 41 | console.log('done'); 42 | } 43 | }); 44 | 45 | sp.on('close', function (err) { 46 | console.log('port closed'); 47 | }); 48 | 49 | sp.on('error', function (err) { 50 | console.error("error", err); 51 | }); 52 | 53 | sp.on('open', function () { 54 | console.log('port opened... Press reset on the Arduino.'); 55 | }); 56 | 57 | -------------------------------------------------------------------------------- /src/win/AutoHandle.h: -------------------------------------------------------------------------------- 1 | /* 2 | Module : AutoHandle.h 3 | Purpose: Defines the interface for a class which supports auto closing of a handle via CloseHandle 4 | Created: PJN / 10-01-2013 5 | 6 | Copyright (c) 2013 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com) 7 | 8 | All rights reserved. 9 | 10 | Copyright / Usage Details: 11 | 12 | You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 13 | when your product is released in binary form. You are allowed to modify the source code in any way you want 14 | except you cannot modify the copyright details at the top of each module. If you want to distribute source 15 | code with your application, then you are only allowed to distribute versions released by the author. This is 16 | to maintain a single distribution point for the source code. 17 | 18 | */ 19 | 20 | 21 | ///////////////////////// Macros / Structs etc //////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #ifndef __AUTOHANDLE_H__ 26 | #define __AUTOHANDLE_H__ 27 | 28 | 29 | ///////////////////////// Classes ///////////////////////////////////////////// 30 | 31 | class CAutoHandle 32 | { 33 | public: 34 | //Constructors / Destructors 35 | CAutoHandle() : m_hHandle(INVALID_HANDLE_VALUE) 36 | { 37 | } 38 | 39 | explicit CAutoHandle(HANDLE hHandle) : m_hHandle(hHandle) 40 | { 41 | } 42 | 43 | ~CAutoHandle() 44 | { 45 | if (m_hHandle != INVALID_HANDLE_VALUE) 46 | { 47 | CloseHandle(m_hHandle); 48 | m_hHandle = INVALID_HANDLE_VALUE; 49 | } 50 | } 51 | 52 | //Methods 53 | operator HANDLE() 54 | { 55 | return m_hHandle; 56 | } 57 | 58 | //Member variables 59 | HANDLE m_hHandle; 60 | }; 61 | 62 | #endif //__AUTOHANDLE_H__ 63 | -------------------------------------------------------------------------------- /bin/serialportTerminal.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var SerialPort = require('../').SerialPort; 4 | var optimist = require('optimist'); 5 | 6 | var args = optimist 7 | .alias('h', 'help') 8 | .alias('h', '?') 9 | .options('portname', { 10 | alias: 'p', 11 | describe: 'Name of serial port. See serialPortList.js for open serial ports.' 12 | }) 13 | .options('baud', { 14 | describe: 'Baud rate.', 15 | default: 9600 16 | }) 17 | .options('databits', { 18 | describe: 'Data bits.', 19 | default: 8 20 | }) 21 | .options('parity', { 22 | describe: 'Parity.', 23 | default: 'none' 24 | }) 25 | .options('stopbits', { 26 | describe: 'Stop bits.', 27 | default: 1 28 | }) 29 | .options('localecho', { 30 | describe: 'Enable local echo.', 31 | boolean: true 32 | }) 33 | .argv; 34 | 35 | if (args.help) { 36 | optimist.showHelp(); 37 | return process.exit(-1); 38 | } 39 | 40 | if (!args.portname) { 41 | console.error("Serial port name is required."); 42 | return process.exit(-1); 43 | } 44 | 45 | process.stdin.resume(); 46 | process.stdin.setRawMode(true); 47 | process.stdin.on('data', function (s) { 48 | if (s[0] === 0x03) { 49 | port.close(); 50 | process.exit(0); 51 | } 52 | if (args.localecho) { 53 | if (s[0] === 0x0d) { 54 | process.stdout.write('\n'); 55 | } else { 56 | process.stdout.write(s); 57 | } 58 | } 59 | port.write(s, function (err) { 60 | if (err) { 61 | console.log(err); 62 | } 63 | }); 64 | }); 65 | 66 | var openOptions = { 67 | baudRate: args.baud, 68 | dataBits: args.databits, 69 | parity: args.parity, 70 | stopBits: args.stopbits 71 | }; 72 | var port = new SerialPort(args.portname, openOptions); 73 | 74 | port.on('data', function (data) { 75 | process.stdout.write(data.toString()); 76 | }); 77 | 78 | port.on('error', function (err) { 79 | console.log(err); 80 | }); 81 | -------------------------------------------------------------------------------- /examples/logger.js: -------------------------------------------------------------------------------- 1 | /* 2 | Simple example that takes a command line provided serial port destination and routes the output to a file of the same name with .log appended to the port name. 3 | 4 | usage: node logger.js /dev/tty.usbserial 5 | 6 | */ 7 | 8 | var SerialPort = require("serialport"); 9 | var fs = require("fs"); 10 | var port = process.argv[2]; 11 | var baudrate = process.argv[3]; 12 | var active = false; 13 | 14 | function attemptLogging(fd, port, baudrate) { 15 | if (!active) { 16 | fs.stat(port, function (err, stats) { 17 | if (!err) { 18 | active = true; 19 | 20 | var serialPort = new SerialPort.SerialPort(port, { 21 | baudrate: baudrate 22 | }); 23 | fs.write(fd, "\n------------------------------------------------------------\nOpening SerialPort: "+target+" at "+Date.now()+"\n------------------------------------------------------------\n"); 24 | serialPort.on("data", function (data) { 25 | fs.write(fd, data.toString()); 26 | }); 27 | serialPort.on("close", function (data) { 28 | active = false; 29 | fs.write(fd, "\n------------------------------------------------------------\nClosing SerialPort: "+target+" at "+Date.now()+"\n------------------------------------------------------------\n"); 30 | }); 31 | } 32 | }); 33 | } 34 | } 35 | 36 | if (!port) { 37 | console.log("You must specify a serial port location."); 38 | } else { 39 | var target = port.split("/"); 40 | target = target[target.length-1]+".log"; 41 | if (!baudrate) { 42 | baudrate = 115200; 43 | } 44 | fs.open("./"+target, 'w', function (err, fd) { 45 | setInterval(function () { 46 | if (!active) { 47 | try { 48 | attemptLogging(fd, port, baudrate); 49 | } catch (e) { 50 | // Error means port is not available for listening. 51 | active = false; 52 | } 53 | } 54 | }, 1000); 55 | }); 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/win/AutoHModule.h: -------------------------------------------------------------------------------- 1 | /* 2 | Module : AutoHModule.h 3 | Purpose: Defines the interface for a class which supports auto closing of a HMODULE via FreeLibrary and 4 | setting of the last Win32 error via SetLastError 5 | Created: PJN / 10-01-2013 6 | 7 | Copyright (c) 2013 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com) 8 | 9 | All rights reserved. 10 | 11 | Copyright / Usage Details: 12 | 13 | You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 14 | when your product is released in binary form. You are allowed to modify the source code in any way you want 15 | except you cannot modify the copyright details at the top of each module. If you want to distribute source 16 | code with your application, then you are only allowed to distribute versions released by the author. This is 17 | to maintain a single distribution point for the source code. 18 | 19 | */ 20 | 21 | 22 | ///////////////////////// Macros / Structs etc //////////////////////////////// 23 | 24 | #pragma once 25 | 26 | #ifndef __AUTOHMODULE_H__ 27 | #define __AUTOHMODULE_H__ 28 | 29 | 30 | ///////////////////////// Classes ///////////////////////////////////////////// 31 | 32 | class CAutoHModule 33 | { 34 | public: 35 | //Constructors / Destructors 36 | CAutoHModule() : m_hModule(NULL), 37 | m_dwError(ERROR_SUCCESS) 38 | { 39 | } 40 | 41 | explicit CAutoHModule(HMODULE hModule) : m_hModule(hModule), 42 | m_dwError(GetLastError()) 43 | { 44 | } 45 | 46 | explicit CAutoHModule(HMODULE hModule, DWORD dwError) : m_hModule(hModule), 47 | m_dwError(dwError) 48 | { 49 | } 50 | 51 | ~CAutoHModule() 52 | { 53 | if (m_hModule != NULL) 54 | { 55 | FreeLibrary(m_hModule); 56 | m_hModule = NULL; 57 | } 58 | SetLastError(m_dwError); 59 | } 60 | 61 | operator HMODULE() 62 | { 63 | return m_hModule; 64 | } 65 | 66 | //Member variables 67 | HMODULE m_hModule; 68 | DWORD m_dwError; 69 | }; 70 | 71 | 72 | #endif //__AUTOHMODULE_H__ 73 | -------------------------------------------------------------------------------- /src/win/AutoHeapAlloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Module : AutoHeapAlloc.h 3 | Purpose: Defines the interface for a class which supports auto closing of a heap pointer allocated via HeapAlloc 4 | Created: PJN / 10-01-2013 5 | 6 | Copyright (c) 2013 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com) 7 | 8 | All rights reserved. 9 | 10 | Copyright / Usage Details: 11 | 12 | You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 13 | when your product is released in binary form. You are allowed to modify the source code in any way you want 14 | except you cannot modify the copyright details at the top of each module. If you want to distribute source 15 | code with your application, then you are only allowed to distribute versions released by the author. This is 16 | to maintain a single distribution point for the source code. 17 | 18 | */ 19 | 20 | 21 | ///////////////////////// Macros / Structs etc //////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #ifndef __AUTOHEAPALLOC_H__ 26 | #define __AUTOHEAPALLOC_H__ 27 | 28 | 29 | ///////////////////////// Includes //////////////////////////////////////////// 30 | 31 | #include 32 | 33 | 34 | ///////////////////////// Classes ///////////////////////////////////////////// 35 | 36 | class CAutoHeapAlloc 37 | { 38 | public: 39 | //Constructors / Destructors 40 | CAutoHeapAlloc(HANDLE hHeap = GetProcessHeap(), DWORD dwHeapFreeFlags = 0) : m_pData(NULL), 41 | m_hHeap(hHeap), 42 | m_dwHeapFreeFlags(dwHeapFreeFlags) 43 | { 44 | } 45 | 46 | BOOL Allocate(SIZE_T dwBytes, DWORD dwFlags = 0) 47 | { 48 | //Validate our parameters 49 | assert(m_pData == NULL); 50 | 51 | m_pData = HeapAlloc(m_hHeap, dwFlags, dwBytes); 52 | return (m_pData != NULL); 53 | } 54 | 55 | ~CAutoHeapAlloc() 56 | { 57 | if (m_pData != NULL) 58 | { 59 | HeapFree(m_hHeap, m_dwHeapFreeFlags, m_pData); 60 | m_pData = NULL; 61 | } 62 | } 63 | 64 | //Methods 65 | 66 | //Member variables 67 | LPVOID m_pData; 68 | HANDLE m_hHeap; 69 | DWORD m_dwHeapFreeFlags; 70 | }; 71 | 72 | #endif //__AUTOHEAPALLOC_H__ 73 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | Version 1.1.3 2 | ------------- 3 | - Remove ATL dependency on Windows (added Visual Studio Pro requirement) 4 | - Update build instructions 5 | - Four small bugfixes 6 | 7 | Version 1.0.7 8 | ------------- 9 | - Guaranteed in-order delivery of messages thanks to Jay Beavers and bnoordhuis 10 | 11 | Version 1.0.6 12 | ------------- 13 | - Support higher baud rates in Mac OS X 14 | 15 | Version 1.0.5 16 | ------------- 17 | - Added flush support. 18 | 19 | Version 1.0.4 20 | ------------- 21 | - Fix for arduino firmata support on windows thanks to @jgautier. 22 | 23 | Version 1.0.3 24 | ------------- 25 | - Fixed issue 65 - https://github.com/voodootikigod/node-serialport/issues/65 26 | - Added note in readme about what is required for the system to be able to compile module, should solve 90% of issues. 27 | 28 | Version 1.0.2 29 | ------------- 30 | - Fixed issue 59 - https://github.com/voodootikigod/node-serialport/issues/59 31 | 32 | Version 1.0.1 33 | ------------- 34 | - Fixed items from Firmata 35 | - Added flexibility for options (camelcase or all lower) 36 | 37 | Version 1.0.0 38 | ------------- 39 | - Added Windows support thanks to Joe Ferner. 40 | - Merged in the various underlying changes from node-serialport2 complete thanks to Joe Ferner for that! 41 | - Verified against known installations. 42 | 43 | 44 | Version 0.6.5 45 | ------------- 46 | - Added SetBaudRate, SetDTR; Custom Baud Rates 47 | - New "close" listener when device being disconnected 48 | 49 | Version 0.2.8 50 | ------------- 51 | - BufferSize fix for readstream (thanks jgautier, you rock) 52 | 53 | Version 0.2.7 54 | ------------- 55 | - Make no port available be an exception not error emitted - Ticket #12. 56 | 57 | Version 0.2.5 - Version 0.2.6 58 | ----------------------------- 59 | - Debugging issue with IOWatcher not holding in the event loop in node.js. 60 | - Converted to ReadStream instead of IOWatcher. 61 | 62 | Version 0.2.4 63 | ------------- 64 | - Integrated arduino tests (rwaldron) 65 | - Integrated options bug fix (w1nk) 66 | - Integrated hardware flow control for crazier serial port action (w1nk) 67 | 68 | Version 0.2.3 69 | ------------- 70 | - Something amazing that has since been lost and forgotten. 71 | 72 | Version 0.2.2 73 | ------------- 74 | - Integrated enhanced version of arduino/readline that actually buffers the data (epeli) 75 | 76 | Version 0.2.1 77 | ------------- 78 | - Refactored the parsing code upon data receipt, now allows for dynamic specification of how incoming data is handled. 79 | - Revised creation interface to use named parameters as an object versions positional parameters. 80 | 81 | Version: 0.2 82 | ------------ 83 | - Upgraded to node v. 0.4.X compatibility 84 | 85 | All other version are not recorded. -------------------------------------------------------------------------------- /src/serialport.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _serialport_h_ 3 | #define _serialport_h_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | enum SerialPortParity { 15 | SERIALPORT_PARITY_NONE = 1, 16 | SERIALPORT_PARITY_MARK = 2, 17 | SERIALPORT_PARITY_EVEN = 3, 18 | SERIALPORT_PARITY_ODD = 4, 19 | SERIALPORT_PARITY_SPACE = 5 20 | }; 21 | 22 | enum SerialPortStopBits { 23 | SERIALPORT_STOPBITS_ONE = 1, 24 | SERIALPORT_STOPBITS_ONE_FIVE = 2, 25 | SERIALPORT_STOPBITS_TWO = 3 26 | }; 27 | 28 | #define ERROR_STRING_SIZE 1024 29 | 30 | v8::Handle List(const v8::Arguments& args); 31 | void EIO_List(uv_work_t* req); 32 | void EIO_AfterList(uv_work_t* req); 33 | 34 | v8::Handle Open(const v8::Arguments& args); 35 | void EIO_Open(uv_work_t* req); 36 | void EIO_AfterOpen(uv_work_t* req); 37 | void AfterOpenSuccess(int fd, v8::Handle dataCallback, v8::Handle disconnectedCallback, v8::Handle errorCallback); 38 | 39 | v8::Handle Write(const v8::Arguments& args); 40 | void EIO_Write(uv_work_t* req); 41 | void EIO_AfterWrite(uv_work_t* req); 42 | 43 | v8::Handle Close(const v8::Arguments& args); 44 | void EIO_Close(uv_work_t* req); 45 | void EIO_AfterClose(uv_work_t* req); 46 | 47 | v8::Handle Flush(const v8::Arguments& args); 48 | void EIO_Flush(uv_work_t* req); 49 | void EIO_AfterFlush(uv_work_t* req); 50 | 51 | SerialPortParity ToParityEnum(const v8::Handle& str); 52 | SerialPortStopBits ToStopBitEnum(double stopBits); 53 | 54 | struct OpenBaton { 55 | public: 56 | char path[1024]; 57 | v8::Persistent callback; 58 | v8::Persistent dataCallback; 59 | v8::Persistent disconnectedCallback; 60 | v8::Persistent errorCallback; 61 | int result; 62 | int baudRate; 63 | int dataBits; 64 | int bufferSize; 65 | bool rtscts; 66 | bool xon; 67 | bool xoff; 68 | bool xany; 69 | bool dsrdtr; 70 | SerialPortParity parity; 71 | SerialPortStopBits stopBits; 72 | char errorString[ERROR_STRING_SIZE]; 73 | }; 74 | 75 | struct WriteBaton { 76 | public: 77 | int fd; 78 | char* bufferData; 79 | size_t bufferLength; 80 | size_t offset; 81 | v8::Persistent buffer; 82 | v8::Persistent callback; 83 | int result; 84 | char errorString[ERROR_STRING_SIZE]; 85 | }; 86 | 87 | struct QueuedWrite { 88 | public: 89 | uv_work_t req; 90 | ngx_queue_t queue; 91 | WriteBaton* baton; 92 | }; 93 | 94 | struct CloseBaton { 95 | public: 96 | int fd; 97 | v8::Persistent callback; 98 | char errorString[ERROR_STRING_SIZE]; 99 | }; 100 | 101 | struct ListResultItem { 102 | public: 103 | std::string comName; 104 | std::string manufacturer; 105 | std::string serialNumber; 106 | std::string pnpId; 107 | std::string locationId; 108 | std::string vendorId; 109 | std::string productId; 110 | }; 111 | 112 | struct ListBaton { 113 | public: 114 | v8::Persistent callback; 115 | std::list results; 116 | char errorString[ERROR_STRING_SIZE]; 117 | }; 118 | 119 | struct FlushBaton { 120 | public: 121 | int fd; 122 | v8::Persistent callback; 123 | int result; 124 | char errorString[ERROR_STRING_SIZE]; 125 | }; 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /src/serialport_poller.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013 Robert Giseburt 2 | // serialport_poller.cpp Written as a part of https://github.com/voodootikigod/node-serialport 3 | // License to use this is the same as that of node-serialport. 4 | 5 | #include 6 | #include "serialport_poller.h" 7 | 8 | using namespace v8; 9 | 10 | SerialportPoller::SerialportPoller() : ObjectWrap() {}; 11 | SerialportPoller::~SerialportPoller() { 12 | // printf("~SerialportPoller\n"); 13 | callback_.Dispose(); 14 | }; 15 | 16 | void _serialportReadable(uv_poll_t *req, int status, int events) { 17 | SerialportPoller* obj = (SerialportPoller*) req->data; 18 | 19 | // We can stop polling until we have read all of the data... 20 | obj->_stop(); 21 | 22 | obj->callCallback(); 23 | } 24 | 25 | void SerialportPoller::callCallback() { 26 | // uv_work_t* req = new uv_work_t; 27 | 28 | // Call the callback to go read more data... 29 | v8::Function::Cast(*callback_)->Call(v8::Context::GetCurrent()->Global(), 0, NULL);//2, argv 30 | } 31 | 32 | void SerialportPoller::Init(Handle target) { 33 | // Prepare constructor template 34 | Local tpl = FunctionTemplate::New(New); 35 | tpl->SetClassName(String::NewSymbol("SerialportPoller")); 36 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 37 | 38 | 39 | // Prototype 40 | 41 | // SerialportPoller.close() 42 | tpl->PrototypeTemplate()->Set(String::NewSymbol("close"), 43 | FunctionTemplate::New(Close)->GetFunction()); 44 | 45 | // SerialportPoller.start() 46 | tpl->PrototypeTemplate()->Set(String::NewSymbol("start"), 47 | FunctionTemplate::New(Start)->GetFunction()); 48 | 49 | Persistent constructor = Persistent::New(tpl->GetFunction()); 50 | target->Set(String::NewSymbol("SerialportPoller"), constructor); 51 | } 52 | 53 | Handle SerialportPoller::New(const Arguments& args) { 54 | HandleScope scope; 55 | 56 | SerialportPoller* obj = new SerialportPoller(); 57 | 58 | if(!args[0]->IsInt32()) { 59 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("First argument must be an fd")))); 60 | } 61 | obj->fd_ = args[0]->ToInt32()->Int32Value(); 62 | 63 | if(!args[1]->IsFunction()) { 64 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("Third argument must be a function")))); 65 | } 66 | obj->callback_ = v8::Persistent::New(v8::Local::Cast(args[1])); 67 | // obj->callCallback(); 68 | 69 | obj->Wrap(args.This()); 70 | 71 | obj->poll_handle_.data = obj; 72 | /*int r = */uv_poll_init(uv_default_loop(), &obj->poll_handle_, obj->fd_); 73 | 74 | uv_poll_start(&obj->poll_handle_, UV_READABLE, _serialportReadable); 75 | 76 | return args.This(); 77 | } 78 | 79 | void SerialportPoller::_start() { 80 | uv_poll_start(&poll_handle_, UV_READABLE, _serialportReadable); 81 | } 82 | 83 | void SerialportPoller::_stop() { 84 | uv_poll_stop(&poll_handle_); 85 | } 86 | 87 | 88 | Handle SerialportPoller::Start(const Arguments& args) { 89 | HandleScope scope; 90 | 91 | SerialportPoller* obj = ObjectWrap::Unwrap(args.This()); 92 | obj->_start(); 93 | 94 | return scope.Close(Undefined()); 95 | } 96 | Handle SerialportPoller::Close(const Arguments& args) { 97 | HandleScope scope; 98 | 99 | SerialportPoller* obj = ObjectWrap::Unwrap(args.This()); 100 | obj->_stop(); 101 | 102 | // DO SOMETHING! 103 | 104 | return scope.Close(Undefined()); 105 | } -------------------------------------------------------------------------------- /src/win/enumser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Module : enumser.h 3 | Purpose: Defines the interface for a class to enumerate the serial ports installed on a PC 4 | using a number of different approaches 5 | Created: PJN / 03-11-1998 6 | 7 | Copyright (c) 1998 - 2013 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com) 8 | 9 | All rights reserved. 10 | 11 | Copyright / Usage Details: 12 | 13 | You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 14 | when your product is released in binary form. You are allowed to modify the source code in any way you want 15 | except you cannot modify the copyright details at the top of each module. If you want to distribute source 16 | code with your application, then you are only allowed to distribute versions released by the author. This is 17 | to maintain a single distribution point for the source code. 18 | 19 | */ 20 | 21 | 22 | ///////////////////////// Macros / Structs etc //////////////////////////////// 23 | 24 | #pragma once 25 | 26 | #ifndef __ENUMSER_H__ 27 | #define __ENUMSER_H__ 28 | 29 | #ifndef CENUMERATESERIAL_EXT_CLASS 30 | #define CENUMERATESERIAL_EXT_CLASS 31 | #endif 32 | 33 | 34 | ///////////////////////// Includes //////////////////////////////////////////// 35 | 36 | #if defined CENUMERATESERIAL_USE_STL 37 | #ifndef _VECTOR_ 38 | #include 39 | #pragma message("To avoid this message, please put vector in your pre compiled header (normally stdafx.h)") 40 | #endif 41 | #ifndef _STRING_ 42 | #include 43 | #pragma message("To avoid this message, please put string in your pre compiled header (normally stdafx.h)") 44 | #endif 45 | #else 46 | #if defined _AFX 47 | #ifndef __AFXTEMPL_H__ 48 | #include 49 | #pragma message("To avoid this message, please put afxtempl.h in your pre compiled header (normally stdafx.h)") 50 | #endif 51 | #else 52 | #ifndef __ATLSTR_H__ 53 | #include 54 | #pragma message("To avoid this message, please put atlstr.h in your pre compiled header (normally stdafx.h)") 55 | #endif 56 | #endif 57 | #endif 58 | 59 | 60 | ///////////////////////// Classes ///////////////////////////////////////////// 61 | 62 | class CENUMERATESERIAL_EXT_CLASS CEnumerateSerial 63 | { 64 | public: 65 | //Methods 66 | #ifndef NO_ENUMSERIAL_USING_CREATEFILE 67 | #if defined CENUMERATESERIAL_USE_STL 68 | static BOOL UsingCreateFile(std::vector& ports); 69 | #elif defined _AFX 70 | static BOOL UsingCreateFile(CUIntArray& ports); 71 | #else 72 | static BOOL UsingCreateFile(CSimpleArray& ports); 73 | #endif 74 | #endif 75 | 76 | #ifndef NO_ENUMSERIAL_USING_QUERYDOSDEVICE 77 | #if defined CENUMERATESERIAL_USE_STL 78 | static BOOL UsingQueryDosDevice(std::vector& ports); 79 | #elif defined _AFX 80 | static BOOL UsingQueryDosDevice(CUIntArray& ports); 81 | #else 82 | static BOOL UsingQueryDosDevice(CSimpleArray& ports); 83 | #endif 84 | #endif 85 | 86 | #ifndef NO_ENUMSERIAL_USING_GETDEFAULTCOMMCONFIG 87 | #if defined CENUMERATESERIAL_USE_STL 88 | static BOOL UsingGetDefaultCommConfig(std::vector& ports); 89 | #elif defined _AFX 90 | static BOOL UsingGetDefaultCommConfig(CUIntArray& ports); 91 | #else 92 | static BOOL UsingGetDefaultCommConfig(CSimpleArray& ports); 93 | #endif 94 | #endif 95 | 96 | #ifndef NO_ENUMSERIAL_USING_SETUPAPI1 97 | #if defined CENUMERATESERIAL_USE_STL 98 | #if defined _UNICODE 99 | static BOOL UsingSetupAPI1(std::vector& ports, std::vector& friendlyNames); 100 | #else 101 | static BOOL UsingSetupAPI1(std::vector& ports, std::vector& friendlyNames); 102 | #endif 103 | #elif defined _AFX 104 | static BOOL UsingSetupAPI1(CUIntArray& ports, CStringArray& friendlyNames); 105 | #else 106 | static BOOL UsingSetupAPI1(CSimpleArray& ports, CSimpleArray& friendlyNames); 107 | #endif 108 | #endif 109 | 110 | #ifndef NO_ENUMSERIAL_USING_SETUPAPI2 111 | #if defined CENUMERATESERIAL_USE_STL 112 | #if defined _UNICODE 113 | static BOOL UsingSetupAPI2(std::vector& ports, std::vector& friendlyNames); 114 | #else 115 | static BOOL UsingSetupAPI2(std::vector& ports, std::vector& friendlyNames); 116 | #endif 117 | #elif defined _AFX 118 | static BOOL UsingSetupAPI2(CUIntArray& ports, CStringArray& friendlyNames); 119 | #else 120 | static BOOL UsingSetupAPI2(CSimpleArray& ports, CSimpleArray& friendlyNames); 121 | #endif 122 | #endif 123 | 124 | #ifndef NO_ENUMSERIAL_USING_ENUMPORTS 125 | #if defined CENUMERATESERIAL_USE_STL 126 | static BOOL UsingEnumPorts(std::vector& ports); 127 | #elif defined _AFX 128 | static BOOL UsingEnumPorts(CUIntArray& ports); 129 | #else 130 | static BOOL UsingEnumPorts(CSimpleArray& ports); 131 | #endif 132 | #endif 133 | 134 | #ifndef NO_ENUMSERIAL_USING_WMI 135 | #if defined CENUMERATESERIAL_USE_STL 136 | #if defined _UNICODE 137 | static BOOL UsingWMI(std::vector& ports, std::vector& friendlyNames); 138 | #else 139 | static BOOL UsingWMI(std::vector& ports, std::vector& friendlyNames); 140 | #endif 141 | #elif defined _AFX 142 | static BOOL UsingWMI(CUIntArray& ports, CStringArray& friendlyNames); 143 | #else 144 | static BOOL UsingWMI(CSimpleArray& ports, CSimpleArray& friendlyNames); 145 | #endif 146 | #endif 147 | 148 | #ifndef NO_ENUMSERIAL_USING_COMDB 149 | #if defined CENUMERATESERIAL_USE_STL 150 | static BOOL UsingComDB(std::vector& ports); 151 | #elif defined _AFX 152 | static BOOL UsingComDB(CUIntArray& ports); 153 | #else 154 | static BOOL UsingComDB(CSimpleArray& ports); 155 | #endif 156 | #endif 157 | 158 | #ifndef NO_ENUMSERIAL_USING_REGISTRY 159 | #if defined CENUMERATESERIAL_USE_STL 160 | #if defined _UNICODE 161 | static BOOL UsingRegistry(std::vector& ports); 162 | #else 163 | static BOOL UsingRegistry(std::vector& ports); 164 | #endif 165 | #elif defined _AFX 166 | static BOOL UsingRegistry(CStringArray& ports); 167 | #else 168 | static BOOL UsingRegistry(CSimpleArray& ports); 169 | #endif 170 | #endif 171 | 172 | protected: 173 | //Methods 174 | #if !defined(NO_ENUMSERIAL_USING_SETUPAPI1) || !defined(NO_ENUMSERIAL_USING_SETUPAPI2) 175 | static BOOL RegQueryValueString(HKEY kKey, LPCTSTR lpValueName, LPTSTR& pszValue); 176 | static BOOL QueryRegistryPortName(HKEY hDeviceKey, int& nPort); 177 | #endif 178 | #if !defined(NO_ENUMSERIAL_USING_SETUPAPI1) || !defined(NO_ENUMSERIAL_USING_SETUPAPI2) || !defined(NO_ENUMSERIAL_USING_COMDB) 179 | static HMODULE LoadLibraryFromSystem32(LPCTSTR lpFileName); 180 | #endif 181 | static BOOL IsNumeric(LPCSTR pszString, BOOL bIgnoreColon); 182 | static BOOL IsNumeric(LPCWSTR pszString, BOOL bIgnoreColon); 183 | }; 184 | 185 | #endif //__ENUMSER_H__ 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | eeeee eeeee eeeee eeee e eeeee 3 | 8 8 8 88 8 8 8 8 8 " 4 | 8e 8 8 8 8e 8 8eee 8e 8eeee 5 | 88 8 8 8 88 8 88 e 88 88 6 | 88 8 8eee8 88ee8 88ee 88 8ee88 8ee88 7 | 8 | eeeee eeee eeeee e eeeee e eeeee eeeee eeeee eeeee 9 | 8 " 8 8 8 8 8 8 8 8 8 8 88 8 8 8 10 | 8eeee 8eee 8eee8e 8e 8eee8 8e 8eee8 8 8 8eee8e 8e 11 | 88 88 88 8 88 88 8 88 88 8 8 88 8 88 12 | 8ee88 88ee 88 8 88 88 8 88eee 88 8eee8 88 8 88 13 | ``` 14 | 15 | Version: 1.1.3 - Released July 29, 2013 16 | 17 | ***** 18 | 19 | Imagine a world where you can write JavaScript to control blenders, lights, security systems, or even robots. Yes, I said robots. That world is here and now with node-serialport. It provides a very simple interface to the low level serial port code necessary to program [Arduino](http://www.arduino.cc/) chipsets, [X10](http://www.smarthome.com/manuals/protocol.txt) wireless communications, or even the rising [Z-Wave](http://www.z-wave.com/modules/ZwaveStart/) and [Zigbee](http://www.zigbee.org/) standards. The physical world is your oyster with this goodie. For a full break down of why we made this, please read [NodeBots - The Rise of JS Robotics](http://www.voodootikigod.com/nodebots-the-rise-of-js-robotics). 20 | 21 | ***** 22 | 23 | Robots, you say? 24 | ================ 25 | 26 | This library is admittedly a base level toolkit for building amazing things with real world (including robots). Here are a couple of those amazing things that leverage node-serialport: 27 | 28 | * [firmata](https://github.com/jgautier/firmata) Talk natively to Arduino using the firmata protocol. 29 | * [tmpad](http://tmpvar.com/project/tmpad/) [source](https://github.com/tmpvar/tmpad) - a DIY midi pad using infrared, arduino, and nodejs. [Video](http://vimeo.com/34575470) 30 | * [duino](https://github.com/ecto/duino) - A higher level framework for working with Arduinos in node.js. 31 | * [Arduino Drinking Game Extravaganza](http://jsconf.eu/2011/arduino_drinking_game_extravaganza.html) - AKA "The Russian" a hexidecimal drinking game for geeks by Uxebu presented at JSConf EU 2011. 32 | * [Arduino controlling popcorn.js](https://gist.github.com/968773) - Controlling a popcorn.js video with an Arduino kit. 33 | * [Robotic JavaScript](http://jsconf.eu/2010/speaker/livingroombindmotion_function.html) - The first live presentation of the node-serialport code set as presented at JSConf EU 2010. 34 | * [devicestack](https://github.com/adrai/devicestack) - This module helps you to represent a device and its protocol. 35 | * [reflecta](https://github.com/jbeavers/reflecta) A communication protocol that combines Arduino Libraries and NodeJS into an integrated system. 36 | 37 | For getting started with node-serialport, we recommend you begin with the following articles: 38 | 39 | * [Arduino Node Security Sensor Hacking](http://nexxylove.tumblr.com/post/20159263403/arduino-node-security-sensor-hacking) - A great all around "how do I use this" article. 40 | * [NodeBots - The Rise of JS Robotics](http://www.voodootikigod.com/nodebots-the-rise-of-js-robotics) - A survey article of why one would want to program robots in JS. 41 | * [Johnny-Five Getting Started Guide](https://github.com/rwldrn/johnny-five#setup-and-assemble-arduino) - Introduction to using the high level Johnny-Five library (awesome). 42 | 43 | How To Use 44 | ========== 45 | 46 | Using node-serialport is pretty easy because it is pretty basic. It provides you with the building block to make great things, it is not a complete solution - just a cog in the (world domination) machine. 47 | 48 | To Install 49 | ---------- 50 | 51 | This assumes you have everything on your system necessary to compile ANY native module for Node.js. This may not be the case, though, so please ensure the following are true for your system before filing an issue about "Does not install". For all operatings systems, please ensure you have Python 2.x installed AND not 3.0, node-gyp (what we use to compile) requires Python 2.x. 52 | 53 | ### Windows: 54 | 55 | * Install Windows 7 or Windows 8. 56 | * Install [Visual Studio Express 2013 Preview for Windows Desktop](http://www.microsoft.com/visualstudio/eng/2013-downloads#d-2013-express) or [Visual Studio Express 2012 for Windows Desktop](http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-for-windows-desktop). 57 | * If you are hacking on an Arduino, be sure to install [the drivers](http://arduino.cc/en/Guide/windows#toc4). 58 | * Install [node.js 0.10.x](http://nodejs.org/) matching the bitness (32 or 64) of your operating system. 59 | * Install [Python 2.7.5](http://www.python.org/download/releases/2.7.5/) matching the bitness of your operating system. For any questions, please refer to their [FAQ](http://docs.python.org/2/faq/windows.html). Default settings are perfect. 60 | * Open the 'Visual Studio Command Prompt' and add Python to the path. When installing serialport, you need to tell the build system (known as node-gyp) that you are using a newer compiler by using: 61 | 62 | ```Batchfile 63 | set path=%path%;C:\Python27 64 | npm install serialport --msvs_version=2012 65 | ``` 66 | 67 | This switch works for both Visual Studio Express 2012 and 2013. 68 | 69 | ### Mac OS X: 70 | 71 | Ensure that you have at a minimum the xCode Command Line Tools installed appropriate for your system configuration. If you recently upgraded the OS, it probably removed your installation of Command Line Tools, please verify before submitting a ticket. 72 | 73 | ### Desktop (Debian/Ubuntu) Linux: 74 | 75 | You know what you need for you system, basically your appropriate analog of build-essential. Keep rocking! 76 | 77 | 78 | ``` 79 | sudo apt-get install build-essential 80 | npm install serialport 81 | ``` 82 | 83 | ### Raspberry Pi Linux: 84 | 85 | * Starting with a a vanilla New Out of the Box Software (NOOBS) Raspbian image (currently tested: 5/25/2013) 86 | * Log into your Raspberry Pi through whatever means works best and ensure you are on a terminal prompt for the remaining steps. This could be local or through an SSH (or a serial connection if you like). 87 | * Issue the following commands to ensure you are up to date: 88 | ``` 89 | sudo apt-get update 90 | sudo apt-get upgrade -y 91 | ``` 92 | * Download and install node.js: 93 | ``` 94 | wget http://nodejs.org/dist/v0.10.12/node-v0.10.12-linux-arm-pi.tar.gz 95 | tar xvfz node-v0.10.12-linux-arm-pi.tar.gz 96 | sudo mv node-v0.10.12-linux-arm-pi /opt/node/ 97 | ``` 98 | * Set up your paths correctly: 99 | ``` 100 | echo 'export PATH="$PATH:/opt/node/bin"' >> ~/.bashrc 101 | source ~/.bashrc 102 | ``` 103 | * Install using npm, note this will take a while as it is actually compiling code and that ARM processor is getting a workout. 104 | ``` 105 | npm install serialport 106 | ``` 107 | 108 | To Use 109 | ------ 110 | 111 | Opening a serial port: 112 | 113 | ```js 114 | var SerialPort = require("serialport").SerialPort 115 | var serialPort = new SerialPort("/dev/tty-usbserial1", { 116 | baudrate: 57600 117 | }); 118 | ``` 119 | 120 | When opening a serial port, you can specify (in this order). 121 | 122 | 1. Path to Serial Port - required. 123 | 1. Options - optional and described below. 124 | 125 | The options object allows you to pass named options to the serial port during initialization. The valid attributes for the options object are the following: 126 | 127 | * baudrate: Baud Rate, defaults to 9600. Must be one of: 115200, 57600, 38400, 19200, 9600, 4800, 2400, 1800, 1200, 600, 300, 200, 150, 134, 110, 75, or 50. 128 | * databits: Data Bits, defaults to 8. Must be one of: 8, 7, 6, or 5. 129 | * stopbits: Stop Bits, defaults to 1. Must be one of: 1 or 2. 130 | * parity: Parity, defaults to 'none'. Must be one of: 'none', 'even', 'mark', 'odd', 'space' 131 | * buffersize: Size of read buffer, defaults to 255. Must be an integer value. 132 | * parser: The parser engine to use with read data, defaults to rawPacket strategy which just emits the raw buffer as a "data" event. Can be any function that accepts EventEmitter as first parameter and the raw buffer as the second parameter. 133 | 134 | **Note, we have added support for either all lowercase OR camelcase of the options (thanks @jagautier), use whichever style you prefer.** 135 | 136 | open event 137 | ---------- 138 | 139 | You MUST wait for the open event to be emitted before reading/writing to the serial port. The open happens asynchronously so installing 'data' listeners and writing 140 | before the open event might result in... nothing at all. 141 | 142 | Assuming you are connected to a serial console, you would for example: 143 | 144 | ```js 145 | serialPort.on("open", function () { 146 | console.log('open'); 147 | serialPort.on('data', function(data) { 148 | console.log('data received: ' + data); 149 | }); 150 | serialPort.write("ls\n", function(err, results) { 151 | console.log('err ' + err); 152 | console.log('results ' + results); 153 | }); 154 | }); 155 | ``` 156 | 157 | You can also call the open function, in this case instanciate the serialport with an additional flag. 158 | 159 | ```js 160 | var SerialPort = require("serialport").SerialPort 161 | var serialPort = new SerialPort("/dev/tty-usbserial1", { 162 | baudrate: 57600 163 | }, false); // this is the openImmediately flag [default is true] 164 | 165 | serialPort.open(function () { 166 | console.log('open'); 167 | serialPort.on('data', function(data) { 168 | console.log('data received: ' + data); 169 | }); 170 | serialPort.write("ls\n", function(err, results) { 171 | console.log('err ' + err); 172 | console.log('results ' + results); 173 | }); 174 | }); 175 | ``` 176 | 177 | List Ports 178 | ---------- 179 | 180 | You can also list the ports along with some metadata as well. 181 | 182 | ```js 183 | serialport.list(function (err, ports) { 184 | ports.forEach(function(port) { 185 | console.log(port.comName); 186 | console.log(port.pnpId); 187 | console.log(port.manufacturer); 188 | }); 189 | }); 190 | ``` 191 | 192 | Parsers 193 | ------- 194 | 195 | Out of the box, node-serialport provides two parsers one that simply emits the raw buffer as a data event and the other which provides familiar "readline" style parsing. To use the readline parser, you must provide a delimiter as such: 196 | 197 | ```js 198 | var serialport = require("serialport"); 199 | var SerialPort = serialport.SerialPort; // localize object constructor 200 | 201 | var sp = new SerialPort("/dev/tty-usbserial1", { 202 | parser: serialport.parsers.readline("\n") 203 | }); 204 | ``` 205 | 206 | To use the raw parser, you just provide the function definition (or leave undefined): 207 | 208 | ```js 209 | var serialport = require("serialport"); 210 | var SerialPort = serialport.SerialPort; // localize object constructor 211 | 212 | var sp = new SerialPort("/dev/tty-usbserial1", { 213 | parser: serialport.parsers.raw 214 | }); 215 | ``` 216 | 217 | 218 | You can get updates of new data from the Serial Port as follows: 219 | 220 | ```js 221 | serialPort.on("data", function (data) { 222 | sys.puts("here: "+data); 223 | }); 224 | ``` 225 | 226 | You can write to the serial port by sending a string or buffer to the write method as follows: 227 | 228 | ```js 229 | serialPort.write("OMG IT WORKS\r"); 230 | ``` 231 | 232 | Enjoy and do cool things with this code. -------------------------------------------------------------------------------- /src/serialport.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "serialport.h" 4 | 5 | #ifdef WIN32 6 | #define strcasecmp stricmp 7 | #else 8 | #include "serialport_poller.h" 9 | #endif 10 | 11 | uv_mutex_t write_queue_mutex; 12 | ngx_queue_t write_queue; 13 | 14 | v8::Handle Open(const v8::Arguments& args) { 15 | v8::HandleScope scope; 16 | 17 | uv_mutex_init(&write_queue_mutex); 18 | ngx_queue_init(&write_queue); 19 | 20 | // path 21 | if(!args[0]->IsString()) { 22 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("First argument must be a string")))); 23 | } 24 | v8::String::Utf8Value path(args[0]->ToString()); 25 | 26 | // options 27 | if(!args[1]->IsObject()) { 28 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("Second argument must be an object")))); 29 | } 30 | v8::Local options = args[1]->ToObject(); 31 | 32 | // callback 33 | if(!args[2]->IsFunction()) { 34 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("Third argument must be a function")))); 35 | } 36 | v8::Local callback = args[2]; 37 | 38 | OpenBaton* baton = new OpenBaton(); 39 | memset(baton, 0, sizeof(OpenBaton)); 40 | strcpy(baton->path, *path); 41 | baton->baudRate = options->Get(v8::String::New("baudRate"))->ToInt32()->Int32Value(); 42 | baton->dataBits = options->Get(v8::String::New("dataBits"))->ToInt32()->Int32Value(); 43 | baton->bufferSize = options->Get(v8::String::New("bufferSize"))->ToInt32()->Int32Value(); 44 | baton->parity = ToParityEnum(options->Get(v8::String::New("parity"))->ToString()); 45 | baton->stopBits = ToStopBitEnum(options->Get(v8::String::New("stopBits"))->ToNumber()->NumberValue()); 46 | baton->rtscts = options->Get(v8::String::New("rtscts"))->ToBoolean()->BooleanValue(); 47 | baton->xon = options->Get(v8::String::New("xon"))->ToBoolean()->BooleanValue(); 48 | baton->xoff = options->Get(v8::String::New("xoff"))->ToBoolean()->BooleanValue(); 49 | baton->xany = options->Get(v8::String::New("xany"))->ToBoolean()->BooleanValue(); 50 | 51 | 52 | baton->callback = v8::Persistent::New(callback); 53 | baton->dataCallback = v8::Persistent::New(options->Get(v8::String::New("dataCallback"))); 54 | baton->disconnectedCallback = v8::Persistent::New(options->Get(v8::String::New("disconnectedCallback"))); 55 | baton->errorCallback = v8::Persistent::New(options->Get(v8::String::New("errorCallback"))); 56 | 57 | uv_work_t* req = new uv_work_t(); 58 | req->data = baton; 59 | uv_queue_work(uv_default_loop(), req, EIO_Open, (uv_after_work_cb)EIO_AfterOpen); 60 | 61 | return scope.Close(v8::Undefined()); 62 | } 63 | 64 | void EIO_AfterOpen(uv_work_t* req) { 65 | OpenBaton* data = static_cast(req->data); 66 | 67 | v8::Handle argv[2]; 68 | if(data->errorString[0]) { 69 | argv[0] = v8::Exception::Error(v8::String::New(data->errorString)); 70 | argv[1] = v8::Undefined(); 71 | } else { 72 | argv[0] = v8::Undefined(); 73 | argv[1] = v8::Int32::New(data->result); 74 | AfterOpenSuccess(data->result, data->dataCallback, data->disconnectedCallback, data->errorCallback); 75 | } 76 | v8::Function::Cast(*data->callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv); 77 | 78 | data->callback.Dispose(); 79 | delete data; 80 | delete req; 81 | } 82 | 83 | v8::Handle Write(const v8::Arguments& args) { 84 | v8::HandleScope scope; 85 | 86 | // file descriptor 87 | if(!args[0]->IsInt32()) { 88 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("First argument must be an int")))); 89 | } 90 | int fd = args[0]->ToInt32()->Int32Value(); 91 | 92 | // buffer 93 | if(!args[1]->IsObject() || !node::Buffer::HasInstance(args[1])) { 94 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("Second argument must be a buffer")))); 95 | } 96 | v8::Persistent buffer = v8::Persistent::New(args[1]->ToObject()); 97 | char* bufferData = node::Buffer::Data(buffer); 98 | size_t bufferLength = node::Buffer::Length(buffer); 99 | 100 | // callback 101 | if(!args[2]->IsFunction()) { 102 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("Third argument must be a function")))); 103 | } 104 | v8::Local callback = args[2]; 105 | 106 | WriteBaton* baton = new WriteBaton(); 107 | memset(baton, 0, sizeof(WriteBaton)); 108 | baton->fd = fd; 109 | baton->buffer = buffer; 110 | baton->bufferData = bufferData; 111 | baton->bufferLength = bufferLength; 112 | // baton->offset = 0; 113 | baton->callback = v8::Persistent::New(callback); 114 | 115 | QueuedWrite* queuedWrite = new QueuedWrite(); 116 | memset(queuedWrite, 0, sizeof(QueuedWrite)); 117 | ngx_queue_init(&queuedWrite->queue); 118 | queuedWrite->baton = baton; 119 | queuedWrite->req.data = queuedWrite; 120 | 121 | uv_mutex_lock(&write_queue_mutex); 122 | bool empty = ngx_queue_empty(&write_queue); 123 | 124 | ngx_queue_insert_tail(&write_queue, &queuedWrite->queue); 125 | 126 | if (empty) { 127 | uv_queue_work(uv_default_loop(), &queuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); 128 | } 129 | uv_mutex_unlock(&write_queue_mutex); 130 | 131 | return scope.Close(v8::Undefined()); 132 | } 133 | 134 | void EIO_AfterWrite(uv_work_t* req) { 135 | QueuedWrite* queuedWrite = static_cast(req->data); 136 | WriteBaton* data = static_cast(queuedWrite->baton); 137 | 138 | v8::Handle argv[2]; 139 | if(data->errorString[0]) { 140 | argv[0] = v8::Exception::Error(v8::String::New(data->errorString)); 141 | argv[1] = v8::Undefined(); 142 | } else { 143 | argv[0] = v8::Undefined(); 144 | argv[1] = v8::Int32::New(data->result); 145 | } 146 | v8::Function::Cast(*data->callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv); 147 | 148 | if (data->offset < data->bufferLength) { 149 | // We're not done with this baton, so throw it right back onto the queue. 150 | // TODO: Add a uv_poll here for unix... 151 | uv_queue_work(uv_default_loop(), req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); 152 | return; 153 | } 154 | 155 | uv_mutex_lock(&write_queue_mutex); 156 | ngx_queue_remove(&queuedWrite->queue); 157 | 158 | if (!ngx_queue_empty(&write_queue)) { 159 | // Always pull the next work item from the head of the queue 160 | ngx_queue_t* head = ngx_queue_head(&write_queue); 161 | QueuedWrite* nextQueuedWrite = ngx_queue_data(head, QueuedWrite, queue); 162 | uv_queue_work(uv_default_loop(), &nextQueuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); 163 | } 164 | uv_mutex_unlock(&write_queue_mutex); 165 | 166 | data->buffer.Dispose(); 167 | data->callback.Dispose(); 168 | delete data; 169 | delete queuedWrite; 170 | } 171 | 172 | v8::Handle Close(const v8::Arguments& args) { 173 | v8::HandleScope scope; 174 | 175 | // file descriptor 176 | if(!args[0]->IsInt32()) { 177 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("First argument must be an int")))); 178 | } 179 | int fd = args[0]->ToInt32()->Int32Value(); 180 | 181 | // callback 182 | if(!args[1]->IsFunction()) { 183 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("Second argument must be a function")))); 184 | } 185 | v8::Local callback = args[1]; 186 | 187 | CloseBaton* baton = new CloseBaton(); 188 | memset(baton, 0, sizeof(CloseBaton)); 189 | baton->fd = fd; 190 | baton->callback = v8::Persistent::New(callback); 191 | 192 | uv_work_t* req = new uv_work_t(); 193 | req->data = baton; 194 | uv_queue_work(uv_default_loop(), req, EIO_Close, (uv_after_work_cb)EIO_AfterClose); 195 | 196 | return scope.Close(v8::Undefined()); 197 | } 198 | 199 | void EIO_AfterClose(uv_work_t* req) { 200 | CloseBaton* data = static_cast(req->data); 201 | 202 | v8::Handle argv[1]; 203 | if(data->errorString[0]) { 204 | argv[0] = v8::Exception::Error(v8::String::New(data->errorString)); 205 | } else { 206 | argv[0] = v8::Undefined(); 207 | } 208 | v8::Function::Cast(*data->callback)->Call(v8::Context::GetCurrent()->Global(), 1, argv); 209 | 210 | data->callback.Dispose(); 211 | delete data; 212 | delete req; 213 | } 214 | 215 | v8::Handle List(const v8::Arguments& args) { 216 | v8::HandleScope scope; 217 | 218 | // callback 219 | if(!args[0]->IsFunction()) { 220 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("First argument must be a function")))); 221 | } 222 | v8::Local callback = args[0]; 223 | 224 | ListBaton* baton = new ListBaton(); 225 | strcpy(baton->errorString, ""); 226 | baton->callback = v8::Persistent::New(callback); 227 | 228 | uv_work_t* req = new uv_work_t(); 229 | req->data = baton; 230 | uv_queue_work(uv_default_loop(), req, EIO_List, (uv_after_work_cb)EIO_AfterList); 231 | 232 | return scope.Close(v8::Undefined()); 233 | } 234 | 235 | void EIO_AfterList(uv_work_t* req) { 236 | ListBaton* data = static_cast(req->data); 237 | 238 | v8::Handle argv[2]; 239 | if(data->errorString[0]) { 240 | argv[0] = v8::Exception::Error(v8::String::New(data->errorString)); 241 | argv[1] = v8::Undefined(); 242 | } else { 243 | v8::Local results = v8::Array::New(); 244 | int i = 0; 245 | for(std::list::iterator it = data->results.begin(); it != data->results.end(); ++it, i++) { 246 | v8::Local item = v8::Object::New(); 247 | item->Set(v8::String::New("comName"), v8::String::New((*it)->comName.c_str())); 248 | item->Set(v8::String::New("manufacturer"), v8::String::New((*it)->manufacturer.c_str())); 249 | item->Set(v8::String::New("serialNumber"), v8::String::New((*it)->serialNumber.c_str())); 250 | item->Set(v8::String::New("pnpId"), v8::String::New((*it)->pnpId.c_str())); 251 | item->Set(v8::String::New("locationId"), v8::String::New((*it)->locationId.c_str())); 252 | item->Set(v8::String::New("vendorId"), v8::String::New((*it)->vendorId.c_str())); 253 | item->Set(v8::String::New("productId"), v8::String::New((*it)->productId.c_str())); 254 | results->Set(i, item); 255 | } 256 | argv[0] = v8::Undefined(); 257 | argv[1] = results; 258 | } 259 | v8::Function::Cast(*data->callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv); 260 | 261 | data->callback.Dispose(); 262 | for(std::list::iterator it = data->results.begin(); it != data->results.end(); ++it) { 263 | delete *it; 264 | } 265 | delete data; 266 | delete req; 267 | } 268 | 269 | v8::Handle Flush(const v8::Arguments& args) { 270 | v8::HandleScope scope; 271 | 272 | // file descriptor 273 | if(!args[0]->IsInt32()) { 274 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("First argument must be an int")))); 275 | } 276 | int fd = args[0]->ToInt32()->Int32Value(); 277 | 278 | // callback 279 | if(!args[1]->IsFunction()) { 280 | return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New("Second argument must be a function")))); 281 | } 282 | v8::Local callback = args[1]; 283 | 284 | FlushBaton* baton = new FlushBaton(); 285 | memset(baton, 0, sizeof(FlushBaton)); 286 | baton->fd = fd; 287 | baton->callback = v8::Persistent::New(callback); 288 | 289 | uv_work_t* req = new uv_work_t(); 290 | req->data = baton; 291 | uv_queue_work(uv_default_loop(), req, EIO_Flush, (uv_after_work_cb)EIO_AfterFlush); 292 | 293 | return scope.Close(v8::Undefined()); 294 | } 295 | 296 | void EIO_AfterFlush(uv_work_t* req) { 297 | FlushBaton* data = static_cast(req->data); 298 | 299 | v8::Handle argv[2]; 300 | 301 | if(data->errorString[0]) { 302 | argv[0] = v8::Exception::Error(v8::String::New(data->errorString)); 303 | argv[1] = v8::Undefined(); 304 | } else { 305 | argv[0] = v8::Undefined(); 306 | argv[1] = v8::Int32::New(data->result); 307 | } 308 | v8::Function::Cast(*data->callback)->Call(v8::Context::GetCurrent()->Global(), 2, argv); 309 | 310 | data->callback.Dispose(); 311 | delete data; 312 | delete req; 313 | } 314 | 315 | SerialPortParity ToParityEnum(const v8::Handle& v8str) { 316 | v8::String::AsciiValue str(v8str); 317 | if(!strcasecmp(*str, "none")) { 318 | return SERIALPORT_PARITY_NONE; 319 | } 320 | if(!strcasecmp(*str, "even")) { 321 | return SERIALPORT_PARITY_EVEN; 322 | } 323 | if(!strcasecmp(*str, "mark")) { 324 | return SERIALPORT_PARITY_MARK; 325 | } 326 | if(!strcasecmp(*str, "odd")) { 327 | return SERIALPORT_PARITY_ODD; 328 | } 329 | if(!strcasecmp(*str, "space")) { 330 | return SERIALPORT_PARITY_SPACE; 331 | } 332 | return SERIALPORT_PARITY_NONE; 333 | } 334 | 335 | SerialPortStopBits ToStopBitEnum(double stopBits) { 336 | if(stopBits > 1.4 && stopBits < 1.6) { 337 | return SERIALPORT_STOPBITS_ONE_FIVE; 338 | } 339 | if(stopBits == 2) { 340 | return SERIALPORT_STOPBITS_TWO; 341 | } 342 | return SERIALPORT_STOPBITS_ONE; 343 | } 344 | 345 | extern "C" { 346 | void init (v8::Handle target) 347 | { 348 | v8::HandleScope scope; 349 | NODE_SET_METHOD(target, "open", Open); 350 | NODE_SET_METHOD(target, "write", Write); 351 | NODE_SET_METHOD(target, "close", Close); 352 | NODE_SET_METHOD(target, "list", List); 353 | NODE_SET_METHOD(target, "flush", Flush); 354 | 355 | #ifndef WIN32 356 | SerialportPoller::Init(target); 357 | #endif 358 | } 359 | } 360 | 361 | NODE_MODULE(serialport, init); 362 | -------------------------------------------------------------------------------- /src/serialport_win.cpp: -------------------------------------------------------------------------------- 1 | #include "serialport.h" 2 | #include 3 | #include "win/disphelper.h" 4 | 5 | #include "win/stdafx.h" 6 | #include "win/enumser.h" 7 | 8 | #ifdef WIN32 9 | 10 | #define MAX_BUFFER_SIZE 1000 11 | 12 | // Declare type of pointer to CancelIoEx function 13 | typedef BOOL (WINAPI *CancelIoExType)(HANDLE hFile, LPOVERLAPPED lpOverlapped); 14 | 15 | 16 | std::list g_closingHandles; 17 | int bufferSize; 18 | void ErrorCodeToString(const char* prefix, int errorCode, char *errorStr) { 19 | switch(errorCode) { 20 | case ERROR_FILE_NOT_FOUND: 21 | _snprintf(errorStr, ERROR_STRING_SIZE, "%s: File not found", prefix); 22 | break; 23 | case ERROR_INVALID_HANDLE: 24 | _snprintf(errorStr, ERROR_STRING_SIZE, "%s: Invalid handle", prefix); 25 | break; 26 | case ERROR_ACCESS_DENIED: 27 | _snprintf(errorStr, ERROR_STRING_SIZE, "%s: Access denied", prefix); 28 | break; 29 | case ERROR_OPERATION_ABORTED: 30 | _snprintf(errorStr, ERROR_STRING_SIZE, "%s: operation aborted", prefix); 31 | break; 32 | default: 33 | _snprintf(errorStr, ERROR_STRING_SIZE, "%s: Unknown error code %d", prefix, errorCode); 34 | break; 35 | } 36 | } 37 | 38 | void EIO_Open(uv_work_t* req) { 39 | OpenBaton* data = static_cast(req->data); 40 | 41 | HANDLE file = CreateFile( 42 | data->path, 43 | GENERIC_READ | GENERIC_WRITE, 44 | 0, 45 | NULL, 46 | OPEN_EXISTING, 47 | FILE_FLAG_OVERLAPPED, 48 | NULL); 49 | if (file == INVALID_HANDLE_VALUE) { 50 | DWORD errorCode = GetLastError(); 51 | char temp[100]; 52 | _snprintf(temp, sizeof(temp), "Opening %s", data->path); 53 | ErrorCodeToString(temp, errorCode, data->errorString); 54 | return; 55 | } 56 | 57 | bufferSize = data->bufferSize; 58 | if(bufferSize > MAX_BUFFER_SIZE) { 59 | bufferSize = MAX_BUFFER_SIZE; 60 | } 61 | 62 | DCB dcb = { 0 }; 63 | dcb.DCBlength = sizeof(DCB); 64 | if(!BuildCommDCB("9600,n,8,1", &dcb)) { 65 | ErrorCodeToString("BuildCommDCB", GetLastError(), data->errorString); 66 | return; 67 | } 68 | 69 | dcb.fBinary = true; 70 | dcb.BaudRate = data->baudRate; 71 | dcb.ByteSize = data->dataBits; 72 | switch(data->parity) { 73 | case SERIALPORT_PARITY_NONE: 74 | dcb.Parity = NOPARITY; 75 | break; 76 | case SERIALPORT_PARITY_MARK: 77 | dcb.Parity = MARKPARITY; 78 | break; 79 | case SERIALPORT_PARITY_EVEN: 80 | dcb.Parity = EVENPARITY; 81 | break; 82 | case SERIALPORT_PARITY_ODD: 83 | dcb.Parity = ODDPARITY; 84 | break; 85 | case SERIALPORT_PARITY_SPACE: 86 | dcb.Parity = SPACEPARITY; 87 | break; 88 | } 89 | switch(data->stopBits) { 90 | case SERIALPORT_STOPBITS_ONE: 91 | dcb.StopBits = ONESTOPBIT; 92 | break; 93 | case SERIALPORT_STOPBITS_ONE_FIVE: 94 | dcb.StopBits = ONE5STOPBITS; 95 | break; 96 | case SERIALPORT_STOPBITS_TWO: 97 | dcb.StopBits = TWOSTOPBITS; 98 | break; 99 | } 100 | 101 | if(!SetCommState(file, &dcb)) { 102 | ErrorCodeToString("SetCommState", GetLastError(), data->errorString); 103 | return; 104 | } 105 | 106 | // Set the com port read/write timeouts 107 | DWORD serialBitsPerByte = 8/*std data bits*/ + 1/*start bit*/; 108 | serialBitsPerByte += (data->parity == SERIALPORT_PARITY_NONE ) ? 0 : 1; 109 | serialBitsPerByte += (data->stopBits == SERIALPORT_STOPBITS_ONE) ? 1 : 2; 110 | DWORD msPerByte = (data->baudRate > 0) ? 111 | ((1000 * serialBitsPerByte + data->baudRate - 1) / data->baudRate) : 112 | 1; 113 | if (msPerByte < 1) { 114 | msPerByte = 1; 115 | } 116 | COMMTIMEOUTS commTimeouts = {0}; 117 | commTimeouts.ReadIntervalTimeout = msPerByte; // Minimize chance of concatenating of separate serial port packets on read 118 | commTimeouts.ReadTotalTimeoutMultiplier = 0; // Do not allow big read timeout when big read buffer used 119 | commTimeouts.ReadTotalTimeoutConstant = 1000; // Total read timeout (period of read loop) 120 | commTimeouts.WriteTotalTimeoutConstant = 1000; // Const part of write timeout 121 | commTimeouts.WriteTotalTimeoutMultiplier = msPerByte; // Variable part of write timeout (per byte) 122 | if(!SetCommTimeouts(file, &commTimeouts)) { 123 | ErrorCodeToString("SetCommTimeouts", GetLastError(), data->errorString); 124 | return; 125 | } 126 | 127 | // Remove garbage data in RX/TX queues 128 | PurgeComm(file, PURGE_RXCLEAR); 129 | PurgeComm(file, PURGE_TXCLEAR); 130 | 131 | data->result = (int)file; 132 | } 133 | 134 | struct WatchPortBaton { 135 | public: 136 | HANDLE fd; 137 | DWORD bytesRead; 138 | char buffer[MAX_BUFFER_SIZE]; 139 | char errorString[ERROR_STRING_SIZE]; 140 | DWORD errorCode; 141 | bool disconnected; 142 | v8::Persistent dataCallback; 143 | v8::Persistent errorCallback; 144 | v8::Persistent disconnectedCallback; 145 | }; 146 | 147 | void EIO_WatchPort(uv_work_t* req) { 148 | WatchPortBaton* data = static_cast(req->data); 149 | data->bytesRead = 0; 150 | data->disconnected = false; 151 | 152 | // Event used by GetOverlappedResult(..., TRUE) to wait for incoming data or timeout 153 | // Event MUST be used if program has several simultaneous asynchronous operations 154 | // on the same handle (i.e. ReadFile and WriteFile) 155 | HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 156 | 157 | while(true) { 158 | OVERLAPPED ov = {0}; 159 | ov.hEvent = hEvent; 160 | 161 | // Start read operation - synchrounous or asynchronous 162 | DWORD bytesReadSync = 0; 163 | if(!ReadFile((HANDLE)data->fd, data->buffer, bufferSize, &bytesReadSync, &ov)) { 164 | data->errorCode = GetLastError(); 165 | if(data->errorCode != ERROR_IO_PENDING) { 166 | // Read operation error 167 | if(data->errorCode == ERROR_OPERATION_ABORTED) { 168 | data->disconnected = true; 169 | } 170 | else { 171 | ErrorCodeToString("Reading from COM port (ReadFile)", data->errorCode, data->errorString); 172 | } 173 | break; 174 | } 175 | 176 | // Read operation is asynchronous and is pending 177 | // We MUST wait for operation completion before deallocation of OVERLAPPED struct 178 | // or read data buffer 179 | 180 | // Wait for async read operation completion or timeout 181 | DWORD bytesReadAsync = 0; 182 | if(!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesReadAsync, TRUE)) { 183 | // Read operation error 184 | data->errorCode = GetLastError(); 185 | if(data->errorCode == ERROR_OPERATION_ABORTED) { 186 | data->disconnected = true; 187 | } 188 | else { 189 | ErrorCodeToString("Reading from COM port (GetOverlappedResult)", data->errorCode, data->errorString); 190 | } 191 | break; 192 | } 193 | else { 194 | // Read operation completed asynchronously 195 | data->bytesRead = bytesReadAsync; 196 | } 197 | } 198 | else { 199 | // Read operation completed synchronously 200 | data->bytesRead = bytesReadSync; 201 | } 202 | 203 | // Return data received if any 204 | if(data->bytesRead > 0) { 205 | break; 206 | } 207 | } 208 | 209 | CloseHandle(hEvent); 210 | } 211 | 212 | bool IsClosingHandle(int fd) { 213 | for(std::list::iterator it=g_closingHandles.begin(); it!=g_closingHandles.end(); ++it) { 214 | if(fd == *it) { 215 | g_closingHandles.remove(fd); 216 | return true; 217 | } 218 | } 219 | return false; 220 | } 221 | 222 | void EIO_AfterWatchPort(uv_work_t* req) { 223 | WatchPortBaton* data = static_cast(req->data); 224 | if(data->disconnected) { 225 | v8::Handle argv[1]; 226 | v8::Function::Cast(*data->disconnectedCallback)->Call(v8::Context::GetCurrent()->Global(), 0, argv); 227 | goto cleanup; 228 | } 229 | 230 | if(data->bytesRead > 0) { 231 | v8::Handle argv[1]; 232 | argv[0] = node::Buffer::New(data->buffer, data->bytesRead)->handle_; 233 | v8::Function::Cast(*data->dataCallback)->Call(v8::Context::GetCurrent()->Global(), 1, argv); 234 | } else if(data->errorCode > 0) { 235 | if(data->errorCode == ERROR_INVALID_HANDLE && IsClosingHandle((int)data->fd)) { 236 | goto cleanup; 237 | } else { 238 | v8::Handle argv[1]; 239 | argv[0] = v8::Exception::Error(v8::String::New(data->errorString)); 240 | v8::Function::Cast(*data->errorCallback)->Call(v8::Context::GetCurrent()->Global(), 1, argv); 241 | Sleep(100); // prevent the errors from occurring too fast 242 | } 243 | } 244 | AfterOpenSuccess((int)data->fd, data->dataCallback, data->disconnectedCallback, data->errorCallback); 245 | 246 | cleanup: 247 | data->dataCallback.Dispose(); 248 | data->errorCallback.Dispose(); 249 | delete data; 250 | delete req; 251 | } 252 | 253 | void AfterOpenSuccess(int fd, v8::Handle dataCallback, v8::Handle disconnectedCallback, v8::Handle errorCallback) { 254 | WatchPortBaton* baton = new WatchPortBaton(); 255 | memset(baton, 0, sizeof(WatchPortBaton)); 256 | baton->fd = (HANDLE)fd; 257 | baton->dataCallback = v8::Persistent::New(dataCallback); 258 | baton->errorCallback = v8::Persistent::New(errorCallback); 259 | baton->disconnectedCallback = v8::Persistent::New(disconnectedCallback); 260 | 261 | uv_work_t* req = new uv_work_t(); 262 | req->data = baton; 263 | 264 | uv_queue_work(uv_default_loop(), req, EIO_WatchPort, (uv_after_work_cb)EIO_AfterWatchPort); 265 | } 266 | 267 | void EIO_Write(uv_work_t* req) { 268 | QueuedWrite* queuedWrite = static_cast(req->data); 269 | WriteBaton* data = static_cast(queuedWrite->baton); 270 | data->result = 0; 271 | 272 | OVERLAPPED ov = {0}; 273 | // Event used by GetOverlappedResult(..., TRUE) to wait for outgoing data or timeout 274 | // Event MUST be used if program has several simultaneous asynchronous operations 275 | // on the same handle (i.e. ReadFile and WriteFile) 276 | ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 277 | 278 | // Start write operation - synchrounous or asynchronous 279 | DWORD bytesWrittenSync = 0; 280 | if(!WriteFile((HANDLE)data->fd, data->bufferData, static_cast(data->bufferLength), &bytesWrittenSync, &ov)) { 281 | DWORD lastError = GetLastError(); 282 | if(lastError != ERROR_IO_PENDING) { 283 | // Write operation error 284 | ErrorCodeToString("Writing to COM port (WriteFile)", lastError, data->errorString); 285 | } 286 | else { 287 | // Write operation is asynchronous and is pending 288 | // We MUST wait for operation completion before deallocation of OVERLAPPED struct 289 | // or write data buffer 290 | 291 | // Wait for async write operation completion or timeout 292 | DWORD bytesWrittenAsync = 0; 293 | if(!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesWrittenAsync, TRUE)) { 294 | // Write operation error 295 | DWORD lastError = GetLastError(); 296 | ErrorCodeToString("Writing to COM port (GetOverlappedResult)", lastError, data->errorString); 297 | } 298 | else { 299 | // Write operation completed asynchronously 300 | data->result = bytesWrittenAsync; 301 | } 302 | } 303 | } 304 | else { 305 | // Write operation completed synchronously 306 | data->result = bytesWrittenSync; 307 | } 308 | 309 | CloseHandle(ov.hEvent); 310 | } 311 | 312 | void EIO_Close(uv_work_t* req) { 313 | CloseBaton* data = static_cast(req->data); 314 | 315 | g_closingHandles.push_back(data->fd); 316 | 317 | HMODULE hKernel32 = LoadLibrary("kernel32.dll"); 318 | // Look up function address 319 | CancelIoExType pCancelIoEx = (CancelIoExType)GetProcAddress(hKernel32, "CancelIoEx"); 320 | // Do something with it 321 | if (pCancelIoEx) 322 | { 323 | // Function exists so call it 324 | // Cancel all pending IO Requests for the current device 325 | pCancelIoEx((HANDLE)data->fd, NULL); 326 | } 327 | if(!CloseHandle((HANDLE)data->fd)) { 328 | ErrorCodeToString("closing connection", GetLastError(), data->errorString); 329 | return; 330 | } 331 | } 332 | 333 | /* 334 | * listComPorts.c -- list COM ports 335 | * 336 | * http://github.com/todbot/usbSearch/ 337 | * 338 | * 2012, Tod E. Kurt, http://todbot.com/blog/ 339 | * 340 | * 341 | * Uses DispHealper : http://disphelper.sourceforge.net/ 342 | * 343 | * Notable VIDs & PIDs combos: 344 | * VID 0403 - FTDI 345 | * 346 | * VID 0403 / PID 6001 - Arduino Diecimila 347 | * 348 | */ 349 | void EIO_List(uv_work_t* req) { 350 | ListBaton* data = static_cast(req->data); 351 | 352 | { 353 | DISPATCH_OBJ(wmiSvc); 354 | DISPATCH_OBJ(colDevices); 355 | 356 | dhInitialize(TRUE); 357 | dhToggleExceptions(FALSE); 358 | 359 | dhGetObject(L"winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2", NULL, &wmiSvc); 360 | dhGetValue(L"%o", &colDevices, wmiSvc, L".ExecQuery(%S)", L"Select * from Win32_PnPEntity"); 361 | 362 | int port_count = 0; 363 | FOR_EACH(objDevice, colDevices, NULL) { 364 | char* name = NULL; 365 | char* pnpid = NULL; 366 | char* manu = NULL; 367 | char* match; 368 | 369 | dhGetValue(L"%s", &name, objDevice, L".Name"); 370 | dhGetValue(L"%s", &pnpid, objDevice, L".PnPDeviceID"); 371 | 372 | if( name != NULL && ((match = strstr( name, "(COM" )) != NULL) ) { // look for "(COM23)" 373 | // 'Manufacturuer' can be null, so only get it if we need it 374 | dhGetValue(L"%s", &manu, objDevice, L".Manufacturer"); 375 | port_count++; 376 | char* comname = strtok( match, "()"); 377 | ListResultItem* resultItem = new ListResultItem(); 378 | resultItem->comName = comname; 379 | resultItem->manufacturer = manu; 380 | resultItem->pnpId = pnpid; 381 | data->results.push_back(resultItem); 382 | dhFreeString(manu); 383 | } 384 | 385 | dhFreeString(name); 386 | dhFreeString(pnpid); 387 | } NEXT(objDevice); 388 | 389 | SAFE_RELEASE(colDevices); 390 | SAFE_RELEASE(wmiSvc); 391 | 392 | dhUninitialize(TRUE); 393 | } 394 | 395 | std::vector ports; 396 | if (CEnumerateSerial::UsingQueryDosDevice(ports)) 397 | { 398 | for (size_t i = 0; i < ports.size(); i++) 399 | { 400 | char comname[64] = { 0 }; 401 | _snprintf(comname, sizeof(comname), "COM%u", ports[i]); 402 | bool bFound = false; 403 | for (std::list::iterator ri = data->results.begin(); ri != data->results.end(); ++ri) 404 | { 405 | if (stricmp((*ri)->comName.c_str(), comname) == 0) 406 | { 407 | bFound = true; 408 | break; 409 | } 410 | } 411 | if (!bFound) 412 | { 413 | ListResultItem* resultItem = new ListResultItem(); 414 | resultItem->comName = comname; 415 | resultItem->manufacturer = ""; 416 | resultItem->pnpId = ""; 417 | data->results.push_back(resultItem); 418 | } 419 | } 420 | } 421 | } 422 | 423 | void EIO_Flush(uv_work_t* req) { 424 | FlushBaton* data = static_cast(req->data); 425 | 426 | if(!FlushFileBuffers((HANDLE)data->fd)) { 427 | ErrorCodeToString("flushing connection", GetLastError(), data->errorString); 428 | return; 429 | } 430 | } 431 | 432 | #endif 433 | -------------------------------------------------------------------------------- /serialport.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /*global process require exports console */ 3 | 4 | // Copyright 2011 Chris Williams 5 | 6 | var Buffer = require('buffer').Buffer; 7 | var SerialPortBinding = require("bindings")("serialport.node"); 8 | var EventEmitter = require('events').EventEmitter; 9 | var util = require('util'); 10 | var fs = require('fs'); 11 | var stream = require('stream'); 12 | var path = require('path'); 13 | var async = require('async'); 14 | var child_process = require('child_process'); 15 | 16 | 17 | // Removing check for valid BaudRates due to ticket: #140 18 | // var BAUDRATES = [500000, 230400, 115200, 57600, 38400, 19200, 9600, 4800, 2400, 1800, 1200, 600, 300, 200, 150, 134, 110, 75, 50]; 19 | 20 | 21 | // VALIDATION ARRAYS 22 | var DATABITS = [5, 6, 7, 8]; 23 | var STOPBITS = [1, 1.5, 2]; 24 | var PARITY = ['none', 'even', 'mark', 'odd', 'space']; 25 | var FLOWCONTROLS = ["XON", "XOFF", "XANY", "RTSCTS"]; 26 | 27 | 28 | // Stuff from ReadStream, refactored for our usage: 29 | var kPoolSize = 40 * 1024; 30 | var kMinPoolSpace = 128; 31 | var pool; 32 | 33 | function allocNewPool() { 34 | pool = new Buffer(kPoolSize); 35 | pool.used = 0; 36 | } 37 | 38 | 39 | var parsers = { 40 | raw: function (emitter, buffer) { 41 | emitter.emit("data", buffer); 42 | }, 43 | //encoding: ascii utf8 utf16le ucs2 base64 binary hex 44 | //More: http://nodejs.org/api/buffer.html#buffer_buffer 45 | readline: function (delimiter, encoding) { 46 | if (typeof delimiter === "undefined" || delimiter === null) { delimiter = "\r"; } 47 | if (typeof encoding === "undefined" || encoding === null) { encoding = "utf8"; } 48 | // Delimiter buffer saved in closure 49 | var data = ""; 50 | return function (emitter, buffer) { 51 | // Collect data 52 | data += buffer.toString(encoding); 53 | // Split collected data by delimiter 54 | var parts = data.split(delimiter) 55 | data = parts.pop(); 56 | parts.forEach(function (part, i, array) { 57 | emitter.emit('data', part); 58 | }); 59 | }; 60 | } 61 | }; 62 | 63 | // The default options, can be overwritten in the 'SerialPort' constructor 64 | var _options = { 65 | baudrate: 9600, 66 | parity: 'none', 67 | rtscts: false, 68 | xon: false, 69 | xoff: false, 70 | xany: false, 71 | databits: 8, 72 | stopbits: 1, 73 | buffersize: 256, 74 | parser: parsers.raw 75 | }; 76 | function SerialPort (path, options, openImmediately) { 77 | options = options || {}; 78 | openImmediately = (openImmediately === undefined || openImmediately === null) ? true : openImmediately; 79 | 80 | var self = this; 81 | 82 | stream.Stream.call(this); 83 | 84 | options.baudRate = options.baudRate || options.baudrate || _options.baudrate; 85 | // Removing check for valid BaudRates due to ticket: #140 86 | // if (BAUDRATES.indexOf(options.baudrate) == -1) { 87 | // throw new Error('Invalid "baudrate": ' + options.baudrate); 88 | // } 89 | 90 | options.dataBits = options.dataBits || options.databits || _options.databits; 91 | if (DATABITS.indexOf(options.dataBits) == -1) { 92 | throw new Error('Invalid "databits": ' + options.dataBits); 93 | } 94 | 95 | options.stopBits = options.stopBits || options.stopbits || _options.stopbits; 96 | if (STOPBITS.indexOf(options.stopBits) == -1) { 97 | throw new Error('Invalid "stopbits": ' + options.stopbits); 98 | } 99 | 100 | options.parity = options.parity || _options.parity; 101 | if (PARITY.indexOf(options.parity) == -1) { 102 | throw new Error('Invalid "parity": ' + options.parity); 103 | } 104 | if (!path) { 105 | throw new Error('Invalid port specified: ' + path); 106 | } 107 | 108 | // flush defaults, then update with provided details 109 | options.rtscts = _options.rtscts; 110 | options.xon = _options.xon; 111 | options.xoff = _options.xoff; 112 | options.xany = _options.xany; 113 | 114 | if (options.flowControl || options.flowcontrol) { 115 | var fc = options.flowControl || options.flowcontrol; 116 | 117 | if (typeof fc == 'boolean') { 118 | options.rtscts = true; 119 | } else { 120 | fc.forEach(function (flowControl) { 121 | var fcup = flowControl.toUpperCase(); 122 | var idx = FLOWCONTROLS.indexOf(fcup); 123 | if (idx < 0) { 124 | throw new Error('Invalid "flowControl": ' + fcup + ". Valid options: "+FLOWCONTROLS.join(", ")); 125 | } else { 126 | 127 | // "XON", "XOFF", "XANY", "DTRDTS", "RTSCTS" 128 | switch (idx) { 129 | case 0: options.xon = true; break; 130 | case 1: options.xoff = true; break; 131 | case 2: options.xany = true; break; 132 | case 3: options.rtscts = true; break; 133 | } 134 | } 135 | }) 136 | } 137 | } 138 | 139 | options.bufferSize = options.bufferSize || options.buffersize || _options.buffersize; 140 | options.parser = options.parser || _options.parser; 141 | 142 | options.dataCallback = function (data) { 143 | options.parser(self, data); 144 | }; 145 | 146 | // options.dataReadyCallback = function () { 147 | // self.readStream._read(4024); 148 | // }; 149 | 150 | options.errorCallback = function (err) { 151 | // console.log("sp err:", JSON.stringify(err)); 152 | self.emit('error', {spErr: err}); 153 | }; 154 | options.disconnectedCallback = function () { 155 | if (self.closing) { 156 | return; 157 | } 158 | self.emit('error', new Error("Disconnected")); 159 | // self.close(); 160 | }; 161 | 162 | if (process.platform == 'win32') { 163 | path = '\\\\.\\' + path; 164 | } else { 165 | // All other platforms: 166 | this.fd = null; 167 | this.paused = true; 168 | this.bufferSize = options.bufferSize || 64 * 1024; 169 | this.readable = true; 170 | this.reading = false; 171 | 172 | if (options.encoding) 173 | this.setEncoding(this.encoding); 174 | } 175 | 176 | this.options = options; 177 | this.path = path; 178 | 179 | if (openImmediately) { 180 | process.nextTick(function () { 181 | self.open(); 182 | }); 183 | } 184 | } 185 | 186 | util.inherits(SerialPort, stream.Stream); 187 | 188 | SerialPort.prototype.open = function (callback) { 189 | var self = this; 190 | SerialPortBinding.open(this.path, this.options, function (err, fd) { 191 | self.fd = fd; 192 | if (err) { 193 | self.emit('error', {openErr: err}); 194 | if (callback) { callback(err); } 195 | return; 196 | } 197 | if (process.platform !== 'win32') { 198 | // self.readStream = new SerialStream(self.fd, { bufferSize: self.options.bufferSize }); 199 | // self.readStream.on("data", self.options.dataCallback); 200 | // self.readStream.on("error", self.options.errorCallback); 201 | // self.readStream.on("close", function () { 202 | // self.close(); 203 | // }); 204 | // self.readStream.on("end", function () { 205 | // console.log(">>END"); 206 | // self.emit('end'); 207 | // }); 208 | // self.readStream.resume(); 209 | self.paused = false; 210 | self.serialPoller = new SerialPortBinding.SerialportPoller(self.fd, function() {self._read();}); 211 | self.serialPoller.start(); 212 | } 213 | 214 | self.emit('open'); 215 | if (callback) { callback(err); } 216 | }); 217 | }; 218 | 219 | SerialPort.prototype.write = function (buffer, callback) { 220 | var self = this; 221 | if (!this.fd) { 222 | if (callback) { 223 | return callback(new Error("Serialport not open.")); 224 | } else { 225 | return; 226 | } 227 | } 228 | 229 | if (!Buffer.isBuffer(buffer)) { 230 | buffer = new Buffer(buffer); 231 | } 232 | SerialPortBinding.write(this.fd, buffer, function (err, results) { 233 | if (err) { 234 | self.emit('error', {spWriteErr: err}); 235 | } 236 | if (callback) { 237 | callback(err, results); 238 | } 239 | }); 240 | }; 241 | 242 | if (process.platform !== 'win32') { 243 | SerialPort.prototype._read = function() { 244 | var self = this; 245 | 246 | // console.log(">>READ"); 247 | if (!self.readable || self.paused || self.reading) return; 248 | 249 | self.reading = true; 250 | 251 | if (!pool || pool.length - pool.used < kMinPoolSpace) { 252 | // discard the old pool. Can't add to the free list because 253 | // users might have refernces to slices on it. 254 | pool = null; 255 | allocNewPool(); 256 | } 257 | 258 | // Grab another reference to the pool in the case that while we're in the 259 | // thread pool another read() finishes up the pool, and allocates a new 260 | // one. 261 | var thisPool = pool; 262 | var toRead = Math.min(pool.length - pool.used, ~~self.bufferSize); 263 | var start = pool.used; 264 | 265 | function afterRead(err, bytesRead, readPool, bytesRequested) { 266 | self.reading = false; 267 | if (err) { 268 | if (err.code && err.code == 'EAGAIN') { 269 | if (self.fd >= 0) 270 | self.serialPoller.start(); 271 | } else { 272 | self.fd = null; 273 | self.options.errorCallback(err); 274 | self.readable = false; 275 | return; 276 | } 277 | } 278 | 279 | // Since we will often not read the number of bytes requested, 280 | // let's mark the ones we didn't need as available again. 281 | pool.used -= bytesRequested - bytesRead; 282 | 283 | // console.log(">>ACTUALLY READ: ", bytesRead); 284 | 285 | if (bytesRead === 0) { 286 | if (self.fd >= 0) 287 | self.serialPoller.start(); 288 | } else { 289 | var b = thisPool.slice(start, start + bytesRead); 290 | 291 | // do not emit events if the stream is paused 292 | if (self.paused) { 293 | self.buffer = Buffer.concat([self.buffer, b]); 294 | return; 295 | } else { 296 | self._emitData(b); 297 | } 298 | 299 | // do not emit events anymore after we declared the stream unreadable 300 | if (!self.readable) return; 301 | 302 | self._read(); 303 | } 304 | } 305 | 306 | // console.log(">>REQUEST READ: ", toRead); 307 | fs.read(self.fd, pool, pool.used, toRead, self.pos, function(err, bytesRead){ 308 | var readPool = pool; 309 | var bytesRequested = toRead; 310 | afterRead(err, bytesRead, readPool, bytesRequested);} 311 | ); 312 | 313 | pool.used += toRead; 314 | }; 315 | 316 | 317 | SerialPort.prototype._emitData = function(d) { 318 | var self = this; 319 | // if (self._decoder) { 320 | // var string = self._decoder.write(d); 321 | // if (string.length) self.options.dataCallback(string); 322 | // } else { 323 | self.options.dataCallback(d); 324 | // } 325 | }; 326 | 327 | SerialPort.prototype.pause = function() { 328 | var self = this; 329 | self.paused = true; 330 | }; 331 | 332 | 333 | SerialPort.prototype.resume = function() { 334 | var self = this; 335 | self.paused = false; 336 | 337 | if (self.buffer) { 338 | var buffer = self.buffer; 339 | self.buffer = null; 340 | self._emitData(buffer); 341 | } 342 | 343 | // No longer open? 344 | if (null == self.fd) 345 | return; 346 | 347 | self._read(); 348 | }; 349 | 350 | } // if !'win32' 351 | 352 | SerialPort.prototype.close = function (callback) { 353 | var self = this; 354 | 355 | var fd = self.fd; 356 | 357 | if (self.closing) { 358 | return; 359 | } 360 | if (!fd) { 361 | if (callback) { 362 | return callback(new Error("Serialport not open.")); 363 | } else { 364 | return; 365 | } 366 | } 367 | 368 | self.closing = true; 369 | try { 370 | if (self.readStream) { 371 | // Make sure we clear the readStream's fd, or it'll try to close() it. 372 | // We already close()d it. 373 | self.readStream.fd = null; 374 | self.readStream.destroy(); 375 | } 376 | 377 | SerialPortBinding.close(fd, function (err) { 378 | if (err) { 379 | self.emit('error', err); 380 | } 381 | if (callback) { 382 | callback(err); 383 | } 384 | self.emit('close'); 385 | self.removeAllListeners(); 386 | self.closing = false; 387 | self.fd = 0; 388 | 389 | if (process.platform !== 'win32') { 390 | self.readable = false; 391 | self.serialPoller.close(); 392 | } 393 | }); 394 | } catch (ex) { 395 | self.closing = false; 396 | throw ex; 397 | } 398 | }; 399 | 400 | function listUnix (callback) { 401 | fs.readdir("/dev/serial/by-id", function (err, files) { 402 | if (err) { 403 | // if this directory is not found this could just be because it's not plugged in 404 | if (err.errno === 34) { 405 | return callback(null, []); 406 | } 407 | return console.log(err); 408 | } 409 | var dirName = "/dev/serial/by-id"; 410 | async.map(files, function (file, callback) { 411 | var fileName = path.join(dirName, file); 412 | fs.readlink(fileName, function (err, link) { 413 | if (err) { 414 | return callback(err); 415 | } 416 | link = path.resolve(dirName, link); 417 | callback(null, { 418 | comName: link, 419 | manufacturer: undefined, 420 | pnpId: file 421 | }); 422 | }); 423 | // Suspect code per ticket: #104 removed for deeper inspection. 424 | // fs.readdir("/dev/serial/by-path", function(err_path, paths) { 425 | // if (err_path) { 426 | // if (err.errno === 34) return callback(null, []); 427 | // return console.log(err); 428 | // } 429 | 430 | // var dirName, items; 431 | // //check if multiple devices of the same id are connected 432 | // if (files.length !== paths.length) { 433 | // dirName = "/dev/serial/by-path"; 434 | // items = paths; 435 | // } else { 436 | // dirName = "/dev/serial/by-id"; 437 | // items = files; 438 | // } 439 | 440 | // async.map(items, function (file, callback) { 441 | // var fileName = path.join(dirName, file); 442 | // fs.readlink(fileName, function (err, link) { 443 | // if (err) { 444 | // return callback(err); 445 | // } 446 | // link = path.resolve(dirName, link); 447 | // callback(null, { 448 | // comName: link, 449 | // manufacturer: undefined, 450 | // pnpId: file 451 | // }); 452 | // }); 453 | // }, callback); 454 | }, callback); 455 | }); 456 | } 457 | 458 | 459 | if (process.platform === 'win32') { 460 | exports.list = SerialPortBinding.list 461 | } else if (process.platform === 'darwin') { 462 | exports.list = SerialPortBinding.list; 463 | } else { 464 | exports.list = listUnix; 465 | } 466 | 467 | SerialPort.prototype.flush = function (callback) { 468 | var self = this; 469 | var fd = self.fd; 470 | 471 | if (!fd) { 472 | if (callback) { 473 | return callback(new Error("Serialport not open.")); 474 | } else { 475 | return; 476 | } 477 | } 478 | 479 | SerialPortBinding.flush(fd, function (err, result) { 480 | if (err) { 481 | self.emit('error', err); 482 | } 483 | if (callback) { 484 | callback(err, result); 485 | } 486 | }); 487 | }; 488 | 489 | module.exports.SerialPort = SerialPort; 490 | module.exports.parsers = parsers; 491 | -------------------------------------------------------------------------------- /src/win/disphelper.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the source code for the DispHelper COM helper library. 2 | * DispHelper allows you to call COM objects with an extremely simple printf style syntax. 3 | * DispHelper can be used from C++ or even plain C. It works with most Windows compilers 4 | * including Dev-CPP, Visual C++ and LCC-WIN32. Including DispHelper in your project 5 | * couldn't be simpler as it is available in a compacted single file version. 6 | * 7 | * Included with DispHelper are over 20 samples that demonstrate using COM objects 8 | * including ADO, CDO, Outlook, Eudora, Excel, Word, Internet Explorer, MSHTML, 9 | * PocketSoap, Word Perfect, MS Agent, SAPI, MSXML, WIA, dexplorer and WMI. 10 | * 11 | * DispHelper is free open source software provided under the BSD license. 12 | * 13 | * Find out more and download DispHelper at: 14 | * http://sourceforge.net/projects/disphelper/ 15 | * http://disphelper.sourceforge.net/ 16 | */ 17 | 18 | 19 | #ifndef DISPHELPER_H_INCLUDED 20 | #define DISPHELPER_H_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | HRESULT dhCreateObject(LPCOLESTR szProgId, LPCWSTR szMachine, IDispatch ** ppDisp); 30 | HRESULT dhGetObject(LPCOLESTR szFile, LPCOLESTR szProgId, IDispatch ** ppDisp); 31 | 32 | HRESULT dhCreateObjectEx(LPCOLESTR szProgId, REFIID riid, DWORD dwClsContext, COSERVERINFO * pServerInfo, void ** ppv); 33 | HRESULT dhGetObjectEx(LPCOLESTR szFile, LPCOLESTR szProgId, REFIID riid, DWORD dwClsContext, LPVOID lpvReserved, void ** ppv); 34 | 35 | HRESULT dhCallMethod(IDispatch * pDisp, LPCOLESTR szMember, ...); 36 | HRESULT dhPutValue(IDispatch * pDisp, LPCOLESTR szMember, ...); 37 | HRESULT dhPutRef(IDispatch * pDisp, LPCOLESTR szMember, ...); 38 | HRESULT dhGetValue(LPCWSTR szIdentifier, void * pResult, IDispatch * pDisp, LPCOLESTR szMember, ...); 39 | 40 | HRESULT dhInvoke(int invokeType, VARTYPE returnType, VARIANT * pvResult, IDispatch * pDisp, LPCOLESTR szMember, ...); 41 | HRESULT dhInvokeArray(int invokeType, VARIANT * pvResult, UINT cArgs, IDispatch * pDisp, LPCOLESTR szMember, VARIANT * pArgs); 42 | 43 | HRESULT dhCallMethodV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); 44 | HRESULT dhPutValueV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); 45 | HRESULT dhPutRefV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); 46 | HRESULT dhGetValueV(LPCWSTR szIdentifier, void * pResult, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); 47 | HRESULT dhInvokeV(int invokeType, VARTYPE returnType, VARIANT * pvResult, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); 48 | 49 | HRESULT dhAutoWrap(int invokeType, VARIANT * pvResult, IDispatch * pDisp, LPCOLESTR szMember, UINT cArgs, ...); 50 | HRESULT dhParseProperties(IDispatch * pDisp, LPCWSTR szProperties, UINT * lpcPropsSet); 51 | 52 | HRESULT dhEnumBegin(IEnumVARIANT ** ppEnum, IDispatch * pDisp, LPCOLESTR szMember, ...); 53 | HRESULT dhEnumBeginV(IEnumVARIANT ** ppEnum, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); 54 | HRESULT dhEnumNextObject(IEnumVARIANT * pEnum, IDispatch ** ppDisp); 55 | HRESULT dhEnumNextVariant(IEnumVARIANT * pEnum, VARIANT * pvResult); 56 | 57 | HRESULT dhInitializeImp(BOOL bInitializeCOM, BOOL bUnicode); 58 | void dhUninitialize(BOOL bUninitializeCOM); 59 | 60 | #define dhInitializeA(bInitializeCOM) dhInitializeImp(bInitializeCOM, FALSE) 61 | #define dhInitializeW(bInitializeCOM) dhInitializeImp(bInitializeCOM, TRUE) 62 | 63 | #ifdef UNICODE 64 | #define dhInitialize dhInitializeW 65 | #else 66 | #define dhInitialize dhInitializeA 67 | #endif 68 | 69 | #define AutoWrap dhAutoWrap 70 | #define DISPATCH_OBJ(objName) IDispatch * objName = NULL 71 | #define dhFreeString(string) SysFreeString((BSTR) string) 72 | 73 | #ifndef SAFE_RELEASE 74 | #ifdef __cplusplus 75 | #define SAFE_RELEASE(pObj) { if (pObj) { (pObj)->Release(); (pObj) = NULL; } } 76 | #else 77 | #define SAFE_RELEASE(pObj) { if (pObj) { (pObj)->lpVtbl->Release(pObj); (pObj) = NULL; } } 78 | #endif 79 | #endif 80 | 81 | #define SAFE_FREE_STRING(string) { dhFreeString(string); (string) = NULL; } 82 | 83 | 84 | 85 | 86 | /* ===================================================================== */ 87 | #ifndef DISPHELPER_NO_WITH 88 | 89 | #define WITH0(objName, pDisp, szMember) { \ 90 | DISPATCH_OBJ(objName); \ 91 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember))) { 92 | 93 | #define WITH1(objName, pDisp, szMember, arg1) { \ 94 | DISPATCH_OBJ(objName); \ 95 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1))) { 96 | 97 | #define WITH2(objName, pDisp, szMember, arg1, arg2) { \ 98 | DISPATCH_OBJ(objName); \ 99 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2))) { 100 | 101 | #define WITH3(objName, pDisp, szMember, arg1, arg2, arg3) { \ 102 | DISPATCH_OBJ(objName); \ 103 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2, arg3))) { 104 | 105 | #define WITH4(objName, pDisp, szMember, arg1, arg2, arg3, arg4) { \ 106 | DISPATCH_OBJ(objName); \ 107 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2, arg3, arg4))) { 108 | 109 | #define WITH WITH0 110 | 111 | #define ON_WITH_ERROR(objName) } else { 112 | 113 | #define END_WITH(objName) } SAFE_RELEASE(objName); } 114 | 115 | #endif /* ----- DISPHELPER_NO_WITH ----- */ 116 | 117 | 118 | 119 | 120 | /* ===================================================================== */ 121 | #ifndef DISPHELPER_NO_FOR_EACH 122 | 123 | #define FOR_EACH0(objName, pDisp, szMember) { \ 124 | IEnumVARIANT * xx_pEnum_xx = NULL; \ 125 | DISPATCH_OBJ(objName); \ 126 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember))) { \ 127 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 128 | 129 | #define FOR_EACH1(objName, pDisp, szMember, arg1) { \ 130 | IEnumVARIANT * xx_pEnum_xx = NULL; \ 131 | DISPATCH_OBJ(objName); \ 132 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1))) { \ 133 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 134 | 135 | #define FOR_EACH2(objName, pDisp, szMember, arg1, arg2) { \ 136 | IEnumVARIANT * xx_pEnum_xx = NULL; \ 137 | DISPATCH_OBJ(objName); \ 138 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2))) { \ 139 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 140 | 141 | 142 | #define FOR_EACH3(objName, pDisp, szMember, arg1, arg2, arg3) { \ 143 | IEnumVARIANT * xx_pEnum_xx = NULL; \ 144 | DISPATCH_OBJ(objName); \ 145 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2, arg3))) { \ 146 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 147 | 148 | 149 | #define FOR_EACH4(objName, pDisp, szMember, arg1, arg2, arg3, arg4) { \ 150 | IEnumVARIANT * xx_pEnum_xx = NULL; \ 151 | DISPATCH_OBJ(objName); \ 152 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2, arg3, arg4))) { \ 153 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 154 | 155 | #define FOR_EACH FOR_EACH0 156 | 157 | #define ON_FOR_EACH_ERROR(objName) SAFE_RELEASE(objName); }} else {{ 158 | 159 | #define NEXT(objName) SAFE_RELEASE(objName); }} SAFE_RELEASE(objName); SAFE_RELEASE(xx_pEnum_xx); } 160 | 161 | #endif /* ----- DISPHELPER_NO_FOR_EACH ----- */ 162 | 163 | 164 | 165 | 166 | /* ===================================================================== */ 167 | #ifndef DISPHELPER_NO_EXCEPTIONS 168 | 169 | /* Structure to store a DispHelper exception */ 170 | typedef struct tagDH_EXCEPTION 171 | { 172 | LPCWSTR szInitialFunction; 173 | LPCWSTR szErrorFunction; 174 | 175 | HRESULT hr; 176 | 177 | WCHAR szMember[64]; 178 | WCHAR szCompleteMember[256]; 179 | 180 | UINT swCode; 181 | LPWSTR szDescription; 182 | LPWSTR szSource; 183 | LPWSTR szHelpFile; 184 | DWORD dwHelpContext; 185 | 186 | UINT iArgError; 187 | 188 | BOOL bDispatchError; 189 | 190 | #ifdef DISPHELPER_INTERNAL_BUILD 191 | BOOL bOld; 192 | #endif 193 | } DH_EXCEPTION, * PDH_EXCEPTION; 194 | 195 | typedef void (*DH_EXCEPTION_CALLBACK) (PDH_EXCEPTION); 196 | 197 | /* Structure to store exception options. */ 198 | typedef struct tagDH_EXCEPTION_OPTIONS 199 | { 200 | HWND hwnd; 201 | LPCWSTR szAppName; 202 | BOOL bShowExceptions; 203 | BOOL bDisableRecordExceptions; 204 | DH_EXCEPTION_CALLBACK pfnExceptionCallback; 205 | } DH_EXCEPTION_OPTIONS, * PDH_EXCEPTION_OPTIONS; 206 | 207 | /* Functions to manipulate global exception options */ 208 | HRESULT dhToggleExceptions(BOOL bShow); 209 | HRESULT dhSetExceptionOptions(PDH_EXCEPTION_OPTIONS pExceptionOptions); 210 | HRESULT dhGetExceptionOptions(PDH_EXCEPTION_OPTIONS pExceptionOptions); 211 | 212 | /* Functions to show an exception, format an exception into a string 213 | * and get a copy of the last exception */ 214 | HRESULT dhShowException(PDH_EXCEPTION pException); 215 | HRESULT dhGetLastException(PDH_EXCEPTION * pException); 216 | HRESULT dhFormatExceptionW(PDH_EXCEPTION pException, LPWSTR szBuffer, UINT cchBufferSize, BOOL bFixedFont); 217 | HRESULT dhFormatExceptionA(PDH_EXCEPTION pException, LPSTR szBuffer, UINT cchBufferSize, BOOL bFixedFont); 218 | 219 | #ifdef UNICODE 220 | #define dhFormatException dhFormatExceptionW 221 | #else 222 | #define dhFormatException dhFormatExceptionA 223 | #endif 224 | 225 | #ifdef DISPHELPER_INTERNAL_BUILD 226 | 227 | void dhEnter(void); 228 | HRESULT dhExitEx(HRESULT hr, BOOL bDispatchError, LPCWSTR szMember, LPCWSTR szCompleteMember, EXCEPINFO * pExcepInfo, UINT iArgError, LPCWSTR szFunctionName); 229 | void dhCleanupThreadException(void); 230 | 231 | #define DH_ENTER(szFunctionName) static LPCWSTR xx_szFunctionName_xx = szFunctionName; \ 232 | dhEnter() 233 | 234 | #define DH_EXITEX(hr, bDispatchError, szMember, szCompleteMember, pExcepInfo, iArgError) \ 235 | dhExitEx(hr, bDispatchError, szMember, szCompleteMember, pExcepInfo, iArgError, xx_szFunctionName_xx) 236 | 237 | #define DH_EXIT(hr, szCompleteMember) DH_EXITEX(hr, FALSE, NULL, szCompleteMember, NULL, 0) 238 | 239 | #endif /* ----- DISPHELPER_INTERNAL_BUILD ----- */ 240 | 241 | #else /* ----- DISPHELPER_NO_EXCEPTIONS ----- */ 242 | 243 | /* These macros define out calls to selected exception functions */ 244 | #define dhToggleExceptions(bShow) (E_NOTIMPL) 245 | #define dhSetExceptionOptions(pExcepOptions) (E_NOTIMPL) 246 | 247 | #ifdef DISPHELPER_INTERNAL_BUILD 248 | #define DH_ENTER(szFunctionName) 249 | #define DH_EXITEX(hr, bDispatchError, szMember, szCompleteMember, pExcepInfo, iArgError) \ 250 | (((hr == DISP_E_EXCEPTION && pExcepInfo) ? \ 251 | (SysFreeString(((EXCEPINFO *)(pExcepInfo))->bstrSource), \ 252 | SysFreeString(((EXCEPINFO *)(pExcepInfo))->bstrDescription), \ 253 | SysFreeString(((EXCEPINFO *)(pExcepInfo))->bstrHelpFile), 0) : (0)), hr) 254 | #define DH_EXIT(hr, szCompleteMember)(hr) 255 | #endif 256 | 257 | #endif /* ----- DISPHELPER_NO_EXCEPTIONS ----- */ 258 | 259 | 260 | 261 | 262 | 263 | 264 | /* ===================================================================== */ 265 | #ifdef DISPHELPER_INTERNAL_BUILD 266 | 267 | #include 268 | #include 269 | #include 270 | 271 | /* Macro to include or lose debug code. */ 272 | #ifdef DEBUG 273 | #define DBG_CODE(code) code 274 | #else 275 | #define DBG_CODE(code) 276 | #endif 277 | 278 | /* Are we in unicode mode? */ 279 | extern BOOL dh_g_bIsUnicodeMode; 280 | 281 | /* Number of objects in an array */ 282 | #undef ARRAYSIZE 283 | #define ARRAYSIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 284 | 285 | /* Maximum number of arguments for a member */ 286 | #define DH_MAX_ARGS 25 287 | 288 | /* Maximum length of a member string */ 289 | #define DH_MAX_MEMBER 512 290 | 291 | /* This macro is missing from Dev-Cpp/Mingw */ 292 | #ifndef V_UI4 293 | #define V_UI4(X) V_UNION(X, ulVal) 294 | #endif 295 | 296 | /* Macro to notify programmer of invalid identifier in debug mode. */ 297 | #define DEBUG_NOTIFY_INVALID_IDENTIFIER(chIdentifier) \ 298 | DBG_CODE( { \ 299 | char buf[256]; \ 300 | sprintf(buf,"DEBUG: The format string or return identifier contained the invalid identifier '%c'.\n" \ 301 | "The valid identifiers are \"d/u/e/b/v/B/S/s/T/o/O/t/W/D/f/m\".\n" \ 302 | "Each %% character should be followed by a valid identifier.\n" \ 303 | "Identifiers are case sensitive.", (chIdentifier)); \ 304 | MessageBoxA(NULL, buf, "DEBUG: Invalid Format Identifier", MB_ICONSTOP); \ 305 | } ) 306 | 307 | #ifdef _MSC_VER 308 | #pragma warning(disable : 4706) /* Assignment in conditional expression */ 309 | #endif 310 | 311 | #ifndef DISPHELPER_NO_PRAGMA_LIB 312 | #ifdef __LCC__ 313 | #pragma lib 314 | #pragma lib 315 | #pragma lib 316 | #endif 317 | #endif 318 | 319 | 320 | #endif /* ----- DISPHELPER_INTERNAL_BUILD ----- */ 321 | 322 | #ifndef DISPHELPER_NO_PRAGMA_LIB 323 | #if defined(_MSC_VER) || defined(__BORLANDC__) 324 | #pragma comment(lib, "ole32.lib") 325 | #pragma comment(lib, "oleaut32.lib") 326 | #pragma comment(lib, "uuid.lib") 327 | #endif 328 | #endif 329 | 330 | 331 | 332 | #ifdef __cplusplus 333 | } 334 | #endif 335 | 336 | 337 | 338 | 339 | /* ===================================================================== */ 340 | #if defined(__cplusplus) && !defined(DISPHELPER_NO_CPP_EXTENSIONS) 341 | 342 | #include 343 | #include 344 | 345 | #ifdef _MSC_VER 346 | #pragma warning( disable : 4290 ) /* throw() specification ignored */ 347 | #endif 348 | 349 | #ifndef DISPHELPER_USE_MS_SMART_PTR 350 | 351 | template 352 | class CDhComPtr 353 | { 354 | public: 355 | CDhComPtr() throw() : m_pInterface (NULL) {} 356 | 357 | CDhComPtr(T* pInterface) throw() : m_pInterface (pInterface) 358 | { 359 | if (m_pInterface) m_pInterface->AddRef(); 360 | } 361 | 362 | CDhComPtr(const CDhComPtr& original) throw() : m_pInterface (original.m_pInterface) 363 | { 364 | if (m_pInterface) m_pInterface->AddRef(); 365 | } 366 | 367 | ~CDhComPtr() throw() 368 | { 369 | Dispose(); 370 | } 371 | 372 | void Dispose() throw() 373 | { 374 | if (m_pInterface) 375 | { 376 | m_pInterface->Release(); 377 | m_pInterface = NULL; 378 | } 379 | } 380 | 381 | T* Detach() throw() 382 | { 383 | T* temp = m_pInterface; 384 | m_pInterface = NULL; 385 | return temp; 386 | } 387 | 388 | inline operator T*() const throw() 389 | { 390 | return m_pInterface; 391 | } 392 | 393 | T** operator&() throw() 394 | { 395 | Dispose(); 396 | return &m_pInterface; 397 | } 398 | 399 | T* operator->() const throw(HRESULT) 400 | { 401 | if (!m_pInterface) throw E_POINTER; 402 | return m_pInterface; 403 | } 404 | 405 | CDhComPtr& operator=(T* pInterface) throw() 406 | { 407 | if (m_pInterface != pInterface) 408 | { 409 | T* pOldInterface = m_pInterface; 410 | m_pInterface = pInterface; 411 | if (m_pInterface) m_pInterface->AddRef(); 412 | if (pOldInterface) pOldInterface->Release(); 413 | } 414 | 415 | return *this; 416 | } 417 | 418 | CDhComPtr& operator=(const int null) throw(HRESULT) 419 | { 420 | if (null != 0) throw(E_POINTER); 421 | return operator=((T*) NULL); 422 | } 423 | 424 | CDhComPtr& operator=(const CDhComPtr& rhs) throw() 425 | { 426 | return operator=(rhs.m_pInterface); 427 | } 428 | 429 | private: 430 | T* m_pInterface; 431 | }; 432 | 433 | typedef CDhComPtr CDispPtr; 434 | typedef CDhComPtr CEnumPtr; 435 | typedef CDhComPtr CUnknownPtr; 436 | 437 | #else /* DISPHELPER_USE_MS_SMART_PTR */ 438 | 439 | #include 440 | typedef IDispatchPtr CDispPtr; 441 | typedef IEnumVARIANTPtr CEnumPtr; 442 | typedef IUnknownPtr CUnknownPtr; 443 | 444 | #endif /* DISPHELPER_USE_MS_SMART_PTR */ 445 | 446 | 447 | 448 | 449 | /* ===================================================================== */ 450 | template 451 | class CDhStringTemplate 452 | { 453 | public: 454 | CDhStringTemplate() throw() : m_strptr (NULL) {} 455 | 456 | CDhStringTemplate(const CDhStringTemplate& original) throw() 457 | { 458 | Copy(original.m_strptr); 459 | } 460 | 461 | CDhStringTemplate(const int null) throw(HRESULT) : m_strptr (NULL) 462 | { 463 | if (null != 0) throw(E_POINTER); 464 | } 465 | 466 | ~CDhStringTemplate() throw() 467 | { 468 | Dispose(); 469 | } 470 | 471 | void Dispose() throw() 472 | { 473 | dhFreeString(m_strptr); 474 | m_strptr = NULL; 475 | } 476 | 477 | T* Detach() throw() 478 | { 479 | T* temp = m_strptr; 480 | m_strptr = NULL; 481 | return temp; 482 | } 483 | 484 | T** operator&() throw() 485 | { 486 | Dispose(); 487 | return &m_strptr; 488 | } 489 | 490 | inline operator T*() const throw() 491 | { 492 | return m_strptr; 493 | } 494 | 495 | inline T& operator[](int nIndex) const throw() 496 | { 497 | return m_strptr[nIndex]; 498 | } 499 | 500 | CDhStringTemplate& operator=(const CDhStringTemplate& rhs) 501 | { 502 | if (m_strptr != rhs.m_strptr) 503 | { 504 | T* temp = m_strptr; 505 | Copy(rhs.m_strptr); 506 | dhFreeString(temp); 507 | } 508 | 509 | return *this; 510 | } 511 | 512 | CDhStringTemplate& operator=(const int null) throw(HRESULT) 513 | { 514 | if (null != 0) throw(E_POINTER); 515 | Dispose(); 516 | return *this; 517 | } 518 | 519 | private: 520 | void Copy(const T* rhs) 521 | { 522 | if (rhs == NULL) 523 | { 524 | m_strptr = NULL; 525 | } 526 | else if (sizeof(T) == sizeof(CHAR)) 527 | { 528 | m_strptr = (T*) SysAllocStringByteLen((LPCSTR) rhs, SysStringByteLen((BSTR) rhs)); 529 | } 530 | else 531 | { 532 | m_strptr = (T*) SysAllocStringLen((OLECHAR *) rhs, SysStringLen((BSTR) rhs)); 533 | } 534 | } 535 | 536 | T* m_strptr; 537 | }; 538 | 539 | typedef CDhStringTemplate CDhStringA; /* Ansi string - LPSTR */ 540 | typedef CDhStringTemplate CDhStringW; /* Unicode string - LPWSTR */ 541 | typedef CDhStringTemplate CDhStringB; /* Unicode bstring - BSTR */ 542 | typedef CDhStringTemplate CDhStringT; /* T string - LPTSTR */ 543 | typedef CDhStringTemplate CDhString; /* T string - LPTSTR */ 544 | 545 | inline std::ostream& operator<<(std::ostream& os, const CDhStringA& s) 546 | { 547 | return os << (s ? s : (char*) "(null)"); 548 | } 549 | 550 | inline std::wostream& operator<<(std::wostream& os, const CDhStringW& s) 551 | { 552 | return os << (s ? s : (wchar_t*) L"(null)"); 553 | } 554 | 555 | 556 | 557 | 558 | /* ===================================================================== */ 559 | class CDhInitialize 560 | { 561 | public: 562 | CDhInitialize(const BOOL bInitCom = TRUE) throw() : m_bInitCom (bInitCom) 563 | { 564 | dhInitialize(m_bInitCom); 565 | } 566 | 567 | ~CDhInitialize() throw() 568 | { 569 | dhUninitialize(m_bInitCom); 570 | } 571 | private: 572 | BOOL m_bInitCom; 573 | }; 574 | 575 | 576 | 577 | 578 | /* ===================================================================== */ 579 | #ifndef DISPHELPER_NO_EXCEPTIONS 580 | class dhThrowFunctions 581 | { 582 | public: 583 | static void throw_string() throw(std::string) 584 | { 585 | CHAR szMessage[512]; 586 | dhFormatExceptionA(NULL, szMessage, sizeof(szMessage)/sizeof(szMessage[0]), TRUE); 587 | throw std::string(szMessage); 588 | } 589 | 590 | static void throw_wstring() throw(std::wstring) 591 | { 592 | WCHAR szMessage[512]; 593 | dhFormatExceptionW(NULL, szMessage, sizeof(szMessage)/sizeof(szMessage[0]), TRUE); 594 | throw std::wstring(szMessage); 595 | } 596 | 597 | static void throw_dhexception() throw(PDH_EXCEPTION) 598 | { 599 | PDH_EXCEPTION pException = NULL; 600 | dhGetLastException(&pException); 601 | throw pException; 602 | } 603 | }; 604 | #endif /* DISPHELPER_NO_EXCEPTIONS */ 605 | 606 | 607 | 608 | 609 | /* ===================================================================== */ 610 | #ifndef DISPHELPER_NO_EXCEPTIONS 611 | inline bool dhIfFailThrowString(HRESULT hr) throw(std::string) 612 | { 613 | if (FAILED(hr)) dhThrowFunctions::throw_string(); 614 | return true; 615 | } 616 | 617 | inline bool dhIfFailThrowWString(HRESULT hr) throw(std::wstring) 618 | { 619 | if (FAILED(hr)) dhThrowFunctions::throw_wstring(); 620 | return true; 621 | } 622 | 623 | inline bool dhIfFailThrowDhException(HRESULT hr) throw(PDH_EXCEPTION) 624 | { 625 | if (FAILED(hr)) dhThrowFunctions::throw_dhexception(); 626 | return true; 627 | } 628 | 629 | #define dhCheck dhIfFailThrowString 630 | 631 | #endif /* DISPHELPER_NO_EXCEPTIONS */ 632 | 633 | 634 | 635 | 636 | /* ===================================================================== */ 637 | #ifndef DISPHELPER_NO_WITH 638 | 639 | #undef WITH0 640 | #define WITH0(objName, pDisp, szMember) { \ 641 | CDispPtr objName; \ 642 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember))) { 643 | 644 | #undef WITH1 645 | #define WITH1(objName, pDisp, szMember, arg1) { \ 646 | CDispPtr objName; \ 647 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1))) { 648 | 649 | #undef WITH2 650 | #define WITH2(objName, pDisp, szMember, arg1, arg2) { \ 651 | CDispPtr objName; \ 652 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2))) { 653 | 654 | #undef WITH3 655 | #define WITH3(objName, pDisp, szMember, arg1, arg2, arg3) { \ 656 | CDispPtr objName; \ 657 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2, arg3))) { 658 | 659 | #undef WITH4 660 | #define WITH4(objName, pDisp, szMember, arg1, arg2, arg3, arg4) { \ 661 | CDispPtr objName; \ 662 | if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2, arg3, arg4))) { 663 | 664 | #undef ON_WITH_ERROR 665 | #define ON_WITH_ERROR(objName) } else { 666 | 667 | #undef END_WITH 668 | #define END_WITH(objName) }} 669 | 670 | #define END_WITH_THROW(objName) } else { dhThrowFunctions::throw_string(); }} 671 | 672 | #endif /* DISPHELPER_NO_WITH */ 673 | 674 | 675 | 676 | 677 | /* ===================================================================== */ 678 | #ifndef DISPHELPER_NO_FOR_EACH 679 | 680 | #undef FOR_EACH0 681 | #define FOR_EACH0(objName, pDisp, szMember) { \ 682 | CEnumPtr xx_pEnum_xx; \ 683 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember))) { \ 684 | CDispPtr objName; \ 685 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 686 | 687 | #undef FOR_EACH1 688 | #define FOR_EACH1(objName, pDisp, szMember, arg1) { \ 689 | CEnumPtr xx_pEnum_xx; \ 690 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1))) { \ 691 | CDispPtr objName; \ 692 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 693 | 694 | #undef FOR_EACH2 695 | #define FOR_EACH2(objName, pDisp, szMember, arg1, arg2) { \ 696 | CEnumPtr xx_pEnum_xx; \ 697 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2))) { \ 698 | CDispPtr objName; \ 699 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 700 | 701 | #undef FOR_EACH3 702 | #define FOR_EACH3(objName, pDisp, szMember, arg1, arg2, arg3) { \ 703 | CEnumPtr xx_pEnum_xx; \ 704 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2, arg3))) { \ 705 | CDispPtr objName; \ 706 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 707 | 708 | #undef FOR_EACH4 709 | #define FOR_EACH4(objName, pDisp, szMember, arg1, arg2, arg3, arg4) { \ 710 | CEnumPtr xx_pEnum_xx; \ 711 | if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2, arg3, arg4))) { \ 712 | CDispPtr objName; \ 713 | while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { 714 | 715 | #undef ON_FOR_EACH_ERROR 716 | #define ON_FOR_EACH_ERROR(objName) }} else {{ 717 | 718 | #undef NEXT 719 | #define NEXT(objName) }}} 720 | 721 | #define NEXT_THROW(objName) }} else { dhThrowFunctions::throw_string(); }} 722 | 723 | #endif /* DISPHELPER_NO_FOR_EACH */ 724 | 725 | #ifdef _MSC_VER 726 | #pragma warning( default : 4290 ) 727 | #endif 728 | 729 | #endif /* defined(__cplusplus) && !defined(DISPHELPER_NO_CPP_EXTENSIONS) */ 730 | 731 | #endif /* ----- DISPHELPER_H_INCLUDED ----- */ 732 | -------------------------------------------------------------------------------- /src/serialport_unix.cpp: -------------------------------------------------------------------------------- 1 | #ifndef WIN32 2 | #include "serialport.h" 3 | #include "serialport_poller.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __APPLE__ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | uv_mutex_t list_mutex; 18 | Boolean lockInitialised = FALSE; 19 | #endif 20 | 21 | #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) 22 | #include 23 | #include 24 | #include 25 | #endif 26 | 27 | #if defined(__linux__) 28 | #include 29 | #include 30 | #endif 31 | 32 | int ToBaudConstant(int baudRate); 33 | int ToDataBitsConstant(int dataBits); 34 | int ToStopBitsConstant(SerialPortStopBits stopBits); 35 | 36 | void AfterOpenSuccess(int fd, v8::Handle dataCallback, v8::Handle disconnectedCallback, v8::Handle errorCallback) { 37 | 38 | } 39 | 40 | int ToBaudConstant(int baudRate) { 41 | switch (baudRate) { 42 | case 0: return B0; 43 | case 50: return B50; 44 | case 75: return B75; 45 | case 110: return B110; 46 | case 134: return B134; 47 | case 150: return B150; 48 | case 200: return B200; 49 | case 300: return B300; 50 | case 600: return B600; 51 | case 1200: return B1200; 52 | case 1800: return B1800; 53 | case 2400: return B2400; 54 | case 4800: return B4800; 55 | case 9600: return B9600; 56 | case 19200: return B19200; 57 | case 38400: return B38400; 58 | case 57600: return B57600; 59 | case 115200: return B115200; 60 | case 230400: return B230400; 61 | #if !defined(__APPLE__) && !defined(__OpenBSD__) 62 | case 460800: return B460800; 63 | case 500000: return B500000; 64 | case 576000: return B576000; 65 | case 921600: return B921600; 66 | case 1000000: return B1000000; 67 | case 1152000: return B1152000; 68 | case 1500000: return B1500000; 69 | case 2000000: return B2000000; 70 | case 2500000: return B2500000; 71 | case 3000000: return B3000000; 72 | case 3500000: return B3500000; 73 | case 4000000: return B4000000; 74 | #endif 75 | } 76 | return -1; 77 | } 78 | 79 | #ifdef __APPLE__ 80 | typedef struct SerialDevice { 81 | char port[MAXPATHLEN]; 82 | char locationId[MAXPATHLEN]; 83 | char vendorId[MAXPATHLEN]; 84 | char productId[MAXPATHLEN]; 85 | char manufacturer[MAXPATHLEN]; 86 | char serialNumber[MAXPATHLEN]; 87 | } stSerialDevice; 88 | 89 | typedef struct DeviceListItem { 90 | struct SerialDevice value; 91 | struct DeviceListItem *next; 92 | int* length; 93 | } stDeviceListItem; 94 | #endif 95 | 96 | int ToDataBitsConstant(int dataBits) { 97 | switch (dataBits) { 98 | case 8: default: return CS8; 99 | case 7: return CS7; 100 | case 6: return CS6; 101 | case 5: return CS5; 102 | } 103 | return -1; 104 | } 105 | 106 | 107 | 108 | void EIO_Open(uv_work_t* req) { 109 | OpenBaton* data = static_cast(req->data); 110 | 111 | int baudRate = ToBaudConstant(data->baudRate); 112 | 113 | // #if not ( defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) ) 114 | // if(baudRate == -1) { 115 | // snprintf(data->errorString, sizeof(data->errorString), "Invalid baud rate setting %d", data->baudRate); 116 | // return; 117 | // } 118 | // #endif 119 | 120 | int dataBits = ToDataBitsConstant(data->dataBits); 121 | if(dataBits == -1) { 122 | snprintf(data->errorString, sizeof(data->errorString), "Invalid data bits setting %d", data->dataBits); 123 | return; 124 | } 125 | 126 | 127 | int flags = (O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY); 128 | int fd = open(data->path, flags); 129 | 130 | if (fd == -1) { 131 | snprintf(data->errorString, sizeof(data->errorString), "Cannot open %s", data->path); 132 | return; 133 | } 134 | 135 | 136 | // struct sigaction saio; 137 | // saio.sa_handler = sigio_handler; 138 | // sigemptyset(&saio.sa_mask); 139 | // saio.sa_flags = 0; 140 | // sigaction(SIGIO, &saio, NULL); 141 | 142 | // //all process to receive SIGIO 143 | // fcntl(fd, F_SETOWN, getpid()); 144 | // int flflags = fcntl(fd, F_GETFL); 145 | // fcntl(fd, F_SETFL, flflags | FNONBLOCK); 146 | 147 | struct termios options; 148 | // Set baud and other configuration. 149 | tcgetattr(fd, &options); 150 | 151 | // Removing check for valid BaudRates due to ticket: #140 152 | // #if not ( defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) ) 153 | // Specify the baud rate 154 | 155 | 156 | // On linux you can alter the meaning of B38400 to mean a custom baudrate... 157 | #if defined(__linux__) && defined(ASYNC_SPD_CUST) 158 | if (baudRate == -1) { 159 | struct serial_struct serinfo; 160 | serinfo.reserved_char[0] = 0; 161 | if (ioctl(fd, TIOCGSERIAL, &serinfo) != -1) { 162 | serinfo.flags &= ~ASYNC_SPD_MASK; 163 | serinfo.flags |= ASYNC_SPD_CUST; 164 | serinfo.custom_divisor = (serinfo.baud_base + (data->baudRate / 2)) / data->baudRate; 165 | if (serinfo.custom_divisor < 1) 166 | serinfo.custom_divisor = 1; 167 | 168 | ioctl(fd, TIOCSSERIAL, &serinfo); 169 | ioctl(fd, TIOCGSERIAL, &serinfo); 170 | // if (serinfo.custom_divisor * rate != serinfo.baud_base) { 171 | // warnx("actual baudrate is %d / %d = %f", 172 | // serinfo.baud_base, serinfo.custom_divisor, 173 | // (float)serinfo.baud_base / serinfo.custom_divisor); 174 | // } 175 | } 176 | 177 | // Now we use "B38400" to trigger the special baud rate. 178 | baudRate = B38400; 179 | } 180 | #endif 181 | 182 | if (baudRate != -1) { 183 | cfsetispeed(&options, baudRate); 184 | cfsetospeed(&options, baudRate); 185 | } 186 | 187 | // Removing check for valid BaudRates due to ticket: #140 188 | // #endif 189 | 190 | /* 191 | IGNPAR : ignore bytes with parity errors 192 | */ 193 | options.c_iflag = IGNPAR; 194 | 195 | /* 196 | ICRNL : map CR to NL (otherwise a CR input on the other computer 197 | will not terminate input) 198 | */ 199 | // Pulling this for now. It should be an option, however. -Giseburt 200 | //options.c_iflag = ICRNL; 201 | 202 | // otherwise make device raw (no other input processing) 203 | 204 | 205 | // Specify data bits 206 | options.c_cflag &= ~CSIZE; 207 | options.c_cflag |= dataBits; 208 | 209 | options.c_cflag &= ~(CRTSCTS); 210 | 211 | if (data->rtscts) { 212 | options.c_cflag |= CRTSCTS; 213 | // evaluate specific flow control options 214 | } 215 | 216 | options.c_iflag &= ~(IXON | IXOFF | IXANY); 217 | 218 | if (data->xon) { 219 | options.c_iflag |= IXON; 220 | } 221 | 222 | if (data->xoff) { 223 | options.c_iflag |= IXOFF; 224 | } 225 | 226 | if (data->xany) { 227 | options.c_iflag |= IXANY; 228 | } 229 | 230 | 231 | switch (data->parity) 232 | { 233 | case SERIALPORT_PARITY_NONE: 234 | options.c_cflag &= ~PARENB; 235 | options.c_cflag &= ~CSTOPB; 236 | options.c_cflag &= ~CSIZE; 237 | break; 238 | case SERIALPORT_PARITY_ODD: 239 | options.c_cflag |= PARENB; 240 | options.c_cflag |= PARODD; 241 | options.c_cflag &= ~CSTOPB; 242 | options.c_cflag &= ~CSIZE; 243 | break; 244 | case SERIALPORT_PARITY_EVEN: 245 | options.c_cflag |= PARENB; 246 | options.c_cflag &= ~PARODD; 247 | options.c_cflag &= ~CSTOPB; 248 | options.c_cflag &= ~CSIZE; 249 | break; 250 | default: 251 | snprintf(data->errorString, sizeof(data->errorString), "Invalid parity setting %d", data->parity); 252 | close(fd); 253 | return; 254 | } 255 | 256 | switch(data->stopBits) { 257 | case SERIALPORT_STOPBITS_ONE: 258 | options.c_cflag &= ~CSTOPB; 259 | break; 260 | case SERIALPORT_STOPBITS_TWO: 261 | options.c_cflag |= CSTOPB; 262 | break; 263 | default: 264 | snprintf(data->errorString, sizeof(data->errorString), "Invalid stop bits setting %d", data->stopBits); 265 | close(fd); 266 | return; 267 | } 268 | 269 | options.c_cflag |= CLOCAL; //ignore status lines 270 | options.c_cflag |= CREAD; //enable receiver 271 | options.c_cflag |= HUPCL; //drop DTR (i.e. hangup) on close 272 | 273 | // Raw output 274 | options.c_oflag = 0; 275 | 276 | // ICANON makes partial lines not readable. It should be otional. 277 | // It works with ICRNL. -Giseburt 278 | options.c_lflag = 0; //ICANON; 279 | options.c_cc[VMIN]=1; 280 | options.c_cc[VTIME]=0; 281 | 282 | // removed this unneeded sleep. 283 | // sleep(1); 284 | tcflush(fd, TCIFLUSH); 285 | tcsetattr(fd, TCSANOW, &options); 286 | 287 | // On OS X, starting in Tiger, we can set a custom baud rate, as follows: 288 | #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) 289 | if (baudRate == -1) { 290 | speed_t speed = data->baudRate; 291 | if (ioctl(fd, IOSSIOSPEED, &speed) == -1) { 292 | snprintf(data->errorString, sizeof(data->errorString), "Error %s calling ioctl( ..., IOSSIOSPEED, %ld )", strerror(errno), speed ); 293 | } 294 | } 295 | #endif 296 | 297 | data->result = fd; 298 | } 299 | 300 | void EIO_Write(uv_work_t* req) { 301 | QueuedWrite* queuedWrite = static_cast(req->data); 302 | WriteBaton* data = static_cast(queuedWrite->baton); 303 | 304 | data->result = 0; 305 | errno = 0; 306 | 307 | // We carefully *DON'T* break out of this loop. 308 | do { 309 | if ((data->result = write(data->fd, data->bufferData + data->offset, data->bufferLength - data->offset)) == -1) { 310 | if (errno == EAGAIN || errno == EWOULDBLOCK) 311 | return; 312 | 313 | // The write call might be interrupted, if it is we just try again immediately. 314 | if (errno != EINTR) { 315 | snprintf(data->errorString, sizeof(data->errorString), "Error %s calling write(...)", strerror(errno) ); 316 | return; 317 | } 318 | 319 | // try again... 320 | continue; 321 | } 322 | // there wasn't an error, do the math on what we actually wrote... 323 | else { 324 | data->offset += data->result; 325 | } 326 | 327 | // if we get there, we really don't want to loop 328 | // break; 329 | } while (data->bufferLength > data->offset); 330 | } 331 | 332 | void EIO_Close(uv_work_t* req) { 333 | CloseBaton* data = static_cast(req->data); 334 | 335 | // printf(">>>> close fd %d\n", data->fd); 336 | 337 | // fcntl(data->fd, F_SETFL, FNONBLOCK); 338 | 339 | ssize_t r; 340 | 341 | r = close(data->fd); 342 | 343 | // printf(">>>> closed fd %d (err: %d)\n", data->fd, errno); 344 | 345 | if (r && r != EBADF) 346 | snprintf(data->errorString, sizeof(data->errorString), "Unable to close fd %d, errno: %d", data->fd, errno); 347 | } 348 | 349 | #ifdef __APPLE__ 350 | 351 | // Function prototypes 352 | static kern_return_t FindModems(io_iterator_t *matchingServices); 353 | static io_registry_entry_t GetUsbDevice(char *pathName); 354 | static stDeviceListItem* GetSerialDevices(); 355 | 356 | 357 | static kern_return_t FindModems(io_iterator_t *matchingServices) 358 | { 359 | kern_return_t kernResult; 360 | CFMutableDictionaryRef classesToMatch; 361 | classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); 362 | if (classesToMatch != NULL) 363 | { 364 | CFDictionarySetValue(classesToMatch, 365 | CFSTR(kIOSerialBSDTypeKey), 366 | CFSTR(kIOSerialBSDAllTypes)); 367 | } 368 | 369 | kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices); 370 | 371 | return kernResult; 372 | } 373 | 374 | static io_registry_entry_t GetUsbDevice(char* pathName) 375 | { 376 | io_registry_entry_t device = 0; 377 | 378 | CFMutableDictionaryRef classesToMatch = IOServiceMatching(kIOUSBDeviceClassName); 379 | if (classesToMatch != NULL) 380 | { 381 | io_iterator_t matchingServices; 382 | kern_return_t kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &matchingServices); 383 | if (KERN_SUCCESS == kernResult) 384 | { 385 | io_service_t service; 386 | Boolean deviceFound = false; 387 | 388 | while ((service = IOIteratorNext(matchingServices)) && !deviceFound) 389 | { 390 | CFStringRef bsdPathAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(service, kIOServicePlane, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, kIORegistryIterateRecursively); 391 | 392 | if (bsdPathAsCFString) 393 | { 394 | Boolean result; 395 | char bsdPath[MAXPATHLEN]; 396 | 397 | // Convert the path from a CFString to a C (NUL-terminated) 398 | result = CFStringGetCString(bsdPathAsCFString, 399 | bsdPath, 400 | sizeof(bsdPath), 401 | kCFStringEncodingUTF8); 402 | 403 | CFRelease(bsdPathAsCFString); 404 | 405 | if (result && (strcmp(bsdPath, pathName) == 0)) 406 | { 407 | deviceFound = true; 408 | //memset(bsdPath, 0, sizeof(bsdPath)); 409 | device = service; 410 | } 411 | else 412 | { 413 | // Release the object which are no longer needed 414 | (void) IOObjectRelease(service); 415 | } 416 | } 417 | } 418 | // Release the iterator. 419 | IOObjectRelease(matchingServices); 420 | } 421 | } 422 | 423 | return device; 424 | } 425 | 426 | static void ExtractUsbInformation(stSerialDevice *serialDevice, IOUSBDeviceInterface **deviceInterface) 427 | { 428 | kern_return_t kernResult; 429 | UInt32 locationID; 430 | kernResult = (*deviceInterface)->GetLocationID(deviceInterface, &locationID); 431 | if (KERN_SUCCESS == kernResult) 432 | { 433 | snprintf(serialDevice->locationId, 11, "0x%08x", locationID); 434 | } 435 | 436 | UInt16 vendorID; 437 | kernResult = (*deviceInterface)->GetDeviceVendor(deviceInterface, &vendorID); 438 | if (KERN_SUCCESS == kernResult) 439 | { 440 | snprintf(serialDevice->vendorId, 7, "0x%04x", vendorID); 441 | } 442 | 443 | UInt16 productID; 444 | kernResult = (*deviceInterface)->GetDeviceProduct(deviceInterface, &productID); 445 | if (KERN_SUCCESS == kernResult) 446 | { 447 | snprintf(serialDevice->productId, 7, "0x%04x", productID); 448 | } 449 | } 450 | 451 | static stDeviceListItem* GetSerialDevices() 452 | { 453 | kern_return_t kernResult; 454 | io_iterator_t serialPortIterator; 455 | char bsdPath[MAXPATHLEN]; 456 | 457 | FindModems(&serialPortIterator); 458 | 459 | io_service_t modemService; 460 | kernResult = KERN_FAILURE; 461 | Boolean modemFound = false; 462 | 463 | // Initialize the returned path 464 | *bsdPath = '\0'; 465 | 466 | stDeviceListItem* devices = NULL; 467 | stDeviceListItem* lastDevice = NULL; 468 | int length = 0; 469 | 470 | while ((modemService = IOIteratorNext(serialPortIterator))) 471 | { 472 | CFTypeRef bsdPathAsCFString; 473 | 474 | bsdPathAsCFString = IORegistryEntrySearchCFProperty(modemService, kIOServicePlane, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, kIORegistryIterateRecursively); 475 | 476 | if (bsdPathAsCFString) 477 | { 478 | Boolean result; 479 | 480 | // Convert the path from a CFString to a C (NUL-terminated) 481 | 482 | result = CFStringGetCString((CFStringRef) bsdPathAsCFString, 483 | bsdPath, 484 | sizeof(bsdPath), 485 | kCFStringEncodingUTF8); 486 | CFRelease(bsdPathAsCFString); 487 | 488 | if (result) 489 | { 490 | stDeviceListItem *deviceListItem = (stDeviceListItem*) malloc(sizeof(stDeviceListItem)); 491 | stSerialDevice *serialDevice = &(deviceListItem->value); 492 | strcpy(serialDevice->port, bsdPath); 493 | memset(serialDevice->locationId, 0, sizeof(serialDevice->locationId)); 494 | memset(serialDevice->vendorId, 0, sizeof(serialDevice->vendorId)); 495 | memset(serialDevice->productId, 0, sizeof(serialDevice->productId)); 496 | serialDevice->manufacturer[0] = '\0'; 497 | serialDevice->serialNumber[0] = '\0'; 498 | deviceListItem->next = NULL; 499 | deviceListItem->length = &length; 500 | 501 | if (devices == NULL) { 502 | devices = deviceListItem; 503 | } 504 | else { 505 | lastDevice->next = deviceListItem; 506 | } 507 | 508 | lastDevice = deviceListItem; 509 | length++; 510 | 511 | modemFound = true; 512 | kernResult = KERN_SUCCESS; 513 | 514 | uv_mutex_lock(&list_mutex); 515 | 516 | io_registry_entry_t device = GetUsbDevice(bsdPath); 517 | 518 | if (device) { 519 | CFStringRef manufacturerAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(device, 520 | kIOServicePlane, 521 | CFSTR(kUSBVendorString), 522 | kCFAllocatorDefault, 523 | kIORegistryIterateRecursively); 524 | 525 | if (manufacturerAsCFString) 526 | { 527 | Boolean result; 528 | char manufacturer[MAXPATHLEN]; 529 | 530 | // Convert from a CFString to a C (NUL-terminated) 531 | result = CFStringGetCString(manufacturerAsCFString, 532 | manufacturer, 533 | sizeof(manufacturer), 534 | kCFStringEncodingUTF8); 535 | 536 | if (result) { 537 | strcpy(serialDevice->manufacturer, manufacturer); 538 | } 539 | 540 | CFRelease(manufacturerAsCFString); 541 | } 542 | 543 | CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(device, 544 | kIOServicePlane, 545 | CFSTR(kUSBSerialNumberString), 546 | kCFAllocatorDefault, 547 | kIORegistryIterateRecursively); 548 | 549 | if (serialNumberAsCFString) 550 | { 551 | Boolean result; 552 | char serialNumber[MAXPATHLEN]; 553 | 554 | // Convert from a CFString to a C (NUL-terminated) 555 | result = CFStringGetCString(serialNumberAsCFString, 556 | serialNumber, 557 | sizeof(serialNumber), 558 | kCFStringEncodingUTF8); 559 | 560 | if (result) { 561 | strcpy(serialDevice->serialNumber, serialNumber); 562 | } 563 | 564 | CFRelease(serialNumberAsCFString); 565 | } 566 | 567 | IOCFPlugInInterface **plugInInterface = NULL; 568 | SInt32 score; 569 | HRESULT res; 570 | 571 | IOUSBDeviceInterface **deviceInterface = NULL; 572 | 573 | kernResult = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, 574 | &plugInInterface, &score); 575 | 576 | if ((kIOReturnSuccess != kernResult) || !plugInInterface) { 577 | continue; 578 | } 579 | 580 | // Use the plugin interface to retrieve the device interface. 581 | res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), 582 | (LPVOID*) &deviceInterface); 583 | 584 | // Now done with the plugin interface. 585 | (*plugInInterface)->Release(plugInInterface); 586 | 587 | if (res || deviceInterface == NULL) { 588 | continue; 589 | } 590 | 591 | // Extract the desired Information 592 | ExtractUsbInformation(serialDevice, deviceInterface); 593 | 594 | // Release the Interface 595 | (*deviceInterface)->Release(deviceInterface); 596 | 597 | // Release the device 598 | (void) IOObjectRelease(device); 599 | } 600 | 601 | uv_mutex_unlock(&list_mutex); 602 | } 603 | } 604 | 605 | // Release the io_service_t now that we are done with it. 606 | (void) IOObjectRelease(modemService); 607 | } 608 | 609 | IOObjectRelease(serialPortIterator); // Release the iterator. 610 | 611 | return devices; 612 | } 613 | 614 | #endif 615 | 616 | void EIO_List(uv_work_t* req) { 617 | // This code exists in javascript for unix platforms 618 | 619 | #ifdef __APPLE__ 620 | if(!lockInitialised) 621 | { 622 | uv_mutex_init(&list_mutex); 623 | lockInitialised = TRUE; 624 | } 625 | 626 | ListBaton* data = static_cast(req->data); 627 | 628 | stDeviceListItem* devices = GetSerialDevices(); 629 | 630 | if (*(devices->length) > 0) 631 | { 632 | stDeviceListItem* next = devices; 633 | 634 | for (int i = 0, len = *(devices->length); i < len; i++) { 635 | stSerialDevice device = (* next).value; 636 | 637 | ListResultItem* resultItem = new ListResultItem(); 638 | resultItem->comName = device.port; 639 | 640 | if (device.locationId != NULL) { 641 | resultItem->locationId = device.locationId; 642 | } 643 | if (device.vendorId != NULL) { 644 | resultItem->vendorId = device.vendorId; 645 | } 646 | if (device.productId != NULL) { 647 | resultItem->productId = device.productId; 648 | } 649 | if (device.manufacturer != NULL) { 650 | resultItem->manufacturer = device.manufacturer; 651 | } 652 | if (device.serialNumber != NULL) { 653 | resultItem->serialNumber = device.serialNumber; 654 | } 655 | data->results.push_back(resultItem); 656 | 657 | stDeviceListItem* current = next; 658 | 659 | if (next->next != NULL) 660 | { 661 | next = next->next; 662 | } 663 | 664 | free(current); 665 | } 666 | 667 | } 668 | 669 | #endif 670 | } 671 | 672 | void EIO_Flush(uv_work_t* req) { 673 | FlushBaton* data = static_cast(req->data); 674 | 675 | data->result = tcflush(data->fd, TCIFLUSH); 676 | } 677 | 678 | #endif -------------------------------------------------------------------------------- /src/win/disphelper.c: -------------------------------------------------------------------------------- 1 | /* This file is the compacted single file version of the DispHelper COM helper library. 2 | * DispHelper allows you to call COM objects with an extremely simple printf style syntax. 3 | * DispHelper can be used from C++ or even plain C. It works with most Windows compilers 4 | * including Dev-CPP, Visual C++ and LCC-WIN32. Including DispHelper in your project 5 | * couldn't be simpler as it is available in a compacted single file version (this file). 6 | * 7 | * Included with DispHelper are over 20 samples that demonstrate using COM objects 8 | * including ADO, CDO, Outlook, Eudora, Excel, Word, Internet Explorer, MSHTML, 9 | * PocketSoap, Word Perfect, MS Agent, SAPI, MSXML, WIA, dexplorer and WMI. 10 | * 11 | * DispHelper is free open source software provided under the BSD license. 12 | * 13 | * Find out more, browse the readable version of the source code 14 | * and download DispHelper at: 15 | * http://sourceforge.net/projects/disphelper/ 16 | * http://disphelper.sourceforge.net/ 17 | */ 18 | 19 | 20 | /* To use DispHelper in your project, include this file(disphelper.c) and the 21 | * header (disphelper.h). For Visual C++, Borland C++ and LCC-Win32 import 22 | * libraries are included via pragma directives. For other compilers you may 23 | * need to add ole32, oleaut32 and uuid. To do this in Dev-CPP add 24 | * "-lole32 -loleaut32 -luuid" to the linker box under Project->Project Options->Parameters. 25 | */ 26 | 27 | 28 | /* If you are using Dev-CPP and get errors when compiling this file: 29 | * Make sure this file is set to compile as C and not C++ under 30 | * Project->Project Options->Files. 31 | */ 32 | 33 | 34 | #define DISPHELPER_INTERNAL_BUILD 35 | #include "disphelper.h" 36 | #include 37 | #include 38 | 39 | /* ----- convert.h ----- */ 40 | 41 | HRESULT ConvertFileTimeToVariantTime(FILETIME * pft, DATE * pDate); 42 | HRESULT ConvertVariantTimeToFileTime(DATE date, FILETIME * pft); 43 | 44 | HRESULT ConvertVariantTimeToSystemTime(DATE date, SYSTEMTIME * pSystemTime); 45 | HRESULT ConvertSystemTimeToVariantTime(SYSTEMTIME * pSystemTime, DATE * pDate); 46 | 47 | HRESULT ConvertTimeTToVariantTime(time_t timeT, DATE * pDate); 48 | HRESULT ConvertVariantTimeToTimeT(DATE date, time_t * pTimeT); 49 | 50 | HRESULT ConvertAnsiStrToBStr(LPCSTR szAnsiIn, BSTR * lpBstrOut); 51 | HRESULT ConvertBStrToAnsiStr(BSTR bstrIn, LPSTR * lpszOut); 52 | 53 | /* ----- dh_create.c ----- */ 54 | 55 | HRESULT dhCreateObjectEx(LPCOLESTR szProgId, REFIID riid, DWORD dwClsContext, 56 | COSERVERINFO * pServerInfo, void ** ppv) 57 | { 58 | CLSID clsid; 59 | HRESULT hr; 60 | IClassFactory * pCf = NULL; 61 | 62 | DH_ENTER(L"CreateObjectEx"); 63 | 64 | if (!szProgId || !riid || !ppv) return DH_EXIT(E_INVALIDARG, szProgId); 65 | 66 | if (L'{' == szProgId[0]) 67 | hr = CLSIDFromString((LPOLESTR) szProgId, &clsid); 68 | else 69 | hr = CLSIDFromProgID(szProgId, &clsid); 70 | 71 | if (SUCCEEDED(hr)) hr = CoGetClassObject(&clsid, dwClsContext, pServerInfo, &IID_IClassFactory, (void **) &pCf); 72 | if (SUCCEEDED(hr)) hr = pCf->lpVtbl->CreateInstance(pCf, NULL, riid, ppv); 73 | 74 | if (pCf) pCf->lpVtbl->Release(pCf); 75 | 76 | return DH_EXIT(hr, szProgId); 77 | } 78 | 79 | HRESULT dhGetObjectEx(LPCOLESTR szPathName, LPCOLESTR szProgId, REFIID riid, 80 | DWORD dwClsContext, LPVOID lpvReserved, void ** ppv) 81 | { 82 | HRESULT hr; 83 | 84 | DH_ENTER(L"GetObjectEx"); 85 | 86 | if ((!szProgId && !szPathName) || !riid || !ppv || lpvReserved) return DH_EXIT(E_INVALIDARG, szProgId); 87 | 88 | if (szPathName) 89 | { 90 | 91 | if (!szProgId) 92 | { 93 | hr = CoGetObject(szPathName, NULL, riid, ppv); 94 | } 95 | else 96 | { 97 | IPersistFile * ppf = NULL; 98 | 99 | hr = dhCreateObjectEx(szProgId, &IID_IPersistFile, dwClsContext, NULL, (void **) &ppf); 100 | 101 | if (SUCCEEDED(hr)) hr = ppf->lpVtbl->Load(ppf, szPathName, 0); 102 | if (SUCCEEDED(hr)) hr = ppf->lpVtbl->QueryInterface(ppf, riid, ppv); 103 | 104 | if (ppf) ppf->lpVtbl->Release(ppf); 105 | } 106 | } 107 | else 108 | { 109 | 110 | CLSID clsid; 111 | IUnknown * pUnk = NULL; 112 | 113 | if (L'{' == szProgId[0]) 114 | hr = CLSIDFromString((LPOLESTR) szProgId, &clsid); 115 | else 116 | hr = CLSIDFromProgID(szProgId, &clsid); 117 | 118 | if (SUCCEEDED(hr)) hr = GetActiveObject(&clsid, NULL, &pUnk); 119 | if (SUCCEEDED(hr)) hr = pUnk->lpVtbl->QueryInterface(pUnk, riid, ppv); 120 | 121 | if (pUnk) pUnk->lpVtbl->Release(pUnk); 122 | } 123 | 124 | return DH_EXIT(hr, szProgId); 125 | } 126 | 127 | HRESULT dhCreateObject(LPCOLESTR szProgId, LPCWSTR szMachine, IDispatch ** ppDisp) 128 | { 129 | COSERVERINFO si = { 0 }; 130 | 131 | DH_ENTER(L"CreateObject"); 132 | 133 | si.pwszName = (LPWSTR) szMachine; 134 | 135 | return DH_EXIT(dhCreateObjectEx(szProgId, &IID_IDispatch, 136 | szMachine ? CLSCTX_REMOTE_SERVER : CLSCTX_LOCAL_SERVER|CLSCTX_INPROC_SERVER, 137 | szMachine ? &si : NULL, (void **) ppDisp), szProgId); 138 | } 139 | 140 | HRESULT dhGetObject(LPCOLESTR szPathName, LPCOLESTR szProgId, IDispatch ** ppDisp) 141 | { 142 | DH_ENTER(L"GetObject"); 143 | 144 | return DH_EXIT(dhGetObjectEx(szPathName, szProgId, &IID_IDispatch, 145 | CLSCTX_LOCAL_SERVER|CLSCTX_INPROC_SERVER, NULL, (void **) ppDisp), szProgId); 146 | } 147 | 148 | HRESULT dhCallMethod(IDispatch * pDisp, LPCOLESTR szMember, ... ) 149 | { 150 | HRESULT hr; 151 | va_list marker; 152 | 153 | DH_ENTER(L"CallMethod"); 154 | 155 | va_start(marker, szMember); 156 | 157 | hr = dhCallMethodV(pDisp, szMember, &marker); 158 | 159 | va_end(marker); 160 | 161 | return DH_EXIT(hr, szMember); 162 | } 163 | 164 | HRESULT dhPutValue(IDispatch * pDisp, LPCOLESTR szMember, ...) 165 | { 166 | HRESULT hr; 167 | va_list marker; 168 | 169 | DH_ENTER(L"PutValue"); 170 | 171 | va_start(marker, szMember); 172 | 173 | hr = dhPutValueV(pDisp, szMember, &marker); 174 | 175 | va_end(marker); 176 | 177 | return DH_EXIT(hr, szMember); 178 | } 179 | 180 | HRESULT dhPutRef(IDispatch * pDisp, LPCOLESTR szMember, ...) 181 | { 182 | HRESULT hr; 183 | va_list marker; 184 | 185 | DH_ENTER(L"PutRef"); 186 | 187 | va_start(marker, szMember); 188 | 189 | hr = dhPutRefV(pDisp, szMember, &marker); 190 | 191 | va_end(marker); 192 | 193 | return DH_EXIT(hr, szMember); 194 | } 195 | 196 | HRESULT dhGetValue(LPCWSTR szIdentifier, void * pResult, IDispatch * pDisp, LPCOLESTR szMember, ...) 197 | { 198 | HRESULT hr; 199 | va_list marker; 200 | 201 | DH_ENTER(L"GetValue"); 202 | 203 | va_start(marker, szMember); 204 | 205 | hr = dhGetValueV(szIdentifier, pResult, pDisp, szMember, &marker); 206 | 207 | va_end(marker); 208 | 209 | return DH_EXIT(hr, szMember); 210 | } 211 | 212 | HRESULT dhInvoke(int invokeType, VARTYPE returnType, VARIANT * pvResult, IDispatch * pDisp, LPCOLESTR szMember, ...) 213 | { 214 | HRESULT hr; 215 | va_list marker; 216 | 217 | DH_ENTER(L"Invoke"); 218 | 219 | va_start(marker, szMember); 220 | 221 | hr = dhInvokeV(invokeType, returnType, pvResult, pDisp, szMember, &marker); 222 | 223 | va_end(marker); 224 | 225 | return DH_EXIT(hr, szMember); 226 | } 227 | 228 | /* ----- dh_core.c ----- */ 229 | 230 | BOOL dh_g_bIsUnicodeMode; 231 | 232 | HRESULT dhInvokeArray(int invokeType, VARIANT * pvResult, UINT cArgs, 233 | IDispatch * pDisp, LPCOLESTR szMember, VARIANT * pArgs) 234 | { 235 | DISPPARAMS dp = { 0 }; 236 | EXCEPINFO excep = { 0 }; 237 | DISPID dispidNamed = DISPID_PROPERTYPUT; 238 | DISPID dispID; 239 | UINT uiArgErr; 240 | HRESULT hr; 241 | 242 | DH_ENTER(L"InvokeArray"); 243 | 244 | if(!pDisp || !szMember || (cArgs != 0 && !pArgs)) return DH_EXIT(E_INVALIDARG, szMember); 245 | 246 | hr = pDisp->lpVtbl->GetIDsOfNames(pDisp, &IID_NULL, (LPOLESTR *) &szMember, 1, LOCALE_USER_DEFAULT, &dispID); 247 | 248 | if(FAILED(hr)) return DH_EXITEX(hr, TRUE, szMember, szMember, NULL, 0); 249 | 250 | if (pvResult != NULL) VariantInit(pvResult); 251 | 252 | dp.cArgs = cArgs; 253 | dp.rgvarg = pArgs; 254 | 255 | if(invokeType & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) 256 | { 257 | dp.cNamedArgs = 1; 258 | dp.rgdispidNamedArgs = &dispidNamed; 259 | } 260 | 261 | hr = pDisp->lpVtbl->Invoke(pDisp, dispID, &IID_NULL, LOCALE_USER_DEFAULT, (WORD) invokeType, &dp, pvResult, &excep, &uiArgErr); 262 | 263 | return DH_EXITEX(hr, TRUE, szMember, szMember, &excep, uiArgErr); 264 | } 265 | 266 | HRESULT dhCallMethodV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) 267 | { 268 | DH_ENTER(L"CallMethodV"); 269 | 270 | return DH_EXIT(dhInvokeV(DISPATCH_METHOD, VT_EMPTY, NULL, pDisp, szMember, marker), szMember); 271 | } 272 | 273 | HRESULT dhPutValueV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) 274 | { 275 | DH_ENTER(L"PutValueV"); 276 | 277 | return DH_EXIT(dhInvokeV(DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, pDisp, szMember, marker), szMember); 278 | } 279 | 280 | HRESULT dhPutRefV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) 281 | { 282 | DH_ENTER(L"PutRefV"); 283 | 284 | return DH_EXIT(dhInvokeV(DISPATCH_PROPERTYPUTREF, VT_EMPTY, NULL, pDisp, szMember, marker), szMember); 285 | } 286 | 287 | HRESULT dhGetValueV(LPCWSTR szIdentifier, void * pResult, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) 288 | { 289 | VARIANT vtResult; 290 | VARTYPE returnType; 291 | HRESULT hr; 292 | 293 | DH_ENTER(L"GetValueV"); 294 | 295 | if (!pResult || !szIdentifier) return DH_EXIT(E_INVALIDARG, szMember); 296 | 297 | if (*szIdentifier == L'%') szIdentifier++; 298 | 299 | switch(*szIdentifier) 300 | { 301 | case L'd': returnType = VT_I4; break; 302 | case L'u': returnType = VT_UI4; break; 303 | case L'e': returnType = VT_R8; break; 304 | case L'b': returnType = VT_BOOL; break; 305 | case L'v': returnType = VT_EMPTY; break; 306 | case L'B': returnType = VT_BSTR; break; 307 | case L'S': returnType = VT_BSTR; break; 308 | case L's': returnType = VT_BSTR; break; 309 | case L'T': returnType = VT_BSTR; break; 310 | case L'o': returnType = VT_DISPATCH; break; 311 | case L'O': returnType = VT_UNKNOWN; break; 312 | case L't': returnType = VT_DATE; break; 313 | case L'W': returnType = VT_DATE; break; 314 | case L'f': returnType = VT_DATE; break; 315 | case L'D': returnType = VT_DATE; break; 316 | #ifndef _WIN64 317 | case L'p': returnType = VT_I4; break; 318 | #else 319 | case L'p': returnType = VT_I8; break; 320 | #endif 321 | default: 322 | DEBUG_NOTIFY_INVALID_IDENTIFIER(*szIdentifier); 323 | return DH_EXIT(E_INVALIDARG, szMember); 324 | } 325 | 326 | hr = dhInvokeV(DISPATCH_PROPERTYGET|DISPATCH_METHOD, returnType, &vtResult, pDisp, szMember, marker); 327 | if (FAILED(hr)) return DH_EXIT(hr, szMember); 328 | 329 | switch(*szIdentifier) 330 | { 331 | case L'd': 332 | *((LONG *) pResult) = V_I4(&vtResult); 333 | break; 334 | 335 | case L'u': 336 | *((ULONG *) pResult) = V_UI4(&vtResult); 337 | break; 338 | 339 | case L'e': 340 | *((DOUBLE *) pResult) = V_R8(&vtResult); 341 | break; 342 | 343 | case L'b': 344 | *((BOOL *) pResult) = V_BOOL(&vtResult); 345 | break; 346 | 347 | case L'v': 348 | *((VARIANT *) pResult) = vtResult; 349 | break; 350 | 351 | case L'B': 352 | *((BSTR *) pResult) = V_BSTR(&vtResult); 353 | break; 354 | 355 | case L'S': 356 | *((LPWSTR *) pResult) = V_BSTR(&vtResult); 357 | break; 358 | 359 | case L's': 360 | hr = ConvertBStrToAnsiStr(V_BSTR(&vtResult), (LPSTR *) pResult); 361 | SysFreeString(V_BSTR(&vtResult)); 362 | break; 363 | 364 | case L'T': 365 | if (dh_g_bIsUnicodeMode) 366 | { 367 | *((LPWSTR *) pResult) = V_BSTR(&vtResult); 368 | } 369 | else 370 | { 371 | hr = ConvertBStrToAnsiStr(V_BSTR(&vtResult), (LPSTR *) pResult); 372 | SysFreeString(V_BSTR(&vtResult)); 373 | } 374 | break; 375 | 376 | case L'o': 377 | *((IDispatch **) pResult) = V_DISPATCH(&vtResult); 378 | if (V_DISPATCH(&vtResult) == NULL) hr = E_NOINTERFACE; 379 | break; 380 | 381 | case L'O': 382 | *((IUnknown **) pResult) = V_UNKNOWN(&vtResult); 383 | if (V_UNKNOWN(&vtResult) == NULL) hr = E_NOINTERFACE; 384 | break; 385 | 386 | case L't': 387 | hr = ConvertVariantTimeToTimeT(V_DATE(&vtResult), (time_t *) pResult); 388 | break; 389 | 390 | case L'W': 391 | hr = ConvertVariantTimeToSystemTime(V_DATE(&vtResult), (SYSTEMTIME *) pResult); 392 | break; 393 | 394 | case L'f': 395 | hr = ConvertVariantTimeToFileTime(V_DATE(&vtResult), (FILETIME *) pResult); 396 | break; 397 | 398 | case L'D': 399 | *((DATE *) pResult) = V_DATE(&vtResult); 400 | break; 401 | 402 | case L'p': 403 | #ifndef _WIN64 404 | *((LPVOID *) pResult) = (LPVOID) V_I4(&vtResult); 405 | #else 406 | *((LPVOID *) pResult) = (LPVOID) V_I8(&vtResult); 407 | #endif 408 | break; 409 | } 410 | 411 | return DH_EXIT(hr, szMember); 412 | } 413 | 414 | /* ----- dh_invoke.c ----- */ 415 | 416 | static HRESULT TraverseSubObjects(IDispatch ** ppDisp, LPWSTR * lpszMember, va_list * marker); 417 | static HRESULT CreateArgumentArray(LPWSTR szTemp, VARIANT * pArgs, BOOL * pbFreeList, UINT * pcArgs, va_list * marker); 418 | static HRESULT InternalInvokeV(int invokeType, VARTYPE returnType, VARIANT * pvResult, IDispatch * pDisp, LPOLESTR szMember, va_list * marker); 419 | static HRESULT ExtractArgument(VARIANT * pvArg, WCHAR chIdentifier, BOOL * pbFreeArg, va_list * marker); 420 | 421 | HRESULT dhInvokeV(int invokeType, VARTYPE returnType, VARIANT * pvResult, 422 | IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) 423 | { 424 | WCHAR szCopy[DH_MAX_MEMBER]; 425 | LPWSTR szTemp = szCopy; 426 | SIZE_T cchDest = ARRAYSIZE(szCopy); 427 | HRESULT hr; 428 | 429 | DH_ENTER(L"InvokeV"); 430 | 431 | if (!pDisp || !szMember || !marker) return DH_EXIT(E_INVALIDARG, szMember); 432 | 433 | do 434 | { 435 | if (cchDest-- == 0) return DH_EXIT(E_INVALIDARG, szMember); 436 | } 437 | while( (*szTemp++ = *szMember++) ); 438 | 439 | szTemp = szCopy; 440 | 441 | hr = TraverseSubObjects(&pDisp, &szTemp, marker); 442 | 443 | if (SUCCEEDED(hr)) 444 | { 445 | hr = InternalInvokeV(invokeType, returnType, pvResult, pDisp, szTemp, marker); 446 | 447 | pDisp->lpVtbl->Release(pDisp); 448 | } 449 | 450 | return DH_EXIT(hr, szMember); 451 | } 452 | 453 | static HRESULT TraverseSubObjects(IDispatch ** ppDisp, LPWSTR * lpszMember, va_list * marker) 454 | { 455 | LPWSTR szSeperator, szTemp; 456 | VARIANT vtObject; 457 | HRESULT hr; 458 | 459 | DH_ENTER(L"TraverseSubObjects"); 460 | 461 | if (**lpszMember == L'.') (*lpszMember)++; 462 | 463 | (*ppDisp)->lpVtbl->AddRef(*ppDisp); 464 | 465 | szSeperator = wcschr(*lpszMember, L'.'); 466 | 467 | if (szSeperator == NULL) return DH_EXIT(NOERROR, *lpszMember); 468 | 469 | szTemp = *lpszMember; 470 | 471 | do 472 | { 473 | *szSeperator = L'\0'; 474 | 475 | hr = InternalInvokeV(DISPATCH_METHOD|DISPATCH_PROPERTYGET, VT_DISPATCH, 476 | &vtObject, *ppDisp, szTemp, marker); 477 | 478 | if (!vtObject.pdispVal && SUCCEEDED(hr)) hr = E_NOINTERFACE; 479 | 480 | (*ppDisp)->lpVtbl->Release(*ppDisp); 481 | 482 | if (FAILED(hr)) break; 483 | 484 | *ppDisp = vtObject.pdispVal; 485 | 486 | szTemp = szSeperator + 1; 487 | 488 | } 489 | while ( (wcschr(szTemp, L'.') ) != NULL); 490 | 491 | *lpszMember = szTemp; 492 | 493 | return DH_EXIT(hr, *lpszMember); 494 | } 495 | 496 | static HRESULT InternalInvokeV(int invokeType, VARTYPE returnType, VARIANT * pvResult, 497 | IDispatch * pDisp, LPOLESTR szMember, va_list * marker) 498 | { 499 | VARIANT vtArgs[DH_MAX_ARGS]; 500 | BOOL bFreeList[DH_MAX_ARGS]; 501 | HRESULT hr; 502 | UINT cArgs, iArg; 503 | 504 | DH_ENTER(L"InternalInvokeV"); 505 | 506 | hr = CreateArgumentArray(szMember, vtArgs, bFreeList, &cArgs, marker); 507 | 508 | if (SUCCEEDED(hr)) 509 | { 510 | hr = dhInvokeArray(invokeType, pvResult, cArgs, pDisp, szMember, &vtArgs[DH_MAX_ARGS - cArgs]); 511 | 512 | for (iArg = DH_MAX_ARGS - cArgs;iArg < DH_MAX_ARGS;iArg++) 513 | { 514 | if (bFreeList[iArg]) VariantClear(&vtArgs[iArg]); 515 | } 516 | 517 | if (SUCCEEDED(hr) && pvResult != NULL && 518 | pvResult->vt != returnType && returnType != VT_EMPTY) 519 | { 520 | hr = VariantChangeType(pvResult, pvResult, 16 , returnType); 521 | if (FAILED(hr)) VariantClear(pvResult); 522 | } 523 | } 524 | 525 | return DH_EXIT(hr, szMember); 526 | } 527 | 528 | static HRESULT CreateArgumentArray(LPWSTR szMember, VARIANT * pArgs, BOOL * pbFreeList, 529 | UINT * pcArgs, va_list * marker) 530 | { 531 | HRESULT hr = NOERROR; 532 | INT iArg = DH_MAX_ARGS; 533 | BOOL bInArguments = FALSE; 534 | 535 | DH_ENTER(L"CreateArgumentArray"); 536 | 537 | while (*szMember) 538 | { 539 | if (!bInArguments && 540 | (*szMember == L'(' || *szMember == L' ' || *szMember == L'=') ) 541 | { 542 | bInArguments = TRUE; 543 | 544 | *szMember = L'\0'; 545 | } 546 | else if (*szMember == L'%') 547 | { 548 | if (!bInArguments) 549 | { 550 | bInArguments = TRUE; 551 | *szMember = L'\0'; 552 | } 553 | 554 | iArg--; 555 | 556 | if (iArg == -1) { hr = E_INVALIDARG; break; } 557 | 558 | szMember++; 559 | 560 | hr = ExtractArgument(&pArgs[iArg], *szMember, &pbFreeList[iArg], marker); 561 | 562 | if (FAILED(hr)) break; 563 | } 564 | 565 | szMember++; 566 | } 567 | 568 | *pcArgs = DH_MAX_ARGS - iArg; 569 | 570 | if (FAILED(hr)) 571 | { 572 | for (++iArg;iArg < DH_MAX_ARGS; iArg++) 573 | { 574 | if (pbFreeList[iArg]) VariantClear(&pArgs[iArg]); 575 | } 576 | } 577 | 578 | return DH_EXIT(hr, szMember); 579 | } 580 | 581 | static HRESULT ExtractArgument(VARIANT * pvArg, WCHAR chIdentifier, BOOL * pbFreeArg, va_list * marker) 582 | { 583 | HRESULT hr = NOERROR; 584 | 585 | *pbFreeArg = FALSE; 586 | 587 | if (chIdentifier == L'T') chIdentifier = (dh_g_bIsUnicodeMode ? L'S' : L's'); 588 | 589 | switch (chIdentifier) 590 | { 591 | case L'd': 592 | V_VT(pvArg) = VT_I4; 593 | V_I4(pvArg) = va_arg(*marker, LONG); 594 | break; 595 | 596 | case L'u': 597 | V_VT(pvArg) = VT_UI4; 598 | V_UI4(pvArg) = va_arg(*marker, ULONG); 599 | break; 600 | 601 | case L'e': 602 | V_VT(pvArg) = VT_R8; 603 | V_R8(pvArg) = va_arg(*marker, DOUBLE); 604 | break; 605 | 606 | case L'b': 607 | V_VT(pvArg) = VT_BOOL; 608 | V_BOOL(pvArg) = ( va_arg(*marker, BOOL) ? VARIANT_TRUE : VARIANT_FALSE ); 609 | break; 610 | 611 | case L'v': 612 | *pvArg = *va_arg(*marker, VARIANT *); 613 | break; 614 | 615 | case L'm': 616 | V_VT(pvArg) = VT_ERROR; 617 | V_ERROR(pvArg) = DISP_E_PARAMNOTFOUND; 618 | break; 619 | 620 | case L'B': 621 | V_VT(pvArg) = VT_BSTR; 622 | V_BSTR(pvArg) = va_arg(*marker, BSTR); 623 | break; 624 | 625 | case L'S': 626 | { 627 | LPOLESTR szTemp = va_arg(*marker, LPOLESTR); 628 | 629 | V_VT(pvArg) = VT_BSTR; 630 | V_BSTR(pvArg) = SysAllocString(szTemp); 631 | 632 | if (V_BSTR(pvArg) == NULL && szTemp != NULL) hr = E_OUTOFMEMORY; 633 | 634 | *pbFreeArg = TRUE; 635 | break; 636 | } 637 | 638 | case L's': 639 | V_VT(pvArg) = VT_BSTR; 640 | hr = ConvertAnsiStrToBStr(va_arg(*marker, LPSTR), &V_BSTR(pvArg)); 641 | *pbFreeArg = TRUE; 642 | break; 643 | 644 | case L'o': 645 | V_VT(pvArg) = VT_DISPATCH; 646 | V_DISPATCH(pvArg) = va_arg(*marker, IDispatch *); 647 | break; 648 | 649 | case L'O': 650 | V_VT(pvArg) = VT_UNKNOWN; 651 | V_UNKNOWN(pvArg) = va_arg(*marker, IUnknown *); 652 | break; 653 | 654 | case L'D': 655 | V_VT(pvArg) = VT_DATE; 656 | V_DATE(pvArg) = va_arg(*marker, DATE); 657 | break; 658 | 659 | case L't': 660 | V_VT(pvArg) = VT_DATE; 661 | hr = ConvertTimeTToVariantTime(va_arg(*marker, time_t), &V_DATE(pvArg)); 662 | break; 663 | 664 | case L'W': 665 | V_VT(pvArg) = VT_DATE; 666 | hr = ConvertSystemTimeToVariantTime(va_arg(*marker, SYSTEMTIME *), &V_DATE(pvArg)); 667 | break; 668 | 669 | case L'f': 670 | V_VT(pvArg) = VT_DATE; 671 | hr = ConvertFileTimeToVariantTime(va_arg(*marker, FILETIME *), &V_DATE(pvArg)); 672 | break; 673 | 674 | case L'p': 675 | #ifndef _WIN64 676 | V_VT(pvArg) = VT_I4; 677 | V_I4(pvArg) = (LONG) va_arg(*marker, LPVOID); 678 | #else 679 | V_VT(pvArg) = VT_I8; 680 | V_I8(pvArg) = (LONGLONG) va_arg(*marker, LPVOID); 681 | #endif 682 | break; 683 | 684 | default: 685 | hr = E_INVALIDARG; 686 | DEBUG_NOTIFY_INVALID_IDENTIFIER(chIdentifier); 687 | break; 688 | } 689 | 690 | return hr; 691 | } 692 | 693 | /* ----- dh_enum.c ----- */ 694 | 695 | HRESULT dhEnumBeginV(IEnumVARIANT ** ppEnum, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) 696 | { 697 | DISPPARAMS dp = { 0 }; 698 | EXCEPINFO excep = { 0 }; 699 | VARIANT vtResult; 700 | IDispatch * pDispObj; 701 | HRESULT hr; 702 | 703 | DH_ENTER(L"EnumBeginV"); 704 | 705 | if (!ppEnum || !pDisp) return DH_EXIT(E_INVALIDARG, szMember); 706 | 707 | if (szMember != NULL) 708 | { 709 | hr = dhGetValueV(L"%o", &pDispObj, pDisp, szMember, marker); 710 | if (FAILED(hr)) return DH_EXIT(hr, szMember); 711 | } 712 | else 713 | { 714 | pDispObj = pDisp; 715 | } 716 | 717 | hr = pDispObj->lpVtbl->Invoke(pDispObj, DISPID_NEWENUM, &IID_NULL, LOCALE_USER_DEFAULT, 718 | DISPATCH_METHOD | DISPATCH_PROPERTYGET, &dp, &vtResult, &excep, NULL); 719 | 720 | if (szMember != NULL) pDispObj->lpVtbl->Release(pDispObj); 721 | 722 | if (FAILED(hr)) return DH_EXITEX(hr, TRUE, L"_NewEnum", szMember, &excep, 0); 723 | 724 | if (vtResult.vt == VT_DISPATCH) 725 | hr = vtResult.pdispVal->lpVtbl->QueryInterface(vtResult.pdispVal, &IID_IEnumVARIANT, (void **) ppEnum); 726 | else if (vtResult.vt == VT_UNKNOWN) 727 | hr = vtResult.punkVal->lpVtbl->QueryInterface(vtResult.punkVal, &IID_IEnumVARIANT, (void **) ppEnum); 728 | else 729 | hr = E_NOINTERFACE; 730 | 731 | VariantClear(&vtResult); 732 | 733 | return DH_EXIT(hr, szMember); 734 | } 735 | 736 | HRESULT dhEnumNextVariant(IEnumVARIANT * pEnum, VARIANT * pvResult) 737 | { 738 | DH_ENTER(L"EnumNextVariant"); 739 | 740 | if (!pEnum || !pvResult) return DH_EXIT(E_INVALIDARG, L"Enumerator"); 741 | 742 | return DH_EXIT(pEnum->lpVtbl->Next(pEnum, 1, pvResult, NULL), L"Enumerator"); 743 | } 744 | 745 | HRESULT dhEnumNextObject(IEnumVARIANT * pEnum, IDispatch ** ppDisp) 746 | { 747 | VARIANT vtResult; 748 | HRESULT hr; 749 | 750 | DH_ENTER(L"EnumNextObject"); 751 | 752 | if (!pEnum || !ppDisp) return DH_EXIT(E_INVALIDARG, L"Enumerator"); 753 | 754 | hr = pEnum->lpVtbl->Next(pEnum, 1, &vtResult, NULL); 755 | 756 | if (hr == S_OK) 757 | { 758 | if (vtResult.vt == VT_DISPATCH) 759 | { 760 | *ppDisp = vtResult.pdispVal; 761 | } 762 | else 763 | { 764 | hr = VariantChangeType(&vtResult, &vtResult, 0, VT_DISPATCH); 765 | if (SUCCEEDED(hr)) *ppDisp = vtResult.pdispVal; 766 | else VariantClear(&vtResult); 767 | } 768 | } 769 | 770 | return DH_EXIT(hr, L"Enumerator"); 771 | } 772 | 773 | HRESULT dhEnumBegin(IEnumVARIANT ** ppEnum, IDispatch * pDisp, LPCOLESTR szMember, ...) 774 | { 775 | HRESULT hr; 776 | va_list marker; 777 | 778 | DH_ENTER(L"EnumBegin"); 779 | 780 | va_start(marker, szMember); 781 | 782 | hr = dhEnumBeginV(ppEnum, pDisp, szMember, &marker); 783 | 784 | va_end(marker); 785 | 786 | return DH_EXIT(hr, szMember); 787 | } 788 | 789 | /* ----- convert.c ----- */ 790 | 791 | static const LONGLONG FILE_TIME_ONE_DAY = 864000000000; 792 | 793 | static const LONGLONG FILE_TIME_VARIANT_DAY0 = 94353120000000000; 794 | 795 | static const ULONGLONG FILE_TIME_VARIANT_OVERFLOW = 2650467744000000000; 796 | 797 | static const DATE VARIANT_FILE_TIME_DAY0 = -109205; 798 | 799 | static const DATE VARIANT_TIMET_DAY0 = 25569; 800 | 801 | static const LONG TIMET_ONE_DAY = 86400; 802 | 803 | #ifndef _WIN64 804 | static const DATE VARIANT_TIMET_MAX = 50424.13480; 805 | #else 806 | static const time_t TIMET_VARIANT_OVERFLOW = 253402300800; 807 | #endif 808 | 809 | HRESULT ConvertFileTimeToVariantTime(FILETIME * pft, DATE * pDate) 810 | { 811 | ULONGLONG ftScalar; 812 | 813 | if (!pft || !pDate) return E_INVALIDARG; 814 | 815 | ftScalar = *((ULONGLONG *) pft) + 500; 816 | 817 | if (ftScalar >= FILE_TIME_VARIANT_OVERFLOW) return E_INVALIDARG; 818 | *pDate = (LONGLONG) (ftScalar - FILE_TIME_VARIANT_DAY0) / (double) FILE_TIME_ONE_DAY; 819 | if (*pDate < 0) *pDate = floor(*pDate) + (floor(*pDate) - *pDate); 820 | 821 | return NOERROR; 822 | } 823 | 824 | HRESULT ConvertVariantTimeToFileTime(DATE date, FILETIME * pft) 825 | { 826 | ULONGLONG ftScalar; 827 | 828 | if (!pft) return E_INVALIDARG; 829 | 830 | if (date < 0) date = ceil(date) + (ceil(date) - date); 831 | 832 | if (date < VARIANT_FILE_TIME_DAY0) return E_INVALIDARG; 833 | ftScalar = (ULONGLONG) ((date * FILE_TIME_ONE_DAY) + FILE_TIME_VARIANT_DAY0); 834 | 835 | *pft = *((FILETIME *) &ftScalar); 836 | 837 | return NOERROR; 838 | } 839 | 840 | HRESULT ConvertVariantTimeToSystemTime(DATE date, SYSTEMTIME * pSystemTime) 841 | { 842 | HRESULT hr; 843 | FILETIME fileTime; 844 | 845 | if (!pSystemTime) return E_INVALIDARG; 846 | if (FAILED(hr = ConvertVariantTimeToFileTime(date, &fileTime))) return hr; 847 | return (FileTimeToSystemTime(&fileTime, pSystemTime) ? NOERROR : HRESULT_FROM_WIN32( GetLastError() )); 848 | } 849 | 850 | HRESULT ConvertSystemTimeToVariantTime(SYSTEMTIME * pSystemTime, DATE * pDate) 851 | { 852 | FILETIME fileTime; 853 | 854 | if (!pSystemTime || !pDate) return E_INVALIDARG; 855 | if (!SystemTimeToFileTime(pSystemTime, &fileTime)) return HRESULT_FROM_WIN32( GetLastError() ); 856 | return ConvertFileTimeToVariantTime(&fileTime, pDate); 857 | } 858 | 859 | HRESULT ConvertVariantTimeToTimeT(DATE date, time_t * pTimeT) 860 | { 861 | struct tm * ptm; 862 | 863 | if (!pTimeT) return E_INVALIDARG; 864 | 865 | #ifndef _WIN64 866 | if (date < VARIANT_TIMET_DAY0 || date > VARIANT_TIMET_MAX) return E_INVALIDARG; 867 | #else 868 | if (date < VARIANT_TIMET_DAY0) return E_INVALIDARG; 869 | #endif 870 | 871 | *pTimeT = (time_t) (((date - VARIANT_TIMET_DAY0) * TIMET_ONE_DAY) + 0.5); 872 | 873 | if ( (ptm = gmtime(pTimeT)) == NULL || !(ptm->tm_isdst = -1) || 874 | (*pTimeT = mktime(ptm)) == (time_t) -1 ) return E_FAIL; 875 | 876 | return NOERROR; 877 | } 878 | 879 | HRESULT ConvertTimeTToVariantTime(time_t timeT, DATE * pDate) 880 | { 881 | struct tm localtm, utctm, * ptm; 882 | time_t timeTLocal, timeTUtc; 883 | 884 | if (!pDate) return E_INVALIDARG; 885 | 886 | if ( (ptm = localtime(&timeT)) == NULL) return E_FAIL; 887 | localtm = *ptm; 888 | 889 | if ( (ptm = gmtime(&timeT)) == NULL) return E_FAIL; 890 | utctm = *ptm; 891 | 892 | localtm.tm_isdst = 0; 893 | utctm.tm_isdst = 0; 894 | 895 | if ( (timeTLocal = mktime(&localtm)) == (time_t) -1 || 896 | (timeTUtc = mktime(&utctm)) == (time_t) -1) return E_FAIL; 897 | 898 | timeT += timeTLocal - timeTUtc; 899 | 900 | #ifdef _WIN64 901 | if (timeT >= TIMET_VARIANT_OVERFLOW) return E_INVALIDARG; 902 | #endif 903 | *pDate = (DATE) (timeT / (double) TIMET_ONE_DAY) + VARIANT_TIMET_DAY0; 904 | 905 | return NOERROR; 906 | } 907 | 908 | HRESULT ConvertAnsiStrToBStr(LPCSTR szAnsiIn, BSTR * lpBstrOut) 909 | { 910 | DWORD dwSize; 911 | 912 | if (lpBstrOut == NULL) return E_INVALIDARG; 913 | if (szAnsiIn == NULL) { *lpBstrOut = NULL; return NOERROR; } 914 | 915 | dwSize = MultiByteToWideChar(CP_ACP, 0, szAnsiIn, -1, NULL, 0); 916 | if (dwSize == 0) return HRESULT_FROM_WIN32( GetLastError() ); 917 | 918 | *lpBstrOut = SysAllocStringLen(NULL, dwSize - 1); 919 | if (*lpBstrOut == NULL) return E_OUTOFMEMORY; 920 | 921 | if ( !MultiByteToWideChar(CP_ACP, 0, szAnsiIn, -1, *lpBstrOut, dwSize) ) 922 | { 923 | SysFreeString(*lpBstrOut); 924 | return HRESULT_FROM_WIN32( GetLastError() ); 925 | } 926 | 927 | return NOERROR; 928 | } 929 | 930 | HRESULT ConvertBStrToAnsiStr(BSTR bstrIn, LPSTR * lpszOut) 931 | { 932 | DWORD dwSize; 933 | 934 | if (lpszOut == NULL) return E_INVALIDARG; 935 | if (bstrIn == NULL) { *lpszOut = NULL; return NOERROR; } 936 | 937 | dwSize = WideCharToMultiByte(CP_ACP, 0, bstrIn, -1, NULL, 0, NULL, NULL); 938 | if (dwSize == 0) return HRESULT_FROM_WIN32( GetLastError() ); 939 | 940 | *lpszOut = (LPSTR) SysAllocStringByteLen(NULL, dwSize - 1); 941 | if (*lpszOut == NULL) return E_OUTOFMEMORY; 942 | 943 | if ( !WideCharToMultiByte(CP_ACP, 0, bstrIn, -1, *lpszOut, dwSize, NULL, NULL) ) 944 | { 945 | SysFreeString((BSTR) *lpszOut); 946 | return HRESULT_FROM_WIN32( GetLastError() ); 947 | } 948 | 949 | return NOERROR; 950 | } 951 | 952 | /* ----- dh_exceptions.c ----- */ 953 | 954 | #ifndef DISPHELPER_NO_EXCEPTIONS 955 | 956 | static DH_EXCEPTION_OPTIONS g_ExceptionOptions; 957 | 958 | static LONG f_lngTlsInitBegin = -1, f_lngTlsInitEnd = -1; 959 | static DWORD f_TlsIdxStackCount, f_TlsIdxException; 960 | 961 | #define SetStackCount(nStackCount) TlsSetValue(f_TlsIdxStackCount, (LPVOID) (nStackCount)) 962 | #define SetExceptionPtr(pException) TlsSetValue(f_TlsIdxException, pException); 963 | #define GetStackCount() (UINT) TlsGetValue(f_TlsIdxStackCount) 964 | #define GetExceptionPtr() TlsGetValue(f_TlsIdxException) 965 | #define CheckTlsInitialized() if (f_lngTlsInitEnd != 0) InitializeTlsIndexes(); 966 | 967 | static void hlprStringCchCopyW(LPWSTR pszDest, SIZE_T cchDest, LPCWSTR pszSrc) 968 | { 969 | assert(cchDest > 0); 970 | 971 | do 972 | { 973 | if (--cchDest == 0) break; 974 | } 975 | while ((*pszDest++ = *pszSrc++)); 976 | 977 | *pszDest = L'\0'; 978 | } 979 | 980 | static void InitializeTlsIndexes(void) 981 | { 982 | if (0 == InterlockedIncrement(&f_lngTlsInitBegin)) 983 | { 984 | f_TlsIdxStackCount = TlsAlloc(); 985 | f_TlsIdxException = TlsAlloc(); 986 | f_lngTlsInitEnd = 0; 987 | } 988 | else 989 | { 990 | while (f_lngTlsInitEnd != 0) Sleep(5); 991 | } 992 | } 993 | 994 | void dhEnter(void) 995 | { 996 | CheckTlsInitialized(); 997 | SetStackCount(GetStackCount() + 1); 998 | } 999 | 1000 | HRESULT dhExitEx(HRESULT hr, BOOL bDispatchError, LPCWSTR szMember, LPCWSTR szCompleteMember, 1001 | EXCEPINFO * pExcepInfo, UINT iArgError, LPCWSTR szFunctionName) 1002 | { 1003 | UINT nStackCount = GetStackCount(); 1004 | 1005 | SetStackCount(nStackCount - 1); 1006 | 1007 | if (FAILED(hr) && !g_ExceptionOptions.bDisableRecordExceptions) 1008 | { 1009 | PDH_EXCEPTION pException = GetExceptionPtr(); 1010 | 1011 | if (!pException) 1012 | { 1013 | pException = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DH_EXCEPTION)); 1014 | if (!pException) return hr; 1015 | SetExceptionPtr(pException); 1016 | } 1017 | else if (pException->bOld) 1018 | { 1019 | SysFreeString(pException->szDescription); 1020 | SysFreeString(pException->szSource); 1021 | SysFreeString(pException->szHelpFile); 1022 | ZeroMemory(pException, sizeof(DH_EXCEPTION)); 1023 | } 1024 | 1025 | if (pException->hr == 0) 1026 | { 1027 | pException->hr = hr; 1028 | pException->iArgError = iArgError; 1029 | pException->szErrorFunction = szFunctionName; 1030 | pException->bDispatchError = bDispatchError; 1031 | 1032 | if (szMember) hlprStringCchCopyW(pException->szMember, ARRAYSIZE(pException->szMember), szMember); 1033 | 1034 | if (pExcepInfo && hr == DISP_E_EXCEPTION) 1035 | { 1036 | if (pExcepInfo->pfnDeferredFillIn && 1037 | !IsBadCodePtr((FARPROC) pExcepInfo->pfnDeferredFillIn)) pExcepInfo->pfnDeferredFillIn(pExcepInfo); 1038 | 1039 | pException->szDescription = pExcepInfo->bstrDescription; 1040 | pException->szSource = pExcepInfo->bstrSource; 1041 | pException->szHelpFile = pExcepInfo->bstrHelpFile; 1042 | pException->dwHelpContext = pExcepInfo->dwHelpContext; 1043 | pException->swCode = (pExcepInfo->wCode ? pExcepInfo->wCode : pExcepInfo->scode); 1044 | } 1045 | } 1046 | 1047 | if (nStackCount == 1) 1048 | { 1049 | pException->bOld = TRUE; 1050 | pException->szInitialFunction = szFunctionName; 1051 | 1052 | if (szCompleteMember) hlprStringCchCopyW(pException->szCompleteMember, ARRAYSIZE(pException->szCompleteMember), szCompleteMember); 1053 | 1054 | if (g_ExceptionOptions.bShowExceptions) 1055 | dhShowException(pException); 1056 | 1057 | if (g_ExceptionOptions.pfnExceptionCallback) 1058 | g_ExceptionOptions.pfnExceptionCallback(pException); 1059 | } 1060 | } 1061 | else if (hr == DISP_E_EXCEPTION && pExcepInfo) 1062 | { 1063 | SysFreeString(pExcepInfo->bstrDescription); 1064 | SysFreeString(pExcepInfo->bstrSource); 1065 | SysFreeString(pExcepInfo->bstrHelpFile); 1066 | } 1067 | 1068 | return hr; 1069 | } 1070 | 1071 | HRESULT dhShowException(PDH_EXCEPTION pException) 1072 | { 1073 | WCHAR szMessage[512]; 1074 | 1075 | dhFormatExceptionW(pException, szMessage, ARRAYSIZE(szMessage), FALSE); 1076 | 1077 | MessageBoxW(g_ExceptionOptions.hwnd, szMessage, g_ExceptionOptions.szAppName, 1078 | MB_ICONSTOP | MB_SETFOREGROUND); 1079 | 1080 | return NOERROR; 1081 | } 1082 | 1083 | HRESULT dhFormatExceptionW(PDH_EXCEPTION pException, LPWSTR szBuffer, UINT cchBufferSize, BOOL bFixedFont) 1084 | { 1085 | HRESULT hr; 1086 | UINT cch = 0; 1087 | # define DESCRIPTION_LENGTH 255 1088 | 1089 | if (!szBuffer && cchBufferSize) return E_INVALIDARG; 1090 | 1091 | if (!pException) 1092 | { 1093 | dhGetLastException(&pException); 1094 | if (!pException) 1095 | { 1096 | if (cchBufferSize != 0) 1097 | { 1098 | _snwprintf(szBuffer, cchBufferSize, L"No error information available."); 1099 | szBuffer[cchBufferSize - 1] = L'\0'; 1100 | } 1101 | 1102 | return NOERROR; 1103 | } 1104 | } 1105 | 1106 | hr = (pException->hr == DISP_E_EXCEPTION && pException->swCode ? 1107 | pException->swCode : pException->hr); 1108 | 1109 | if (!pException->szSource) 1110 | { 1111 | if (pException->bDispatchError) 1112 | pException->szSource = SysAllocString(L"IDispatch Interface"); 1113 | else 1114 | pException->szSource = SysAllocString(L"Application"); 1115 | } 1116 | 1117 | if (!pException->szDescription) 1118 | { 1119 | pException->szDescription = SysAllocStringLen(NULL, DESCRIPTION_LENGTH); 1120 | 1121 | if (pException->szDescription) 1122 | { 1123 | switch (hr) 1124 | { 1125 | case E_NOINTERFACE: 1126 | _snwprintf(pException->szDescription, DESCRIPTION_LENGTH, L"Object required"); 1127 | break; 1128 | 1129 | case DISP_E_UNKNOWNNAME: 1130 | case DISP_E_MEMBERNOTFOUND: 1131 | _snwprintf(pException->szDescription, DESCRIPTION_LENGTH, L"Object doesn't support this property or method: '%s'", pException->szMember); 1132 | break; 1133 | 1134 | case DISP_E_TYPEMISMATCH: 1135 | if (pException->szMember[0]) 1136 | { 1137 | _snwprintf(pException->szDescription, DESCRIPTION_LENGTH, L"Type mismatch: '%s'. Argument Index: %d", pException->szMember, pException->iArgError); 1138 | break; 1139 | } 1140 | 1141 | default: 1142 | { 1143 | #ifndef UNICODE 1144 | CHAR szDescription[DESCRIPTION_LENGTH]; 1145 | #else 1146 | LPWSTR szDescription = pException->szDescription; 1147 | #endif 1148 | cch = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 1149 | NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 1150 | szDescription, DESCRIPTION_LENGTH, NULL); 1151 | 1152 | if (!cch) wcscpy(pException->szDescription, L"Unknown runtime error"); 1153 | #ifndef UNICODE 1154 | else MultiByteToWideChar(CP_ACP, 0, szDescription, -1, pException->szDescription, DESCRIPTION_LENGTH); 1155 | #endif 1156 | } 1157 | } 1158 | } 1159 | } 1160 | 1161 | if (pException->szDescription) 1162 | { 1163 | 1164 | if (!cch) cch = wcslen(pException->szDescription); 1165 | 1166 | if (cch >= 2 && pException->szDescription[cch - 2] == L'\r') 1167 | pException->szDescription[cch - 2] = L'\0'; 1168 | else if (cch >= 1 && pException->szDescription[cch - 1] == L'\n') 1169 | pException->szDescription[cch - 1] = L'\0'; 1170 | } 1171 | 1172 | if (cchBufferSize) 1173 | { 1174 | if (!bFixedFont) 1175 | { 1176 | _snwprintf(szBuffer, cchBufferSize, L"Member:\t %s\r\nFunction:\t %s\t\t\r\nError In:\t %s\r\nError:\t %s\r\nCode:\t %x\r\nSource:\t %s", 1177 | pException->szCompleteMember, 1178 | pException->szInitialFunction, pException->szErrorFunction, 1179 | pException->szDescription, hr, 1180 | pException->szSource); 1181 | } 1182 | else 1183 | { 1184 | _snwprintf(szBuffer, cchBufferSize, L"Member: %s\r\nFunction: %s\r\nError In: %s\r\nError: %s\r\nCode: %x\r\nSource: %s", 1185 | pException->szCompleteMember, 1186 | pException->szInitialFunction, pException->szErrorFunction, 1187 | pException->szDescription, hr, 1188 | pException->szSource); 1189 | } 1190 | 1191 | szBuffer[cchBufferSize - 1] = L'\0'; 1192 | } 1193 | 1194 | return NOERROR; 1195 | } 1196 | 1197 | HRESULT dhFormatExceptionA(PDH_EXCEPTION pException, LPSTR szBuffer, UINT cchBufferSize, BOOL bFixedFont) 1198 | { 1199 | WCHAR szBufferW[1024]; 1200 | 1201 | dhFormatExceptionW(pException, szBufferW, ARRAYSIZE(szBufferW), bFixedFont); 1202 | 1203 | if (0 == WideCharToMultiByte(CP_ACP, 0, szBufferW, -1, szBuffer, cchBufferSize, NULL, NULL)) 1204 | return HRESULT_FROM_WIN32( GetLastError() ); 1205 | 1206 | return NOERROR; 1207 | } 1208 | 1209 | HRESULT dhGetLastException(PDH_EXCEPTION * ppException) 1210 | { 1211 | if (!ppException) return E_INVALIDARG; 1212 | 1213 | CheckTlsInitialized(); 1214 | *ppException = GetExceptionPtr(); 1215 | 1216 | return NOERROR; 1217 | } 1218 | 1219 | HRESULT dhToggleExceptions(BOOL bShow) 1220 | { 1221 | g_ExceptionOptions.bShowExceptions = bShow; 1222 | if (bShow) g_ExceptionOptions.bDisableRecordExceptions = FALSE; 1223 | 1224 | return NOERROR; 1225 | } 1226 | 1227 | HRESULT dhSetExceptionOptions(PDH_EXCEPTION_OPTIONS pExceptionOptions) 1228 | { 1229 | if (!pExceptionOptions) return E_INVALIDARG; 1230 | 1231 | g_ExceptionOptions.hwnd = pExceptionOptions->hwnd; 1232 | g_ExceptionOptions.szAppName = pExceptionOptions->szAppName; 1233 | g_ExceptionOptions.bShowExceptions = pExceptionOptions->bShowExceptions; 1234 | g_ExceptionOptions.bDisableRecordExceptions = pExceptionOptions->bDisableRecordExceptions; 1235 | g_ExceptionOptions.pfnExceptionCallback = pExceptionOptions->pfnExceptionCallback; 1236 | 1237 | return NOERROR; 1238 | } 1239 | 1240 | HRESULT dhGetExceptionOptions(PDH_EXCEPTION_OPTIONS pExceptionOptions) 1241 | { 1242 | if (!pExceptionOptions) return E_INVALIDARG; 1243 | 1244 | pExceptionOptions->hwnd = g_ExceptionOptions.hwnd; 1245 | pExceptionOptions->szAppName = g_ExceptionOptions.szAppName; 1246 | pExceptionOptions->bShowExceptions = g_ExceptionOptions.bShowExceptions; 1247 | pExceptionOptions->bDisableRecordExceptions = g_ExceptionOptions.bDisableRecordExceptions; 1248 | pExceptionOptions->pfnExceptionCallback = g_ExceptionOptions.pfnExceptionCallback; 1249 | 1250 | return NOERROR; 1251 | } 1252 | 1253 | void dhCleanupThreadException(void) 1254 | { 1255 | PDH_EXCEPTION pException; 1256 | 1257 | CheckTlsInitialized(); 1258 | pException = GetExceptionPtr(); 1259 | 1260 | if (pException) 1261 | { 1262 | SysFreeString(pException->szDescription); 1263 | SysFreeString(pException->szSource); 1264 | SysFreeString(pException->szHelpFile); 1265 | 1266 | HeapFree(GetProcessHeap(), 0, pException); 1267 | 1268 | SetExceptionPtr(NULL); 1269 | } 1270 | } 1271 | 1272 | #endif 1273 | 1274 | /* ----- dh_init.c ----- */ 1275 | 1276 | HRESULT dhInitializeImp(BOOL bInitializeCOM, BOOL bUnicode) 1277 | { 1278 | dh_g_bIsUnicodeMode = bUnicode; 1279 | 1280 | if (bInitializeCOM) return CoInitialize(NULL); 1281 | 1282 | return NOERROR; 1283 | } 1284 | 1285 | void dhUninitialize(BOOL bUninitializeCOM) 1286 | { 1287 | #ifndef DISPHELPER_NO_EXCEPTIONS 1288 | dhCleanupThreadException(); 1289 | #endif 1290 | if (bUninitializeCOM) CoUninitialize(); 1291 | } 1292 | 1293 | --------------------------------------------------------------------------------