Read the content of the string intern pool

前端 未结 2 994
情书的邮戳
情书的邮戳 2021-01-05 12:53

I would like to enumerate the strings that are in the string intern pool.

That is to say, I want to get the list of all the instances s

相关标签:
2条回答
  • 2021-01-05 13:16

    The SSCLI function that its pointing to is

    STRINGREF*AppDomainStringLiteralMap::GetStringLiteral(EEStringData *pStringData) 
    { 
        ... 
        DWORD dwHash = m_StringToEntryHashTable->GetHash(pStringData);
        if (m_StringToEntryHashTable->GetValue(pStringData, &Data, dwHash))
        {
            STRINGREF *pStrObj = NULL;
            pStrObj = ((StringLiteralEntry*)Data)->GetStringObject();
            _ASSERTE(!bAddIfNotFound || pStrObj);
            return pStrObj;
        }
        else { ... }
    
        return NULL; //Here, if this returns, the string is not interned
    }
    

    If you manage to find the native address of m_StringToEntryHashTable, you can enumerate the strings that exist.

    0 讨论(0)
  • 2021-01-05 13:34

    Thanks to the advice of @HansPassant, I managed to get the list of string literals in an assembly. Which is extremely close to what I originally wanted.

    You need to use read assembly meta-data, and enumerate user-strings. This can be done with these three methods of IMetaDataImport:

    [ComImport, Guid("7DAC8207-D3AE-4C75-9B67-92801A497D44")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IMetaDataImport
    {
        void CloseEnum(IntPtr hEnum);
    
        uint GetUserString(uint stk, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] char[] szString, uint cchString, out uint pchString);
    
        uint EnumUserStrings(ref IntPtr phEnum, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]uint[] rStrings, uint cmax, out uint pcStrings);
    
        // interface also contains 62 irrelevant methods
    }
    

    To get the instance of IMetaDataImport, you need to get a IMetaDataDispenser:

    [ComImport, Guid("809C652E-7396-11D2-9771-00A0C9B4D50C")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [CoClass(typeof(CorMetaDataDispenser))]
    interface IMetaDataDispenser
    {
        uint OpenScope([MarshalAs(UnmanagedType.LPWStr)]string szScope, uint dwOpenFlags, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppIUnk);
    
        // interface also contains 2 irrelevant methods
    }
    
    [ComImport, Guid("E5CB7A31-7512-11D2-89CE-0080C792E5D8")]
    class CorMetaDataDispenser
    {
    }
    

    Here is how it goes:

    var dispenser = new IMetaDataDispenser();
    var metaDataImportGuid = new Guid("7DAC8207-D3AE-4C75-9B67-92801A497D44");
    
    object scope;
    var hr = dispenser.OpenScope(location, 0, ref metaDataImportGuid, out scope);
    
    metaDataImport = (IMetaDataImport)scope;    
    

    where location is the path to the assembly file.

    After that, calling EnumUserStrings() and GetUserString() is straighforward.

    Here is a blog post with more detail, and a demo project on GitHub.

    0 讨论(0)
提交回复
热议问题