Comm tests
Uwe Bonnes
bon at elektron.ikp.physik.tu-darmstadt.de
Wed Jun 8 15:57:11 CDT 2005
Changelog:
dlls/kernel/comm.c: WaitCommEvent
For now, disable this function, which doesn't work for nearly all
cases. Add test cases. However most added tests need a loopback plug
and need to be enabled in the Code
Tested with and without loopback plug on XP. Test on wine hangs forever without
dlls/kernel/comm.c: WaitCommEven disabled
--
Uwe Bonnes bon at elektron.ikp.physik.tu-darmstadt.de
Institut fuer Kernphysik Schlossgartenstrasse 9 64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------
Index: wine/dlls/kernel/comm.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/comm.c,v
retrieving revision 1.94
diff -u -r1.94 comm.c
--- wine/dlls/kernel/comm.c 16 May 2005 17:52:11 -0000 1.94
+++ wine/dlls/kernel/comm.c 8 Jun 2005 20:55:37 -0000
@@ -2022,7 +2022,9 @@
OVERLAPPED ov;
int ret;
- TRACE("(%p %p %p )\n",hFile, lpdwEvents,lpOverlapped);
+ FIXME("(%p %p %p )\n",hFile, lpdwEvents,lpOverlapped);
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
if(lpOverlapped)
return COMM_WaitCommEvent(hFile, lpdwEvents, lpOverlapped);
Index: wine/dlls/kernel/tests/comm.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/tests/comm.c,v
retrieving revision 1.4
diff -u -r1.4 comm.c
--- wine/dlls/kernel/tests/comm.c 28 Jan 2005 14:02:52 -0000 1.4
+++ wine/dlls/kernel/tests/comm.c 8 Jun 2005 20:55:41 -0000
@@ -1,6 +1,7 @@
/* Unit test suite for comm functions
*
* Copyright 2003 Kevin Groeneveld
+ * 2005 Uwe Bonnes
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,6 +24,26 @@
#include "winbase.h"
#include "winnls.h"
+#define TIMEOUT 1000 /* one second for Timeouts*/
+#define SLOWBAUD 150
+#define FASTBAUD 115200
+#define TIMEDELTA 100 /* 100 ms uncertainty allowed */
+
+/* Define the appropriate LOOPBACK(s) TRUE if you have a Loopback cable with
+ * the mentioned shorts connected to your Serial port
+ */
+#define LOOPBACK_TXD_RXD FALSE /* Sub-D 9: Short 2-3 */
+#define LOOPBACK_CTS_RTS FALSE /* Sub-D 9: Short 7-8 */
+#define LOOPBACK_DTR_DSR FALSE /* Sub-D 9: Short 4-6 */
+#define LOOPBACK_DTR_RING FALSE /* Sub-D 9: Short 4-9 */
+#define LOOPBACK_DTR_DCD FALSE /* Sub-D 9: Short 4-1 */
+
+/* SETBREAK seems to give instable results, MSDN mentions possible problems
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcedebug5/\
+ * html/wce50conSerialPortDriverTest.asp
+ * but perhaps it's only a programming error, that causes it... */
+#define BROKEN_SETBREAK TRUE
+
typedef struct
{
char string[100];
@@ -634,7 +655,881 @@
}
}
+static HANDLE test_OpenComm(BOOL doOverlap)
+{
+ HANDLE hcom;
+ char port_name[] = "COMx";
+ static BOOL shown = FALSE;
+
+ /* Try to find a port */
+ for(port_name[3] = '1'; port_name[3] <= '9'; port_name[3]++)
+ {
+ hcom = CreateFile( port_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ (doOverlap)?FILE_FLAG_OVERLAPPED:0, NULL );
+ if (hcom != INVALID_HANDLE_VALUE)
+ break;
+ }
+ if(!shown)
+ {
+ if (hcom == INVALID_HANDLE_VALUE)
+ trace("Could not find a valid COM port. Skipping test_ReadTimeOut\n");
+ else
+ trace("Found Com port %s. Connected devices may disturbe results\n", port_name);
+ //shown = TRUE;
+ }
+ return hcom;
+}
+
+static void test_GetModemStatus(HANDLE hcom)
+{
+ DWORD ModemStat;
+
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ trace("GetCommModemStatus returned 0x%08lx->%s%s%s%s\n", ModemStat,
+ (ModemStat &MS_RLSD_ON)?"MS_RLSD_ON ":"",
+ (ModemStat &MS_RING_ON)?"MS_RING_ON ":"",
+ (ModemStat &MS_DSR_ON)?"MS_DSR_ON ":"",
+ (ModemStat &MS_CTS_ON)?"MS_CTS_ON ":"");
+}
+
+/* When we don't write anything, Read should time out even on a loopbacked port */
+static void test_ReadTimeOut(HANDLE hcom)
+{
+ DCB dcb;
+ COMMTIMEOUTS timeouts;
+ char rbuf[32];
+ DWORD before, after, read, timediff, LastError;
+ BOOL res;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.BaudRate = FASTBAUD;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+
+ ZeroMemory( &timeouts, sizeof(timeouts));
+ timeouts.ReadTotalTimeoutConstant = TIMEOUT;
+ ok(SetCommTimeouts(hcom, &timeouts),"SetCommTimeouts failed\n");
+
+ before = GetTickCount();
+ res = ReadFile(hcom, rbuf, sizeof(rbuf), &read, NULL);
+ LastError = GetLastError();
+ after = GetTickCount();
+ todo_wine ok( res == TRUE, "A timed-out read should return TRUE\n");
+ todo_wine ok( LastError == NO_ERROR, "A timed-out read is not an error\n");
+ timediff = after - before;
+ ok( timediff > TIMEOUT>>2 && timediff < TIMEOUT *2,
+ "Unexpected TimeOut %ld, expected %d\n", timediff, TIMEOUT);
+}
+
+static void test_waittxempty(HANDLE hcom)
+{
+ DCB dcb;
+ COMMTIMEOUTS timeouts;
+ char tbuf[]="Some Characters\n";
+ DWORD before, after, written, timediff, evtmask = 0;
+ BOOL res_write, res;
+ DWORD baud = SLOWBAUD;
+
+ /* set a low baud rate to have ample time*/
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.BaudRate = baud;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+
+ ZeroMemory( &timeouts, sizeof(timeouts));
+ timeouts.ReadTotalTimeoutConstant = TIMEOUT;
+ ok(SetCommTimeouts(hcom, &timeouts),"SetCommTimeouts failed\n");
+
+ ok(SetupComm(hcom,1024,1024),"SetUpComm failed\n");
+ ok(SetCommMask(hcom, EV_TXEMPTY), "SetCommMask failed\n");
+
+ before = GetTickCount();
+ res_write=WriteFile(hcom, tbuf, sizeof(tbuf), &written, NULL);
+ after = GetTickCount();
+ ok(res_write == TRUE, "WriteFile failed\n");
+ ok(written == sizeof(tbuf),
+ "WriteFile: Unexpected write_size %ld , expected %d\n", written, sizeof(tbuf));
+
+ trace("WriteFile succeeded, took %ld ms to write %d Bytes at %ld Baud\n",
+ after - before, sizeof(tbuf), baud);
+
+ before = GetTickCount();
+ res = WaitCommEvent(hcom, &evtmask, NULL);
+ after = GetTickCount();
+
+ todo_wine ok(res == TRUE, "WaitCommEvent failed\n");
+ todo_wine ok(evtmask == EV_TXEMPTY,
+ "WaitCommEvent: Unexpected EvtMask 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_TXEMPTY);
+
+ timediff = after - before;
+
+ trace("WaitCommEvent for EV_TXEMPTY took %ld ms\n", timediff);
+ /* 050604: This shows a difference between XP (tested with mingw compiled crosstest):
+ XP returns Writefile only after everything went out of the Serial port,
+ while wine returns immedate.
+ Thus on XP, WaintCommEvent after setting the CommMask for EV_TXEMPTY
+ nearly return immediate,
+ while on wine the most time is spent here
+ */
+
+}
+
+/* A new open handle should not return error or have bytes in the Queues */
+static void test_ClearCommErrors(HANDLE hcom)
+{
+ DWORD errors;
+ COMSTAT lpStat;
+
+ ok(ClearCommError(hcom, &errors, &lpStat), "ClearCommError failed\n");
+ ok(lpStat.cbInQue == 0, "Unexpected %ld Bytes in InQueue\n", lpStat.cbInQue);
+ ok(lpStat.cbOutQue == 0, "Unexpected %ld Bytes in OutQueue\n", lpStat.cbOutQue);
+ ok(errors == 0, "ClearCommErrors: Unexpected error 0x%08lx\n", errors);
+}
+
+/**/
+static void test_LoopbackRead(HANDLE hcom)
+{
+ DCB dcb;
+ COMMTIMEOUTS timeouts;
+ char rbuf[32];
+ DWORD before, after, diff, read, written, evtmask=0;
+ BOOL res;
+ char tbuf[]="Some Characters\n";
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.BaudRate = FASTBAUD;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+
+ ZeroMemory( &timeouts, sizeof(timeouts));
+ timeouts.ReadTotalTimeoutConstant = TIMEOUT;
+ ok(SetCommTimeouts(hcom, &timeouts),"SetCommTimeouts failed\n");
+
+ ok(SetCommMask(hcom, EV_TXEMPTY), "SetCommMask failed\n");
+
+ ok(WriteFile(hcom,tbuf,sizeof(tbuf),&written, NULL), "WriteFile failed\n");
+ ok(written == sizeof(tbuf),"WriteFile %ld bytes written, expected %d\n",
+ written, sizeof(tbuf));
+
+ /* make sure all bytes are written, so Readfile will succeed in one call*/
+ todo_wine ok(WaitCommEvent(hcom, &evtmask, NULL), "WaitCommEvent failed\n");
+ todo_wine ok(evtmask == EV_TXEMPTY,
+ "WaitCommEvent: Unexpected EvtMask 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_TXEMPTY);
+
+ read=0;
+ todo_wine ok(ReadFile(hcom, rbuf, sizeof(rbuf), &read, NULL), "Readfile failed\n");
+ todo_wine ok(read == sizeof(tbuf),"ReadFile read %ld bytes, expected %d\n", read, sizeof(tbuf));
+
+ /* Now do the same withe a slow Baud rate.
+ On XP, nothing should change, as WriteFile only returns
+ after all Bytes have gone to the physical line
+ */
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.BaudRate = 9600;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+
+ ok(SetCommMask(hcom, EV_RXCHAR), "SetCommMask failed\n");
+ ok(WriteFile(hcom,tbuf,sizeof(tbuf),&written, NULL), "WriteFile failed\n");
+ ok(written == sizeof(tbuf),"WriteFile %ld bytes written, expected %d\n",
+ written, sizeof(tbuf));
+
+ todo_wine ok(WaitCommEvent(hcom, &evtmask, NULL), "WaitCommEvent failed\n");
+ todo_wine ok(evtmask == EV_RXCHAR, "WaitCommEvent: Unexpected EvtMask 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_RXCHAR);
+
+ before = GetTickCount();
+ res = ReadFile(hcom, rbuf, sizeof(rbuf), &read, NULL);
+ after = GetTickCount();
+ todo_wine ok(res, "Readfile failed\n");
+ todo_wine ok(read == sizeof(tbuf),"ReadFile read %ld bytes, expected %d\n", read, sizeof(tbuf));
+ diff = after - before;
+ trace("Readfile for %d chars with %d avail took %ld ms\n",
+ sizeof(rbuf), sizeof(tbuf), diff);
+ ok( (diff > TIMEOUT - TIMEDELTA) && (diff < TIMEOUT + TIMEDELTA),
+ "Timedout Wait took %ld ms, expected around %d\n", diff, TIMEOUT);
+}
+
+static void test_LoopbackCtsRts(HANDLE hcom)
+{
+ DWORD ModemStat, defaultStat;
+ DCB dcb;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fRtsControl == RTS_CONTROL_HANDSHAKE)
+ {
+ trace("RTS_CONTROL_HANDSHAKE is set, so don't manipulate RTS\n");
+ return;
+ }
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ /* XP returns some values in the low nibble, so mask them out*/
+ defaultStat &= MS_CTS_ON|MS_DSR_ON|MS_RING_ON|MS_RLSD_ON;
+ if(defaultStat & MS_CTS_ON)
+ {
+ ok(EscapeCommFunction(hcom, CLRRTS), "EscapeCommFunction failed to clear RTS\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_CTS_ON) == 0, "CTS didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat & ~MS_CTS_ON));
+ ok(EscapeCommFunction(hcom, SETRTS), "EscapeCommFunction failed to clear RTS\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore CTS: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+ else
+ {
+ ok(EscapeCommFunction(hcom, SETRTS), "EscapeCommFunction failed to set RTS\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ todo_wine ok ((ModemStat & MS_CTS_ON) == MS_CTS_ON,
+ "CTS didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat | MS_CTS_ON));
+ ok(EscapeCommFunction(hcom, CLRRTS), "EscapeCommFunction failed to clear RTS\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore CTS: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+}
+
+static void test_LoopbackDtrDcd(HANDLE hcom)
+{
+ DWORD ModemStat, defaultStat;
+ DCB dcb;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_HANDSHAKE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ /* XP returns some values in the low nibble, so mask them out*/
+ defaultStat &= MS_CTS_ON|MS_DSR_ON|MS_RING_ON|MS_RLSD_ON;
+ if(defaultStat & MS_RLSD_ON)
+ {
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_RLSD_ON) == 0, "RLSD didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat & ~MS_RLSD_ON));
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore RLSD: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+ else
+ {
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ todo_wine ok ((ModemStat & MS_RLSD_ON) == MS_RLSD_ON,
+ "RLSD didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat | MS_RLSD_ON));
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore RLSD: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+}
+
+static void test_LoopbackDtrDsr(HANDLE hcom)
+{
+ DWORD ModemStat, defaultStat;
+ DCB dcb;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_DISABLE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ /* XP returns some values in the low nibble, so mask them out*/
+ defaultStat &= MS_CTS_ON|MS_DSR_ON|MS_RING_ON|MS_RLSD_ON;
+ if(defaultStat & MS_DSR_ON)
+ {
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_DSR_ON) == 0, "CTS didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat & ~MS_DSR_ON));
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ todo_wine ok (ModemStat == defaultStat, "Failed to restore DSR: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+ else
+ {
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ todo_wine ok ((ModemStat & MS_DSR_ON) == MS_DSR_ON,
+ "CTS didn't react: 0x%04lx,expected 0x%04lx\n",
+ ModemStat, (defaultStat | MS_DSR_ON));
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore DSR: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+}
+
+static void test_LoopbackDtrRing(HANDLE hcom)
+{
+ DWORD ModemStat, defaultStat;
+ DCB dcb;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_HANDSHAKE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ /* XP returns some values in the low nibble, so mask them out*/
+ defaultStat &= MS_CTS_ON|MS_DSR_ON|MS_RING_ON|MS_RLSD_ON;
+ if(defaultStat & MS_RING_ON)
+ {
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_RING_ON) == 0, "RING didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat & ~MS_RING_ON));
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore RING: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+ else
+ {
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ todo_wine ok ((ModemStat & MS_RING_ON) == MS_RING_ON,
+ "RING didn't react: 0x%04lx,expected 0x%04lx\n",
+ ModemStat, (defaultStat | MS_RING_ON));
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore RING: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+}
+
+/*
+ Set up a WaitCommEvent for anything in the receive buffer,
+ then write to TX to put a character
+ into the RX buffer via the Loopback
+*/
+
+static void test_WaitRx(HANDLE hcom)
+{
+ OVERLAPPED overlapped, overlapped_w;
+ HANDLE hComPortEvent, hComWriteEvent;
+ DWORD before, after, after1, diff, success_wait = FALSE, success_write;
+ DWORD err_wait, err_write, written, evtmask=0;
+
+ ok(SetCommMask(hcom, EV_RXCHAR), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+
+ ok((hComWriteEvent = CreateEvent( NULL, TRUE, FALSE, NULL )) !=0,
+ "CreateEvent res 0x%08lx\n",
+ GetLastError());
+ ZeroMemory( &overlapped_w, sizeof(overlapped_w));
+ overlapped_w.hEvent = hComWriteEvent;
+
+ before = GetTickCount();
+ todo_wine {success_wait = WaitCommEvent(hcom, &evtmask, &overlapped);}
+ err_wait = GetLastError();
+ after = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success_wait, err_wait, evtmask);
+ todo_wine ok(success_wait || err_wait == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+
+ success_write= WriteFile(hcom, "X", 1, &written, &overlapped_w);
+ err_write = GetLastError();
+ ok(success_write || err_write == ERROR_IO_PENDING,
+ "overlapped WriteFile failed, err 0x%08lx\n",
+ err_write);
+
+ if (!success_write && (err_write == ERROR_IO_PENDING)) {
+ success_write = WaitForSingleObjectEx(hComWriteEvent, TIMEOUT, TRUE);
+ err_write = GetLastError();
+ ok(success_write == WAIT_OBJECT_0, "WaitForSingleObjectEx, res 0x%08lx, err 0x%08lx\n",
+ success_write, err_write);
+ }
+ Sleep(TIMEOUT >>1);
+ success_write = GetOverlappedResult(hcom, &overlapped_w, &written, FALSE);
+ err_write = GetLastError();
+
+ trace("Write after Wait res 0x%08lx err 0x%08lx\n",success_write, err_write);
+ ok(success_write && written ==1, "Write after Wait res 0x%08lx err 0x%08lx\n",
+ success_write, err_write);
+
+ if (!success_wait && (err_wait == ERROR_IO_PENDING)) {
+ success_wait = WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE);
+ err_wait = GetLastError();
+ ok(success_wait == WAIT_OBJECT_0, "wait hComPortEvent, res 0x%08lx, err 0x%08lx\n",
+ success_wait, err_wait);
+ }
+ success_wait = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err_wait = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success_wait, err_wait, evtmask, after-before, after1-before);
+
+ todo_wine ok(evtmask & EV_RXCHAR, "Detect EV_RXCHAR: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_RXCHAR);
+ diff = after1 - before;
+ ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+}
+
+/* Change the controling line after the given timeout to the given state
+ By the loopback, this should trigger the WaitCommEvent
+*/
+static DWORD CALLBACK toggle_ctlLine(LPVOID arg)
+{
+ DWORD *args = (DWORD *) arg;
+ DWORD timeout = args[0];
+ DWORD ctl = args[1];
+ HANDLE hcom = (HANDLE) args[2];
+ HANDLE hComPortEvent = (HANDLE) args[3];
+ DWORD success, err;
+
+ trace("toggle_ctlLine timeout %ld clt 0x%08lx handle 0x%08lx\n",
+ args[0], args[1], args[2]);
+ Sleep(timeout);
+ ok(EscapeCommFunction(hcom, ctl),"EscapeCommFunction 0x%08lx failed\n", ctl);
+ trace("toggle_ctline done\n");
+ success = WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE);
+ err = GetLastError();
+ trace("toggle_ctline WaitForSingleObjectEx res 0x%08lx err 0x%08lx\n",
+ success, err);
+ return 0;
+}
+
+static void test_WaitCts(HANDLE hcom)
+{
+ DCB dcb;
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[4], defaultStat;
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == RTS_CONTROL_DISABLE)
+ {
+ trace("RTS_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ args[0]= TIMEOUT >>1;
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_CTS_ON)
+ args[1] = CLRRTS;
+ else
+ args[1] = SETRTS;
+ args[2]=(DWORD) hcom;
+
+ trace("test_WaitCts timeout %ld clt 0x%08lx handle 0x%08lx\n",args[0], args[1], args[2]);
+
+ ok(SetCommMask(hcom, EV_CTS), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ args[3] = (DWORD) hComPortEvent;
+ alarmThread = CreateThread(NULL, 0, toggle_ctlLine, (void *) &args, 0, &alarmThreadId);
+ Sleep(100);
+ trace("Thread created\n");
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ todo_wine ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+ if (!success && (err == ERROR_IO_PENDING))
+ todo_wine ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "wait hComPortEvent failed\n");
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ todo_wine ok(evtmask & EV_CTS, "Failed to detect EV_CTS: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_CTS);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_CTS_ON)
+ todo_wine ok((evtmask & MS_CTS_ON) == 0,"CTS didn't change state!\n");
+ else
+ todo_wine ok((evtmask & MS_CTS_ON), "CTS didn't change state!\n");
+
+ diff = after1 - before;
+ todo_wine ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+ /*restore RTS Settings*/
+ if(defaultStat & MS_CTS_ON)
+ args[1] = SETRTS;
+ else
+ args[1] = CLRRTS;
+}
+
+static void test_WaitDsr(HANDLE hcom)
+{
+ DCB dcb;
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[3], defaultStat;
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_DISABLE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ args[0]= TIMEOUT >>1;
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_DSR_ON)
+ args[1] = CLRDTR;
+ else
+ args[1] = SETDTR;
+ args[2]=(DWORD) hcom;
+
+ trace("test_WaitDsr timeout %ld clt 0x%08lx handle 0x%08lx\n",args[0], args[1], args[2]);
+
+ ok(SetCommMask(hcom, EV_DSR), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ alarmThread = CreateThread(NULL, 0, toggle_ctlLine, (void *) &args, 0, &alarmThreadId);
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ todo_wine ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+ if (!success && (err == ERROR_IO_PENDING))
+ todo_wine ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "wait hComPortEvent failed\n");
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ todo_wine ok(evtmask & EV_DSR, "Failed to detect EV_DSR: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_CTS);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_DSR_ON)
+ todo_wine ok((evtmask & MS_DSR_ON) == 0,"DTR didn't change state!\n");
+ else
+ todo_wine ok((evtmask & MS_DSR_ON), "DTR didn't change state!\n");
+
+ diff = after1 - before;
+ todo_wine ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+ /*restore RTS Settings*/
+ if(defaultStat & MS_DSR_ON)
+ args[1] = SETDTR;
+ else
+ args[1] = CLRDTR;
+}
+
+static void test_WaitRing(HANDLE hcom)
+{
+ DCB dcb;
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[3], defaultStat;
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_DISABLE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ args[0]= TIMEOUT >>1;
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_RING_ON)
+ args[1] = CLRDTR;
+ else
+ args[1] = SETDTR;
+ args[2]=(DWORD) hcom;
+
+ trace("test_WaitRing timeout %ld clt 0x%08lx handle 0x%08lx\n",args[0], args[1], args[2]);
+
+ ok(SetCommMask(hcom, EV_RING), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ alarmThread = CreateThread(NULL, 0, toggle_ctlLine, (void *) &args, 0, &alarmThreadId);
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ todo_wine ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+ if (!success && (err == ERROR_IO_PENDING))
+ todo_wine ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "wait hComPortEvent failed\n");
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ todo_wine ok(evtmask & EV_RING, "Failed to detect EV_RING: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_CTS);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_RING_ON)
+ todo_wine ok((evtmask & MS_RING_ON) == 0,"DTR didn't change state!\n");
+ else
+ todo_wine ok((evtmask & MS_RING_ON), "DTR didn't change state!\n");
+
+ diff = after1 - before;
+ todo_wine ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+ /*restore RTS Settings*/
+ if(defaultStat & MS_RING_ON)
+ args[1] = SETDTR;
+ else
+ args[1] = CLRDTR;
+}
+
+static void test_WaitDcd(HANDLE hcom)
+{
+ DCB dcb;
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[3], defaultStat;
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_DISABLE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ args[0]= TIMEOUT >>1;
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_RLSD_ON)
+ args[1] = CLRDTR;
+ else
+ args[1] = SETDTR;
+ args[2]=(DWORD) hcom;
+
+ trace("test_WaitDcd timeout %ld clt 0x%08lx handle 0x%08lx\n",args[0], args[1], args[2]);
+
+ ok(SetCommMask(hcom, EV_RLSD), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ alarmThread = CreateThread(NULL, 0, toggle_ctlLine, (void *) &args, 0, &alarmThreadId);
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ todo_wine ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+ if (!success && (err == ERROR_IO_PENDING))
+ todo_wine ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "wait hComPortEvent failed\n");
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ todo_wine ok(evtmask & EV_RLSD, "Failed to detect EV_RLSD: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_CTS);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_RLSD_ON)
+ todo_wine ok((evtmask & MS_RLSD_ON) == 0,"DTR didn't change state!\n");
+ else
+ todo_wine ok((evtmask & MS_RLSD_ON), "DTR didn't change state!\n");
+
+ diff = after1 - before;
+ todo_wine ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+ /*restore RTS Settings*/
+ if(defaultStat & MS_RLSD_ON)
+ args[1] = SETDTR;
+ else
+ args[1] = CLRDTR;
+}
+
+/*
+ Set the Break status after the given timeout to the given state
+*/
+static void test_WaitBreak(HANDLE hcom)
+{
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ DWORD before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(SetCommMask(hcom, EV_BREAK), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+
+ Sleep(TIMEOUT >>1);
+
+ ok(SetCommBreak(hcom), "SetCommBreak err 0x%08lx\n", GetLastError());
+ trace("Break set\n");
+ ok(ClearCommError(hcom, &err, NULL),"ClearCommError err 0x%08lx\n", GetLastError());
+ ok((err & CE_BREAK)!= 0, "Break condition should be reported\n");
+ ok(ClearCommBreak(hcom), "ClearCommBreak err 0x%08lx\n", GetLastError());
+ trace("Break cleared\n");
+ ok(ClearCommError(hcom, &err, NULL),"ClearCommError err 0x%08lx\n", GetLastError());
+ ok((err & CE_BREAK)== 0, "Break should be cleared now\n");
+
+ if (!success && (err == ERROR_IO_PENDING))
+ ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "wait hComPortEvent res 0x%08lx\n", GetLastError());
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ ok(evtmask & EV_BREAK, "Failed to detect EV_BREAK: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_BREAK);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+
+ diff = after1 - before;
+ ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+}
+
START_TEST(comm)
{
- test_BuildCommDCB();
+ HANDLE hcom;
+ /* use variabel and not #define to compile the code */
+ BOOL loopback_txd_rxd = LOOPBACK_TXD_RXD;
+ BOOL loopback_rts_cts = LOOPBACK_CTS_RTS;
+ BOOL loopback_dtr_dsr = LOOPBACK_DTR_DSR;
+ BOOL loopback_dtr_ring = LOOPBACK_DTR_RING;
+ BOOL loopback_dtr_dcd = LOOPBACK_DTR_DCD;
+ BOOL broken_setbreak = BROKEN_SETBREAK;
+
+#if 1
+ test_BuildCommDCB();
+ hcom = test_OpenComm(FALSE);
+ if (hcom != INVALID_HANDLE_VALUE)
+ {
+ test_GetModemStatus(hcom);
+ test_ReadTimeOut(hcom);
+ test_waittxempty(hcom);
+ CloseHandle(hcom);
+ }
+ hcom = test_OpenComm(FALSE);
+ if (hcom != INVALID_HANDLE_VALUE)
+ {
+ test_ClearCommErrors(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_txd_rxd) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackRead(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_rts_cts) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackCtsRts(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_dsr) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackDtrDsr(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_ring) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackDtrRing(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_dcd) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackDtrDcd(hcom);
+ CloseHandle(hcom);
+ }
+#endif
+ if((loopback_txd_rxd) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitRx(hcom);
+ CloseHandle(hcom);
+ }
+#if 1
+ if((loopback_rts_cts) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitCts(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_dsr) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitDsr(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_ring) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitRing(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_dcd) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitDcd(hcom);
+ CloseHandle(hcom);
+ }
+ if(!broken_setbreak && (hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE)
+ {
+ test_WaitBreak(hcom);
+ CloseHandle(hcom);
+ }
+#endif
}
More information about the wine-patches
mailing list