|
leon thanks so much I implemented the code and problems went away meaning with out the wait in the Main Thread The Framework was able to notify me via CAsynSocket::OnSend
The actual testing will come when I execute my Z/OS (MainFrame) FTP code to download the file
to that folder that is being watched
Much thanks
|
|
|
|
|
Be aware that the change folder notification occurs at any change, which means the full write may not have happened. One solution is to write the file to another location and then atomically move it to the monitored directory.
|
|
|
|
|
while (myWatch->threadExitComplete == FALSE) {};
In this example,
WaitForSingleObject(workerThread, INFINITE) would be better.
For the handle array in the thread, prefer putting the stop event first to ensure it has a higher priority. I'd also directly test:
if (dwWaitStatus == WAIT_OBJECT_0)
break;
Since you aren't looking at any other conditions, you don't need to check for them.
|
|
|
|
|
I have a device (Fluke data logger which uses 9600-8-N-1 and no flow control) for which I need to write an serial interface in Visual Studio 2010 (C++) but have not been able to get more than a single response from the unit. I can connect, send the request for (as an example) device identification, but subsequent requests for data from the unit produce no response (variable read remains equal to zero after each Readfile request). I know the unit works fine because I can use a standard terminal program to obtain the responses I a need.
Nothing in the forums here, on MSDN, or repeated Google searches seems to answer my question. This has been frustrating me for more than a week so I am looking for a fresh set of eyes to point out what I am probably missing.
This is a stripped-down version of the test code. Any ideas are welcome. For purposes of testing the device response I'm not using overlapping for testing, but will employ overlapping in the "real" code.
#include "stdafx.h"
#include <stdio.h>
#include <conio.h>
#include <string.h>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
int ch;
HANDLE screen = GetStdHandle(STD_OUTPUT_HANDLE);
printf("Getting keyboard mode\n");
HANDLE keyboard = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
if (!GetConsoleMode(keyboard, &mode))
{
printf ("GetConsoleMode failed with error %d.\n",
GetLastError());
}
printf("Setting keyboard mode\n");
mode &= ~ ENABLE_PROCESSED_INPUT;
if (!SetConsoleMode(keyboard, mode))
{
printf ("SetConsoleMode failed with error %d.\n",
GetLastError());
}
printf("opening comm port\n");
TCHAR *port_name = TEXT("COM1"); HANDLE file;
file = CreateFile(port_name,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if ( INVALID_HANDLE_VALUE == file) {
printf ("CreateFile failed with error %d.\n",
GetLastError());
CloseHandle(file);
if ( _kbhit() ) {
ch = _getch();
}
return 1;
}
printf("Getting com state\n");
DCB port;
SecureZeroMemory(&port, sizeof(port));
port.DCBlength = sizeof(port);
if (!GetCommState(file, &port))
{
printf ("GetCommState failed with error %d.\n",
GetLastError());
CloseHandle(file);
if ( _kbhit() ) {
ch = _getch();
}
return 1;
}
printf("Building com state\n");
TCHAR *DCBsettings = TEXT("baud=9600 parity=n data=8 stop=1");
if (!BuildCommDCB(DCBsettings, &port))
{
printf ("BuildCommDCB failed with error %d.\n",
GetLastError());
CloseHandle(file);
if ( _kbhit() ) {
ch = _getch();
}
return 1;
}
printf("Adjusting com settings\n");
if (!SetCommState(file, &port))
{
printf ("SetCommState failed with error %d.\n",
GetLastError());
CloseHandle(file);
if ( _kbhit() ) {
ch = _getch();
}
return 1;
}
printf("Setting port time-outs.\n");
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutMultiplier = 1;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.WriteTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 1;
if (!SetCommTimeouts(file, &timeouts))
{
printf ("SetCommTimeouts failed with error %d.\n",
GetLastError());
CloseHandle(file);
if ( _kbhit() ) {
ch = _getch();
}
return 1;
}
printf("Writing *idn?<cr> to port\n");
char init[] = "*idn?\n";
DWORD written;
if (!WriteFile(file, init, sizeof(init), &written, NULL))
{
printf ("WriteFile failed with error %d.\n",
GetLastError());
}
if (written != sizeof(init))
{
printf("not all data written to port\n");
}
char buffer[256];
memset(&buffer[0], 0, sizeof(buffer));
DWORD read;
char dataReq[] = "fetch? 3\n";
int i;
do {
if( !ReadFile(file, buffer, sizeof(buffer), &read, NULL) )
{
printf ("ReadFile failed with error %d.\n",
GetLastError());
}
if (read){
printf ("Characters read = %d.\n",
read);
for(i=0; i<(read+1); i++)
{
printf("%d ", buffer[i]);
}
printf("\n");
printf("%s\n", buffer);
memset(&buffer[0], 0, sizeof(buffer));
Sleep(1000);
printf("Writing fetch? 3<cr> to port\n");
if (!WriteFile(file, dataReq, sizeof(dataReq), &written, NULL))
{
printf ("WriteFile failed with error %d.\n",
GetLastError());
}
if (written != sizeof(dataReq))
{
printf("not all data written to port\n");
}
}
if ( _kbhit() ) {
ch = _getch();
WriteFile(file, &ch, 1, &written, NULL);
}
} while ( ch != 127);
CloseHandle(keyboard);
CloseHandle(file);
return 0;
}
|
|
|
|
|
Have you tried, lengthening your timeouts, or even disabling them (but then you need to read 256 bytes in order to return) ?
"the debugger doesn't tell me anything because this code compiles just fine" - random QA comment
"Facebook is where you tell lies to your friends. Twitter is where you tell the truth to strangers." - chriselst
"I don't drink any more... then again, I don't drink any less." - Mike Mullikins uncle
|
|
|
|
|
I tried both lengthening the timeouts and rewriting the code using overlapped communication as per bt[^] but the result is the same. First command is received and I can get a response. The second command, while sent without error, is followed by no response.
If it helps, after running the code I have to run the terminal program and issue a command twice to get it back to a responsive state. Otherwise running the code twice in a row doesn't even return anything for the first command. It's like it gets stuck waiting after sending the response.
|
|
|
|
|
Could the terminal program be appending and sending a necessary line feed or carraige return, that the other device needs?
"the debugger doesn't tell me anything because this code compiles just fine" - random QA comment
"Facebook is where you tell lies to your friends. Twitter is where you tell the truth to strangers." - chriselst
"I don't drink any more... then again, I don't drink any less." - Mike Mullikins uncle
|
|
|
|
|
I already append a carriage return (the documentation says either a carriage return or linefeed will be accepted). Even adding extra CRs and LFs has no effect.
|
|
|
|
|
Could you connect a PC with a terminal program in place of the device and see what is being sent by your app and make sure it is OK?
"the debugger doesn't tell me anything because this code compiles just fine" - random QA comment
"Facebook is where you tell lies to your friends. Twitter is where you tell the truth to strangers." - chriselst
"I don't drink any more... then again, I don't drink any less." - Mike Mullikins uncle
|
|
|
|
|
I tried different timeouts and the following work for the first read:
CTimeouts.ReadIntervalTimeout = 20; CTimeouts.ReadTotalTimeoutMultiplier = 10; CTimeouts.ReadTotalTimeoutConstant = 100; CTimeouts.WriteTotalTimeoutMultiplier = 0; CTimeouts.WriteTotalTimeoutConstant = 0;
I also added a loop using ClearCommError to verify that cbInQue has returned before performing ReadFile so was able to do that as soon as data arrived. Everything in that first request-receipt cycle works fine.
I still have the problem that the next WriteFile is essentially ignored, although it is sent without errors. The device appears to be waiting for something after the read was completed before it can respond to whatever is sent on the transmit line.
|
|
|
|
|
Does WriteFile say bytes sent > 0? Do you see data on the wire (a breakout box might be handy)?
Can you connect a PC with a terminal program in place of the device (with a null modem cable) so you can see (and verify) what, if anything is being sent by your program and manually send the data you expect to get. A scenario where you could set break points and see exactly what code is being executed and the value of the variables.
"the debugger doesn't tell me anything because this code compiles just fine" - random QA comment
"Facebook is where you tell lies to your friends. Twitter is where you tell the truth to strangers." - chriselst
"I don't drink any more... then again, I don't drink any less." - Mike Mullikins uncle
|
|
|
|
|
It ended up that I had to force-transmit a carriage return after the ReadFile to "kick" the device using TransmitCommChar . After the "kick" it responds to the next WriteFile with the appropriate response. Using a second PC was going to be my next course of action.
|
|
|
|
|
First housekeeping when you open a comport you really need to put the colon in ... its "COM1:".
Yes your string works on some versions of Windows but not all, the documentation spells it out
To open a serial port
- Insert a colon after the communication port pointed to with the first parameter, lpzPortName.
For example, specify COM1: as the communication port.
In the ReadFile documentation it spells out the blocking function.
ReadFile function (Windows)[^]
You will have an error which will be a Readtimeout. Why because you set the read timeout to
1 second and execute a read and there won't be any data because you haven't yet sent another
command.
From that moment on you are toast as your code does not ever clear the error condition and
ReadFile will always return with reading zero bytes because you haven't cleared the error.
The big obvious is there isn't even a single GetLastError in your code much less dealing
with the error. The reason the terminal program fixes the problem is at some point they go
and clear the error.
You could try brutal and SetLastError to zero just before the ReadFile .. I don't like it
but it will probably work
While the thread error variable has an error value I am pretty sure that none of the IO
functions will work so I am pretty sure WriteFile will also bail always writing zero
bytes while it's set. It should also return success but the actual bytes written will
always be zero because the fail out is for pre-existing error that hasn't been cleared.
I don't particually like the way you are using the com port but probably the easiest way
to fix you problem is make the ReadFile timeout bail immediately .. so set no timeout on
the read. How to do that is given in the documentation
COMMTIMEOUTS structure (Windows)[^]
Quote => "A value of MAXDWORD, combined with zero values for both the ReadTotalTimeoutConstant
and ReadTotalTimeoutMultiplier members, specifies that the read operation is to return
immediately with the bytes that have already been received, even if no bytes have been
received."
That should stop you getting a read timeout which should fix your problem with your current code
and it getting into a block condition. It just means it will spin around your do loop like a crazy
person rather than slow 1 second pace it currently is.
On the plus side it will be more responsive
However you really need to actually put in code to deal with errors, you set the com timeouts
but do nothing if the timeout or any other error actually occurs.
In vino veritas
modified 28-Sep-16 0:32am.
|
|
|
|
|
Excellent points and suggestions, and I've implemented much of what you suggested. In the end it turned out the device was waiting for a response from the computer to indicate the read was completed, which I accomplished with a TransmitCommChar . Clunky but it works.
|
|
|
|
|
Much thanks to all who made suggestions. I implemented many of them.
In the end, the problem was solved by "kicking" the device with TransmitCommChar(hComm, 13); after the read request.
|
|
|
|
|
If anyone has a minute can they confirm a weird behaviour of _tcscpy_s which caught me totally off guard that it trashes the unused part of the return buffer.
The code I would like you to test follows, you will need include TCHAR.H for the standard _tcscpy_s function
#include <TCHAR.H>
TCHAR szFilter[512];
memset(szFilter, 0, sizeof(szFilter));
_tcscpy_s(szFilter, _countof(szFilter), _T("All files"));
int i = _tcslen(szFilter);
TCHAR Ch1 = szFilter[i + 2];
memset(szFilter, 0, sizeof(szFilter));
_tcscpy_s(szFilter, _tcslen(_T("All files"))+1, _T("All files"));
TCHAR Ch2 = szFilter[i + 2];
What I am interested in is the part of the buffer the result is not written in. What stunned me was it was all trashed.
Okay the function does not define what it does with the unused parts of the buffer but it seems a waste of time to play with it.
The stranger part is if you lie to the function and make it have just the right size buffer it doesn't trash the buffer.
It's so quirky I am trying to work out if it is a machine/processor dependent implementation of the function.
In vino veritas
|
|
|
|
|
I have seen this before and I think it is to do with the fact that the _s variants are "safe" mode functions. The first thing they do is to overwrite the buffer with some known pattern (hex FEFE in my Unicode test), before doing the copy - at least in Debug mode. You could try a Release mode version and add some code to examine the buffer at each point. A 16 character buffer will be big enough.
|
|
|
|
|
Yes it's hex FEFE pattern for me as well. I tried release and it's not there .. must be something to do with debug mode.
That is what was doing my head in a code that would run in release mode but not in debug mode ... now that is a turn up.
Update: David Crowe got the weirdness there is a FILL_STRING call put on the balance of the buffer. I can't for the life of me think why they do it so have referred it to Microsoft as to why debug mode does it, and will work around the behaviour.
In vino veritas
|
|
|
|
|
Just confirmed this using ordinary strcpy_s , so nothing to do with TCHAR s. Also when I run it in Release mode the buffer is not filled with the xFE characters.
|
|
|
|
|
|
A good catch that does the job. Setting it to zero and then putting the value back after the code solves my problem.
Well you learn something everyday, I was completely unaware they had added that functionality into the safe routines but at least they do realize the problem and allow control of it.
In vino veritas
|
|
|
|
|
I reckon it has something to do with _tcscpy_s() internally calling _FILL_STRING() for the balance of the destination string. If you can live without the safe version, _tcscpy() will work as expected.
It looks like you may need to use memcpy() instead.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"You can easily judge the character of a man by how he treats those who can do nothing for him." - James D. Miles
modified 29-Sep-16 0:11am.
|
|
|
|
|
You are correct the FILL_STRING is the culprit and no I need the function so will just do the buffer fill long handed. As Richard worked out it isn't there in release mode so it's something to do with debug mode display functions. My guess is it's a pattern for there watch for change implementation.
It did however totally catch me out
Update: Chris correctly identified why it's doing it and how you control it.
In vino veritas
modified 28-Sep-16 2:28am.
|
|
|
|
|
leon de boer wrote: ...so will just do the buffer fill long handed. Just curious but what does it matter what's after the \0 value in the buffer?
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"You can easily judge the character of a man by how he treats those who can do nothing for him." - James D. Miles
|
|
|
|
|
I was try to make a filter for OPENFILENAME
OPENFILENAME structure (Windows)[^]
It's not a great structure from MS and it look something like this "DXF Files\0*.dxf\0PLT Files\0*.plt\0\0 "
You can't string copy that in because of the terminating 0 characters in it.
For me it's worse "DXF files", "PLT files" is the English version I have German, French etc strings
to replace it so the string changes.
So I have to build that structure and the old code basically relied on the buffer being zeroed
and it just wrote the string bits in without having to track all the string lengths. It was assumed
strcpy doesn't play with parts of the buffer it isn't writing the result into.
So the correct way to write it would be strcpy + strcat + strcat + strcat + strcat(\0).... which I did.
The safe functions mean you have to track the amount of buffer left to pass in and so it becomes
a lot of code for a very trivial thing ... BUT IT'S GUARANTEED TO WORK
So basically while I was trying to debug the program the code was failing because the code relied
on something not guaranteed. For such a simple string structure its a pain to have to make code
track multiple string pointers just to create it but I should have done it .. lesson 1 never assume
anything not actually specified!!!!
The bigger issue for me and why I went after it was performance. If they are playing around with
stuff they don't need to they are wasting time. String functions are used often and everywhere
and on very large memory blocks and I was concerned about the impact.
In vino veritas
modified 29-Sep-16 0:12am.
|
|
|
|