前置き
kernel32.dllのCreateFile
でシリアル接続しているコードがあったので、勉強ついでにSystem.IO.Ports.SerialPort
に置き換えようとしていた
COMポートはcom0comで用意したCOM11,COM12の2ポートを用いる
SerialPort
では難なくテストができたのにCreateFile
ではなぜかポートが見つからない、という初歩的な問題が発生していた
結論
上記に則ってprefixを与えるだけ。初歩的ですね…
portName = "\\\\.\\COM11";
com0comのReadMeにもそんなようなことが書いてあるという
Q. The HyperTerminal test succeeds, but I get a failure when trying to open the port with CreateFile("CNCA0", ...). GetLastError() returns ERROR_FILE_NOT_FOUND.
A. You must prefix the port name with the special characters "\\.\". Try to open the port with CreateFile("\\\\.\\CNCA0", ...).
SerialPortの実装
特に上記のようなprefixなしにSerialPort
は接続できていたので多分内部的につけてくれているのだろうと思って確認すると、付けてくれている
ただしポート番号のみで判断しているのでCOM**という名前のポートしか扱えなくなっている模様
ポート名を自由に定義してる場合は面倒ですね
// SerialStream.Windows.cs
internal SerialStream(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, Handshake handshake,
bool dtrEnable, bool rtsEnable, bool discardNull, byte parityReplace)
{
ArgumentNullException.ThrowIfNull(portName);
if (!portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase) ||
!uint.TryParse(
#if NETCOREAPP
portName.AsSpan(3),
#else
portName.Substring(3),
#endif
out uint portNumber))
{
throw new ArgumentException(SR.Format(SR.Arg_InvalidSerialPort, portName), nameof(portName));
}
// Error checking done in SerialPort.
SafeFileHandle tempHandle = OpenPort(portNumber);
// 以下略
}
// SerialStream.Win32.cs
namespace System.IO.Ports
{
internal sealed partial class SerialStream
{
private static SafeFileHandle OpenPort(uint portNumber)
{
return Interop.Kernel32.CreateFile(
@"\\?\COM" + portNumber.ToString(CultureInfo.InvariantCulture),
Interop.Kernel32.GenericOperations.GENERIC_READ | Interop.Kernel32.GenericOperations.GENERIC_WRITE,
FileShare.None, // comm devices must be opened w/exclusive-access
FileMode.Open, // comm devices must use OPEN_EXISTING
Interop.Kernel32.FileOperations.FILE_FLAG_OVERLAPPED);
}
}
}