IntelliPort






4.74/5 (57投票s)
您可以使用 IntelliPort 通过串行端口将大文件从一台计算机传输到便携式计算机,而无需设置便携式计算机的网络连接。
引言
正如您所知,从 Windows 7 开始,Microsoft 就已停止开发 HyperTerminal。IntelliPort 是一款程序,您可以使用它通过直通电缆或以太网连接到其他计算机。IntelliPort 会记录在连接的另一端计算机之间传递的消息。因此,在设置和使用调制解调器时,它可以作为宝贵的故障排除工具。为了确保您的调制解调器连接正确或查看调制解调器的设置,您可以通过 IntelliPort 发送命令并检查结果。IntelliPort 具有滚动功能,可让您查看已滚出屏幕的已接收文本。您可以使用 IntelliPort 通过串行端口将大文件从一台计算机传输到便携式计算机,而无需设置便携式计算机的网络连接。IntelliPort 被设计为易于使用的工具,并不旨在取代市场上其他功能齐全的工具。您可以使用 IntelliPort 执行上述特定任务,但不要尝试使用 IntelliPort 来满足更复杂的通信需求。
背景
此 MFC 应用程序的主要目的是通过串行端口或 UDP 套接字从嵌入式系统(如 EFTPOS 设备)获取日志,并且它基于 PJ Naughter 的 CSerialPort
和 CWSocket
类。对于不知道的人来说,EFTPOS 是无需携带现金即可支付商品或服务费用的方法。在进行购买时,EFTPOS 客户将 EFTPOS 卡交给收银员,收银员将其插入现场的 EFTPOS 机。当 EFTPOS 客户通过签名或安全 PIN 码确认购买时,EFTPOS 设备会通过电子方式联系商店的银行进行交易。还会向客户的银行发送一条消息。除非 EFTPOS 交易有理由不完成,否则资金将在两个账户之间转移。EFTPOS 交易仅需几秒钟。在 EFTPOS 客户将商品装袋之前,EFTPOS 交易就会完成。EFTPOS 交易的确认会发送到商店,并以打印的 EFTPOS 交易记录的形式传递给客户。
如何开始?
首先,配置 MFC 应用程序以从串行端口或 TCP/UDP 套接字连接获取日志。请检查 CConfigureDlg
类以获取实现细节。
接下来,您现在可以连接/断开与数据源的连接。请检查 OnOpenSerialPort
和 OnCloseSerialPort
函数以获取实现细节。
void CMainFrame::OnOpenSerialPort()
{
try
{
CString strFormat, strMessage;
switch (theApp.m_nConnection)
{
case 0:
{
CString strFullPortName;
strFullPortName.Format(_T("\\\\.\\%s"), static_cast<LPCWSTR>(theApp.m_strSerialName));
m_pSerialPort.Open(
strFullPortName,
theApp.m_nBaudRate,
(CSerialPort::Parity) theApp.m_nParity,
(BYTE)theApp.m_nDataBits,
(CSerialPort::StopBits) theApp.m_nStopBits,
(CSerialPort::FlowControl) theApp.m_nFlowControl,
FALSE);
if (m_pSerialPort.IsOpen())
{
m_nThreadRunning = true;
m_hSerialPortThread = CreateThread(nullptr, 0, SerialPortThreadFunc, this, 0, &m_nSerialPortThreadID);
strFormat.LoadString(IDS_SERIAL_PORT_OPENED);
strMessage.Format(strFormat, static_cast<LPCWSTR>(theApp.m_strSerialName));
SetCaptionBarText(strMessage);
}
break;
}
case 1:
case 2:
{
CString strServerIP = theApp.m_strServerIP;
UINT nServerPort = theApp.m_nServerPort;
CString strClientIP = theApp.m_strClientIP;
UINT nClientPort = theApp.m_nClientPort;
if (theApp.m_nConnection == 1) // TCP Socket
{
if (theApp.m_nSocketType == 1) // Client
{
m_pSocket.CreateAndConnect(strServerIP, nServerPort);
}
else // TCP Server
{
m_pSocket.SetBindAddress(strClientIP);
m_pSocket.CreateAndBind(nClientPort, SOCK_STREAM, AF_INET);
m_dlgIncoming.ShowWindow(SW_SHOW);
m_dlgIncoming.CenterWindow(this);
m_dlgIncoming.Invalidate();
m_dlgIncoming.UpdateWindow();
m_pSocket.Listen();
m_pSocket.Accept(m_pIncomming);
m_dlgIncoming.ShowWindow(SW_HIDE);
}
}
else // UDP Socket
{
m_pSocket.SetBindAddress(strClientIP);
m_pSocket.CreateAndBind(nClientPort, SOCK_DGRAM, AF_INET);
strServerIP = strClientIP;
nServerPort = nClientPort;
}
if (m_pSocket.IsCreated())
{
m_nThreadRunning = true;
m_hSocketThread = CreateThread(nullptr, 0, SocketThreadFunc, this, 0, &m_nSocketTreadID);
strFormat.LoadString(IDS_SOCKET_CREATED);
strMessage.Format(strFormat, ((theApp.m_nConnection == 1) ? _T("TCP") : _T("UDP")), static_cast<LPCWSTR>(strServerIP), nServerPort);
SetCaptionBarText(strMessage);
}
break;
}
}
}
catch (CSerialException& pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException.GetErrorMessage2(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
// pException->Delete();
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
catch (CWSocketException* pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException->GetErrorMessage(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
pException->Delete();
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
}
void CMainFrame::OnCloseSerialPort()
{
if (m_nThreadRunning)
{
m_nThreadRunning = false;
DWORD nThreadCount = 0;
HANDLE hThreadArray[2] = { 0, 0 };
if (m_hSerialPortThread != nullptr)
{
hThreadArray[nThreadCount++] = m_hSerialPortThread;
}
if (m_hSocketThread != nullptr)
{
hThreadArray[nThreadCount++] = m_hSocketThread;
}
if (nThreadCount > 0)
{
WaitForMultipleObjects(nThreadCount, hThreadArray, TRUE, INFINITE);
}
}
try
{
CString strFormat, strMessage;
switch (theApp.m_nConnection)
{
case 0:
{
if (!m_pSerialPort.IsOpen())
{
strFormat.LoadString(IDS_SERIAL_PORT_CLOSED);
strMessage.Format(strFormat, static_cast<LPCWSTR>(theApp.m_strSerialName));
SetCaptionBarText(strMessage);
}
break;
}
case 1:
case 2:
{
CString strServerIP = theApp.m_strServerIP;
UINT nServerPort = theApp.m_nServerPort;
CString strClientIP = theApp.m_strClientIP;
UINT nClientPort = theApp.m_nClientPort;
if (!m_pSocket.IsCreated())
{
if (theApp.m_nConnection == 1) // TCP Socket
{
if (theApp.m_nSocketType == 1) // Client
{
}
else // TCP Server
{
}
}
else // UDP Socket
{
strServerIP = strClientIP;
nServerPort = nClientPort;
}
strFormat.LoadString(IDS_SOCKET_CLOSED);
strMessage.Format(strFormat, ((theApp.m_nConnection == 1) ? _T("TCP") : _T("UDP")), static_cast<LPCWSTR>(strServerIP), nServerPort);
SetCaptionBarText(strMessage);
}
break;
}
}
}
catch (CSerialException& pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException.GetErrorMessage2(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
// pException->Delete();
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
catch (CWSocketException* pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException->GetErrorMessage(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
pException->Delete();
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
}
void CMainFrame::OnSendReceive()
{
try
{
CInputDlg dlgInput(this);
if (dlgInput.DoModal() == IDOK)
{
CStringA pBuffer(dlgInput.m_strSendData);
const int nLength = pBuffer.GetLength();
switch (theApp.m_nConnection)
{
case 0:
{
m_pMutualAccess.lock();
m_pSerialPort.Write(pBuffer.GetBufferSetLength(nLength), nLength);
m_pMutualAccess.unlock();
pBuffer.ReleaseBuffer();
break;
}
case 1:
case 2:
{
CString strServerIP = theApp.m_strServerIP;
const UINT nServerPort = theApp.m_nServerPort;
if (theApp.m_nConnection == 1) // TCP Socket
{
if (theApp.m_nSocketType == 1) // Client
{
if (m_pSocket.IsWritable(1000))
{
m_pMutualAccess.lock();
m_pSocket.Send(pBuffer.GetBufferSetLength(nLength), nLength, 0);
pBuffer.ReleaseBuffer();
m_pMutualAccess.unlock();
}
}
else
{
if (m_pIncomming.IsWritable(1000))
{
m_pMutualAccess.lock();
m_pIncomming.Send(pBuffer.GetBufferSetLength(nLength), nLength, 0);
pBuffer.ReleaseBuffer();
m_pMutualAccess.unlock();
}
}
}
else
{
if (m_pSocket.IsWritable(1000))
{
m_pMutualAccess.lock();
m_pSocket.SendTo(pBuffer.GetBufferSetLength(nLength), nLength, nServerPort, strServerIP, 0);
pBuffer.ReleaseBuffer();
m_pMutualAccess.unlock();
}
}
break;
}
}
}
}
catch (CSerialException& pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException.GetErrorMessage2(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
// pException->Delete();
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
catch (CWSocketException* pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException->GetErrorMessage(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
pException->Delete();
SetCaptionBarText(lpszErrorMessage);
m_nThreadRunning = false;
}
}
关注点
为了保持 GUI 响应,MFC 应用程序在单独的工作线程中进行读取
DWORD WINAPI SerialPortThreadFunc(LPVOID pParam)
{
COMSTAT status = { 0, };
char pBuffer[0x10000] = { 0, };
CMainFrame* pMainFrame = (CMainFrame*) pParam;
CRingBuffer& pRingBuffer = pMainFrame->m_pRingBuffer;
CSerialPort& pSerialPort = pMainFrame->m_pSerialPort;
std::mutex& pMutualAccess = pMainFrame->m_pMutualAccess;
while (pMainFrame->m_nThreadRunning)
{
try
{
memset(&status, 0, sizeof(status));
pSerialPort.GetStatus(status);
if (status.cbInQue > 0)
{
memset(pBuffer, 0, sizeof(pBuffer));
const int nLength = pSerialPort.Read(pBuffer, sizeof(pBuffer));
pMutualAccess.lock();
pRingBuffer.WriteBinary(pBuffer, nLength);
pMutualAccess.unlock();
}
else
{
::Sleep(10);
}
}
catch (CSerialException& pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException.GetErrorMessage2(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
// pException->Delete();
pMainFrame->SetCaptionBarText(lpszErrorMessage);
pMainFrame->m_nThreadRunning = false;
pSerialPort.Close();
}
}
pSerialPort.Close();
return 0;
}
DWORD WINAPI SocketThreadFunc(LPVOID pParam)
{
char pBuffer[0x10000] = { 0, };
CMainFrame* pMainFrame = (CMainFrame*) pParam;
CRingBuffer& pRingBuffer = pMainFrame->m_pRingBuffer;
CWSocket& pSocket = pMainFrame->m_pSocket;
CWSocket& pIncomming = pMainFrame->m_pIncomming;
std::mutex& pMutualAccess = pMainFrame->m_pMutualAccess;
bool bIsTCP = (theApp.m_nConnection == 1);
bool bIsClient = (theApp.m_nSocketType == 1);
CString strServerIP = theApp.m_strServerIP;
UINT nServerPort = theApp.m_nServerPort;
while (pMainFrame->m_nThreadRunning)
{
try
{
if (bIsTCP)
{
if (bIsClient)
{
if (pSocket.IsReadible(1000))
{
memset(pBuffer, 0, sizeof(pBuffer));
const int nLength = pSocket.Receive(pBuffer, sizeof(pBuffer), 0);
pMutualAccess.lock();
pRingBuffer.WriteBinary(pBuffer, nLength);
pMutualAccess.unlock();
}
else
{
::Sleep(10);
}
}
else
{
if (pIncomming.IsReadible(1000))
{
memset(pBuffer, 0, sizeof(pBuffer));
const int nLength = pIncomming.Receive(pBuffer, sizeof(pBuffer), 0);
pMutualAccess.lock();
pRingBuffer.WriteBinary(pBuffer, nLength);
pMutualAccess.unlock();
}
else
{
::Sleep(10);
}
}
}
else
{
if (pSocket.IsReadible(1000))
{
memset(pBuffer, 0, sizeof(pBuffer));
const int nLength = pSocket.ReceiveFrom(pBuffer, sizeof(pBuffer), strServerIP, nServerPort, 0);
pMutualAccess.lock();
pRingBuffer.WriteBinary(pBuffer, nLength);
pMutualAccess.unlock();
}
else
{
::Sleep(10);
}
}
}
catch (CWSocketException* pException)
{
const int nErrorLength = 0x100;
TCHAR lpszErrorMessage[nErrorLength] = { 0, };
pException->GetErrorMessage(lpszErrorMessage, nErrorLength);
TRACE(_T("%s\n"), lpszErrorMessage);
pException->Delete();
pMainFrame->SetCaptionBarText(lpszErrorMessage);
pMainFrame->m_nThreadRunning = false;
pIncomming.Close();
pSocket.Close();
}
}
pIncomming.Close();
pSocket.Close();
return 0;
}
结束语
IntelliPort 应用程序使用了许多在 Code Project 上发布的组件。非常感谢
- PJ Naughter 的
EnumSerialPorts
类 - Larry Antram 的
CRingBuffer
类 - PJ Naughter 的
CSerialPort
类 - PJ Naughter 的
CWSocket
类 - PJ Naughter 提供的
CVersionInfo
类
后续计划:我想尽快添加 Telnet 和 SSH 支持。
USB 端口取代串行端口的 3 个原因
最初的 IBM PC 包含 RS-232 串行端口,帮助推广了一种持续了数十年的数据传输格式。成千上万的工业设备,从流量计到其他类型的实验室仪器,传统上都将 RS-232 作为标准输入/输出端口。2004 年,大多数新 PC 已不再将 RS-232 串行接口作为其标准或基本配置的一部分。常见的 PC 桌面外围设备,如打印机、扫描仪和传真机,目前都采用 USB 端口。
通用串行总线(Universal Serial Bus,简称 USB)是一种外部端口,可在外部设备和计算机之间进行接口。最初的 IBM 个人计算机带有 RS-232 端口,用于连接键盘或鼠标等外部设备。如今,USB 端口正在取代 RS-232 端口。几乎任何设备都可以插入 USB 端口。这包括键盘、相机、鼠标、游戏杆、调制解调器、Zip 驱动器、软盘驱动器、打印机和扫描仪。
USB 为外设制造商提供了三个主要优势
- 兼容性:在过去几年里,串行端口几乎从 PC 上消失了,USB 端口取而代之。有数千种工业设备仍然使用串行端口,这一变化正导致问题。幸运的是,您可以购买便宜的适配器,允许您将 USB 端口连接到串行设备。这些设备效果很好,但它们只是权宜之计。
- 速度:USB 的平均数据传输速度是普通并行端口的十倍。它也比串行端口快。平均串行端口传输速率为 150 kbps;USB 端口最高可达 12 Mbps。USB 2 的速度是 USB 1 的四十倍,最高传输速率为 480 Mbps。它向后兼容 USB 1。这意味着,如果新计算机配备 USB 2,旧的 USB 设备仍然可以使用。当然,它们将以 USB 1 的速度运行,但它们仍然可以正常工作。
- 耐用性:USB 端口比串行端口更坚固。串行端口不坚固,细小的针脚很容易弯曲或折断。另一方面,USB 端口非常坚固。
如果您的系统有空闲的 USB 端口,您可以在 USB 和串行信号之间进行转换。USB 转串行适配器是一种小型设备,一端是 USB 连接器,另一端至少有一个(可能多个)串行连接器。
可以使用 USB 进行串行通信吗? 是的,您可以使用 USB-to-serial 适配器进行串行通信。此适配器允许通过串行端口通信的设备连接到计算机的 USB 端口。这是连接旧设备(如调制解调器、工业设备和某些类型的微控制器)到可能缺少原生串行端口的现代计算机的常见解决方案。
历史
- 版本 1.3 (2014 年 7 月 20 日):初始发布。
- 版本 1.5 (2014 年 7 月 28 日):修复了串行端口和 UDP 套接字日志功能的多个错误。
- 版本 1.6 (2015 年 8 月 30 日):修复了 Microsoft Windows 10 64 位版本的问题。
- 版本 1.7 (2019 年 4 月 3 日):性能和安全修复。
- 版本 1.8 (2019 年 6 月 15 日)
- 添加了罗马尼亚语翻译;
- 添加了 PJ Naughter 的
CInstanceChecker
类。
- 版本 1.9 (2019 年 7 月 27 日):修复了串行端口名称的错误:请参阅评论中提到的文章。
- 将源代码从 CodeProject 迁移到 GitLab (2019 年 12 月 7 日)。
- 版本 1.10 (2020 年 3 月 25 日):更改了主对话框和输入对话框的字体大小。
- 版本 1.11 (2020 年 5 月 9 日):添加了法语翻译,感谢 Stefan Gaftoniuc。
- 版本 1.12 (2020 年 6 月 6 日):添加了意大利语翻译,感谢 InterLingua。
- 版本 1.13 (2020 年 6 月 13 日):添加了德语翻译,感谢 InterLingua。
- 版本 1.14 (2020 年 6 月 20 日):添加了西班牙语翻译,感谢 InterLingua。
- 版本 1.15 (2020 年 7 月 19 日):添加了俄语翻译,感谢 InterLingua。
- 版本 1.16 (2020 年 7 月 31 日):添加了希腊语翻译,感谢 InterLingua。
- 版本 1.17 (2020 年 9 月 12 日):进行了改进并修复了错误,使 #IntelliPort 对您来说更好
- 将 PJ Naughter 的
CSerialPort
库更新到最新版本; - 将 PJ Naughter 的
CWSocket
库更新到最新版本。
- 将 PJ Naughter 的
- 版本 1.18 (2020 年 9 月 25 日):覆盖了
CEditCtrl
的 64K 限制。 - 版本 1.19 (2022 年 1 月 7 日):使用新的电子邮件地址更新了“关于”对话框。
- 版本 1.20 (2022 年 1 月 14 日):将 PJ Naughter 的
CVersionInfo
库更新到最新版本。 - 版本 1.21 (2022 年 2 月 4 日):更改了外部网站地址。
- 版本 1.22 (2022 年 2 月 11 日):修复了法语、意大利语、德语、西班牙语、俄语、希腊语翻译中的“文件打开”/“另存为”的关键错误。
- 版本 1.23 (2022 年 4 月 28 日):将 LICENSE 添加到安装文件夹。
- 版本 1.24 (2022 年 5 月 12 日):将所有行尾符转换为 Windows 格式 (CR LF)。
- 版本 1.25 (2022 年 5 月 19 日):将 PJ Naughter 的
CEnumerateSerial
库更新到最新版本。 - 版本 1.26 (2022 年 5 月 24 日):修复了次要错误。
- 版本 1.27 (2022 年 9 月 9 日):向 AboutBox 对话框添加了贡献者超链接。
- 2022 年 12 月 23 日:将源代码从 GitLab 迁移到 GitHub。
- 版本 1.28 (2023 年 1 月 20 日):删除了 PJ Naughter 的单实例类。
- 版本 1.29 (2023 年 1 月 23 日):将 PJ Naughter 的
CVersionInfo
库更新到最新版本。更新了代码,将 C++ 统一初始化用于所有变量声明。
- 将代码库中的
NULL
替换为nullptr
。
将代码库中的BOOL
替换为bool
。
这意味着该应用程序的最低要求现在是Microsoft Visual C++ 2010。 - 版本 1.30 (2023 年 4 月 2 日):实现了套接字和串行端口连接的错误处理。
- 版本 1.31 (2023 年 4 月 13 日):重新设计了线程同步并删除了所有 Sleep 调用。
- 版本 1.32 (2023 年 5 月 27 日):在“关于”对话框中添加了 GPLv3 通知。
- 版本 1.33 (2023 年 6 月 13 日):使应用程序的设置持久化(由 wvd_vegt 请求)。
- 版本 1.34 (2023 年 6 月 22 日):将 PJ Naughter 的
CEnumerateSerial
库更新到最新版本。 - 版本 1.35 (2023 年 7 月 22 日):用 PJ Naughter 的
CHLinkCtrl
库替换了旧的 CHyperlinkStatic 类。 - 版本 1.36 (2023 年 9 月 29 日)
- 切换到 Visual Studio Enterprise 2022(源代码做了一些更改);
- 更改了文章的下载链接。更新了“关于”对话框(电子邮件和网站)。
- 版本 1.37 (2024 年 1 月 3 日)
- 添加了社交媒体链接:Twitter、LinkedIn、Facebook 和 Instagram;
- 添加了 GitHub 仓库的 Issues、Discussions 和 Wiki 的快捷方式。
- 添加了“USB 端口取代串行端口的 3 个原因”部分。
- 版本 1.38 (2024 年 1 月 27 日):将 ReleaseNotes.html 和 SoftwareContentRegister.html 添加到 GitHub 仓库。
- 版本 1.39 (2024 年 2 月 21 日):将 MFC 应用程序的主题切换回原生 Windows。
- 版本 1.40.1 (2024 年 9 月 26 日)
- 改进了 UTF8 格式文本的加载/保存/发送/接收。
- 在“帮助”菜单中实现了 用户手册 选项。
- 在帮助菜单中添加了检查更新…选项。