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,