diff --git a/src/main/cpp/_nix_based/jssc.cpp b/src/main/cpp/_nix_based/jssc.cpp index cab25db09..d0f931cb0 100644 --- a/src/main/cpp/_nix_based/jssc.cpp +++ b/src/main/cpp/_nix_based/jssc.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -40,7 +39,6 @@ #endif #ifdef __sun #include //Needed for FIONREAD in Solaris - #include //Needed for select() function #endif #ifdef __APPLE__ #include //Needed for IOSSIOSPEED in Mac OS X (Non standard baudrate) @@ -62,8 +60,6 @@ #include #include "version.h" -//#include //-lCstd use for Solaris linker - /* * Get native library version */ @@ -529,33 +525,31 @@ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setDTR /* * Writing data to the port */ -JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_writeBytes +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_writeBytes (JNIEnv *env, jobject, jlong portHandle, jbyteArray buffer){ - if( buffer == NULL ){ + ssize_t ret = 0; + jbyte *jBuffer = NULL; + if( !buffer ){ jclass exClz = env->FindClass("java/lang/NullPointerException"); - if( exClz != NULL ) env->ThrowNew(exClz, "buffer"); - return 0; + if( exClz ) env->ThrowNew(exClz, "buffer"); + goto Finally; } - jboolean ret = JNI_FALSE; - jbyte* jBuffer = env->GetByteArrayElements(buffer, JNI_FALSE); - if( jBuffer == NULL ){ - jclass exClz = env->FindClass("java/lang/RuntimeException"); - if( exClz != NULL ) env->ThrowNew(exClz, "jni->GetByteArrayElements() failed"); - return 0; + jBuffer = env->GetByteArrayElements(buffer, NULL); + if( !jBuffer ){ + jclass exClz = env->ExceptionCheck() ? NULL : env->FindClass("java/lang/RuntimeException"); + if( exClz ) env->ThrowNew(exClz, "jni->GetByteArrayElements() failed"); + goto Finally; } - jint bufferSize = env->GetArrayLength(buffer); - jint result = write(portHandle, jBuffer, (size_t)bufferSize); - if( result == -1 ){ - int err = errno; /*bakup errno*/ + ret = write(portHandle, jBuffer, env->GetArrayLength(buffer)); + if( ret == -1 ){ + int err = errno; jclass exClz = env->FindClass("java/io/IOException"); - assert(exClz != NULL); - env->ThrowNew(exClz, strerror(err)); + if( exClz ) env->ThrowNew(exClz, strerror(err)); goto Finally; } - ret = (result == bufferSize) ? JNI_TRUE : JNI_FALSE; Finally: - env->ReleaseByteArrayElements(buffer, jBuffer, 0); - return ret; + if( jBuffer ) env->ReleaseByteArrayElements(buffer, jBuffer, 0); + return (jint)ret; } /** @@ -676,7 +670,7 @@ JNIEXPORT jbyteArray JNICALL Java_jssc_SerialNativeInterface_readBytes lpBuffer = (jbyte*)malloc(byteCount*sizeof*lpBuffer); if( !lpBuffer ){ char emsg[32]; emsg[0] = '\0'; - snprintf(emsg, sizeof emsg, "malloc(%d) failed", byteCount*sizeof*lpBuffer); + snprintf(emsg, sizeof emsg, "malloc(%zu) failed", byteCount*sizeof*lpBuffer); jclass exClz = env->FindClass("java/lang/RuntimeException"); if( exClz ) env->ThrowNew(exClz, emsg); returnArray = NULL; goto Finally; diff --git a/src/main/cpp/jssc_SerialNativeInterface.h b/src/main/cpp/jssc_SerialNativeInterface.h index afde9b032..ded00f413 100644 --- a/src/main/cpp/jssc_SerialNativeInterface.h +++ b/src/main/cpp/jssc_SerialNativeInterface.h @@ -15,6 +15,8 @@ extern "C" { #define jssc_SerialNativeInterface_OS_SOLARIS 2L #undef jssc_SerialNativeInterface_OS_MAC_OS_X #define jssc_SerialNativeInterface_OS_MAC_OS_X 3L +#undef jssc_SerialNativeInterface_OS_UNKNOWN +#define jssc_SerialNativeInterface_OS_UNKNOWN -1L #undef jssc_SerialNativeInterface_ERR_PORT_BUSY #define jssc_SerialNativeInterface_ERR_PORT_BUSY -1LL #undef jssc_SerialNativeInterface_ERR_PORT_NOT_FOUND @@ -114,9 +116,9 @@ JNIEXPORT jbyteArray JNICALL Java_jssc_SerialNativeInterface_readBytes /* * Class: jssc_SerialNativeInterface * Method: writeBytes - * Signature: (J[B)Z + * Signature: (J[B)I */ -JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_writeBytes +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_writeBytes (JNIEnv *, jobject, jlong, jbyteArray); /* @@ -170,4 +172,4 @@ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_sendBreak #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/src/main/cpp/windows/jssc.cpp b/src/main/cpp/windows/jssc.cpp index 0f08718f0..755a003a8 100644 --- a/src/main/cpp/windows/jssc.cpp +++ b/src/main/cpp/windows/jssc.cpp @@ -29,7 +29,17 @@ #include #include "version.h" -//#include +// For snprintf formatting +#if defined(_MSC_VER) && _MSC_VER < 1800 +# define PRIsz "Iu" +# define PRIssz "Id" +#elif defined(__MINGW32__) && !defined(__MINGW64__) +# define PRIsz "u" +# define PRIssz "d" +#else +# define PRIsz "zu" +# define PRIssz "zd" +#endif #define MAX_PORT_NAME_STR_LEN 32 @@ -232,38 +242,61 @@ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setDTR * portHandle - port handle * buffer - byte array for sending */ -JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_writeBytes +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_writeBytes (JNIEnv *env, jobject, jlong portHandle, jbyteArray buffer){ HANDLE hComm = (HANDLE)portHandle; DWORD lpNumberOfBytesTransferred; DWORD lpNumberOfBytesWritten; - jboolean returnValue = JNI_FALSE; - if( buffer == NULL ){ + jint returnValue = -1; + if( !buffer ){ jclass exClz = env->FindClass("java/lang/NullPointerException"); - if( exClz != NULL ) env->ThrowNew(exClz, "buffer"); + if( exClz ) env->ThrowNew(exClz, "buffer"); return 0; } - jbyte* jBuffer = env->GetByteArrayElements(buffer, JNI_FALSE); - if( jBuffer == NULL ){ - jclass exClz = env->FindClass("java/lang/RuntimeException"); - if( exClz != NULL ) env->ThrowNew(exClz, "jni->GetByteArrayElements() failed"); + jbyte *jBuffer = env->GetByteArrayElements(buffer, NULL); + if( !jBuffer ){ + jclass exClz = env->ExceptionCheck() ? NULL : env->FindClass("java/lang/RuntimeException"); + if( exClz ) env->ThrowNew(exClz, "jni->GetByteArrayElements() failed"); return 0; } OVERLAPPED *overlapped = new OVERLAPPED(); overlapped->hEvent = CreateEventA(NULL, true, false, NULL); - if(WriteFile(hComm, jBuffer, (DWORD)env->GetArrayLength(buffer), &lpNumberOfBytesWritten, overlapped)){ - returnValue = JNI_TRUE; - } - else if(GetLastError() == ERROR_IO_PENDING){ - if(WaitForSingleObject(overlapped->hEvent, INFINITE) == WAIT_OBJECT_0){ - if(GetOverlappedResult(hComm, overlapped, &lpNumberOfBytesTransferred, false)){ - returnValue = JNI_TRUE; - } + DWORD err = 0; + do{ + err = !WriteFile(hComm, jBuffer, (DWORD)env->GetArrayLength(buffer), &lpNumberOfBytesWritten, overlapped); + if( !err ){ /* successfully written. we're already done. */ + returnValue = lpNumberOfBytesWritten; + break; } - } + err = GetLastError(); + if( err != ERROR_IO_PENDING ){ + break; /* some unknown error occurred. Go reporting it. */ + } + /* our write above was async (IO_PENDING). So it was only fired off, but + * we do not know the result yet. Therefore we've to wait for the result. */ + if( WaitForSingleObject(overlapped->hEvent, INFINITE) != WAIT_OBJECT_0 ){ + /* too bad :( wait failed. */ + err = GetLastError(); + break; + } + /* waited successfully. Time to get the result. */ + if( GetOverlappedResult(hComm, overlapped, &lpNumberOfBytesTransferred, false) ){ + /* we know the result now */ + returnValue = lpNumberOfBytesTransferred; + err = 0; + }else{ /* GetOverlappedResult has failed :( */ + err = GetLastError(); + } + }while(0); env->ReleaseByteArrayElements(buffer, jBuffer, 0); CloseHandle(overlapped->hEvent); delete overlapped; + if( err ){ + char emsg[128]; + snprintf(emsg, sizeof emsg, "Error %lu: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes#system-error-codes", (long unsigned)err); + jclass exClz = env->FindClass("java/io/IOException"); + if( exClz ) env->ThrowNew(exClz, emsg); + } return returnValue; } @@ -298,7 +331,7 @@ JNIEXPORT jbyteArray JNICALL Java_jssc_SerialNativeInterface_readBytes lpBuffer = (jbyte*)malloc(byteCount*sizeof*lpBuffer); if( !lpBuffer ){ char emsg[32]; emsg[0] = '\0'; - snprintf(emsg, sizeof emsg, "malloc(%d) failed", byteCount*sizeof*lpBuffer); + snprintf(emsg, sizeof emsg, "malloc(%" PRIsz ") failed", byteCount*sizeof*lpBuffer); jclass exClz = env->FindClass("java/lang/RuntimeException"); if( exClz ) env->ThrowNew(exClz, emsg); returnArray = NULL; goto Finally; diff --git a/src/main/java/jssc/SerialNativeInterface.java b/src/main/java/jssc/SerialNativeInterface.java index 091dc3848..cf6b983b0 100644 --- a/src/main/java/jssc/SerialNativeInterface.java +++ b/src/main/java/jssc/SerialNativeInterface.java @@ -277,7 +277,7 @@ public static String getLibraryVersion() { * * @return If the operation is successfully completed, the method returns true, otherwise false */ - public native boolean writeBytes(long handle, byte[] buffer) throws IOException; + public native int writeBytes(long handle, byte[] buffer) throws IOException; /** * Get bytes count in buffers of port diff --git a/src/main/java/jssc/SerialPort.java b/src/main/java/jssc/SerialPort.java index a203bdec2..a417cd058 100644 --- a/src/main/java/jssc/SerialPort.java +++ b/src/main/java/jssc/SerialPort.java @@ -28,6 +28,8 @@ import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; +import static jssc.SerialPortException.wrapNativeException; + /** * * @author scream3r @@ -397,15 +399,15 @@ public boolean setDTR(boolean enabled) throws SerialPortException { } /** - * Write byte array to port + * Write byte array to port. * - * @param buffer byte[] array to write + * @param buffer byte[] array to write. * - * @return If the operation is successfully completed, the method returns true, otherwise false - * - * @throws SerialPortException if exception occurred + * @return number of bytes written. + * + * @throws SerialPortException */ - public boolean writeBytes(byte[] buffer) throws SerialPortException { + public int writeBytes(byte[] buffer) throws SerialPortException { checkPortOpened("writeBytes()"); try { return serialInterface.writeBytes(portHandle, buffer); @@ -419,13 +421,13 @@ public boolean writeBytes(byte[] buffer) throws SerialPortException { * * @param singleByte single byte value to write * - * @return If the operation is successfully completed, the method returns true, otherwise false + * @return Number of bytes written. * * @throws SerialPortException if exception occurred * * @since 0.8 */ - public boolean writeByte(byte singleByte) throws SerialPortException { + public int writeByte(byte singleByte) throws SerialPortException { checkPortOpened("writeByte()"); return writeBytes(new byte[]{singleByte}); } @@ -435,15 +437,25 @@ public boolean writeByte(byte singleByte) throws SerialPortException { * * @param string String value to write * - * @return If the operation is successfully completed, the method returns true, otherwise false + * @return + * Number of bytes written. WARN: Number of BYTES, NOT number of CHARS! + * This may differ from string.length() if passed in string + * contains chars outside the english alphabet. If you need reliable + * return lengths, consider calling + * {@link java.lang.String#getBytes(java.nio.charset.Charset)} + * yourself and then passing result to + * {@link #writeBytes(byte[])} + * instead. This gives you the chance to verify the expected return + * value correctly. * * @throws SerialPortException if exception occurred * * @since 0.8 */ - public boolean writeString(String string) throws SerialPortException { + public int writeString(String string) throws SerialPortException { checkPortOpened("writeString()"); - return writeBytes(string.getBytes()); + byte[] bytes = string.getBytes(); + return writeBytes(bytes); } /** @@ -451,16 +463,27 @@ public boolean writeString(String string) throws SerialPortException { * * @param string String value to write * @param charsetName valid Charset name, e.g. "UTF-8" - * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @return + * Number of bytes written. WARN: Number of BYTES, NOT number of CHARS! + * This may differ from string.length() if passed in string + * contains chars outside the english alphabet. If you need reliable + * return lengths, consider calling + * {@link java.lang.String#getBytes(java.nio.charset.Charset)} + * yourself and then passing result to + * {@link #writeBytes(byte[])} + * instead. This gives you the chance to verify the expected return + * value correctly. * * @throws SerialPortException if exception occurred * @throws UnsupportedEncodingException if encoding exception occurred * * @since 2.8.0 */ - public boolean writeString(String string, String charsetName) throws SerialPortException, UnsupportedEncodingException { + public int writeString(String string, String charsetName) throws SerialPortException, UnsupportedEncodingException { checkPortOpened("writeString()"); - return writeBytes(string.getBytes(charsetName)); + byte[] bytes = string.getBytes(charsetName); + return writeBytes(bytes); } /** @@ -468,13 +491,13 @@ public boolean writeString(String string, String charsetName) throws SerialPortE * * @param singleInt single int value to write * - * @return If the operation is successfully completed, the method returns true, otherwise false + * @return Number of bytes written. * * @throws SerialPortException if exception occurred * * @since 0.8 */ - public boolean writeInt(int singleInt) throws SerialPortException { + public int writeInt(int singleInt) throws SerialPortException { checkPortOpened("writeInt()"); return writeBytes(new byte[]{(byte)singleInt}); } @@ -484,13 +507,13 @@ public boolean writeInt(int singleInt) throws SerialPortException { * * @param buffer int[] array to write * - * @return If the operation is successfully completed, the method returns true, otherwise false + * @return Number of bytes written. * * @throws SerialPortException if exception occurred * * @since 0.8 */ - public boolean writeIntArray(int[] buffer) throws SerialPortException { + public int writeIntArray(int[] buffer) throws SerialPortException { checkPortOpened("writeIntArray()"); byte[] byteArray = new byte[buffer.length]; for(int i = 0; i < buffer.length; i++){ @@ -1415,4 +1438,5 @@ public void run() { } } } + }