diff --git a/COREDLL/COREDLL.vcxproj b/COREDLL/COREDLL.vcxproj
index 1bb75cf..622c779 100644
--- a/COREDLL/COREDLL.vcxproj
+++ b/COREDLL/COREDLL.vcxproj
@@ -240,6 +240,7 @@
+
@@ -258,6 +259,7 @@
Create
Create
+
diff --git a/COREDLL/COREDLL.vcxproj.filters b/COREDLL/COREDLL.vcxproj.filters
index b8a0a98..db9da89 100644
--- a/COREDLL/COREDLL.vcxproj.filters
+++ b/COREDLL/COREDLL.vcxproj.filters
@@ -24,6 +24,9 @@
Header Files
+
+ Header Files
+
@@ -77,6 +80,9 @@
Source Files
+
+ Source Files
+
diff --git a/COREDLL/Exports.def b/COREDLL/Exports.def
index 5b139af..36295d3 100644
--- a/COREDLL/Exports.def
+++ b/COREDLL/Exports.def
@@ -31,7 +31,7 @@ EXPORTS
_wcsupr @232
vfwprintf=vfwprintf_WCECL @721
fflush @1122
- fgetws=fgetws_WCECL @1143
+ fgetws @1143
fputwc @1141
fputws @1144
_getstdfilex=_getstdfilex_WCECL @1100
diff --git a/COREDLL/other.cpp b/COREDLL/other.cpp
index 065ddee..0ac01ac 100644
--- a/COREDLL/other.cpp
+++ b/COREDLL/other.cpp
@@ -194,59 +194,14 @@ void CeLogSetZones(DWORD dwZoneUser, // User-defined zones
// wtf!?
}
-FILE* WINAPI _getstdfilex_WCECL(DWORD type)
-{
- switch (type)
- {
- case 0:
- return stdin;
- case 1:
- return stdout;
- case 2:
- return stderr;
- default:
- Assert32(FALSE);
- return NULL;
- }
-}
-
-BOOL WINAPI SetStdioPathW_WCECL(
- DWORD id,
- PWSTR pwszPath)
-{
- /* TODO: test */
- switch (id)
- {
- case 0:
- return (_wfreopen(pwszPath, L"r", stdin) != NULL);
- case 1:
- return (_wfreopen(pwszPath, L"w", stdout) != NULL);
- case 2:
- return (_wfreopen(pwszPath, L"w", stderr) != NULL);
- default:
- Assert32(FALSE);
- return NULL;
- }
-}
-
-BOOL WINAPI GetStdioPathW_WCECL(
- DWORD id,
- PWSTR pwszBuf,
- LPDWORD lpdwLen)
+void* _fileno_WCECL(FILE* file)
{
- /* TODO: test */
- FILE* filePtr = _getstdfilex_WCECL(id);
- HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(filePtr));
- if (GetFinalPathNameByHandleW(hFile, pwszBuf, *lpdwLen, 0) < *lpdwLen)
+ void* result = WceclTryGetOrAllocStdHandle(file);
+ if (result != NULL)
{
- return TRUE;
+ return result;
}
- return FALSE;
-}
-
-void* _fileno_WCECL(FILE* file)
-{
return (void*)_get_osfhandle(_fileno(file));
}
@@ -271,22 +226,6 @@ int WINAPI WideCharToMultiByte_WCECL(
lpUsedDefaultChar);
}
-wchar_t* fgetws_WCECL(wchar_t* w, int count, FILE* file)
-{
- wchar_t* result = fgetws(w, count, file);
- if (result == NULL &&
- file == stdin && count > 2)
- {
- result = w;
- wsprintf(w, L"");
- }
- else
- {
- return result;
- }
- return result;
-}
-
// Stubs
Stub(_chkstk_WCECL);
Stub(WaitForAPIReady);
diff --git a/COREDLL/stdafx.h b/COREDLL/stdafx.h
index 8f5c58a..b44d098 100644
--- a/COREDLL/stdafx.h
+++ b/COREDLL/stdafx.h
@@ -85,3 +85,5 @@ BOOL ProgramErrorDialog(LPCWSTR Text, BOOL YesNo);
VOID DisplayAssert32ErrorDialog(LPCWSTR ExpressionText, LPCWSTR Comment, BOOL ShowLastError);
DWORD GetBaseAddress(HANDLE pHandle);
HMODULE GetModule(HANDLE pHandle);
+
+#include "stdio_wcecl.h"
diff --git a/COREDLL/stdio_wcecl.cpp b/COREDLL/stdio_wcecl.cpp
new file mode 100644
index 0000000..4f63dcf
--- /dev/null
+++ b/COREDLL/stdio_wcecl.cpp
@@ -0,0 +1,414 @@
+#include "stdafx.h"
+#include
+#include
+
+static HANDLE ControlEvent = NULL;
+
+/* https://learn.microsoft.com/en-us/windows/console/clearing-the-screen */
+static BOOL WceclConsoleClearScreen(
+ HANDLE hDevice)
+{
+ CHAR_INFO charInfo;
+ COORD scrollTarget;
+ SMALL_RECT scrollRect;
+ CONSOLE_SCREEN_BUFFER_INFO screenInfo;
+
+ if (!GetConsoleScreenBufferInfo(hDevice, &screenInfo))
+ {
+ return FALSE;
+ }
+
+ charInfo.Char.UnicodeChar = L' ';
+ charInfo.Attributes = screenInfo.wAttributes;
+
+ scrollRect.Left = 0;
+ scrollRect.Top = 0;
+ scrollRect.Right = screenInfo.dwSize.X;
+ scrollRect.Bottom = screenInfo.dwSize.Y;
+
+ scrollTarget.X = 0;
+ scrollTarget.Y = -screenInfo.dwSize.Y;
+
+ if (ScrollConsoleScreenBufferW(
+ hDevice,
+ &scrollRect,
+ NULL,
+ scrollTarget,
+ &charInfo) == 0)
+ {
+ return FALSE;
+ }
+
+ screenInfo.dwCursorPosition.X = 0;
+ screenInfo.dwCursorPosition.Y = 0;
+
+ SetConsoleCursorPosition(hDevice, screenInfo.dwCursorPosition);
+}
+
+static DWORD GetWin32ConsoleModeFromWce(DWORD wceConsoleMode)
+{
+ DWORD out = 0;
+
+ if (wceConsoleMode & CECONSOLE_MODE_ECHO_INPUT)
+ {
+ out |= ENABLE_ECHO_INPUT;
+ }
+ if (wceConsoleMode & CECONSOLE_MODE_LINE_INPUT)
+ {
+ out |= ENABLE_LINE_INPUT;
+ }
+ if (wceConsoleMode & CECONSOLE_MODE_PROCESSED_OUTPUT)
+ {
+ out |= ENABLE_PROCESSED_OUTPUT;
+ }
+
+ return out;
+}
+
+static DWORD GetWceConsoleModeFromWin32(DWORD win32ConsoleMode)
+{
+ DWORD out = 0;
+
+ if (win32ConsoleMode & ENABLE_ECHO_INPUT)
+ {
+ out |= CECONSOLE_MODE_ECHO_INPUT;
+ }
+ if (win32ConsoleMode & ENABLE_LINE_INPUT)
+ {
+ out |= CECONSOLE_MODE_LINE_INPUT;
+ }
+ if (win32ConsoleMode & ENABLE_PROCESSED_OUTPUT)
+ {
+ out |= CECONSOLE_MODE_PROCESSED_OUTPUT;
+ }
+
+ return out;
+}
+
+static BOOL WceclConsoleSetMode(
+ HANDLE hDevice,
+ DWORD* lpInBuf,
+ DWORD nInBufSize)
+{
+ if (nInBufSize < sizeof(DWORD))
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+ return SetConsoleMode(
+ hDevice,
+ GetWin32ConsoleModeFromWce(*lpInBuf));
+}
+
+static BOOL WceclConsoleGetMode(
+ HANDLE hDevice,
+ DWORD* lpOutBuf,
+ DWORD nOutBufSize)
+{
+ DWORD win32ConsoleMode;
+ BOOL result;
+
+ if (nOutBufSize < sizeof(DWORD))
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+ result = GetConsoleMode(hDevice, &win32ConsoleMode);
+ if (!result)
+ {
+ return FALSE;
+ }
+ *((DWORD*)lpOutBuf) = GetWceConsoleModeFromWin32(win32ConsoleMode);
+ return TRUE;
+}
+
+static BOOL WceclConsoleSetTitle(
+ LPCSTR lpInBuf,
+ DWORD nInBufSize)
+{
+ BOOL result;
+
+ /* Create a copy of the buffer to not read garbage if the original buffer
+ is short. */
+ char* bufferCopy = new char[nInBufSize + 1];
+ bufferCopy[nInBufSize] = '\0';
+ memcpy(bufferCopy, lpInBuf, nInBufSize);
+
+ result = SetConsoleTitleA(bufferCopy);
+
+ delete[] bufferCopy;
+ return result;
+}
+
+static BOOL WceclConsoleGetTitle(
+ LPSTR lpOutBuf,
+ DWORD nOutBufSize)
+{
+ return GetConsoleTitleA(lpOutBuf, nOutBufSize);
+}
+
+
+static BOOL WceclConsoleGetRowsCols(
+ HANDLE hDevice,
+ PDWORD lpCols,
+ PDWORD lpRows,
+ DWORD nOutBufSize)
+{
+ CONSOLE_SCREEN_BUFFER_INFO screenInfo;
+ DWORD win32ConsoleMode;
+ BOOL result;
+
+ if (nOutBufSize < sizeof(DWORD))
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+
+ if (!GetConsoleScreenBufferInfo(hDevice, &screenInfo))
+ {
+ return FALSE;
+ }
+
+ if (lpRows != NULL)
+ {
+ *lpRows = screenInfo.dwSize.Y;
+ }
+ if (lpCols != NULL)
+ {
+ *lpCols = screenInfo.dwSize.X;
+ }
+ if (lpCols == NULL && lpRows == NULL)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL WceclConsoleSetControlHandler(PHANDLER_ROUTINE* pInBuffer, DWORD nInBufSize)
+{
+ if (nInBufSize < sizeof(PHANDLER_ROUTINE))
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+ return SetConsoleCtrlHandler(*pInBuffer, FALSE);
+}
+
+BOOL __stdcall WceclConsoleSignalControlEvent(DWORD ctrlType)
+{
+ if (ControlEvent != NULL)
+ {
+ SetEvent(ControlEvent);
+
+ /* TODO: what should this return? */
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL WceclConsoleSetControlEvent(HANDLE* pInBuffer, DWORD nInBufSize)
+{
+ if (nInBufSize < sizeof(HANDLE))
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+ ControlEvent = *pInBuffer;
+ return SetConsoleCtrlHandler(WceclConsoleSignalControlEvent, FALSE);
+}
+BOOL WceclConsoleIoControl(
+ HANDLE hDevice,
+ DWORD dwIoControlCode,
+ LPVOID lpInBuf,
+ DWORD nInBufSize,
+ LPVOID lpOutBuf,
+ DWORD nOutBufSize,
+ LPDWORD lpBytesReturned,
+ LPOVERLAPPED lpOverlapped)
+{
+ switch (dwIoControlCode)
+ {
+ case IOCTL_CONSOLE_SETMODE:
+ return WceclConsoleSetMode(hDevice, (DWORD*)lpInBuf, nInBufSize);
+ case IOCTL_CONSOLE_GETMODE:
+ return WceclConsoleGetMode(hDevice, (DWORD*)lpOutBuf, nOutBufSize);
+ case IOCTL_CONSOLE_SETTITLE:
+ return WceclConsoleSetTitle((LPCSTR)lpInBuf, nInBufSize);
+ case IOCTL_CONSOLE_GETTITLE:
+ return WceclConsoleGetTitle((LPSTR)lpOutBuf, nOutBufSize);
+ case IOCTL_CONSOLE_CLS:
+ return WceclConsoleClearScreen(hDevice);
+ case IOCTL_CONSOLE_FLUSHINPUT:
+ return FlushConsoleInputBuffer(hDevice);
+ case IOCTL_CONSOLE_GETSCREENROWS:
+ return WceclConsoleGetRowsCols(hDevice, NULL, (PDWORD)lpOutBuf, nOutBufSize);
+ case IOCTL_CONSOLE_GETSCREENCOLS:
+ return WceclConsoleGetRowsCols(hDevice, (PDWORD)lpOutBuf, NULL, nOutBufSize);
+ case IOCTL_CONSOLE_SETCONTROLCHANDLER:
+ return WceclConsoleSetControlHandler((PHANDLER_ROUTINE*)lpInBuf, nInBufSize);
+ case IOCTL_CONSOLE_SETCONTROLCEVENT:
+ return WceclConsoleSetControlEvent((PHANDLE)lpInBuf, nInBufSize);
+ }
+
+ return FALSE;
+}
+
+FILE* WINAPI _getstdfilex_WCECL(DWORD type)
+{
+ switch (type)
+ {
+ case 0:
+ return stdin;
+ case 1:
+ return stdout;
+ case 2:
+ return stderr;
+ default:
+ Assert32(FALSE);
+ return NULL;
+ }
+}
+
+BOOL WINAPI SetStdioPathW_WCECL(
+ DWORD id,
+ PWSTR pwszPath)
+{
+ /* TODO: test */
+ switch (id)
+ {
+ case 0:
+ return (_wfreopen(pwszPath, L"r", stdin) != NULL);
+ case 1:
+ return (_wfreopen(pwszPath, L"w", stdout) != NULL);
+ case 2:
+ return (_wfreopen(pwszPath, L"w", stderr) != NULL);
+ default:
+ Assert32(FALSE);
+ return NULL;
+ }
+}
+
+BOOL WINAPI GetStdioPathW_WCECL(
+ DWORD id,
+ PWSTR pwszBuf,
+ LPDWORD lpdwLen)
+{
+ /* TODO: test */
+ FILE* filePtr = _getstdfilex_WCECL(id);
+ HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(filePtr));
+ if (GetFinalPathNameByHandleW(hFile, pwszBuf, *lpdwLen, 0) < *lpdwLen)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static BOOL WceclTryGetStdHandle(FILE* file, PHANDLE handle)
+{
+ if (file == stdin)
+ {
+ *handle = GetStdHandle(STD_INPUT_HANDLE);
+ }
+ else if (file == stdout)
+ {
+ *handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ }
+ else if (file == stderr)
+ {
+ *handle = GetStdHandle(STD_ERROR_HANDLE);
+ }
+ else
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* This is only really executed, if the WinCE application has been converted to
+ Win32 GUI application (WinCE has no distinction between GUI and CUI subsystems) */
+static BOOL WceclAllocateStdio()
+{
+ HWND hWndConsole;
+ BOOL bConsoleAllocated;
+
+ HANDLE hOldStdIn, hOldStdOut, hOldStdErr;
+
+ hWndConsole = GetConsoleWindow();
+
+ hOldStdIn = GetStdHandle(STD_INPUT_HANDLE);
+ hOldStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ hOldStdErr = GetStdHandle(STD_ERROR_HANDLE);
+
+ if (hWndConsole == NULL)
+ {
+ AllocConsole();
+ hWndConsole = GetConsoleWindow();
+
+ if (hWndConsole == NULL)
+ {
+ return FALSE;
+ }
+
+ bConsoleAllocated = TRUE;
+ }
+ else
+ {
+ bConsoleAllocated = FALSE;
+ }
+
+ assert(hOldStdErr == NULL || hOldStdIn == NULL || hOldStdOut == NULL);
+
+ if (hOldStdIn == NULL)
+ {
+ if (freopen("CONIN$", "r", stdin) == NULL)
+ {
+ goto CLEANUP;
+ }
+ }
+ if (hOldStdOut == NULL)
+ {
+ if(freopen("CONOUT$", "w", stdout) == NULL)
+ {
+ goto CLEANUP;
+ }
+ }
+ if (hOldStdErr == NULL)
+ {
+ if(freopen("CONERR$", "w", stderr) == NULL)
+ {
+ goto CLEANUP;
+ }
+ }
+
+ return TRUE;
+CLEANUP:
+ if (bConsoleAllocated)
+ {
+ FreeConsole();
+ }
+ return FALSE;
+}
+
+/* It seems that CE programs launch a console only when it is about to be
+ used. */
+HANDLE WceclTryGetOrAllocStdHandle(FILE* file)
+{
+ HANDLE hFile;
+
+ if (WceclTryGetStdHandle(file, &hFile) == FALSE)
+ {
+ return NULL;
+ }
+
+ if (hFile == NULL)
+ {
+ if (WceclAllocateStdio() == FALSE)
+ {
+ return FALSE;
+ }
+ }
+
+ return hFile;
+}
\ No newline at end of file
diff --git a/COREDLL/stdio_wcecl.h b/COREDLL/stdio_wcecl.h
new file mode 100644
index 0000000..84d2680
--- /dev/null
+++ b/COREDLL/stdio_wcecl.h
@@ -0,0 +1,32 @@
+#pragma once
+#include "stdafx.h"
+
+#define FILE_DEVICE_CONSOLE 0x0102
+
+#define CECONSOLE_MODE_ECHO_INPUT 0x01
+#define CECONSOLE_MODE_LINE_INPUT 0x02
+#define CECONSOLE_MODE_PROCESSED_OUTPUT 0x04
+
+#define CONSOLE_CTL(i) ((FILE_DEVICE_CONSOLE<<16)|(i << 2))
+#define IOCTL_CONSOLE_SETMODE CONSOLE_CTL(1)
+#define IOCTL_CONSOLE_GETMODE CONSOLE_CTL(2)
+#define IOCTL_CONSOLE_SETTITLE CONSOLE_CTL(3)
+#define IOCTL_CONSOLE_GETTITLE CONSOLE_CTL(4)
+#define IOCTL_CONSOLE_CLS CONSOLE_CTL(5)
+#define IOCTL_CONSOLE_FLUSHINPUT CONSOLE_CTL(6)
+#define IOCTL_CONSOLE_GETSCREENROWS CONSOLE_CTL(7)
+#define IOCTL_CONSOLE_SETCONTROLCHANDLER CONSOLE_CTL(8)
+#define IOCTL_CONSOLE_GETSCREENCOLS CONSOLE_CTL(9)
+#define IOCTL_CONSOLE_SETCONTROLCEVENT CONSOLE_CTL(10)
+
+BOOL WceclConsoleIoControl(
+ HANDLE hDevice,
+ DWORD dwIoControlCode,
+ LPVOID lpInBuf,
+ DWORD nInBufSize,
+ LPVOID lpOutBuf,
+ DWORD nOutBufSize,
+ LPDWORD lpBytesReturned,
+ LPOVERLAPPED lpOverlapped);
+
+HANDLE WceclTryGetOrAllocStdHandle(FILE* file);
diff --git a/COREDLL/winbase_wcecl.cpp b/COREDLL/winbase_wcecl.cpp
index 2d2444e..983577c 100644
--- a/COREDLL/winbase_wcecl.cpp
+++ b/COREDLL/winbase_wcecl.cpp
@@ -487,6 +487,21 @@ BOOL WINAPI DeviceIoControl_WCECL(
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped)
{
+ USHORT type = ((dwIoControlCode & 0xFFFF0000) >> 16);
+
+ if (type == FILE_DEVICE_CONSOLE)
+ {
+ return WceclConsoleIoControl(
+ hDevice,
+ dwIoControlCode,
+ lpInBuf,
+ nInBufSize,
+ lpOutBuf,
+ nOutBufSize,
+ lpBytesReturned,
+ lpOverlapped);
+ }
+
auto result = DeviceIoControl(
hDevice,
dwIoControlCode,