/**
 * Shows a balloon message using VBoxTray's notification area in the
 * Windows task bar.
 *
 * @param   hwndParent          Window handle of parent.
 * @param   string_size         Size of variable string.
 * @param   variables           The actual variable string.
 * @param   stacktop            Pointer to a pointer to the current stack.
 */
VBOXINSTALLHELPER_EXPORT VBoxTrayShowBallonMsg(HWND hwndParent, int string_size,
                                               TCHAR *variables, stack_t **stacktop)
{
    EXDLL_INIT();

    VBOXTRAYIPCHEADER hdr;
    hdr.ulMsg = VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG;
    hdr.cbBody = sizeof(VBOXTRAYIPCMSG_SHOWBALLOONMSG);

    VBOXTRAYIPCMSG_SHOWBALLOONMSG msg;
    HRESULT hr = vboxPopString(msg.szContent, sizeof(msg.szContent) / sizeof(TCHAR));
    if (SUCCEEDED(hr))
        hr = vboxPopString(msg.szTitle, sizeof(msg.szTitle) / sizeof(TCHAR));
    if (SUCCEEDED(hr))
        hr = vboxPopULong(&msg.ulType);
    if (SUCCEEDED(hr))
        hr = vboxPopULong(&msg.ulShowMS);

    if (SUCCEEDED(hr))
    {
        msg.ulFlags = 0;

        HANDLE hPipe = vboxIPCConnect();
        if (hPipe)
        {
            hr = vboxIPCWriteMessage(hPipe, (BYTE*)&hdr, sizeof(VBOXTRAYIPCHEADER));
            if (SUCCEEDED(hr))
                hr = vboxIPCWriteMessage(hPipe, (BYTE*)&msg, sizeof(VBOXTRAYIPCMSG_SHOWBALLOONMSG));
            vboxIPCDisconnect(hPipe);
        }
    }

    /* Push simple return value on stack. */
    SUCCEEDED(hr) ? pushstring("0") : pushstring("1");
}
/**
 * Retrieves a file's vendor.
 * Outputs the vendor's name or an error message (if not found/invalid) on stack.
 *
 * @param   hwndParent          Window handle of parent.
 * @param   string_size         Size of variable string.
 * @param   variables           The actual variable string.
 * @param   stacktop            Pointer to a pointer to the current stack.
 */
VBOXINSTALLHELPER_EXPORT FileGetVendor(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop)
{
    NOREF(hwndParent);
    EXDLL_INIT();

    TCHAR szFile[MAX_PATH + 1];
    HRESULT hr = vboxPopString(szFile, sizeof(szFile) / sizeof(TCHAR));
    if (SUCCEEDED(hr))
    {
        DWORD dwInfoSize = GetFileVersionInfoSize(szFile, NULL /* lpdwHandle */);
        if (dwInfoSize)
        {
            void *pFileInfo = GlobalAlloc(GMEM_FIXED, dwInfoSize);
            if (pFileInfo)
            {
                if (GetFileVersionInfo(szFile, 0, dwInfoSize, pFileInfo))
                {
                    LPVOID pvInfo;
                    UINT puInfoLen;
                    if (VerQueryValue(pFileInfo, _T("\\VarFileInfo\\Translation"),
                                      &pvInfo, &puInfoLen))
                    {
                        WORD wCodePage = LOWORD(*(DWORD*)pvInfo);
                        WORD wLanguageID = HIWORD(*(DWORD*)pvInfo);

                        TCHAR szQuery[MAX_PATH];
#pragma warning(suppress:4995) /* warning C4995: '_sntprintf': name was marked as #pragma deprecated */
                        _sntprintf(szQuery, sizeof(szQuery), _T("StringFileInfo\\%04X%04X\\CompanyName"),
                                   wCodePage,wLanguageID);

                        LPCTSTR pcData;
                        if (VerQueryValue(pFileInfo, szQuery,(void**)&pcData, &puInfoLen))
                        {
                            pushstring(pcData);
                        }
                        else
                            hr = __HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
                    }
                    else
                        hr = __HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
                }
                GlobalFree(pFileInfo);
            }
            else
                hr = __HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
        }
        else
            hr = __HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
    }

    if (FAILED(hr))
        vboxPushResultAsString(hr);
}
/**
 * Disables the Windows File Protection for a specified file
 * using an undocumented SFC API call. Don't try this at home!
 *
 * @param   hwndParent          Window handle of parent.
 * @param   string_size         Size of variable string.
 * @param   variables           The actual variable string.
 * @param   stacktop            Pointer to a pointer to the current stack.
 */
VBOXINSTALLHELPER_EXPORT DisableWFP(HWND hwndParent, int string_size,
                                    TCHAR *variables, stack_t **stacktop)
{
    EXDLL_INIT();

    TCHAR szFile[MAX_PATH + 1];
    HRESULT hr = vboxPopString(szFile, sizeof(szFile) / sizeof(TCHAR));
    if (SUCCEEDED(hr))
    {
        HMODULE hSFC = loadSystemDll("sfc_os.dll"); /** @todo Replace this by RTLdr APIs. */
        if (NULL != hSFC)
        {
            g_pfnSfcFileException = (PFNSFCFILEEXCEPTION)GetProcAddress(hSFC, "SfcFileException");
            if (g_pfnSfcFileException == NULL)
            {
                /* If we didn't get the proc address with the call above, try it harder with
                 * the (zero based) index of the function list. */
                g_pfnSfcFileException = (PFNSFCFILEEXCEPTION)GetProcAddress(hSFC, (LPCSTR)5);
                if (g_pfnSfcFileException == NULL)
                    hr = HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND);
            }
        }
        else
            hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);

        if (SUCCEEDED(hr))
        {
            WCHAR *pwszFile;
            hr = vboxChar2WCharAlloc(szFile, &pwszFile);
            if (SUCCEEDED(hr))
            {
                if (g_pfnSfcFileException(0, pwszFile, -1) != 0)
                    hr = HRESULT_FROM_WIN32(GetLastError());
                vboxChar2WCharFree(pwszFile);
            }
        }

        if (hSFC)
            FreeLibrary(hSFC);
    }

    vboxPushResultAsString(hr);
}
/**
 * Shows a balloon message using VBoxTray's notification area in the
 * Windows task bar.
 *
 * @param   hwndParent          Window handle of parent.
 * @param   string_size         Size of variable string.
 * @param   variables           The actual variable string.
 * @param   stacktop            Pointer to a pointer to the current stack.
 */
VBOXINSTALLHELPER_EXPORT VBoxTrayShowBallonMsg(HWND hwndParent, int string_size,
                                               TCHAR *variables, stack_t **stacktop)
{
    EXDLL_INIT();

    char szMsg[256];
    char szTitle[128];
    HRESULT hr = vboxPopString(szMsg, sizeof(szMsg) / sizeof(char));
    if (SUCCEEDED(hr))
        hr = vboxPopString(szTitle, sizeof(szTitle) / sizeof(char));

    /** @todo Do we need to restore the stack on failure? */

    if (SUCCEEDED(hr))
    {
        RTR3InitDll(0);

        uint32_t cbMsg = sizeof(VBOXTRAYIPCMSG_SHOWBALLOONMSG)
                       + strlen(szMsg) + 1    /* Include terminating zero */
                       + strlen(szTitle) + 1; /* Dito. */
        Assert(cbMsg);
        PVBOXTRAYIPCMSG_SHOWBALLOONMSG pIpcMsg =
            (PVBOXTRAYIPCMSG_SHOWBALLOONMSG)RTMemAlloc(cbMsg);
        if (pIpcMsg)
        {
            /* Stuff in the strings. */
            memcpy(pIpcMsg->szMsgContent, szMsg, strlen(szMsg)   + 1);
            memcpy(pIpcMsg->szMsgTitle, szTitle, strlen(szTitle) + 1);

            /* Pop off the values in reverse order from the stack. */
            if (SUCCEEDED(hr))
                hr = vboxPopULong((ULONG*)&pIpcMsg->uType);
            if (SUCCEEDED(hr))
                hr = vboxPopULong((ULONG*)&pIpcMsg->uShowMS);

            if (SUCCEEDED(hr))
            {
                RTLOCALIPCSESSION hSession = 0;
                int rc = vboxConnectToVBoxTray(&hSession);
                if (RT_SUCCESS(rc))
                {
                    VBOXTRAYIPCHEADER ipcHdr = { VBOXTRAY_IPC_HDR_MAGIC, 0 /* Header version */,
                                                 VBOXTRAYIPCMSGTYPE_SHOWBALLOONMSG, cbMsg };

                    rc = RTLocalIpcSessionWrite(hSession, &ipcHdr, sizeof(ipcHdr));
                    if (RT_SUCCESS(rc))
                        rc = RTLocalIpcSessionWrite(hSession, pIpcMsg, cbMsg);

                    int rc2 = RTLocalIpcSessionClose(hSession);
                    if (RT_SUCCESS(rc))
                        rc = rc2;
                }

                if (RT_FAILURE(rc))
                    hr = __HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE);
            }

            RTMemFree(pIpcMsg);
        }
        else
            hr = __HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
    }

    /* Push simple return value on stack. */
    SUCCEEDED(hr) ? pushstring("0") : pushstring("1");
}
/**
 * Retrieves a file's architecture (x86 or amd64).
 * Outputs "x86", "amd64" or an error message (if not found/invalid) on stack.
 *
 * @param   hwndParent          Window handle of parent.
 * @param   string_size         Size of variable string.
 * @param   variables           The actual variable string.
 * @param   stacktop            Pointer to a pointer to the current stack.
 */
VBOXINSTALLHELPER_EXPORT FileGetArchitecture(HWND hwndParent, int string_size,
                                             TCHAR *variables, stack_t **stacktop)
{
    EXDLL_INIT();

    TCHAR szFile[MAX_PATH + 1];
    HRESULT hr = vboxPopString(szFile, sizeof(szFile) / sizeof(TCHAR));
    if (SUCCEEDED(hr))
    {
        /* See: http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx */
        FILE *pFh = fopen(szFile, "rb");
        if (pFh)
        {
            /* Assume the file is invalid. */
            hr = __HRESULT_FROM_WIN32(ERROR_FILE_INVALID);

            BYTE byOffsetPE; /* Absolute offset of PE signature. */

            /* Do some basic validation. */
            /* Check for "MZ" header (DOS stub). */
            BYTE byBuf[255];
            if (   fread(&byBuf, sizeof(BYTE), 2, pFh) == 2
                && !memcmp(&byBuf, "MZ", 2))
            {
                /* Seek to 0x3C to get the PE offset. */
                if (!fseek(pFh, 60L /*0x3C*/, SEEK_SET))
                {
                    /* Read actual offset of PE signature. */
                    if (fread(&byOffsetPE, sizeof(BYTE), 1, pFh) == 1)
                    {
                        /* ... and seek to it. */
                        if (!fseek(pFh, byOffsetPE, SEEK_SET))
                        {
                            /* Validate PE signature. */
                            if (fread(byBuf, sizeof(BYTE), 4, pFh) == 4)
                            {
                                if (!memcmp(byBuf, "PE\0\0", 4))
                                    hr = S_OK;
                            }
                        }
                    }
                }
            }

            /* Validation successful? */
            if (SUCCEEDED(hr))
            {
                BYTE byOffsetCOFF = byOffsetPE + 0x4; /* Skip PE signature. */

                /** @todo When we need to do more stuff here, we probably should
                 *        mmap the file w/ a struct so that we easily could access
                 *        all the fixed size stuff. Later. */

                /* Jump to machine type (first entry, 2 bytes):
                 * Use absolute PE offset retrieved above. */
                if (!fseek(pFh, byOffsetCOFF, SEEK_SET))
                {
                    WORD wMachineType;
                    if (fread(&wMachineType, 1,
                              sizeof(wMachineType), pFh) == 2)
                    {
                        switch (wMachineType)
                        {
                            case 0x14C: /* Intel 86 */
                                pushstring("x86");
                                break;

                            case 0x8664: /* AMD64 / x64 */
                                pushstring("amd64");
                                break;

                            default:
                                hr = __HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
                                break;
                        }
                    }
                    else
                        hr = __HRESULT_FROM_WIN32(ERROR_FILE_INVALID);
                }
                else
                    hr = __HRESULT_FROM_WIN32(ERROR_FILE_INVALID);
            }

            fclose(pFh);
        }
        else
            hr = __HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
    }

    if (FAILED(hr))
        vboxPushResultAsString(hr);
}
/**
 * Retrieves a file's architecture (x86 or amd64).
 * Outputs "x86", "amd64" or an error message (if not found/invalid) on stack.
 *
 * @param   hwndParent          Window handle of parent.
 * @param   string_size         Size of variable string.
 * @param   variables           The actual variable string.
 * @param   stacktop            Pointer to a pointer to the current stack.
 */
VBOXINSTALLHELPER_EXPORT FileGetArchitecture(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop)
{
    NOREF(hwndParent);
    EXDLL_INIT();

    TCHAR szFile[MAX_PATH + 1];
    HRESULT hr = vboxPopString(szFile, sizeof(szFile) / sizeof(TCHAR));
    if (SUCCEEDED(hr))
    {
        /* See: http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx */
        FILE *pFh = fopen(szFile, "rb");
        if (pFh)
        {
            /* Assume the file is invalid. */
            hr = __HRESULT_FROM_WIN32(ERROR_FILE_INVALID);

            BYTE offPeHdr = 0; /* Absolute offset of PE signature. */

            /* Do some basic validation. */
            /* Check for "MZ" header (DOS stub). */
            BYTE byBuf[255];
            if (   fread(&byBuf, sizeof(BYTE), 2, pFh) == 2
                && !memcmp(&byBuf, "MZ", 2))
            {
                /* Seek to 0x3C to get the PE offset. */
                if (!fseek(pFh, 60L /*0x3C*/, SEEK_SET))
                {
                    /* Read actual offset of PE signature. */
/** @todo r=bird: You've obviously no clue about the structure you're messing with here.  The field is NOT a BYTE
 * field but a int32_t/uint32_t!  The MZ header is defined as IMAGE_DOS_HEADER by windows.h (well, winnt.h), and the
 * field you're accessing is e_lfanew.  Please rewrite this hack to use the structures!  (Also, the MZ structure is
 * OPTIONAL, just in case you didn't know.)  */
#ifdef DEBUG_andy
# error "Fix this"
#endif
                    if (fread(&offPeHdr, sizeof(BYTE), 1, pFh) == 1)
                    {
                        /* ... and seek to it. */
                        if (!fseek(pFh, offPeHdr, SEEK_SET))
                        {
                            /* Validate PE signature. */
                            if (fread(byBuf, sizeof(BYTE), 4, pFh) == 4)
                            {
                                if (!memcmp(byBuf, "PE\0\0", 4))
                                    hr = S_OK;
                            }
                        }
                    }
                }
            }

            /* Validation successful? */
            if (SUCCEEDED(hr))
            {
                BYTE offFileHeaderMachineField = offPeHdr + 0x4; /* Skip PE signature. */

                /** @todo When we need to do more stuff here, we probably should
                 *        mmap the file w/ a struct so that we easily could access
                 *        all the fixed size stuff. Later. */

                /* Jump to machine type (first entry, 2 bytes):
                 * Use absolute PE offset retrieved above. */
                if (!fseek(pFh, offFileHeaderMachineField, SEEK_SET))
                {
                    WORD wMachineType;
                    if (fread(&wMachineType, 1,
                              sizeof(wMachineType), pFh) == 2)
                    {
                        switch (wMachineType)
                        {
                            case 0x14C: /* Intel 86 */
                                pushstring("x86");
                                break;

                            case 0x8664: /* AMD64 / x64 */
                                pushstring("amd64");
                                break;

                            default:
                                hr = __HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
                                break;
                        }
                    }
                    else
                        hr = __HRESULT_FROM_WIN32(ERROR_FILE_INVALID);
                }
                else
                    hr = __HRESULT_FROM_WIN32(ERROR_FILE_INVALID);
            }

            fclose(pFh);
        }
        else
            hr = __HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
    }

    if (FAILED(hr))
        vboxPushResultAsString(hr);
}