2012-03-16 19 views
1

MSIがバイナリモードで実行されているときに、UIに表示される文字列/テキストを表示したかったのです。バイナリ形式のMSI文字列を表示

私はローカライズされたwxlファイルとローカライズされたmsiファイルを持っています。テキストを比較したかった

私のアプローチは、比較する文字列のバイナリコンテンツを表示することです。誰でも私が使用できるツールを教えてもらえますか?

orcaの使用文字列を確認できます。しかし、私はそれらのバイナリ/ 16進値を見たいと思います。

どうもありがとう

よろしく、 マルク・

答えて

5

は私がバイナリとして文字列をエクスポートするツールを知りません。ほとんどの場合、実際には必要ありません。

あなたは直接MSIから情報を読み取るためにSTATFLAG_NONAMEパラメータとIStream::ReadIStorage::OpenStreamIStream::Statを使用できる文字列についてのバイナリ情報を入手する必要がある場合。スティングに関する情報は、 "_StringData"と "_StringPool"という名前のストリームに保存されます。ストリームの名前は簡単な方法でエンコードされています。興味があれば、名前をデコードする方法を示すコードを投稿することができます。

更新済み:古いユーティリティから小さなデモを作成しました。デモでは、 "_StringData"と "_StringPool"から文字列を読み込み、情報を読み込み可能な形式でダンプします。

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

(下記参照)より詳細な情報をダンプすることができます。同様に、対応するストリームをバイナリとしてファイルに保存するコードを簡単に変更できます。

あなたは

#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 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/' 
// This function convert it to the corresponding character from the another array 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' 
static BYTE MsiBase64Encode (BYTE x) 
{ 
    // 0-0x3F converted to 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' 
    // all other values higher as 0x3F converted also to '_' 
    if (x < 10) 
     return x + '0';    // 0-9 (0x0-0x9) -> '' 
    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 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/' to the corresponding codes from 0 till 63 (0x3F) 
// This function convert character from the another array 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' 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 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' are allowed and converted to 0-0x3F 
    if ((ch>=L'0') && (ch<=L'9')) // '' -> 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 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' 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の究極のvs_setup.msiの出力の例を見つけるCコード:

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 

は私がデコードする方法を見つけるために、その時点で多くの時間を過ごします文字列IDはサイズが3バイトであり、それほど長い文字列テーブルではない小さな設定では典型的な2バイトだけではありません。

+0

wow。これは本当に素晴らしいです。情報に感謝します。私はコードに非常に興味があります。投稿してください:) – Mark

+0

@マーク:私は私の答えを更新し、私が約束するコードを含めました。私は約8年前に書いた古いコードからポートを作ります。私が提出したコードをテストしましたが、入力パラメータを分析しません。コードが機能するはずです。 – Oleg

関連する問題