Open IvanGit opened 4 days ago
The OpenDirect
method is a workaround for drivers that don’t have physical layer, and usually ignore or bork on settings for parity, baud, data bits, etc.
if your PL2303GT is a TTL device with real signals, then it means the default baud rates, etc. as configured by the driver (or last used settings by another program) will be used.
Windows would also return an error code to these APIs. You can enable logging within the SerialPortStream and determine perhaps why it is failing this way. I can say all my tests with my USB TTL (Prolific, FTDI, 16550A, and others) work.
It might be setting a particular property that breaks. For this, you need to tell me the NuGet version, and best also to provide logs on which P/Inoke call that Win32API failed at.
I have installed RJCP.Diagnostics.Trace, added to app.config as described in 6.1.2, but file logfile.txt is not created.
I use .NET 4.7.2 and package
<package id="SerialPortStream" version="2.4.2" targetFramework="net472" />
When you use .NET Framework, it's simpler. Just create/modify your App.config
to include tracing. See https://github.com/jcurl/RJCP.DLL.SerialPortStream/blob/v2.x/test/SerialPortStreamTest/App.config as an example:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="false" indentsize="4"/>
<sources>
<source name="IO.Ports.SerialPortStream" switchValue="Verbose">
<listeners>
<add name="logListener"/>
<remove name="Default"/>
</listeners>
</source>
<source name="IO.Ports.SerialPortStream_ReadTo" switchValue="Verbose">
<listeners>
<add name="consoleListener"/>
<remove name="Default"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="logListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="trace.log" />
<add name="consoleListener" type="System.Diagnostics.ConsoleTraceListener" />
<add name="xmlListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData= "c:\logs\Traces.svclog" />
</sharedListeners>
</system.diagnostics>
</configuration>
I have modified file as described, but nothing. No logs, only IOException in runtime while Open().
The exception occurs in SerialPortStream::Open() => SerialPortStream::Open(true) => WinNativeSerial::SetPortSettings() => m_CommState.SetCommState() after Kernel32.SetCommState returned false.
In your case, the Kernel driver (PL2303GT) is throwing the error (SetCommState).
public void SetCommState()
{
if (!Kernel32.SetCommState(m_ComPortHandle, ref m_Dcb)) {
throw new IOException("Unable to set the serial port state", Marshal.GetLastWin32Error());
}
}
Could you try something like:
var sps = new SerialPortStream("COM1");
sps.GetPortSetttings();
sps.Open(); // Does this work?
sps.Close()
If that works, then one can try changing the properties and seeing which one the driver is complaining about.
var sps = new SerialPortStream("COM1");
sps.GetPortSettings();
sps.Baud=115200;
sps.Open();
sps.Close();
It could be that the driver is complaining about a particular field in the DCB.
It looks like 2.4.2 does not contain GetPortSettings() method.
But I have implemented via reflection.
var m_NativeSerial = sps.GetType().GetField("m_NativeSerial", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(sps);
var type = m_NativeSerial.GetType();
type.InvokeMember("Open", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, m_NativeSerial, null);
type.InvokeMember("GetPortSettings", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, m_NativeSerial, null);
type.InvokeMember("Close", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, m_NativeSerial, null);
sps.Open(); // Does this work?
sps.Close();
Yes, it works. Default baud rate is 115200.
var m_NativeSerial = sps.GetType().GetField("m_NativeSerial", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(sps);
var type = m_NativeSerial.GetType();
type.InvokeMember("Open", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, m_NativeSerial, null);
type.InvokeMember("GetPortSettings", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, m_NativeSerial, null);
type.InvokeMember("Close", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, m_NativeSerial, null);
sps.BaudRate = 115200;
sps.Open();
sps.Close();
It works too.
It works without stopbits:
(int BaudRate, int DataBits, Parity Parity, StopBits StopBits, int ReadTimeout, int WriteTimeout) =
(sps.BaudRate, sps.DataBits, sps.Parity, sps.StopBits, sps.ReadTimeout, sps.WriteTimeout);
var m_NativeSerial = sps.GetType().GetField("m_NativeSerial", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(sps);
var type = m_NativeSerial.GetType();
type.InvokeMember("Open", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, m_NativeSerial, null);
type.InvokeMember("GetPortSettings", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, m_NativeSerial, null);
type.InvokeMember("Close", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, m_NativeSerial, null);
(sps.BaudRate, sps.DataBits, sps.Parity, /*sps.StopBits,*/ sps.ReadTimeout, sps.WriteTimeout) =
(BaudRate, DataBits, Parity, /*StopBits,*/ ReadTimeout, WriteTimeout);
sps.Open();
Debug.Assert(sps.IsOpen);
The reason was in StopBits. RJCP.IO.Ports.Parity is compatible with System.IO.Ports.Parity, but RJCP.IO.Ports.StopBits is not compatible with System.IO.Ports.StopBits. Code was ported from SerialPort and stream setup before like
sps.StopBits = (RJCP.IO.Ports.StopBits)db_Int32Value;
I have created extension
public static class SerialPortStreamStopBitsConverter
{
private static readonly Dictionary<System.IO.Ports.StopBits, RJCP.IO.Ports.StopBits> _map = new Dictionary<System.IO.Ports.StopBits, RJCP.IO.Ports.StopBits>()
{
{ System.IO.Ports.StopBits.One, RJCP.IO.Ports.StopBits.One },
{ System.IO.Ports.StopBits.OnePointFive, RJCP.IO.Ports.StopBits.One5 },
{ System.IO.Ports.StopBits.Two, RJCP.IO.Ports.StopBits.Two }
};
public static RJCP.IO.Ports.StopBits ToStopBits(this System.IO.Ports.StopBits stopBits)
{
return _map.TryGetValue(stopBits, out var value) ? value : default;
}
}
and use it like
sps.StopBits = ((System.IO.Ports.StopBits)db_Int32Value).ToStopBits();
and now it works. Because One bit has been cast to One5 previously.
Great to see you got to the root cause. Are you sure you needed reflection? I looked at the 2.x branch and SerialPortStream
has public void GetPortSettings()
.
Yes, you are right. I have rechecked, reflection is not needed here. Now GetPortSettings() is appeared. No idea why it wasn't compiling before.
The error occurs when calling Open()
in RJCP.IO.Ports.Native.Windows.CommState.SetCommState() в C:\Users\jcurl\Documents\Programming\rjcp.base\framework\serialportstream\code\Native\Windows\CommState.cs:line66 in RJCP.IO.Ports.Native.WinNativeSerial.SetPortSettings() в C:\Users\jcurl\Documents\Programming\rjcp.base\framework\serialportstream\code\Native\WinNativeSerial.cs:line 803 in RJCP.IO.Ports.SerialPortStream.Open(Boolean setCommState) в C:\Users\jcurl\Documents\Programming\rjcp.base\framework\serialportstream\code\SerialPortStream.cs:line 313
The error disappears when calling OpenDirect().OS: Windows 10 64bit Port driver: Prolific PL2303GT USB Serial COM Port
Should I use OpenDirect instead of Open and what is the difference?