C++ コードを使用して、USB 経由で接続されたモデムに AT コマンドを送信しようとしています。この特定のモデムで電話番号を返す「AT+MDN」を使用してテストしており、シリアルポートに接続するパテを介してテストすると正しく動作します。GetComPort は、モデムが接続されている COM ポート番号を決定し、そのハンドルを作成します。sendCommand() メソッドはポートを開き、コマンドを送信し、バッファをフラッシュしてから結果を読み取ろうとしますが、送信されたコマンドしか受信しないため、この例では「AT+MDN」を受信します。この問題に対して見つけたさまざまな修正 (バッファをフラッシュし、書き込みと読み取りの間にスリープを追加する) をすべて試しましたが、どれも機能しませんでした。
SerialATDT.cpp
#include "SerialATDT.h"
#include "EventSem.h"
#include "Traces.h"
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>
SerialATDT::SerialATDT(String ident) : mComPortIdentifier(ident)
{
}
SerialATDT::~SerialATDT(void)
{
}
String SerialATDT::getComPortId()
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
int devOffset = 0;
DWORD propertyDataType;
HKEY devKey;
DWORD portNameSize;
DWORD result;
String comPort = "";
BYTE friendlyName[4096];
TCHAR devName[4096];
TCHAR portName[4096];
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_MODEM, 0, 0, DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
TRACE_L(TEXT("\nSetupDiGetClassDevs() failed: %d\n"), GetLastError());
return "";
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
while (SetupDiEnumDeviceInfo(hDevInfo, devOffset++, &DeviceInfoData))
{
if (!SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_FRIENDLYNAME,
&propertyDataType, friendlyName, sizeof(friendlyName), NULL))
{
TRACE_L(TEXT("\nSetupDiEnumDeviceInfo() failed: %d\n"), GetLastError());
continue;
}
// Look for identifying info in the name
if ( mComPortIdentifier.size() > 0 ) {
const char *temp = strstr((const char*)friendlyName, mComPortIdentifier.c_str());
if ( temp == 0 ) {
continue;
}
}
// Get the device name.
if (!SetupDiGetDeviceInstanceId(hDevInfo, &DeviceInfoData, devName, MAX_PATH, NULL))
{
TRACE_L(TEXT("\nSetupDiGetDeviceInstanceId() failed: %d\n"), GetLastError());
continue;
}
// Open the registry key associated with the device.
devKey = SetupDiOpenDevRegKey(hDevInfo, &DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
if (devKey == INVALID_HANDLE_VALUE)
{
TRACE_L(TEXT("\nSetupDiOpenDevRegKey() failed: %d\n"), GetLastError());
continue;
}
// Read the PortName registry key.
portNameSize = sizeof(portName);
result = RegQueryValueEx(devKey, TEXT("PortName"), NULL, NULL, (LPBYTE) portName, &portNameSize);
if(result != ERROR_SUCCESS)
{
TRACE_L(TEXT("\nRegQueryValueEx() failed: %d\n"), result);
continue;
}
// We are not guaranteed a null terminated string from RegQueryValueEx, so explicitly NULL-terminate it.
portName[portNameSize / sizeof(TCHAR)] = TEXT('\0');
// Close the registry key.
result = RegCloseKey(devKey);
if (result != ERROR_SUCCESS)
{
TRACE_L(TEXT("\nRegCloseKey() failed: %d\n"), result);
continue;
}
// Try to open the COM port.
comPort = portName;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return comPort;
}
bool SerialATDT::getComPort(HANDLE *hFile)
{
String comPort = getComPortId();
*hFile = INVALID_HANDLE_VALUE;
if ( comPort.size() > 0 ) {
String comPortStr;
comPortStr.Format("\\\\.\\%s", comPort.c_str());
*hFile = ::CreateFile( comPortStr.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL );
if ( *hFile == INVALID_HANDLE_VALUE ) {
TRACE_L("AT file open error %ld", GetLastError());
}
}
return *hFile != INVALID_HANDLE_VALUE;
}
bool SerialATDT::sendCommand(String command, String &response)
{
bool retFlag = true;
HANDLE hFile = NULL;
response = "";
if ( !getComPort(&hFile) ) {
TRACE("SerialATDT-Unable to get comport");
return false;
}
::SetupComm( hFile, 2048, 2048 );
DCB dcb = { 0 };
dcb.DCBlength = sizeof(DCB);
::GetCommState( hFile, &dcb );
dcb.BaudRate = 9600;
// set additional parameters for 8-bit, no parity, one stop bit, no flow control
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
::SetCommState( hFile, &dcb );
// Retrieve the timeout parameters for all read and write operations on the port.
COMMTIMEOUTS CommTimeouts;
::GetCommTimeouts (hFile, &CommTimeouts);
#define BUFFER_LENGTH 256
// Change the COMMTIMEOUTS structure settings.
CommTimeouts.ReadIntervalTimeout = 50;
CommTimeouts.ReadTotalTimeoutMultiplier = (2000/BUFFER_LENGTH); // Only wait 2 seconds (2000ms).
CommTimeouts.ReadTotalTimeoutConstant = 50;
CommTimeouts.WriteTotalTimeoutMultiplier = 50;
CommTimeouts.WriteTotalTimeoutConstant = 50;
// Set the timeout parameters for all read and write operations on the port.
::SetCommTimeouts (hFile, &CommTimeouts);
char buffer[BUFFER_LENGTH+1];
OVERLAPPED m_ReadSync;
unsigned error = 0;
EventSem readWait;
bool finished = false;
memset(&m_ReadSync, 0, sizeof(OVERLAPPED));
m_ReadSync.hEvent = readWait.GetHandle();
m_ReadSync.Pointer = (void*)buffer;
static int index = 0;
// Edited here
command += "\r";
// First write the command out to the serial port
UInt32 numBytesWritten = 0;
UInt32 totalBytesWritten = 0;
do {
if ( !::WriteFile(hFile, command.c_str(), command.length(), &numBytesWritten, NULL) ) {
TRACE("SerialATDT-sendCommand failed on sending.");
retFlag = false;
break;
}
totalBytesWritten += numBytesWritten;
} while (totalBytesWritten < command.length() );
::FlushFileBuffers(hFile);
// Read in the response
if ( retFlag ) {
Sleep(1000);
while ( !finished ) {
UInt32 numBytesRead = 0;
buffer[0] = '\0';
if ( !::ReadFile(hFile, buffer, BUFFER_LENGTH, &numBytesRead, &m_ReadSync ) )
{
if ( ( error = ::GetLastError() ) == ERROR_IO_PENDING )
{
error = 0;
if ( !::GetOverlappedResult( hFile, &m_ReadSync, &numBytesRead, true ) )
{
error = ::GetLastError();
}
}
}
// We read something
if ( numBytesRead > 0 ) {
buffer[numBytesRead] = '\0';
response += buffer;
} else { // We timed out so just drop out
finished = true;
}
}
}
// Remove the command from what we read in the response
if ( response.find(command) != std::string::npos ) {
response = response.substr(command.length());
}
if ( hFile != INVALID_HANDLE_VALUE ) {
::CloseHandle( hFile );
}
return retFlag;
}
および SerialATDT.h
#ifndef SERIALATDT_H
#define SERIALATDT_H
#pragma once
#include "SgiString.h"
#include "FileSystem.h"
using namespace sgi;
class SerialATDT
{
private:
String mComPortIdentifier;
bool getComPort(HANDLE *hFile);
String getComPortId();
public:
SerialATDT(String ident);
~SerialATDT(void);
bool sendCommand(String command, String &response);
};
#endif