diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/os2/os2ses/os2.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/os2/os2ses/os2.c')
-rw-r--r-- | private/os2/os2ses/os2.c | 1750 |
1 files changed, 1750 insertions, 0 deletions
diff --git a/private/os2/os2ses/os2.c b/private/os2/os2ses/os2.c new file mode 100644 index 000000000..f69f05aa0 --- /dev/null +++ b/private/os2/os2ses/os2.c @@ -0,0 +1,1750 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + os2.c + +Abstract: + + This module contains the main of the session console process (OS2.EXE). + +Author: + + Avi Nathan (avin) 17-Jul-1991 + +Environment: + + User Mode Only + +Revision History: + +--*/ + +#include <stdio.h> +#include <io.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#define WIN32_ONLY +#include "os2ses.h" +#include "trans.h" +#include "os2res.h" +#include "os2win.h" +#include "conapi.h" +#ifdef PMNT +#define INCL_32BIT +#include "pmnt.h" +extern ULONG PMNTGetOurWindow(void); +extern ULONG PMSubprocSem32; +extern BOOLEAN Ow2WriteBackCloseEvent(); +extern APIRET DosSemClear(ULONG hsem); +#endif + +BOOLEAN fService = FALSE; // Are we running as a service ? +BOOLEAN fRootService = FALSE; // Directly invoked by the service + +/* + * External prototypes + */ + +#undef InitOs2SessionPort +DWORD InitOs2ssSessionPort(); + +DWORD +Ow2CommandLineWToCommandLineA( + IN LPWSTR CommandLineW, + OUT PSZ *CommandLineA + ); + +BOOLEAN +Od2DllInitialize( + IN PVOID DllHandle, + IN ULONG Reason, + IN PCONTEXT Context OPTIONAL + ); + +BOOLEAN +Od2ProcessIsDetached(VOID); + +int +Loader_main(VOID); + +VOID + Od2FinalProcessCleanup(); +/* + * Internal prototypes + */ + +UINT +GetPgmName( + int argc + ); + +UINT +InitStdConout(); + +#define OS2_VIO_MAX_ROW 100 + +WORD StartUpwAttributes = 0x7; +CONSOLE_SCREEN_BUFFER_INFO StartUpScreenInfo; +CONSOLE_CURSOR_INFO StartUpCursorInfo; +PVOID BASE_TILE; +HANDLE handOS2 = NULL; + + // + // Environment related global variables + // +PSZ Od2CommandLinePtr; // to be used by dllinit.c +char Od2PgmFullPathBuf[MAX_PATH + 1]; +ULONG Od2PgmFullPathBufLength; +PSZ Od2PgmFilePath; +DWORD Od2ForegroundWindow; + +ULONG Od2DosExitIsDone = 0; + +#if DBG +BYTE SetEventHandlerStr[] = "SetEventHandler"; +BYTE InitStdConoutStr[] = "InitStdConout"; +BYTE SesGrpInitStr[] = "SesGrpInit"; +BYTE RestoreWin32ParmsBeforeTerminationStr[] = "RestoreWin32ParmsBeforeTermination"; +BYTE CreateServerThreadsStr[] = "CreateServerThreads"; +BYTE ResumeServerThreadsStr[] = "ResumeServerThreads"; +#endif + +extern BOOLEAN Od2ReceivedSignalAtInit; +extern DWORD Od2InitSignalType; +BOOL +EventHandlerRoutine (IN ULONG CtrlType); + +extern PVOID Od2Process; +PVOID IsOs2Thread(); +ULONG Od2ThreadId(); +ULONG Od2ProcessId(); +PSZ Od2ApplName(); +PSZ Od2GetLastAPI(); + +// global variable to keep exception information + +EXCEPTION_POINTERS ExPtrs; +EXCEPTION_RECORD ExRec; +WORD wSavedFpStatus = 0, wSavedFpCtrl = 0; + +/* + * Os2ServiceThread: + * Created to allow the service who started this copy of OS2.EXE to terminate + * the process tree. + */ +VOID +Os2ServiceThread( + IN PVOID Parameter + ) +{ + CHAR SemName[MAX_PATH]; + HANDLE hServiceEvent; + + wsprintf(SemName, "OS2SSService-%d", GetCurrentProcessId()); + + // + // Create the service event for this process + // + hServiceEvent = CreateEventA( + NULL, + FALSE, // automatic reset + FALSE, // initial state = non-signaled + SemName); + + if (hServiceEvent == NULL) + { +#if DBG + DbgPrint("OS2: Os2ServiceThread(), error at CreateEvent, error=%d\n", + GetLastError()); +#endif + } + + // Wait on the Service semaphore + if (WaitForSingleObject( + hServiceEvent, + INFINITE) == WAIT_FAILED) + { +#if DBG + DbgPrint("OS2: Os2ServiceThread(), failed to NtWaitForSingleObject PMShellEvent, error=%d\n", + GetLastError()); +#endif + } + +#if DBG + DbgPrint("OS2: Os2ServiceThread() - event signaled\n"); +#endif +#if PMNT + // + // PM apps handling + // + if (ProcessIsPMProcess()) + { + // Regular app (i.e. not PMShell) + if (!ProcessIsPMShell()) + { + if (!Ow2WriteBackCloseEvent()) + { + // We failed to write-back a close event: + // must be DosExecPgm proc; Pass event through semaphore + DosSemClear(PMSubprocSem32); + Sleep(7900L); + } + } + else // PMSHELL + { +#if DBG + DbgPrint("OS2: Os2ServiceThread(), ignoring signal - process is PMShell\n"); +#endif + } + return; + } +#endif // PMNT + SendSignalToOs2Srv(XCPT_SIGNAL_KILLPROC); +} + +DWORD Ow2FaultFilter(ULONG wFaultFilter, PEXCEPTION_POINTERS lpExP) +{ + + // copy the exception record to global variable + ExPtrs = *lpExP; + ExRec = *lpExP->ExceptionRecord; + + _asm { + fnstcw wSavedFpCtrl + fnstsw wSavedFpStatus + } + return(wFaultFilter); +} + +void Ow2DisplayExceptionInfo() +{ + char ErrMsg[512]; + + wsprintf(ErrMsg, + "OS2: Internal Exception 0x%lx occured at %lx\nApplication Name=%s\nLast OS/2 API=%s\nTID=%d PID=%d\nFP: Ctrl=%lx, Status=%lx\nEAX=%lx EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\nESP=%lx EBP=%lx", + ExRec.ExceptionCode, + ExRec.ExceptionAddress, + Od2Process ? Od2ApplName() : "None", + (IsOs2Thread()) ? (Od2GetLastAPI()) : "None", + (IsOs2Thread()) ? (Od2ThreadId()) : 0, + Od2Process ? Od2ProcessId() : 0, + (DWORD) wSavedFpCtrl, + (DWORD) wSavedFpStatus, + (ExPtrs.ContextRecord)->Eax, + (ExPtrs.ContextRecord)->Ebx, + (ExPtrs.ContextRecord)->Ecx, + (ExPtrs.ContextRecord)->Edx, + (ExPtrs.ContextRecord)->Esi, + (ExPtrs.ContextRecord)->Edi, + (ExPtrs.ContextRecord)->Esp, + (ExPtrs.ContextRecord)->Ebp + ); + + MessageBox(NULL, ErrMsg, "Error", MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL | MB_SETFOREGROUND); +} + +void __cdecl +main (int argc, + char *argv[], + char *envp[]) +{ + UINT StringCode; + ULONG tmp, ConStringCode; +#if PMNT + HANDLE NewForegroundWindow; +#endif //PMNT + + // + // Get the handle of the foreground window which is the window of the + // curent process. + // BUGBUG - This value is not neccessarily the right value because + // the user might immediately switch to another window which + // results in OS2.EXE having the handle of some other window + // + // Do not remove this statement without consulting with PM/NT team - the + // Ow2ForegroundWindow below is used by PM/NT call(s). + Ow2ForegroundWindow = GetForegroundWindow(); + + // + // Put the entire execution of the os/2 program inside a try/except. + // This way we ensure that we recover from 32 bit exceptions + // + // Note that the above is done only for RETAIL build. For DBG builds, + // it is better to let NTSD handle the exception so that we can debug + // the problem right away. + // + try { + timing = 0; +#if DBG + Os2Debug = 0; + fVerbose = FALSE; + fTrace = FALSE; + fBrkOnStart = FALSE; +#endif + //timing = GetTickCount(); + //printf("Os2 main init time is %d\n", timing); + //Os2Debug = OS2_DEBUG_OS2_EXE; + Os2ReturnCode = 0; + Od2SignalEnabled = FALSE; + + SetErrorMode(1); + /* + * get the full path of the program to execute + */ + + if (timing) + { + printf("Os2 time before GetPgmName is %d\n", (GetTickCount()) - timing); + } + if (StringCode = GetPgmName(argc)) + { + Ow2Exit(StringCode, Od2PgmFullPathBuf, 1); + } + + if (!fService) + { + char TmpBuffer[256]; + + // OS/2 child processes of OS/2 apps started from a service don't + // have the /S switch but they should find a variable 'Os2SSService' + // in their environment + + if (GetEnvironmentVariable( + "Os2SSService", + &TmpBuffer[0], + 256)) + { + // non-zero return code means variable was found + fService = TRUE; + } + } + else + { + if (!SetEnvironmentVariable( + "Os2SSService", + "1")) + { +#if DBG + KdPrint(("OS2: failed to SetEnvironment variable Os2SSService, error=%d\n", + GetLastError())); +#endif + } + } + +#if DBG + if (fService) + KdPrint(("Os2: Loading %s (as a service)\n", Od2PgmFullPathBuf)); + else + KdPrint(("Os2: Loading %s\n", Od2PgmFullPathBuf)); +#endif + /* + * Set event handlers to handle Ctrl-C etc. + */ + + if (timing) + { + printf("Os2 time before SetEventHandlers is %d\n", (GetTickCount()) - timing); + } + + SetEventHandlers(TRUE); + + /* + * Set Std-Handles and open CONOUT$ + */ + + if (timing) + { + printf("Os2 time before InitStdConout is %d\n", (GetTickCount()) - timing); + } + + ConStringCode = InitStdConout(); + + + /* + * Connect with OS2SS + */ + + if (timing) + { + printf("Os2 time before InitOs2ssSessionPort is %d\n", (GetTickCount()) - timing); + } + tmp = InitOs2ssSessionPort(); + + // + // InitOs2ssSessionPort returns: + // 0L - problem with resources, like memory + // -1L - problem connecting to os2srv + // otherwise - OK + // + if (tmp == -1L) + { + Ow2Exit(IDS_OS2_NOCONNECT, NULL, 1); + } + else if (tmp == 0) + { + Ow2Exit(IDS_OS2_NOMEMORY, NULL, 1); + } + + Sleep(2); + +#if PMNT + NewForegroundWindow = (HANDLE)PMNTGetOurWindow(); + if ((NewForegroundWindow != 0) && + (NewForegroundWindow != Ow2ForegroundWindow)) + { +#if DBG + DbgPrint("Os2:main(), warning Ow2ForeGroundWindow changed from %x to %x\n", + Ow2ForegroundWindow, + NewForegroundWindow); +#endif // DBG + Ow2ForegroundWindow = NewForegroundWindow; + } +#endif // PMNT + + if (timing) + { + printf("Os2 time before calling Od2DllInitialize is %d\n", (GetTickCount()) - timing); + } + + if (!Od2DllInitialize(NULL, DLL_PROCESS_ATTACH, NULL)) + { +#if DBG + KdPrint(("OS2SES: Od2DllInitialize failed\n")); +#endif + Ow2Exit(IDS_OS2_INITFAIL, NULL, 1); + // Ow2Exit(0, NULL, 1); + } + + if (Od2CommandLinePtr) + if (LocalFree(Od2CommandLinePtr) != NULL) + { +#if DBG + KdPrint(("OS2SES: Failed to free PsevdoArgv\n")); +#endif + } + + if ( ConStringCode ) + { + + if (!Od2ProcessIsDetached()) + { +#if DBG + KdPrint(("Os2: InitStdConout returned error %d\n", ConStringCode)); +#endif +/** + For now, we do nothing, because the code below popup a window for a second. + if we find a better way, we'll use it, otherwise do nothing. + // + // Two cases - created with DETACH_PROCESS by non-OS/2 app + // OR + // failed somehow to create the console. + // + // No official way to test it, so we allocate console, + // if we succeed, this is the 1st case, free it and continue. + // if we fail, it means we already have one but can't access + // it somehow, fail the app. + // + if (AllocConsole()) + { + FreeConsole(); + } + else + Ow2Exit(ConStringCode, NULL, 1); +**/ + } + } + + if (fRootService) + { + HANDLE ThreadHandle; + ULONG Tid; + + ThreadHandle = CreateThread( NULL, + 0, + (LPTHREAD_START_ROUTINE)Os2ServiceThread, + NULL, + 0, + &Tid); + + if (!ThreadHandle) + { +#if DBG + DbgPrint("OS2: main(), fail to create service thread, error %d\n", + GetLastError()); +#endif + } + else + { + if (!CloseHandle(ThreadHandle)) + { +#if DBG + DbgPrint("OS2: main(), CloseHandle(service thread=%x) failed, error=%d\n", + ThreadHandle, GetLastError()); +#endif // DBG + } + } + } + + if (timing) + { + printf("Os2 time before calling loader_main is %d\n", (GetTickCount()) - timing); + } + Loader_main(); + if (Od2ReceivedSignalAtInit) { + // + // OS2.EXE received a signal before complete loading, + // handle it now + // + EventHandlerRoutine(Od2InitSignalType); + } + } + + // + // if Os2Debug is on, and ntsd is attached, it will get the second chance + // +#if DBG + except( (Os2Debug ? Ow2FaultFilter(EXCEPTION_CONTINUE_SEARCH, GetExceptionInformation()): + + Ow2FaultFilter(EXCEPTION_EXECUTE_HANDLER, GetExceptionInformation())) ) { +#else + except( Ow2FaultFilter(EXCEPTION_EXECUTE_HANDLER, GetExceptionInformation()) ) { +#endif +#if DBG + KdPrint(("OS2SES: Internal error - Exception occured in 32bit os2ss code\n")); +#endif + Ow2DisplayExceptionInfo(); + + // + // Cleanup client state, server will get notified + // via the exitprocess debug event + // + Od2FinalProcessCleanup(); + Ow2Exit(IDS_OS2_INTERNAL_ERROR, NULL, 1); + } +} + + +VOID SetEventHandlers(IN BOOL fSet) +{ + + Or2WinSetConsoleCtrlHandler( + #if DBG + SetEventHandlerStr, + #endif + EventHandlerRoutine, + fSet + ); +} + +struct +{ + LPTHREAD_START_ROUTINE lpStartAddress; + int Priority; + HANDLE *hServerThread; +} SERVER_THREAD_TABLE[] = + { + { + EventServerThread, + OS2_EVENT_THREAD_PRIORITY, + &EventServerThreadHandle + }, + { + SessionRequestThread, + OS2_SERVER_THREAD_PRIORITY, + &Ow2hSessionRequestThread + }, + { + NULL, + 0, + NULL + } + }; + + +DWORD +CreateServerThreads(VOID) +{ + HANDLE tHandle; + DWORD Tid, i; + + /* + * create 1 for getting Input Event and dispatch them to 2 seperate + * queues for Kbd & Mou + */ + + for ( i = 0 ; SERVER_THREAD_TABLE[i].lpStartAddress ; i++ ) + { + + if((tHandle = Or2WinCreateThread( + #if DBG + CreateServerThreadsStr, + #endif + NULL, + 0, + SERVER_THREAD_TABLE[i].lpStartAddress, + NULL, + CREATE_SUSPENDED, + &Tid + )) == NULL) + { +#if DBG + KdPrint(("OS2SES(Os2-CreateServerThreads): error %lu CreateThread %u\n", + GetLastError(), i)); + ASSERT( FALSE ); +#endif + return(1L); + } + + *SERVER_THREAD_TABLE[i].hServerThread = tHandle; + } + + if(Or2WinResumeThread( + #if DBG + CreateServerThreadsStr, + #endif + Ow2hSessionRequestThread + ) == -1) + { +#if DBG + KdPrint(("OS2SES(Os2-CreateServerThreads): error %lu ResumeThread ServerRequest\n", + GetLastError())); + ASSERT( FALSE ); +#endif + return(1L); + } + + if (!Or2WinSetThreadPriority( + #if DBG + CreateServerThreadsStr, + #endif + Ow2hSessionRequestThread, + OS2_SERVER_THREAD_PRIORITY + )) + { +#if DBG + IF_OD2_DEBUG( OS2_EXE ) + KdPrint(("OS2SES(Os2-CreateServerThreads): error %lu SetThreadPriority RequestThread\n", + GetLastError())); +#endif + } + return(0L); +} + + +DWORD +ResumeServerThreads(VOID) +{ + HANDLE tHandle; + DWORD i; + + for ( i = 0 ; SERVER_THREAD_TABLE[i].lpStartAddress ; i++ ) + { + if ((tHandle = *SERVER_THREAD_TABLE[i].hServerThread) != + Ow2hSessionRequestThread) + { + if(Or2WinResumeThread( + #if DBG + ResumeServerThreadsStr, + #endif + tHandle + ) == -1) + { +#if DBG + KdPrint(("OS2SES(Os2-CreateServerThreads): error %lu CreateThread %u\n", + GetLastError(), i)); + ASSERT( FALSE ); +#endif + return(1L); + } + + if (!Or2WinSetThreadPriority( + #if DBG + ResumeServerThreadsStr, + #endif + tHandle, + SERVER_THREAD_TABLE[i].Priority + )) + { +#if DBG + IF_OD2_DEBUG( OS2_EXE ) + KdPrint(("OS2SES(Os2-CreateServerThreads): error %lu SetThreadPriority %u\n", + GetLastError(), i)); +#endif + } + } + } + return(0L); +} + + +#define SKIP_ARG( Ptr ) \ +{ \ + register char ch; \ + argc --; \ + while( ch = *Ptr++ ) \ + { \ + if(( ch == ' ' ) || ( ch == '\t' )) \ + { \ + break; \ + } \ + } \ +} \ + +UINT +GetPgmName( + int argc + ) +{ + char *lpPgmName = NULL, CurChar, *ArgvPtr; + DWORD Rc; + int i; + CHAR ch; + + /* + * Get the command line in OEM code page. + * Format is : "OS2 /P <full path> /C <original CommandLine>" + */ + + Rc = Ow2CommandLineWToCommandLineA( + GetCommandLineW(), + &ArgvPtr + ); + + //RtlProcessHeap + if ( Rc ) + { + return(IDS_OS2_NOMEMORY); + } + + /* + * skip program name ("OS2.EXE") + */ + + SKIP_ARG( ArgvPtr ) + + /* + * look for flags for os2 up to /C + * + */ + + while ( argc ) + { + if ( ArgvPtr[0] == '/' ) + { + CurChar = ArgvPtr[1] | ('a'-'A'); + ArgvPtr += 2; + if ( CurChar == 'c' ) + { + /* + * Skip the /C and break from the while loop + */ + + ArgvPtr++; + break; + + } else switch ( CurChar ) + { + case 'p': + SKIP_ARG(ArgvPtr); + lpPgmName = ArgvPtr; + break; +#if DBG + case 'b': + fBrkOnStart = TRUE; + KdPrint(( + "OS2: Breakpoint caused by /B switch!\n")); + _asm int 3; + break; + + case 'v': + fVerbose = TRUE; + break; + + case 't': + fTrace = TRUE; + break; +#endif + case 's': + fService = TRUE; +#if PMNT + // Don't consider PMSHELL a root service, i.e. don't + // create a termination thread for it. We don't want + // PMSHELL to terminate even if the service which started it + // is stopped because there may be PM apps out there which + // weren't started by services. + if (!ProcessIsPMShell()) + fRootService = TRUE; +#else + fRootService = TRUE; +#endif + break; + + default: + strncpy(Od2PgmFullPathBuf, ArgvPtr, MAX_PATH); + Od2PgmFullPathBuf[MAX_PATH - 1] = '\0'; + return(IDS_OS2_WHATFLAG); + } + } else + { + return(IDS_OS2_USAGE); + } + + SKIP_ARG(ArgvPtr); + } + + /* + * We exit the loop when "/C" was found or when "argc=0". + * Make sure "/C" was found and "/P <appl_full_path>" had been + * found. + */ + + if (( CurChar != 'c' ) || !lpPgmName ) + { + return(IDS_OS2_NOCMD); + } + + /* + * The full path of the program to execute is pointed by lpPgmName + * but is space terminated. ArgvPtr points to the original command + * line. + */ + + for ( i = 0 ; + ( lpPgmName[i] != '\0' ) && + ( lpPgmName[i] != ' ' ) && + ( lpPgmName[i] != '\t' ) && + ( i < MAX_PATH ) + ; Od2PgmFullPathBuf[i] = lpPgmName[i], i++ ); + + if ( i == MAX_PATH ) + { + return(IDS_OS2_NOMEMORY); + } + + Od2PgmFullPathBuf[i] = '\0'; + Od2PgmFullPathBufLength = i; + + // Code below replaces the program name with the full-path. This caused + // the PM Deskpic screen-saver to GP because it looked for '\' + // (31-May-94, RAID bug#2932) + + SKIP_ARG(ArgvPtr); + + ch = lpPgmName[i]; + + if (argc > 1) + { + Od2CommandLinePtr = + (PSZ)LocalAlloc(0, + // Path + SPACE + arguments + /0 + Od2PgmFullPathBufLength + 1 + strlen(ArgvPtr) + 1); + } + else + { + Od2CommandLinePtr = + (PSZ)LocalAlloc(0, + // Path + /0 + Od2PgmFullPathBufLength + 1); + } + + if (Od2CommandLinePtr == NULL) + { + return(IDS_OS2_NOMEMORY); + } + + strcpy(Od2CommandLinePtr, Od2PgmFullPathBuf); + + if (argc > 1) + { + strncat(Od2CommandLinePtr, &ch, 1); // Cat the argv0-1 seperating char + strcat(Od2CommandLinePtr, ArgvPtr); // Fill in all the rest (argvs) + } + + // Find the program name by skipping up to (and include) '\', '/' and ':' + + Od2PgmFilePath = &Od2PgmFullPathBuf[0]; + + for ( i = 0 ; ( Od2PgmFullPathBuf[i] != '\0' ) ; i++ ) + { + if (( Od2PgmFullPathBuf[i] == ':' ) || + ( Od2PgmFullPathBuf[i] == '/' ) || + ( Od2PgmFullPathBuf[i] == '\\' )) + { + Od2PgmFilePath = &Od2PgmFullPathBuf[i+1]; + } + } + + return(0); +} + + +UINT +InitStdConout() +{ +// SECURITY_ATTRIBUTES SecurityAttributes; +#if DBG + DWORD Status; +#endif + + /* + * Get a handle to CONIN$ & CONOUT$ for KBD & VIO requests + */ + + hConsoleStdIn = GetStdHandle(STD_INPUT_HANDLE); + hConsoleStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + hConsoleStdErr = GetStdHandle(STD_ERROR_HANDLE); + + hStdInConsoleType = (USHORT)(CONSOLE_HANDLE(hConsoleStdIn) && + VerifyConsoleIoHandle(hConsoleStdIn)); + hStdOutConsoleType = (USHORT)(CONSOLE_HANDLE(hConsoleStdOut) && + VerifyConsoleIoHandle(hConsoleStdOut)); + hStdErrConsoleType = (USHORT)(CONSOLE_HANDLE(hConsoleStdErr) && + VerifyConsoleIoHandle(hConsoleStdErr)); + +// SecurityAttributes.bInheritHandle = FALSE; +// SecurityAttributes.lpSecurityDescriptor = NULL; +// SecurityAttributes.nLength = sizeof (SECURITY_ATTRIBUTES); + + /* + * Open CONOUT$ if StdOut id redirected + */ + + if (hStdOutConsoleType) + { + hConsoleOutput = hConsoleStdOut; + } else + { + hConsoleOutput = Or2WinCreateFileW( + #if DBG + InitStdConoutStr, + #endif + L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, /* &SecurityAttributes, */ + OPEN_EXISTING, /* CREATE_ALWAYS, */ + 0, + NULL + ); + +// hConsoleOutput = Or2WinCreateConsoleScreenBuffer( +// #if DBG +// InitStdConoutStr, +// #endif +// GENERIC_READ | GENERIC_WRITE, +// FILE_SHARE_READ|FILE_SHARE_WRITE, +// NULL, /* &SecurityAttributes */ +// CONSOLE_TEXTMODE_BUFFER, +// NULL); + + if (hConsoleOutput == INVALID_HANDLE_VALUE) + { + return (IDS_OS2_CREATECONOUT); + } + + if (!Or2WinSetConsoleActiveScreenBuffer( + #if DBG + InitStdConoutStr, + #endif + hConsoleOutput + )) + { +#if DBG +// return (IDS_OS2_ACTIVECONOUT); + Status = GetLastError(); + IF_OD2_DEBUG( ANY ) + { + KdPrint(("OS2SES(Os2-SetConsoleActiveScreenBuffer): can't activate CONOUT(%lu) in Full-Screen\n", Status)); + } +#endif + } + } + + hConOut = hConsoleOutput; + + SetConsoleInputModeAgain = 0; + SetConsoleOutputModeAgain = 0; + + return(0); +} + + +DWORD +SesGrpInit() +{ + DWORD Type, Rc, i; + HANDLE *Handle; + USHORT *fType, FileType; + COORD Coord; + CONSOLE_FONT_INFO ConsoleCurrentFont; +#if DBG + UCHAR ErrBuff[ERROR_BUFFER_SIZE]; +#endif + + //SesGrp->WinProcessNumberInSession = 0; + //SesGrp->WinSyncProcessNumberInSession = 0; + + SesGrp->hConsoleInput = hConsoleInput; + SesGrp->hConsoleOutput = hConsoleOutput; + + SesGrp->StdIn = hConsoleStdIn; + SesGrp->StdOut = hConsoleStdOut; + SesGrp->StdErr = hConsoleStdErr; + + SesGrp->StdInFlag = hStdInConsoleType; + SesGrp->StdOutFlag = hStdOutConsoleType; + SesGrp->StdErrFlag = hStdErrConsoleType; + + //SesGrp->KbdInFocus = 0; + SesGrp->NoKbdFocus = TRUE; + SesGrp->FirstProcess = TRUE; + SesGrp->StdInHandleCount = 1; + SesGrp->StdOutHandleCount = 1; + SesGrp->StdErrHandleCount = 1; + //SesGrp->hConsolePopUp = NULL; + + for ( i=0, Handle = &(SesGrp->StdIn), + fType = &(SesGrp->StdInFileType) ; + i<3 ; i++, Handle++, fType++ ) + { + Type = GetFileType(*Handle); + // BUGBUG maybe call NtQueryVolumeInformationFile directly (base\client\filehops.h) + switch (Type) + { + case FILE_TYPE_DISK: + FileType = 0x0000; // FILE_TYPE_FILE + break; + + case FILE_TYPE_CHAR: + FileType = 0x0001; // FILE_TYPE_DEV + break; + + case FILE_TYPE_PIPE: + FileType = 0x0002; // FILE_TYPE_PIPE + break; + + //case FILE_TYPE_UNKNOWN: + default: +#if DBG + IF_OD2_DEBUG( OS2_EXE ) + { + DbgPrint("OS2SES(SesGrpInit): GetFileType(handle %lx) failed, LastError = %ld\n", + *Handle, GetLastError()); + } +#endif + FileType = 0x0001; // FILE_TYPE_DEV - to be safety + break; + } + +#if DBG + IF_OD2_DEBUG( OS2_EXE ) + { + KdPrint(("OS2SES(HandlesTypes): Handle %lx(%lu), WinType %lx, Os2Type %lx\n", + *Handle, i, Type, FileType )); + } +#endif + *fType = FileType; + } + + Or2WinGetConsoleScreenBufferInfo( + #if DBG + SesGrpInitStr, + #endif + hConsoleOutput, + &StartUpScreenInfo + ); + + SesGrp->ScreenColNum = StartUpScreenInfo.dwSize.X; + if ((SesGrp->ScreenRowNum = StartUpScreenInfo.dwSize.Y) > OS2_VIO_MAX_ROW) + { +#if DBG + IF_OD2_DEBUG( ANY ) + { + KdPrint(("OS2SES: Screen size is bigger than the maximum for OS/2.\n")); + KdPrint((" OS2SS will use only first %d rows of the %d available\n", + OS2_VIO_MAX_ROW, StartUpScreenInfo.dwSize.Y)); + } +#endif + SesGrp->ScreenRowNum = OS2_VIO_MAX_ROW; + } + SesGrp->CellVSize = SesGrp->CellHSize = 8; + + if (!GetCurrentConsoleFont(hConsoleOutput, + TRUE, /* maximize window */ + &ConsoleCurrentFont)) + { +#if DBG + Rc = GetLastError(); + if ( Rc == ERROR_FULLSCREEN_MODE ) + { + IF_OD2_DEBUG( ANY ) + { + KdPrint(("OS2SES(Os2-GetCurrentConsoleFont): can't query current font(%lu) in Full-Screen\n", + Rc)); + } + } else + { + sprintf(ErrBuff, "OS2SES(Os2-GetCurrentConsoleFont): can't query current font(%lu)\n", Rc); + ASSERT1( ErrBuff, FALSE ); + } +#endif + } else + { + Coord = GetConsoleFontSize(hConsoleOutput, + ConsoleCurrentFont.nFont); + +#if DBG + if ((!Coord.X) && (!Coord.Y)) + { + Rc = GetLastError(); + sprintf(ErrBuff, "OS2SES(Os2-GetConsoelFontSize): can't query font Size(%lu)\n", Rc); + ASSERT1( ErrBuff, FALSE ); + } +#endif + + SesGrp->CellVSize = Coord.Y; + SesGrp->CellHSize = Coord.X; + } + + StartUpwAttributes = StartUpScreenInfo.wAttributes; + + if (!Or2WinGetConsoleMode( + #if DBG + SesGrpInitStr, + #endif + hConsoleOutput, + &SesGrp->DefaultWinOutputMode + )) + { + Rc = GetLastError(); +#if DBG + ASSERT1( "Can not get CONOUT for default Mode\n", FALSE ); +#endif + SesGrp->DefaultWinOutputMode = WINDOW_DEFAULT_OUTPUT_MODE; + } + + SesGrp->OutputModeFlags = WINDOW_DEFAULT_OUTPUT_MODE; + + if (!Or2WinSetConsoleMode( + #if DBG + SesGrpInitStr, + #endif + hConsoleOutput, + OS2_DEFAULT_OUTPUT_MODE + )) + { + Rc = GetLastError(); +#if DBG + ASSERT1( "Can not set CONOUT for default Mode\n", FALSE ); +#endif + } + else + SesGrp->OutputModeFlags = OS2_DEFAULT_OUTPUT_MODE; + + SesGrp->MaxLVBsize = + (SesGrp->ScreenColNum * SesGrp->ScreenRowNum * 4 ); + + if (SesGrp->MaxLVBsize < 80 * 100 * 4) /* buffer for 100x80 window */ + { + SesGrp->MaxLVBsize = 80 * 100 * 4; + } + + if (SesGrp->MaxLVBsize > (64 * 1024)) /* more than 64K */ + { + SesGrp->MaxLVBsize = 64 * 1024; + } + +#if DBG + IF_OD2_DEBUG( OS2_EXE ) + { + KdPrint(("OS2SES(Os2-Handles): hIn %lx, hOut %lx, StdIn %lx (%s), StdOut %lx(%s), StdErr %lx(%s)\n", + hConsoleInput, hConsoleOutput, + hConsoleStdIn, ((hStdInConsoleType) ? "Std" : "Rdr" ), + hConsoleStdOut, ((hStdOutConsoleType) ? "Std" : "Rdr" ), + hConsoleStdErr, ((hStdErrConsoleType) ? "Std" : "Rdr" ) + )); + } +#endif + +#if DBG +// KdPrint(("OS2SES(Os2-ConInfo): Size %x:%x, Pos %x:%x, Attr %x, Win %x:%x-%x:%x, Max %x:%x\n", +// StartUpScreenInfo.dwSize.Y, StartUpScreenInfo.dwSize.X, +// StartUpScreenInfo.dwCursorPosition.Y, StartUpScreenInfo.dwCursorPosition.X, +// StartUpScreenInfo.wAttributes, +// StartUpScreenInfo.srWindow.Top, StartUpScreenInfo.srWindow.Left, +// StartUpScreenInfo.srWindow.Bottom, StartUpScreenInfo.srWindow.Right, +// StartUpScreenInfo.dwMaximumWindowSize.Y, StartUpScreenInfo.dwMaximumWindowSize.X )); +#endif + + return(0L); +} + + +VOID +RestoreWin32ParmsBeforeTermination() +{ + Or2WinSetConsoleMode( + #if DBG + RestoreWin32ParmsBeforeTerminationStr, + #endif + hConsoleInput, + DefaultWinInputMode + ); + + Or2WinSetConsoleMode( + #if DBG + RestoreWin32ParmsBeforeTerminationStr, + #endif + hConsoleOutput, + SesGrp->DefaultWinOutputMode + ); + + /* + * This is a workaround since the CMD doesn't restore its CurType + * + * Bug #4323 (OS2SS: CURSOR DISAPPEARS AFTER EXITING WORD5.0) + */ + + Or2WinSetConsoleCursorInfo( + #if DBG + RestoreWin32ParmsBeforeTerminationStr, + #endif + hConsoleOutput, + &StartUpCursorInfo + ); +} + + +VOID +Ow2Exit( + IN UINT StringCode, + IN PCHAR ErrorText, + IN int ExitCode + ) +/*++ + +Routine Description: + + This routine performs the exit from OS2.EXE for OS/2 application. + +Arguments: + + StringCode - A code to retrieve an error message from the string RC file + (os2.rc). It's printed to the stderr. If zero - no message will be + printed. + + ErrorText - test to use in the error message (in place of %s, like + DLL name, ordinal number, etc.) + + ExitCode - code to return to Win32 CRT + +Return Value: + + +Note: + + This routine calls CRT's exit() and doesn't return. + +--*/ +{ + WCHAR ErrBuffW[ERROR_BUFFER_SIZE]; + CHAR ErrBuff[ERROR_BUFFER_SIZE]; + DWORD Count; + DWORD Rc; + + //if ((handOS2 = GetModuleHandle("os2.exe")) == NULL) + if (StringCode) + { + if ((handOS2 == NULL) && + ((handOS2 = GetModuleHandle(NULL)) == NULL)) + { + Rc = GetLastError(); +#if DBG + KdPrint(("OS2 ended! (error %lu on GetModuleHandle for ExitCode %lu)\n", + Rc, StringCode)); +#endif + } else + { + if ((Count = LoadStringW(handOS2, + StringCode, + ErrBuffW, + ERROR_BUFFER_SIZE)) == 0L) + { + Rc = GetLastError(); +#if DBG + KdPrint(("OS2 ended! (error %lu on LoadStringW for ExitCode %lu)\n", + Rc, StringCode)); +#endif + } else + { + Count = WideCharToMultiByte( + CP_OEMCP, + 0L, + ErrBuffW, + Count, + ErrBuff, + ERROR_BUFFER_SIZE, + NULL, + NULL); + + if (Count != 0) { + + ErrBuff[Count] = '\0'; + fprintf(stderr, ErrBuff, ErrorText); +#if DBG + KdPrint((ErrBuff, ErrorText)); +#endif + } else { + Rc = GetLastError(); +#if DBG + KdPrint(("OS2 ended! (error %lu on WideCharToMultiByte for ExitCode %lu)\n", + Rc, StringCode)); +#endif + } + } + } + } + +#if DBG + IF_OD2_DEBUG( ANY ) + { + KdPrint(( "OS2 ended! (%lx)\n", ExitCode )); + } +#endif + + ExitProcess(ExitCode); +} + + +#define CAP_BUFFER_SIZE 80 +#define TEXT_BUFFER_SIZE 256 + +CHAR DefaultConfigSysAccessCap[] = "OS/2 Subsystem -- CONFIG.SYS Access"; +CHAR DefaultConfigSysAccessText[] = "An OS/2 Application requested access to CONFIG.SYS - Read Only access is granted. In order to modify OS/2 CONFIG.SYS, logon as ADMINISTRATOR.\n"; +CHAR DriveNotReadyDefaultMsg[] = "There is no disk in the drive.\nPlease insert a disk into drive%s.\n" ; +CHAR WriteProtectDefaultMsg[] = "The disk cannot be written to because it is write protected.\nPlease remove the write protection from the volume\nin drive%s.\n" ; +CHAR DriveNotReadyDefaultHdr[] = "%s.EXE - No Disk"; +CHAR WriteProtectDefaultHdr[] = "%s.EXE - Write Protect Error"; + +CHAR DefaultBoundAppLoadCap[] = "%s - OS/2 Subsystem Bound Application Load Failure"; +CHAR DefaultBoundAppLoadText[] = + "This application uses an unsupported OS/2 API, and therefore " + "cannot be executed by the OS/2 Subsystem. " + "After the application terminates, you may try re-running it " + "using forcedos, as the DOS Subsystem may be able to support it. " + "Press Enter to terminate the application."; + +VOID +Ow2ConfigSysPopup( + VOID + ) + +/*++ + +Routine Description: + + Pops up a window informing the user that s/he cannot update the registry due + to insufficient privilege. + + The message is only popped up once per program. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + static MessageAlreadyShown = FALSE; + CHAR TextBuff[TEXT_BUFFER_SIZE]; + CHAR CapBuff[CAP_BUFFER_SIZE]; + + if (MessageAlreadyShown) { + return; + } + + MessageAlreadyShown = TRUE; + + if ((handOS2 == NULL) && + ((handOS2 = GetModuleHandle(NULL)) == NULL)) + { +#if DBG + KdPrint(("Ow2ConfigSysPopup: error %lu on GetModuleHandle\n", + GetLastError())); +#endif + } + + if (( handOS2 == NULL) || + !LoadString(handOS2, + IDS_OS2_CONFIGSYS_ACCESS_TXT, + TextBuff, + TEXT_BUFFER_SIZE)) + { +#if DBG + if ( handOS2 != NULL) + { + KdPrint(("Ow2ConfigSysPopup: error %lu on LoadString1\n", + GetLastError())); + } +#endif + strncpy(TextBuff, DefaultConfigSysAccessText, TEXT_BUFFER_SIZE - 1); + } + + if (( handOS2 == NULL) || + !LoadString(handOS2, + IDS_OS2_CONFIGSYS_ACCESS_CAP, + CapBuff, + CAP_BUFFER_SIZE)) + { +#if DBG + if ( handOS2 != NULL) + { + KdPrint(("Ow2ConfigSysPopup: error %lu on LoadString2\n", + GetLastError())); + } +#endif + strncpy(CapBuff, DefaultConfigSysAccessCap, CAP_BUFFER_SIZE - 1); + } + + MessageBoxEx( NULL, + TextBuff, + CapBuff, + MB_APPLMODAL | MB_ICONEXCLAMATION | MB_OK | MB_SETFOREGROUND, + 0 + ); +} + + +int +Ow2DisplayHardErrorPopup( + IN int Drive, + IN BOOLEAN WriteProtectError, + IN PUCHAR AppName + ) +{ + int size; + char CaptionMessage[CAP_BUFFER_SIZE], TextMessage[TEXT_BUFFER_SIZE]; + char CaptionBuffer[CAP_BUFFER_SIZE], TextBuffer[TEXT_BUFFER_SIZE]; + char ApplNameBuff[OS2_MAX_APPL_NAME], DriveBuf[4]; + char *ErrMsg, *ErrHdr; + UINT CapCode, TextCode; + + + strncpy(ApplNameBuff, AppName, OS2_MAX_APPL_NAME); + size = strlen(ApplNameBuff); + if ((size > 4 ) && !stricmp(&ApplNameBuff[size-4], ".exe")) { + ApplNameBuff[size-4] = '\0'; + } + strupr(ApplNameBuff); + + if (Drive != 0) + { + sprintf(DriveBuf, " %c:", ('A' - 1) + Drive ); + } else + { + DriveBuf[0] = '\0'; + } + + if (WriteProtectError) + { + ErrMsg = WriteProtectDefaultMsg; + ErrHdr = WriteProtectDefaultHdr; + CapCode = IDS_OS2_WRITE_PROTECT_CAP; + TextCode = IDS_OS2_WRITE_PROTECT_TXT; + } else + { + ErrMsg = DriveNotReadyDefaultMsg; + ErrHdr = DriveNotReadyDefaultHdr; + CapCode = IDS_OS2_DEVIVE_NOT_READY_CAP; + TextCode = IDS_OS2_DEVIVE_NOT_READY_TXT; + } + + if ((handOS2 == NULL) && + ((handOS2 = GetModuleHandle(NULL)) == NULL)) + { +#if DBG + KdPrint(("Ow2DisplayHardErrorPopup: error %lu on GetModuleHandle\n", + GetLastError())); +#endif + } + + if (( handOS2 == NULL) || + !LoadString(handOS2, + TextCode, + TextMessage, + TEXT_BUFFER_SIZE)) + { +#if DBG + if ( handOS2 != NULL) + { + KdPrint(("Ow2DispalyardErrorPopup: error %lu on LoadString1\n", + GetLastError())); + } +#endif + strncpy(TextMessage, ErrMsg, TEXT_BUFFER_SIZE - 1); + } + + if (( handOS2 == NULL) || + !LoadString(handOS2, + CapCode, + CaptionMessage, + CAP_BUFFER_SIZE)) + { +#if DBG + if ( handOS2 != NULL) + { + KdPrint(("Ow2DispalyardErrorPopup: error %lu on LoadString2\n", + GetLastError())); + } +#endif + strncpy(CaptionMessage, ErrHdr, CAP_BUFFER_SIZE - 1); + } + + sprintf(CaptionBuffer, CaptionMessage, ApplNameBuff); + sprintf(TextBuffer, TextMessage, DriveBuf); + + return (MessageBox( + GetActiveWindow(), + TextBuffer, + CaptionBuffer, + MB_ABORTRETRYIGNORE | MB_DEFBUTTON2 | MB_ICONSTOP | MB_SYSTEMMODAL | MB_SETFOREGROUND + )); +} + +VOID +Ow2BoundAppLoadPopup( + IN PSZ AppName + ) + +/*++ + +Routine Description: + + Pops up a window informing the user that an attempt to load a bound app has + failed, and that s/he may try to use forcedos. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + CHAR TextBuff[512]; + CHAR CapBuff[CAP_BUFFER_SIZE]; + CHAR CapBuff2[CAP_BUFFER_SIZE]; + + if ((handOS2 == NULL) && + ((handOS2 = GetModuleHandle(NULL)) == NULL)) + { +#if DBG + KdPrint(("Ow2BoundAppLoadPopup: error %lu on GetModuleHandle\n", + GetLastError())); +#endif + } + + if (( handOS2 == NULL) || + !LoadString(handOS2, + IDS_OS2_BOUND_APP_LOAD_TXT, + TextBuff, + 512)) + { +#if DBG + if ( handOS2 != NULL) + { + KdPrint(("Ow2BoundAppLoadPopup: error %lu on LoadString1\n", + GetLastError())); + } +#endif + strncpy(TextBuff, DefaultBoundAppLoadText, 511); + } + + if (( handOS2 == NULL) || + !LoadString(handOS2, + IDS_OS2_BOUND_APP_LOAD_CAP, + CapBuff, + CAP_BUFFER_SIZE)) + { +#if DBG + if ( handOS2 != NULL) + { + KdPrint(("Ow2BoundAppLoadPopup: error %lu on LoadString2\n", + GetLastError())); + } +#endif + strncpy(CapBuff, DefaultBoundAppLoadCap, CAP_BUFFER_SIZE - 1); + } + + sprintf(CapBuff2, CapBuff, AppName); + + MessageBoxEx( NULL, + TextBuff, + CapBuff2, + MB_APPLMODAL | MB_ICONSTOP | MB_OK | MB_SETFOREGROUND, + 0 + ); +} + +#ifdef PMNT + +CHAR DefaultPMShellNotUpCap[] = "%s - PM Subsystem Application Load Failure"; +CHAR DefaultPMShellNotUpText[] = "You are attempting to execute an application under the PM Subsystem. \ +PM Shell needs to be running before this application. \ +Click on OK, or press ENTER to terminate the application, \ +then start PM Shell and re-try."; +CHAR Default2ndPMShellCap[] = "%s - PM Subsystem 2nd PM Shell Failure"; +CHAR Default2ndPMShellText[] = "You are attempting to execute PM Shell. \ +Another copy of PM shell is already running, and therefore \ +this copy cannot be executed by the PM Subsystem."; +CHAR DefaultPMShellFullScreenCap[] = "%s - PM Subsystem PM Shell Load Failure"; +CHAR DefaultPMShellFullScreenText[] = "PM Shell cannot be started from a full-screen CMD session. \ +Please start it from the Program Manager or from a windowed CMD session."; + +VOID +Ow2PMShellErrorPopup( + IN PSZ AppName, + IN int error_flag + ) + +/*++ + +Routine Description: + + Pops up a window informing the user that: + 1. an attempt to load a PM has failed because PM Shell is not up + 2. an attemp to load PM Shelll has failed because another copy + of PM Shell is already up. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + CHAR TextBuff[512]; + CHAR CapBuff[CAP_BUFFER_SIZE]; + CHAR CapBuff2[CAP_BUFFER_SIZE]; + UINT ids_txt,ids_cap; + CHAR *default_txt,*default_cap; + + if (error_flag == ERROR_PMSHELL_NOT_UP) { + ids_txt = IDS_OS2_PMSHELL_NOT_UP_TXT; + ids_cap = IDS_OS2_PMSHELL_NOT_UP_CAP; + default_txt = DefaultPMShellNotUpText; + default_cap = DefaultPMShellNotUpCap; + } + else if (error_flag == ERROR_PMSHELL_FULLSCREEN) + { + ids_txt = IDS_OS2_PMSHELL_FULLSCREEN_TXT; + ids_cap = IDS_OS2_PMSHELL_FULLSCREEN_CAP; + default_txt = DefaultPMShellFullScreenText; + default_cap = DefaultPMShellFullScreenCap; + } + else { + ids_txt = IDS_OS2_2ND_PMSHELL_TXT; + ids_cap = IDS_OS2_2ND_PMSHELL_CAP; + default_txt = Default2ndPMShellText; + default_cap = Default2ndPMShellCap; + } + + if ((handOS2 == NULL) && + ((handOS2 = GetModuleHandle(NULL)) == NULL)) + { +#if DBG + KdPrint(("Ow2PMShellErrorPopup: error %lu on GetModuleHandle\n", + GetLastError())); +#endif + } + + if (( handOS2 == NULL) || + !LoadString(handOS2, + ids_txt, + TextBuff, + 512)) + { +#if DBG + if ( handOS2 != NULL) + { + KdPrint(("Ow2PMShellErrorPopup: error %lu on LoadString1\n", + GetLastError())); + } +#endif + strncpy(TextBuff, default_txt, 511); + } + + if (( handOS2 == NULL) || + !LoadString(handOS2, + ids_cap, + CapBuff, + CAP_BUFFER_SIZE)) + { +#if DBG + if ( handOS2 != NULL) + { + KdPrint(("Ow2PMShellErrorPopup: error %lu on LoadString2\n", + GetLastError())); + } +#endif + strncpy(CapBuff, default_cap, CAP_BUFFER_SIZE - 1); + } + + sprintf(CapBuff2, CapBuff, AppName); + + MessageBoxEx( NULL, + TextBuff, + CapBuff2, + MB_APPLMODAL | MB_ICONSTOP | MB_OK | MB_SETFOREGROUND, + 0 + ); +} + +// PatrickQ: This function is called from another module (client\dllpmnt.c) and +// is here just because os2ses\os2.c has the right set of include files for +// WIN32 calls. + +VOID PMNTRemoveCloseMenuItem() +{ + HMENU SystemMenu; + DWORD rc; + + SystemMenu = GetSystemMenu(Ow2ForegroundWindow, FALSE); + if (SystemMenu == 0) + { +#if DBG + DbgPrint("Failed to get system menu !\n"); +#endif + return; + } + + rc = DeleteMenu( + SystemMenu, + SC_CLOSE, + MF_BYCOMMAND); + +#if DBG + if (!rc) + { + DbgPrint("Failed to delete menu - last error=%d\n", + GetLastError()); + } +#endif +} + +#endif //PMNT |