以二进制形式查看 MSI 字符串 [英] view MSI strings in binary

查看:20
本文介绍了以二进制形式查看 MSI 字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当 MSI 以二进制模式运行时,我想查看显示在 UI 上的字符串/文本.

I wanted to view the strings/text that are/is displayed on the UI when MSI is run in binary mode.

基本上我有本地化的 wxl 文件和本地化的 msi.想比较文字.

basically i have the localized wxl files and the localized msi. wanted to compare the text.

所以我的方法是查看要比较的字符串的二进制内容.谁能建议我可以使用哪些工具?

So my approach is to view the binary content of the strings to compare. Can anyone please suggest me what tools i can use?

使用 orca 我能够看到字符串.但我想看看那些二进制/十六进制值.

Using orca I am able to see the strings. But I would like to see the binary/hex value for those.

非常感谢

最好的问候,马克

推荐答案

我不知道将字符串导出为二进制的工具.大多数情况下确实不需要.

I don't know tools which export strings as binary. In the most cases it is really not needed.

如果确实需要获取有关字符串的二进制信息,可以使用 IStorage::OpenStreamIStream::StatSTATFLAG_NONAME 参数和 IStream::Read 直接从 MSI 读取信息.有关字符串的信息保存在名称为_StringData"和_StringPool"的流中.流的名称以简单的方式编码.如果您有兴趣,我可以向您发布显示如何解码名称的代码.

If you do need to get binary information about the strings you can use IStorage::OpenStream, IStream::Stat with STATFLAG_NONAME parameter and IStream::Read to read information from the MSI directly. The information about the stings are saved in streams with the name "_StringData" and "_StringPool". The names of the streams are in a simple way encoded. If you have an interest I could post you the code which shows how to decode the names.

更新:我从我的旧实用程序中准备了小演示.该演示从_StringData"和_StringPool"加载字符串并以可读格式转储信息.如果调整行中的常量

UPDATED: I prepared small demo from my old utility. The demo loads the strings from the "_StringData" and "_StringPool" and dump the information in the readable format. If you adjust the constants in the line

bSuccess = LoadStringPool (pStg, TRUE, 80, 10, 10);

(见下文)您可以转储更完整的信息.同样,您可以轻松修改代码,将相应的流以二进制形式保存在文件中.

(see below) you can dump more full information. In the same way you can easy modify the code to save the corresponding streams in a file as binary.

您在下面找到的 C 代码

The C code you find below

#define STRICT
#define _WIN32_WINNT 0x501
#define COBJMACROS

#include <stdio.h>
#include <windows.h>
#include <ShLwApi.h>    // for wnsprintf
#include <malloc.h>     // for _alloca
#include <lmerr.h>
#include <tchar.h>
// IPropertyUI in <ShObjIdl.h>
//#include <msi.h>

#define ARRAY_SIZE(arr)     (sizeof(arr)/sizeof(arr[0]))
#define CONST_STR_LEN(s)    (ARRAY_SIZE(s) - 1)

#pragma comment (lib, "ole32.lib")
#pragma comment (lib, "ShLwApi.lib")

typedef struct tagMSISTRINGTABLE {
    UINT    cStrings;
    LPWSTR  pszStringData;      // have '\0' bytes between strings
    LPWSTR *ppszStringPool;     // array of pointers to the corresponding string in pszStringData data block
    WORD    cbStringIdSize;     // size of StringId in all tables in bytes. Typically if cStrings<32K, cbStringIdSize=2, then 3 or more.
                                // cbStringIdSize value will be calculated based on first bytes of _StringPool stream.
} MSISTRINGTABLE, *PMSISTRINGTABLE;

MSISTRINGTABLE g_StringTable = {0, NULL, NULL, 2};

#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}

MIDL_DEFINE_GUID (CLSID, CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);  //.mst
MIDL_DEFINE_GUID (CLSID, CLSID_MsiDatabase,  0x000c1084, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);  //.msi, .msm
MIDL_DEFINE_GUID (CLSID, CLSID_MsiPatch,     0x000c1086, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);  //.msp

void DisplayErrorMessage (DWORD dwErrorCode, LPCTSTR pszTemplate, ...)
{
    va_list pa;
    TCHAR szText[1024]; // 1024 is the maximum which wsprintf and wvsprintf support
    LPTSTR  pErrorString;
    LPCTSTR pszErrorDll = NULL;
    HMODULE hModule = NULL;
    //DWORD dwFacility = HRESULT_FACILITY(dwErrorCode);

    va_start (pa, pszTemplate);
    wvnsprintf (szText, ARRAY_SIZE(szText), pszTemplate, pa);
    va_end (pa);

    // HRESULT_FROM_WIN32 HRESULT_FROM_SETUPAPI
    // Choose default Error DLL
    if (HRESULT_FACILITY(dwErrorCode) == FACILITY_WINDOWS ||
        (HRESULT_FACILITY(dwErrorCode) == FACILITY_WIN32 && HRESULT_SEVERITY(dwErrorCode) == SEVERITY_ERROR))
        dwErrorCode = HRESULT_CODE(dwErrorCode);
    else if (HRESULT_FACILITY(dwErrorCode) == FACILITY_INTERNET && dwErrorCode > INET_E_ERROR_FIRST && dwErrorCode < INET_E_ERROR_LAST)
        pszErrorDll = TEXT("UrlMon.dll");
    else if (HRESULT_FACILITY(dwErrorCode) == FACILITY_INTERNET && dwErrorCode > 0xC00CE000L && dwErrorCode < 0xC00CE5FFL)
        pszErrorDll = TEXT("msxmlr.dll");   // TEXT("msxmlr4.dll");
    else if (HRESULT_FACILITY(dwErrorCode) == FACILITY_MSMQ)
        pszErrorDll = TEXT("MQUtil.dll");
    else if (dwErrorCode >= NERR_BASE && dwErrorCode <= MAX_NERR)
        pszErrorDll = TEXT("NetMsg.dll");
    else if (dwErrorCode >= 0xC0040002L && dwErrorCode <= 0xC004001FL)
        pszErrorDll = TEXT("IoLogMsg.dll");
    else if ((LONG)dwErrorCode < 0)
        pszErrorDll = TEXT("ntdll.dll");

    // Load the DLL if needed
    if (pszErrorDll) {
        hModule = LoadLibraryEx (pszErrorDll, NULL, LOAD_LIBRARY_AS_DATAFILE);
        if (!hModule) {
            _tprintf (TEXT("Can not load DLL \"%s\" to display description for error 0x%08lX.\r\n"), pszErrorDll, dwErrorCode);
            //StringFormatedOutput (ERROR_OUTPUT, TEXT("Can not load DLL \"%s\" to display description for error 0x%08lX.\r\n"),
            //                      pszErrorDll, dwErrorCode);
            return;
        }
    }

    // Query Error text.
    // See Q149409 as an example.
    if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |    // Always search in system message table !!!
                        FORMAT_MESSAGE_ALLOCATE_BUFFER |
                        FORMAT_MESSAGE_IGNORE_INSERTS |
                        (hModule ? FORMAT_MESSAGE_FROM_HMODULE : 0),
                        hModule,                // source of message definition
                        dwErrorCode,            // message ID
//                        0,                      // language ID
//                        GetUserDefaultLangID(), // language ID
//                        GetSystemDefaultLangID(),
                        MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
                        (LPTSTR)&pErrorString,   // pointer for buffer to allocate
                        0,                      // min number of chars to allocate
                        NULL)) {

        if (dwErrorCode & 0xC0000000) {
            _tprintf (szText);
            _tprintf (TEXT("Unknown error. Error code 0x%08lX.\r\n"), dwErrorCode);
            //StringFormatedOutput (ERROR_OUTPUT, TEXT("%sUnknown error. Error code 0x%08lX.\r\n"), szText, dwErrorCode);
        }
        else {
            _tprintf (szText);
            _tprintf (TEXT("Unknown error. Error code %lu.\r\n"), dwErrorCode);
            //StringFormatedOutput (ERROR_OUTPUT, TEXT("%sUnknown error. Error code %lu.\r\n"), szText, dwErrorCode);
        }
    }
    else {
        _tprintf (szText);
        _tprintf (pErrorString);
        _tprintf (TEXT("\r\n"));
        //StringFormatedOutput (ERROR_OUTPUT, TEXT("%s%s\r\n"), szText, pErrorString);
        LocalFree (pErrorString);
    }

    if (hModule)
        FreeLibrary (hModule);
}

// This function do almost the same as Base64 encoding used for example in MIME (see 6.8 in http://www.ietf.org/rfc/rfc2045.txt).
// Base64 convert codes from 0 till 63 (0x3F) to the corresponding character from the array 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
// This function convert it to the corresponding character from the another array '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._'
static BYTE MsiBase64Encode (BYTE x)
{
    // 0-0x3F converted to '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._'
    // all other values higher as 0x3F converted also to '_'
    if (x < 10)
        return x + '0';             // 0-9 (0x0-0x9) -> '0123456789'
    else if (x < (10+26))
        return x - 10 + 'A';        // 10-35 (0xA-0x23) -> 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    else if (x < (10+26+26))
        return x - 10 - 26 + 'a';   // 36-61 (0x24-0x3D) -> 'abcdefghijklmnopqrstuvwxyz'
    else if (x == (10+26+26))       // 62 (0x3E) -> '.'
        return '.';
    else
        return '_';                 // 63-0xffffffff (0x3F-0xFFFFFFFF) -> '_'
}

#pragma warning (disable: 4706)
static UINT DecodeStreamName (LPWSTR pszInStreamName, LPWSTR pszOutStreamName)
{
    WCHAR ch;
    DWORD count = 0;

    while ((ch = *pszInStreamName++)) {
        if ((ch >= 0x3800) && (ch < 0x4840)) {
            // a part of Unicode charecterd used with CJK Unified Ideographs Extension A. (added with Unicode 3.0) used by
            // Windows Installer for encoding one or two ANSI characters. This subset of Unicode characters are not currently
            // used nether in "MS PMincho" or "MS PGothic" font nor in "Arial Unicode MS"
            if (ch >= 0x4800)   // 0x4800 - 0x483F
                // only one charecter can be decoded
                ch = (WCHAR) MsiBase64Encode ((BYTE)(ch - 0x4800));
            else {              // 0x3800 - 0x383F
                // the value contains two characters
                ch -= 0x3800;
                *pszOutStreamName++ = (WCHAR) MsiBase64Encode ((BYTE)(ch & 0x3f));
                count++;
                ch = (WCHAR) MsiBase64Encode ((BYTE)((ch >> 6) & 0x3f));
            }
        }
        // all characters lower as 0x3800 or higher or equel to 0x4840 will be saved without any decoding

        *pszOutStreamName++ = ch;
        count++;
    }
    *pszOutStreamName = L'\0';

    return count;
}
#pragma warning (default: 4706)

#define INVALID_DECODING_RESULT ((BYTE)(-1))
// This function do almost the same as Base64 decoding used for example in MIME (see 6.8 in http://www.ietf.org/rfc/rfc2045.txt).
// Base64 convert character from the array 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' to the corresponding codes from 0 till 63 (0x3F)
// This function convert character from the another array '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' to it to 0 till 63 (0x3F)
static BYTE MsiBase64Decode (BYTE ch)
// returns values 0 till 0x3F or 0xFF in the case of an error
{
    // only '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' are allowed and converted to 0-0x3F
    if ((ch>=L'0') && (ch<=L'9'))   // '0123456789' -> 0-9  (0x0-0x9)
        return ch-L'0';
    else if ((ch>=L'A') && (ch<=L'Z'))   // 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -> 10-35 (26 chars) - (0xA-0x23)
        return ch-'A'+10;
    else if ((ch>=L'a') && (ch<=L'z'))   // 'abcdefghijklmnopqrstuvwxyz' -> 36-61 (26 chars) - (0x24-0x3D)
        return ch-L'a'+10+26;
    else if (ch==L'.')
        return 10+26+26;        // '.' -> 62 (0x3E)
    else if (ch==L'_')
        return 10+26+26+1;      // '_' -> 63 (0x3F) - 6 bits
    else
        return INVALID_DECODING_RESULT; // other -> -1 (0xFF)
}

#define MAX_STREAM_NAME 0x1f

static void EncodeStreamName (BOOL bTable, LPCWSTR pszInStreamName, LPWSTR pszOutStreamName, UINT cchOutStreamName)
{
    LPWSTR pszCurrentOut = pszOutStreamName;

    if (bTable) {
         *pszCurrentOut++ = 0x4840;
         cchOutStreamName--;
    }

    while (cchOutStreamName--) {
        WCHAR ch = *pszInStreamName++;

        if (ch && (ch < 0x80) && (MsiBase64Decode((BYTE)ch) <= 0x3F)) {
            WCHAR chNext = *pszInStreamName;

            // MsiBase64Decode() convert any "standard" character '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' to 0-0x3F.
            // One can pack two charecters together in 0-0xFFF. To do so, one needs convert the first one with respect of MsiBase64Decode(),
            // convert the next character also with respect MsiBase64Decode() and shift it 6 bits on the left. Two characters together
            // produce a value from 0 till 0xFFF. We add 0x3800 to the result. We receive a value between 0x3800 and 0x47FF
            if (chNext && (chNext < 0x80) && (MsiBase64Decode((BYTE)chNext) <= 0x3F)) {
                ch = (WCHAR)(MsiBase64Decode((BYTE)ch) + 0x3800 + (MsiBase64Decode((BYTE)chNext)<<6));
                pszInStreamName++;
            }
            else
                ch = MsiBase64Decode((BYTE)ch) + 0x4800;
        }
        *pszCurrentOut++ = ch;

        if (!ch)
            break;
    }
}

static HRESULT LoadStreamInMemory (IStorage *pStg, LPCWSTR pszStreamName, PBYTE *ppData, PUINT pSize)
{
    HRESULT hr = E_UNEXPECTED;
    IStream *pStm = NULL;

    // set defaults
    *ppData = NULL;
    *pSize = 0;

    __try {
        STATSTG stat;
        ULONG cbRead;

        hr = IStorage_OpenStream (pStg, pszStreamName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStm);   // STGM_SHARE_EXCLUSIVE
        if (FAILED(hr)) {
            DisplayErrorMessage (hr, TEXT("Failed IStorage::OpenStream(). "));
            __leave;
        }

        hr = IStream_Stat (pStm, &stat, STATFLAG_NONAME);
        if (FAILED(hr)) {
            DisplayErrorMessage (hr, TEXT("Failed IStream::Stat(). "));
            __leave;
        }

        if (stat.cbSize.HighPart) {
            hr = HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER);
            __leave;
        }

        *pSize = stat.cbSize.LowPart;
        if (*pSize) {
            *ppData = (PBYTE) LocalAlloc (LMEM_FIXED, *pSize);
            if (!*ppData) {
                hr = HRESULT_FROM_WIN32 (ERROR_NOT_ENOUGH_MEMORY);
                __leave;
            }

            //r = IStream_Read (stm, pData, sz, &cbRead);
            hr = pStm->lpVtbl->Read (pStm, *ppData, *pSize, &cbRead);
            //hr = IStream_Read (pStm, *ppData, *pSize, &cbRead);
            if (FAILED(hr) || (cbRead != *pSize)) {
                *ppData = (PBYTE) LocalFree (*ppData);
                if (SUCCEEDED(hr))
                    hr = HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER);
                __leave;
            }
            else 
                hr = S_OK;
        }
        else
            hr = S_OK;
    }
    __finally {
        if (pStm)
            IStream_Release (pStm);
    }

    return hr;
}

UINT DumpString (LPCWSTR pszString, LPCTSTR pszFormat, UINT nMaxLen)
{
    UINT cchPrinted = 0;
    LPWSTR pszText = (LPWSTR) _alloca (max(5,min((UINT)lstrlenW(pszString),nMaxLen)+1)*sizeof(WCHAR));

    if ((UINT)lstrlenW(pszString) <= nMaxLen)
        cchPrinted = _tprintf (pszFormat, pszString);
    else if (nMaxLen > 3) {
        lstrcpynW (pszText, pszString, nMaxLen-2);
        pszText[nMaxLen] = L'\0';
        pszText[nMaxLen-1] = L'.';
        pszText[nMaxLen-2] = L'.';
        pszText[nMaxLen-3] = L'.';
        cchPrinted = _tprintf (pszFormat, pszText);
    }
    else if (nMaxLen == 3) {
        pszText[0] = pszString[0];
        pszText[1] = pszString[1];
        pszText[2] = L'.';
        pszText[3] = L'\0';
        cchPrinted = _tprintf (pszFormat, pszText);
    }
    else if (nMaxLen == 2) {
        pszText[0] = pszString[0];
        pszText[1] = L'.';
        pszText[2] = L'\0';
        cchPrinted = _tprintf (pszFormat, pszText);
    }
    else if (nMaxLen == 1) {
        pszText[0] = pszString[0];
        pszText[1] = L'\0';
        cchPrinted = _tprintf (pszFormat, pszText);
    }

    return cchPrinted;
}

static HRESULT LoadTableFromStream (IStorage *pStg, LPCWSTR pszTableName, PBYTE *ppData, PUINT pSize)
{
    WCHAR szEncodedStreamName[32];
    HRESULT hr;

    EncodeStreamName (TRUE, pszTableName, szEncodedStreamName, ARRAY_SIZE(szEncodedStreamName));

    hr = LoadStreamInMemory (pStg, szEncodedStreamName, ppData, pSize);
    if (FAILED(hr))
        DisplayErrorMessage (hr, TEXT("Failed LoadStreamInMemory() for the table %ls. "), pszTableName);

    return hr;
}

BOOL LoadStringPool (IStorage *pStg, BOOL bDumpStringPool, UINT cMaxStrOutLen, UINT cMaxFirstRowsOut, UINT cMaxLastRowsOut)
{
    UINT nOffsetSrc = 0, nOffsetDest = 0, nStringPoolLength, nStringDataLength;
    UINT iStringId, iSrc, uBufferSize;
    PSTR pszStringData = NULL;
    struct _StringPool {
        WORD wLength;
        WORD wRefcnt;
    } *pStringPool = NULL;
    HRESULT hr;
    DWORD dwCodePage;
    BOOL bAllPrinted = TRUE;
    UINT cStringIdsPrinted = 0;

    hr = LoadTableFromStream (pStg, OLESTR("_StringPool"), (PBYTE *)&pStringPool, &nStringPoolLength);
    if (FAILED(hr))
        return FALSE;

    dwCodePage = pStringPool[0].wLength;
    if (pStringPool[0].wRefcnt == 0)
        g_StringTable.cbStringIdSize = 2;
    else if (pStringPool[0].wRefcnt == 0x8000)
        g_StringTable.cbStringIdSize = 3;

    if (bDumpStringPool)
        _tprintf (TEXT("\r\nString ID size: %d\r\n"), g_StringTable.cbStringIdSize);

    // convert bytes to indexes
    nStringPoolLength /= sizeof (struct _StringPool);

    hr = LoadTableFromStream (pStg, OLESTR("_StringData"), (PBYTE *)&pszStringData, &nStringDataLength);
    if (FAILED(hr))
        return FALSE;

    // Allocate buffer large enough to hold all strings from _StringData steam together with '\0' at the end of each string.
    // We allocate all memory in one block and not per string, to speed up allocation and to reduce overhead in heap menagement.
    uBufferSize = nStringDataLength + nStringPoolLength;
    g_StringTable.pszStringData = (PWSTR) LocalAlloc (LPTR, uBufferSize*sizeof(WCHAR));

    // allocate and initialize to NULL all pointers
    g_StringTable.ppszStringPool = (PWSTR *) LocalAlloc (LPTR, nStringPoolLength * sizeof (PWSTR *));

    if (bDumpStringPool) {
        _tprintf (TEXT("\r\nCode page of the string pool: %d\r\n"), dwCodePage);
        _tprintf (TEXT("+++String Pool Entries+++\r\n"));
    }

    for (iSrc=1, iStringId=1; iSrc<nStringPoolLength; iSrc++) {
        DWORD dwLen = pStringPool[iSrc].wLength;
        if (pStringPool[iSrc].wLength == 0) {
            // A string is lagrer as 64K. In the case one create one dummy entry with pStringPool[iStringId].wLength
            // and high word of string length saved in the next entry will be saved in pStringPool[iStringId].wRefcnt
            if (pStringPool[iSrc].wRefcnt == 0) // empty entry
                iStringId++;
            continue;
        }
        if (iSrc != 1 && pStringPool[iSrc-1].wLength == 0 && pStringPool[iSrc-1].wRefcnt != 0)
            // current string have length over 64K
            dwLen += pStringPool[iSrc-1].wRefcnt << 16; //* 0x10000;

        if (dwLen < uBufferSize) {
            MultiByteToWideChar (dwCodePage, MB_PRECOMPOSED | MB_ERR_INVALID_CHARS,
                                 pszStringData+nOffsetSrc, (int)dwLen,
                                 g_StringTable.pszStringData+nOffsetDest, uBufferSize);
            g_StringTable.pszStringData[nOffsetDest+dwLen] = L'\0';
            uBufferSize -= dwLen+1;
            g_StringTable.ppszStringPool[iStringId] = g_StringTable.pszStringData+nOffsetDest;
            if (bDumpStringPool) {
                //_tprintf (TEXT("\tId:%5d  Refcnt:%5d  String: %ls\r\n"), iStringId, pStringPool[iStringId].wRefcnt, g_StringTable.pszStringData+nOffsetDest);
                if (cStringIdsPrinted<cMaxFirstRowsOut || iStringId+cMaxLastRowsOut>=nStringPoolLength) {
                    _tprintf (TEXT("\tId:%5d  Refcnt:%5d  String: "), iStringId, pStringPool[iStringId].wRefcnt);
                    DumpString (g_StringTable.pszStringData+nOffsetDest, TEXT("%ls\r\n"), cMaxStrOutLen);
                    cStringIdsPrinted++;
                }
                else {
                    if (bAllPrinted)
                        _tprintf (TEXT("...\r\n"));

                    bAllPrinted = FALSE;
                }
            }
            iStringId++;
            nOffsetDest += dwLen+1;
        }
        nOffsetSrc += dwLen;
        if (nOffsetSrc >= nStringDataLength)
            break;
    }

    if (iStringId < nStringPoolLength)
        g_StringTable.cStrings = iStringId;
    else
        g_StringTable.cStrings = iStringId-1;

    return TRUE;
}

int _tmain (int argc, LPTSTR argv[])
{
    HRESULT hr = S_OK;
    IStorage *pStg = NULL;
    LPTSTR pszFileName;
    LPWSTR pszwFileName;
    BOOL bSuccess = FALSE;

    if (argc < 2) {
        _tprintf (TEXT("Usage:    GetMsiStringTable <filename>\r\n"));
        return 1;
    }

    pszFileName = argv[1];

#ifdef _UNICODE
    pszwFileName = pszFileName;
#else
    {
        DWORD cchLen = lstrlenA (pszFileName) + 1;
        pszwFileName = _alloca (cchLen*sizeof(WCHAR));
        MultiByteToWideChar (CP_ACP, MB_ERR_INVALID_CHARS | MB_PRECOMPOSED, pszFileName, -1, pszwFileName, cchLen);
    }
#endif

    __try {
        CLSID clsidStg;

        STGOPTIONS stgOption = {0};
        stgOption.usVersion = STGOPTIONS_VERSION;

        // Open the root storage.
        hr = StgOpenStorageEx (pszwFileName,
                               STGM_DIRECT_SWMR | STGM_READ | STGM_SHARE_DENY_NONE,
                               //STGM_DIRECT_SWMR | STGM_READ | STGM_SHARE_DENY_WRITE,
                               //STGM_READ|STGM_SHARE_EXCLUSIVE,
                               STGFMT_DOCFILE,  //STGFMT_ANY,
                               0,
                               &stgOption,      // NULL,
                               NULL,
                               &IID_IStorage,   // instaed of IID_IStorage it is possible to use IID_IPropertySetStorage
                               (PVOID *)&pStg);
        if (FAILED(hr)) {
            DisplayErrorMessage (hr, TEXT("Error: couldn't open storage \"%ls\". "), pszFileName);
            __leave;
        }

        hr = ReadClassStg (pStg, &clsidStg);
        if (SUCCEEDED(hr)) {
            // MsiInfo.exe
            //
            // Transform: Class Id for the MSI storage is {000C1082-0000-0000-C000-000000000046} CLSID_MsiTransform
            // MSI:       Class Id for the MSI storage is {000C1084-0000-0000-C000-000000000046} CLSID_MsiDatabase
            // Patch:     Class Id for the MSI storage is {000C1086-0000-0000-C000-000000000046} CLSID_MsiPatch
            OLECHAR szClsidStg[39];
            StringFromGUID2 (&clsidStg, szClsidStg, ARRAY_SIZE(szClsidStg));
            _tprintf (TEXT("Class Id for the storage is %ls:\r\n"), szClsidStg);

            if (IsEqualCLSID (&clsidStg, &CLSID_MsiDatabase))
                _tprintf (TEXT("\tStorage has MSI database/Merge module class id.\r\n"));
            else if (IsEqualCLSID (&clsidStg, &CLSID_MsiPatch))
                _tprintf (TEXT("\tStorage has MSI patch class id.\r\n"));
            else if (IsEqualCLSID (&clsidStg, &CLSID_MsiTransform))
                _tprintf (TEXT("\tStorage has MSI transform class id.\r\n"));
            else {
                _tprintf (TEXT("\tStorage is not a Windows Installer file.\r\n"));
                __leave;
            }
        }

        bSuccess = LoadStringPool (pStg, TRUE, 80, 10, 10);
        if (!bSuccess)
            __leave;

    }
    __finally {
        if (pStg)
            IStorage_Release (pStg);
    }

    return 0;
}

Visual Studio 2010 Ultimate vs_setup.msi 的输出示例:

An example of the output of vs_setup.msi of Visual Studio 2010 Ultimate:

Class Id for the storage is {000C1084-0000-0000-C000-000000000046}:
        Storage has MSI database/Merge module class id.

String ID size: 3

Code page of the string pool: 1252
+++String Pool Entries+++
        Id:    1  Refcnt:  542  String: Name
        Id:    2  Refcnt:    7  String: Table
        Id:    4  Refcnt:    7  String: Type
        Id:    5  Refcnt:    5  String: _sqlAssembly
        Id:    6  Refcnt:    8  String: File_
        Id:    7  Refcnt:   18  String: MS.VS.vspGridControl.dll.27F9E354_F6F7_44D7_9637_42C9575D0C37
        Id:    8  Refcnt:    7  String: _sqlFollowComponents
        Id:    9  Refcnt:    2  String: FollowComponent
        Id:   10  Refcnt:   30  String: Component_
        Id:   11  Refcnt:    2  String: ParentComponent_
...
        Id:94617  Refcnt:    1  String: VS_Debugging_ServiceModelSink.MachineConfigV4.3205
        Id:94618  Refcnt:    1  String: VS_Debugging_ServiceModelSink.MachineConfigV4.3206
        Id:94619  Refcnt:    1  String: VS_Debugging_ServiceModelSink.MachineConfigV4.3207
        Id:94620  Refcnt:    1  String: VS_Debugging_ServiceModelSink.MachineConfigV4.3208
        Id:94621  Refcnt:    1  String: VS_Debugging_ServiceModelSink.MachineConfigV4.3209
        Id:94622  Refcnt:    3  String: VS_Debugging_ServiceModelSink.MachineConfigV4.3210
        Id:94623  Refcnt:    1  String: VS_Debugging_ServiceModelSink.MachineConfigV4.3211
        Id:94624  Refcnt:    1  String: VS_Debugging_ServiceModelSink.MachineConfigV4.3212
        Id:94625  Refcnt:    1  String: Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior, Microsoft.Visua...
        Id:94626  Refcnt:    1  String: VS_Debugging_ServiceModelSink.MachineConfigV4.3213

当时我花了很多时间来了解如何解码具有 3 个字节的字符串 ID,而不仅仅是 2 个字节,这对于没有那么长字符串表的小型设置来说是典型的.

I spend many time at that time to find out how to decode String ID which has size 3 bytes and not only 2 bytes which is typical for small setups with not so long string table.

这篇关于以二进制形式查看 MSI 字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆