问题
So in some Arduino code I am Serial.print
a few numbers like this (var1=##,var2=##,var3=##
). I have a C++ DLL that I am making to get this information and Parse it into a variant array like this ("var1=",##.##,"var2=",##.##,"var3=",##.##
) storing the string portions as variant BSTRs and the #
s as Variant doubles. This variant array comes from Excel and the purpose of my C++ DLL is to allow serial communications to and from Excel and an Arduino board.
My problem is that instead of getting the information back like I would like, I am instead getting a lot of extra gibberish at the end and cant figure out where it is coming from. I am including my related code before, leaving out the serial communications because I know that that part works for sure. For now, the Arduino Code is just sending (##,##,##
) then Serial.println();
to make things simpler as there are no strings, just numbers.
The VBA Code:
'collects all rows available currently up to 1000(global numRows) lines each time, stores result in dataArray
Public Function getAllData()
Dim stringArray() As Variant
Dim variantArray() As Variant
ReDim stringArray(0 To 3000) As Variant
ReDim variantArray(0 To 3000) As Variant
If Main.dataCollectionActive Then
Dim staringDataRow As Integer
staringDataRow = currentDataRow
Dim returnValue As Long
returnValue = GetAllDataTypes(stringArray(), variantArray(), currentDataRow)
If Not (returnValue = 0) Then
If Not (returnValue = -1) Then
If printWhileCollectingData = True Then
' Call printVariantData
Dim row As Integer
Dim col As Integer
For row = startingDataRow To (currentDataRow - 1)
Main.dataBox.Text = Main.dataBox.Text & stringArray(row)
For col = 0 To (getNumColumns() - 1)
Main.Cells(row + startingRow, col + startingCol).Value = variantArray(col + (row * getNumColumns()))
Next col
Next row
End If
End If
End If
'only stoped in Main sheets code for stopDataButton or if the array has reached its limit
If Not ((currentDataRow * getNumColumns) = UBound(variantArray())) Then
Application.OnTime Now + TimeValue("00:00:01"), "getAllData"
End If
Main.Range("$L$4").Value = currentDataRow
End If
End Function
The C++ DLL code:
//end1 comes before end2, so end1 = i-1 and end2 = i characters
DLL_EXPORT bool WINAPI isEndLine(char end1, char end2){
//check for CRLF
if (end1 == '\r') {
if (end2 == '\n') {
return true;
}
}
//check for normal endLine \0
else if ((end1 == '\0')) {
return true;
}
//check for users own endLine character
else{
for(int i = 0; i < MAX_ENDLINE_LENGTH; i++){
if (end1 == endLine[i]){
return true;
}
}
}
return false;
}
//reads a single character from buffer, if there is nothing returned then the global, bufferAvailable is set to false, so that no more characters are asked for, for now
DLL_EXPORT char WINAPI readCharFromSerial() {
char dataChar[1];
DWORD dwBytesRead = 0; //number of data bytes read in
if(!ReadFile(hSerial, dataChar, 1, &dwBytesRead, NULL)) { //gets data if successful, if not then notifies user
ErrorExit("ReadFile, reading a character");
}
if (dwBytesRead == 0) {
bufferAvailable = false;
}
return dataChar[0]; //returns read in data
}
//reads a single line by pulling one character at a time until it finds the end of line character, if the buffer has characters in it
DLL_EXPORT void WINAPI readLineFromSerialPort(char* line, int length) {
bufferAvailable = true;
int i = 0;
line[i] = readCharFromSerial();
i++;
if (bufferAvailable){
do{
line[i] = readCharFromSerial();
i++;
}while((!isEndLine(line[i-2], line[i-1])) && (i < length));
if(line[i-2] == '\r') {
line[i-2] = '\n';
line[i-1] = '\0';
}
if(!(i<length)){
MessageBoxA(NULL, (LPCSTR)("string length not long enough"), TEXT("readLineFromSerialPort"), MB_OK);
}
}
}
DLL_EXPORT void WINAPI PlaceDblInVarDbl(varArr* VD, double data) {
(VD->ptr[VD->index]).vt = VT_R8; //FIXME add function to do this
(VD->ptr[VD->index]).dblVal = data;
VD->index++;
}
DLL_EXPORT void WINAPI PlaceCharPtrInVarBstr(varArr* VD, char* cString) {
BSTR bstr = new OLECHAR[MAX_DATA_LENGTH];
const char* Cstring = (const char*)cString;
mbstowcs(bstr, Cstring, MAX_DATA_LENGTH);
VD->ptr[VD->index].vt = VT_BSTR;
SysReAllocString(&((VD->ptr[VD->index]).bstrVal), bstr);
VD->index++;
}
DLL_EXPORT int WINAPI ParseCharPtrToVarBstrAndVarDbl(varArr* VD, char* dataString) {
//FIXME perhaps make an array of chars for strings that are allowed without being coppied, this would be added to the if begging with !isdigit (like the '.')
bool hasLetters = false;
int endIndex = 0;
int startIndex = 0;
// MessageBoxA(NULL, (LPCSTR)("entered"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
while (!(isEndLine(dataString[endIndex], dataString[endIndex+1]))) {
hasLetters = false;
startIndex = endIndex;
while (!isDelim(dataString[endIndex]) && (!(isEndLine(dataString[endIndex-1], dataString[endIndex])))) {
if (!(isdigit(dataString[endIndex])) && !(dataString[endIndex] == ' ') && !(dataString[endIndex] == '.')) {
hasLetters = true;
}
endIndex++;
}
// MessageBoxA(NULL, (LPCSTR)("delimeter found"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
if((startIndex + 1 == endIndex) || (startIndex == endIndex)/* && !(isEndLine(dataString[endIndex-1], dataString[endIndex]))*/) {
//FIXME odd way to fix CRLF problem
// MessageBoxA(NULL, (LPCSTR)("triggered if"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
}
else if (hasLetters) { //string
char smallerString[MAX_DATA_LENGTH];
const char* DStr = (const char*)(&(dataString[startIndex]));
strncpy(smallerString, DStr, endIndex-startIndex+1);
smallerString[endIndex-startIndex+1] = '\0';
PlaceCharPtrInVarBstr(VD, smallerString);
// MessageBoxA(NULL, (LPCSTR)("triggered hasLetters"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
}
else { //double
//FIXME remove whitespace
char* start = &dataString[startIndex];
char* eOS = &(dataString[endIndex]);
char** endOfString = &eOS;
double data = strtod(start, endOfString);
//FIXME do some error checking
PlaceDblInVarDbl(VD, data);
// MessageBoxA(NULL, (LPCSTR)("triggered does not hasLetters"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
}
endIndex++;
}
//, MessageBoxA(NULL, (LPCSTR)("exited"), TEXT("ParseCharPtrToVarBstrAndVarDbl"), MB_OK);
return VD->startingIndex - VD->index;
}
//first read a lines from the serial port and then parse it into dataArray, does this until there is no data in buffer or array is full
//returns how many rows that it read
DLL_EXPORT int WINAPI GetAllDataTypes(LPSAFEARRAY* unparsedData, LPSAFEARRAY* parsedData, int* currentDataRow) {
bufferAvailable = true;
varArr UPD;
OpenVariantSafeArray(&UPD, unparsedData, *currentDataRow); if (UPD.failed) { return -1; }
varArr PD;
OpenVariantSafeArray(&PD, parsedData, *currentDataRow); if (PD.failed) { return -1; }
while (bufferAvailable && ((PD.index + 10) < PD.uBound)) {
char dataString[MAX_DATA_LENGTH];
readLineFromSerialPort(dataString, MAX_DATA_LENGTH);
if (bufferAvailable) {
PlaceCharPtrInVarBstr(&UPD, dataString);
ParseCharPtrToVarBstrAndVarDbl(&PD, dataString);
}
}
CloseVariantSafeArray(&UPD, unparsedData); if (UPD.failed) { return -1; }
CloseVariantSafeArray(&PD, parsedData); if (PD.failed) { return -1; }
*currentDataRow += UPD.index - UPD.startingIndex;
bufferAvailable = true;
return UPD.index - UPD.startingIndex;
}
The Resulting Error:
This is the unparsed String that is returned as it is printed in a textbox in Excel.
0.00,0.01,0.00 0.10,0.02,0.01 0.20,0.03,0.02 0.30,0.04,0.03 0.40,0.05,0.04 0.50,0.06,0.05 0.60,0.07,0.06 0.70,0.08,0.07 0.80,0.09,0.08 0.90,0.10,0.09 1.00,0.10,0.10
However the parsed string that is printed in cells by VBA for loops. Still has random characters. It appears that there is something wrong with my parsing in DLL as the last double always ends up being a string, and then it always has random strings afterward.
0.000 0.010 "0.00 " - 0.100 0.020 "0.01 " - 0.200 0.030 "0.02 " - 0.300 0.040 "0.03 " - 0.400 0.050 0.900 0.100 "0.09 " - 0.000 0.100 "0.10 " - 0.100 0.110 "0.11 " - 0.200 0.120 "0.12 " - 0.300 0.130 "0.13 " - 0.400 0.140 "0.14 " - 0.500 0.150 "0.15 " - 0.600 0.160 "0.16 " - "0.25 " - 0.600 0.240 "0.26 " - 0.700 0.250 "0.27 " -
回答1:
The final endIndex++ in ParseCharPtrToVarBstrAndVarDbl() in DLL code to skip the delimiter skipped over the null terminator in the strings. The fix is to add a final if statement for the end of string and if it is then return.
来源:https://stackoverflow.com/questions/45516146/random-characters-from-casting-arduino-string-to-excel-bstr-through-c-dll