File indexing completed on 2024-03-24 15:27:21

0001 /*
0002    This file is part of the KDE libraries
0003    Copyright (C) 2004 Jarosław Staniek <staniek@kde.org>
0004    Copyright (C) 2007 Christian Ehrlicher <ch.ehrlicher@gmx.de>
0005    Copyright (C) 2007 Bernhard Loos <nhuh.put@web.de>
0006    Copyright (C) 2008-2009 Ralf Habacker <ralf.habacker@freenet.de>
0007 
0008    This library is free software; you can redistribute it and/or
0009    modify it under the terms of the GNU Library General Public
0010    License version 2 as published by the Free Software Foundation.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020    Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "kkernel_win.h"
0024 
0025 #include <QTextCodec>
0026 
0027 #ifdef Q_OS_WIN
0028 
0029 #include <klocalizedstring.h>
0030 
0031 #include <QDir>
0032 #include <QLibrary>
0033 
0034 #include <shellapi.h>
0035 #include <process.h>
0036 
0037 // console related includes
0038 #include <stdio.h>
0039 #include <fcntl.h>
0040 #include <io.h>
0041 #include <iostream>
0042 #include <fstream>
0043 #ifndef _USE_OLD_IOSTREAMS
0044 using namespace std;
0045 #endif
0046 
0047 #if defined(__MINGW32__)
0048 # define WIN32_CAST_CHAR (const WCHAR*)
0049 #else
0050 # define WIN32_CAST_CHAR (LPCWSTR)
0051 #endif
0052 
0053 #ifndef _WIN32_WCE
0054 static HINSTANCE kdecoreDllInstance = NULL;
0055 #else
0056 static HANDLE kdecoreDllInstance = NULL;
0057 #endif
0058 #ifdef KDELIBS_STATIC_LIBS
0059 static bool kde4prefixInitialized = false;
0060 #endif
0061 static wchar_t kde4prefixUtf16[MAX_PATH + 2] = L"";
0062 
0063 static QString *kde4Prefix = NULL;
0064 
0065 void initKde4prefixUtf16()
0066 {
0067     //the path is C:\some\path\kde4\bin\kdecore.dll
0068 #ifndef _WIN32_WCE
0069     GetModuleFileNameW(kdecoreDllInstance, kde4prefixUtf16, MAX_PATH + 1);
0070 #else
0071     GetModuleFileNameW((HMODULE)kdecoreDllInstance, kde4prefixUtf16, MAX_PATH + 1);
0072 #endif
0073     int bs1 = 0, bs2 = 0;
0074 
0075     //we convert \ to / and remove \bin\kdecore.dll from the string
0076     int pos;
0077     for (pos = 0; pos < MAX_PATH + 1 && kde4prefixUtf16[pos] != 0; ++pos) {
0078         if (kde4prefixUtf16[pos] == '\\') {
0079             bs1 = bs2;
0080             bs2 = pos;
0081             kde4prefixUtf16[pos] = '/';
0082         }
0083     }
0084     Q_ASSERT(bs1);
0085     Q_ASSERT(pos < MAX_PATH + 1);
0086     kde4prefixUtf16[bs1] = '/';
0087     kde4prefixUtf16[bs1 + 1] = 0;
0088 }
0089 
0090 // can't use QCoreApplication::applicationDirPath() because sometimes we
0091 // don't have an instantiated QCoreApplication
0092 QString getKde4Prefix()
0093 {
0094 #ifdef _WIN32_WCE
0095     if (kde4prefixInitialized) {
0096         return QString::fromUtf16((ushort *) kde4prefixUtf16);
0097     }
0098 
0099     QDir kde4prefixDir(QString::fromUtf16((ushort *) STATIC_INSTALL_PATH));
0100     if (kde4prefixDir.exists()) {
0101         wcscpy(kde4prefixUtf16, STATIC_INSTALL_PATH);
0102         kde4prefixUtf16[wcslen(kde4prefixUtf16)] = 0;
0103         kde4prefixInitialized = true;
0104         return QString::fromUtf16((ushort *) kde4prefixUtf16);
0105     } else {
0106         bool ok;
0107         QString retval = getWin32RegistryValue(HKEY_LOCAL_MACHINE, "Software\\kde", "KDEDIRS", &ok);
0108         if (!ok) {
0109             return QString();
0110         } else {
0111             retval = QDir::fromNativeSeparators(retval);
0112             wcscpy(kde4prefixUtf16, retval.utf16());
0113             kde4prefixUtf16[wcslen(kde4prefixUtf16)] = 0;
0114             kde4prefixInitialized = true;
0115             return retval;
0116         }
0117     }
0118 #else
0119     // we can get called after DLL_PROCESS_DETACH!
0120     return kde4Prefix ? *kde4Prefix : QString::fromUtf16((ushort *) kde4prefixUtf16);
0121 #endif
0122 }
0123 
0124 #ifndef KDELIBS_STATIC_LIBS
0125 /**
0126  * The dll entry point - get the instance handle for GetModuleFleNameW
0127  * Maybe also some special initialization / cleanup can be done here
0128  **/
0129 extern "C"
0130 #ifndef _WIN32_WCE
0131 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
0132 #else
0133 BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
0134 #endif
0135 {
0136     switch (fdwReason) {
0137     case DLL_PROCESS_ATTACH:
0138         kdecoreDllInstance = hinstDLL;
0139         initKde4prefixUtf16();
0140         kde4Prefix = new QString(QString::fromUtf16((ushort *) kde4prefixUtf16));
0141         break;
0142     case DLL_PROCESS_DETACH:
0143         /* msdn:
0144            When handling DLL_PROCESS_DETACH, a DLL should free resources such
0145            as heap memory only if the DLL is being unloaded dynamically (the
0146            lpReserved parameter is NULL). If the process is terminating (the
0147            lpvReserved parameter is non-NULL), all threads in the process except
0148            the current thread either have exited already or have been explicitly
0149            terminated by a call to the ExitProcess function, which might leave
0150            some process resources such as heaps in an inconsistent state. In this
0151            case, it is not safe for the DLL to clean up the resources. Instead,
0152            the DLL should allow the operating system to reclaim the memory.
0153          */
0154         if (lpReserved == NULL) {
0155             delete kde4Prefix;
0156         }
0157         kde4Prefix = 0;
0158         break;
0159     default:
0160         break;
0161     }
0162     return true;
0163 }
0164 
0165 #endif
0166 
0167 /**
0168  \return a value from MS Windows native registry.
0169  @param key is usually one of HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE
0170         constants defined in WinReg.h.
0171  @param subKey is a registry subkey defined as a path to a registry folder, eg.
0172         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
0173         ('\' delimiter must be used)
0174  @param item is an item inside subKey or "" if default folder's value should be returned
0175  @param ok if not null, will be set to true on success and false on failure
0176 */
0177 QString getWin32RegistryValue(HKEY key, const QString &subKey, const QString &item, bool *ok)
0178 {
0179 #define FAILURE \
0180     { if (ok) \
0181             *ok = false; \
0182         return QString(); }
0183 
0184     if (subKey.isEmpty()) {
0185         FAILURE;
0186     }
0187     HKEY hKey;
0188     TCHAR *lszValue;
0189     DWORD dwType = REG_SZ;
0190     DWORD dwSize;
0191 
0192     if (ERROR_SUCCESS != RegOpenKeyExW(key, WIN32_CAST_CHAR subKey.utf16(), 0, KEY_READ, &hKey)) {
0193         FAILURE;
0194     }
0195 
0196     if (ERROR_SUCCESS != RegQueryValueExW(hKey, WIN32_CAST_CHAR item.utf16(), NULL, NULL, NULL, &dwSize)) {
0197         FAILURE;
0198     }
0199 
0200     lszValue = new TCHAR[dwSize];
0201 
0202     if (ERROR_SUCCESS != RegQueryValueExW(hKey, WIN32_CAST_CHAR item.utf16(), NULL, &dwType, (LPBYTE) lszValue, &dwSize)) {
0203         delete [] lszValue;
0204         FAILURE;
0205     }
0206     RegCloseKey(hKey);
0207 
0208     QString res = QString::fromUtf16((const ushort *) lszValue);
0209     delete [] lszValue;
0210 
0211     if (ok) {
0212         *ok = true;
0213     }
0214 
0215     return res;
0216 }
0217 
0218 bool showWin32FilePropertyDialog(const QString &fileName)
0219 {
0220     QString path_ = QDir::toNativeSeparators(QFileInfo(fileName).absoluteFilePath());
0221 
0222 #ifndef _WIN32_WCE
0223     SHELLEXECUTEINFOW execInfo;
0224 #else
0225     SHELLEXECUTEINFO execInfo;
0226 #endif
0227     memset(&execInfo, 0, sizeof(execInfo));
0228     execInfo.cbSize = sizeof(execInfo);
0229 #ifndef _WIN32_WCE
0230     execInfo.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
0231 #else
0232     execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
0233 #endif
0234     const QString verb(QLatin1String("properties"));
0235     execInfo.lpVerb = WIN32_CAST_CHAR verb.utf16();
0236     execInfo.lpFile = WIN32_CAST_CHAR path_.utf16();
0237 #ifndef _WIN32_WCE
0238     return ShellExecuteExW(&execInfo);
0239 #else
0240     return ShellExecuteEx(&execInfo);
0241     //There is no native file property dialog in wince
0242     // return false;
0243 #endif
0244 }
0245 
0246 // note: QLocale().name().left(2).toLatin1() returns the same
0247 
0248 QByteArray getWin32LocaleName()
0249 {
0250     bool ok;
0251     QString localeNumber = getWin32RegistryValue(HKEY_CURRENT_USER,
0252                            QLatin1String("Control Panel\\International"),
0253                            QLatin1String("Locale"), &ok);
0254     if (!ok) {
0255         return QByteArray();
0256     }
0257     QString localeName = getWin32RegistryValue(HKEY_LOCAL_MACHINE,
0258                          QLatin1String("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout\\DosKeybCodes"),
0259                          localeNumber, &ok);
0260     if (!ok) {
0261         return QByteArray();
0262     }
0263     return localeName.toLatin1();
0264 }
0265 
0266 /**
0267  \return a value from MS Windows native registry for shell folder \a folder.
0268 */
0269 QString getWin32ShellFoldersPath(const QString &folder)
0270 {
0271     return getWin32RegistryValue(HKEY_CURRENT_USER,
0272                                  QLatin1String("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"),
0273                                  folder);
0274 }
0275 
0276 /**
0277   kde and qt debug message printer using windows debug message port
0278  */
0279 static void kMessageOutputDebugString(QtMsgType type, const char *msg)
0280 {
0281     int BUFSIZE = 4096;
0282     char *buf = new char[BUFSIZE];
0283     switch (type) {
0284     case QtDebugMsg:
0285         strlcpy(buf, "Debug:", BUFSIZE);
0286         strlcat(buf, msg, BUFSIZE);
0287         break;
0288     case QtWarningMsg:
0289         strlcpy(buf, "Warning:", BUFSIZE);
0290         strlcat(buf, msg, BUFSIZE);
0291         break;
0292     case QtCriticalMsg:
0293         strlcpy(buf, "Critical:", BUFSIZE);
0294         strlcat(buf, msg, BUFSIZE);
0295         break;
0296     case QtFatalMsg:
0297         strlcpy(buf, "Fatal:", BUFSIZE);
0298         strlcat(buf, msg, BUFSIZE);
0299         //abort();
0300         break;
0301     }
0302     strlcat(buf, "\n", BUFSIZE);
0303     OutputDebugStringW((WCHAR *)QString::fromLatin1(buf).utf16());
0304     delete[] buf;
0305 }
0306 
0307 /**
0308   kde and qt debug message printer using FILE pointer based output
0309  */
0310 static void kMessageOutputFileIO(QtMsgType type, const char *msg)
0311 {
0312     switch (type) {
0313     case QtDebugMsg:
0314         fprintf(stderr, "Debug: %s\n", msg);
0315         break;
0316     case QtWarningMsg:
0317         fprintf(stderr, "Warning: %s\n", msg);
0318         break;
0319     case QtCriticalMsg:
0320         fprintf(stderr, "Critical: %s\n", msg);
0321         break;
0322     case QtFatalMsg:
0323         fprintf(stderr, "Fatal: %s\n", msg);
0324         //abort();
0325     }
0326 }
0327 
0328 /**
0329   try to attach to the parents console
0330   \return true if console has been attached, false otherwise
0331 */
0332 typedef BOOL (WINAPI *attachConsolePtr)(DWORD dwProcessId);
0333 static attachConsolePtr attachConsole = 0;
0334 static bool attachConsoleResolved = false;
0335 static bool attachToConsole()
0336 {
0337     bool out = true;
0338     if (!attachConsoleResolved) {
0339         attachConsoleResolved = true;
0340         attachConsole = (attachConsolePtr)QLibrary::resolve(QLatin1String("kernel32"), "AttachConsole");
0341     }
0342     out = attachConsole ? attachConsole(~0U) != 0 : false;
0343     if (GetLastError() == ERROR_ACCESS_DENIED) {
0344         //we are already atatched to a console
0345         out  = true;
0346     }
0347     return out;
0348 }
0349 
0350 /**
0351   redirect stdout, stderr and
0352   cout, wcout, cin, wcin, wcerr, cerr, wclog and clog to console
0353 */
0354 static void redirectToConsole()
0355 {
0356 //FIXME: for wince we cannot set stdio buffers
0357 #ifndef _WIN32_WCE
0358     int hCrt;
0359     FILE *hf;
0360     int i;
0361 
0362     hCrt = _open_osfhandle((intptr_t) GetStdHandle(STD_INPUT_HANDLE), _O_TEXT);
0363     if (hCrt != -1) {
0364         hf = _fdopen(hCrt, "r");
0365         *stdin = *hf;
0366         i = setvbuf(stdin, NULL, _IONBF, 0);
0367     }
0368 
0369     hCrt = _open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
0370     if (hCrt != -1) {
0371         hf = _fdopen(hCrt, "w");
0372         *stdout = *hf;
0373         i = setvbuf(stdout, NULL, _IONBF, 0);
0374     }
0375 
0376     hCrt = _open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), _O_TEXT);
0377     if (hCrt != -1) {
0378         hf = _fdopen(hCrt, "w");
0379         *stderr = *hf;
0380         i = setvbuf(stderr, NULL, _IONBF, 0);
0381     }
0382     // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
0383     // point to console as well
0384     ios::sync_with_stdio();
0385 #endif
0386 }
0387 
0388 #include <streambuf>
0389 
0390 /**
0391   ios related debug message printer for win32
0392 */
0393 class debug_streambuf: public std::streambuf
0394 {
0395 public:
0396     debug_streambuf(const char *prefix)
0397     {
0398         strcpy(buf, prefix);
0399         index = rindex = strlen(buf);
0400     }
0401 
0402 protected:
0403     virtual int overflow(int c = EOF)
0404     {
0405         if (c != EOF) {
0406             char cc = traits_type::to_char_type(c);
0407             // @TODO: buffer size checking
0408             buf[index++] = cc;
0409             if (cc == '\n') {
0410                 buf[index] = '\0';
0411                 OutputDebugStringW((WCHAR *)QString::fromLatin1(buf).utf16());
0412                 index = rindex;
0413             }
0414         }
0415         return traits_type::not_eof(c);
0416     }
0417 private:
0418     char buf[4096];
0419     int index, rindex;
0420 };
0421 
0422 /**
0423   retrieve type of win32 subsystem from the executable header
0424   \return type of win32 subsystem - the subsystem types are defined at http://msdn.microsoft.com/en-us/library/ms680339(VS.85).aspx
0425 */
0426 static int subSystem()
0427 {
0428 #ifdef _WIN32_WCE
0429     // there is only one subsystem on Windows CE
0430     return IMAGE_SUBSYSTEM_WINDOWS_CE_GUI;
0431 #else
0432     static int subSystem = -1;
0433     if (subSystem > -1) {
0434         return subSystem;
0435     }
0436 
0437     // get base address of memory mapped executable
0438     PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);
0439     PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((char *)dosHeader + dosHeader->e_lfanew);
0440     if (ntHeader->Signature != 0x00004550) {
0441         subSystem = IMAGE_SUBSYSTEM_UNKNOWN;
0442         return subSystem;
0443     }
0444     subSystem = ntHeader->OptionalHeader.Subsystem;
0445     return subSystem;
0446 #endif
0447 }
0448 
0449 /**
0450  win32 debug and console output handling
0451 
0452  source type of output
0453     1. kde/qt debug system  - kDebug(), kWarning(), kFatal(), kError(), qDebug(), qWarning(), qFatal()
0454     2. ios  - cout, wcout, wcerr, cerr, wclog and clog
0455     3. FILE * - stdout,stderr
0456 
0457  application  console    ------------------ output -----------------
0458     type     available   qt/kde-debug         ios             FILE *
0459 
0460     cui        yes        console           console         console
0461     cui        no        win32debug         win32debug      no output[1]
0462 
0463     gui        yes       win32debug         console         console
0464     gui        no        win32debug         win32debug      win32debug
0465 
0466     win-ce     no        win32debug         win32debug      win32debug
0467 
0468 [1]no redirect solution for FILE * based output yet
0469 
0470  TODO: report events to the windows event log system
0471  http://msdn.microsoft.com/en-us/library/aa363680(VS.85).aspx
0472 */
0473 
0474 /**
0475  setup up debug output
0476 */
0477 static class kMessageOutputInstaller
0478 {
0479 public:
0480     kMessageOutputInstaller() : stdoutBuffer("stdout:"), stderrBuffer("stderr:"), oldStdoutBuffer(0), oldStderrBuffer(0)
0481     {
0482         if (subSystem() == IMAGE_SUBSYSTEM_WINDOWS_CUI) {
0483             if (attachToConsole()) {
0484                 // setup kde and qt level
0485                 qInstallMsgHandler(kMessageOutputFileIO);
0486                 // redirect ios and file io to console
0487                 redirectToConsole();
0488             } else {
0489                 // setup kde and qt level
0490                 qInstallMsgHandler(kMessageOutputDebugString);
0491                 // redirect ios to debug message port
0492                 oldStdoutBuffer = std::cout.rdbuf(&stdoutBuffer);
0493                 oldStderrBuffer = std::cerr.rdbuf(&stderrBuffer);
0494             }
0495         } else if (subSystem() == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
0496             // setup kde and qt level
0497             qInstallMsgHandler(kMessageOutputDebugString);
0498             // try to get a console
0499             if (attachToConsole()) {
0500                 redirectToConsole();
0501             } else {
0502                 // redirect ios to debug message port
0503                 oldStdoutBuffer = std::cout.rdbuf(&stdoutBuffer);
0504                 oldStderrBuffer = std::cerr.rdbuf(&stderrBuffer);
0505                 // TODO: redirect FILE * level to console, no idea how to do yet
0506             }
0507         } else if (subSystem() == IMAGE_SUBSYSTEM_WINDOWS_CE_GUI) {
0508             // do not try to get a console on WinCE systems
0509             qInstallMsgHandler(kMessageOutputDebugString);
0510             oldStdoutBuffer = std::cout.rdbuf(&stdoutBuffer);
0511             oldStderrBuffer = std::cerr.rdbuf(&stderrBuffer);
0512         } else {
0513             qWarning("unknown subsystem %d detected, could not setup qt message handler", subSystem());
0514         }
0515     }
0516     ~kMessageOutputInstaller()
0517     {
0518         if (oldStdoutBuffer) {
0519             std::cout.rdbuf(oldStdoutBuffer);
0520         }
0521         if (oldStderrBuffer) {
0522             std::cerr.rdbuf(oldStderrBuffer);
0523         }
0524     }
0525 
0526 private:
0527     debug_streambuf stdoutBuffer;
0528     debug_streambuf stderrBuffer;
0529     std::streambuf *oldStdoutBuffer;
0530     std::streambuf *oldStderrBuffer;
0531 
0532 } kMessageOutputInstallerInstance;
0533 
0534 bool isExecutable(const QString &file)
0535 {
0536     return (file.endsWith(QLatin1String(".exe")) ||
0537             file.endsWith(QLatin1String(".com")) ||
0538             file.endsWith(QLatin1String(".bat")) ||
0539             file.endsWith(QLatin1String(".sln")) ||
0540             file.endsWith(QLatin1String(".lnk")));
0541 
0542 }
0543 
0544 #endif  // Q_OS_WIN