如何在 win32 或 MFC 中按名称获取字符串资源?

How do I get a string resource by name in win32 or MFC?

我想在获取某个字符串之前查询它是否在字符串 table 中,因为某些项目可能不在字符串 table 中。 为了说明这一点,符号可能已声明也可能未声明,所以我不能只指定字符串 ID,因为它可能存在也可能不存在,如果不存在将导致编译错误' t.

从控制台应用程序库,我试过这个:

ConsoleApplication4.rc:

STRINGTABLE
BEGIN
   IDS_APP_TITLE       "ConsoleApplication4"
   IDS_TEST_Home_HERE  "Home here"
END

Resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by ConsoleApplication4.rc
//

#define IDS_APP_TITLE           103
#define IDS_TEST_Home_HERE      104
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE    101
#define _APS_NEXT_COMMAND_VALUE     40001
#define _APS_NEXT_CONTROL_VALUE     1000
#define _APS_NEXT_SYMED_VALUE       101
#endif
#endif

ConsoleApplication4.cpp:

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    HMODULE hModule = ::GetModuleHandle(NULL);

    if (hModule != NULL)
    {
        // initialize MFC and print and error on failure
        if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
        {
            // TODO: change error code to suit your needs
            _tprintf(_T("Fatal Error: MFC initialization failed\n"));
            nRetCode = 1;
        }
        else
        {
            // TODO: code your application's behavior here.
        }
    }
    else
    {
        // TODO: change error code to suit your needs
        _tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
        nRetCode = 1;
    }

    // vvvv My code below here vvvv:

    HRSRC hResource = FindResource(hModule, _T("IDS_TEST_Home_HERE"), RT_STRING);
    HGLOBAL hgString = LoadResource(hModule, hResource);
    LPCTSTR string = (LPCTSTR)LockResource(hgString);

    cout << string << endl;

    // ^^^^ My code above here ^^^^

    return nRetCode;
}

然而,FindResource()调用returnsNULL。我错过了什么?

编辑:

对于任何感兴趣的人,我写了一种方法来从文件中读入 id 符号名称并将它们转换为符号的数值,只要读入的符号以某种方式基于正在查询的原始符号 id关于。

这样做可以让我查询附加符号的字符串资源值,可以从预先确定的选项列表中选择。

您可以看到解决方案

您可以将资源 (.rc) 文件中的字符串 table 视为 Key/Value 对。 FindResource 的工作原理是您提供密钥,它 returns 值。

_T("IDS_TEST_Home_HERE") 的正确语法是:

MAKEINTRESOURCE(IDS_TEST_Home_HERE)

您不需要引号。

字符串资源无法通过名称识别,只能通过整数值识别。该值表示它们在字符串中的位置 table.

资源实际上是 table 本身(或者更准确地说,是字符串包)。在某种意义上,table 中的单个字符串不是资源。您可以使用资源 API 函数访问单个字符串这一事实对于这些函数来说确实有点特殊。

陈百强报道了internal format of string tables

好的,所以如果有人感兴趣,这是我使用令牌拼接的解决方案。

#pragma once
#include <vector>
#include <algorithm>

//////////////////////////////////////////////////////////////////////////////
// This header is to allow the capturing of macro token values and is 
// accomplished by token splicing.
//
// To be able to determine what the id value is, one must know what the token
// could be ahead of time, even if it is one of a few choices.  This can be
// accomplished by splicing a token together so that you can limit your search.
// Example:
//
//   #define ID_X           1
//   #define IDS_1__ID_X__  5
//
//   // x must be be passed with '__' appended to it to prevent it from being
//   // expanded. This only works if no macro with that name already exists.
//   //
//   // NOTE: This is an intermediate macro which does the heavy lifting, and
//   //       is called by one or more interface macros.
//   #define GET_VALUE(x, idAsString) \
//     CMacroIdAsStringToValue().add(CString("IDS_1__")+T_STRINGIZE(x), T_STRINGIZE(CONCAT(IDS_1__, x))) \
//     .getValue(idAsString)
//
//   // This is an example of an interface macro, which will get the associated 
//   // value and possibly do other things with the original id.
//   #define GET_ASSOCIATED_VALUE(x, idAsString) GET_VALUE(x##__, idAsString)
//
//   int x = GET_ASSOCIATED_VALUE(ID_X, _T("IDS_1__ID_X__")); // Will return 5.
//   int y = GET_ASSOCIATED_VALUE(ID_X, _T("IDS_1__ID_Y__")); // Will cause an assertion and return -1.
//   int y = GET_ASSOCIATED_VALUE(ID_Y, _T("IDS_1__ID_Y__")); // Will cause an assertion and return -1.
//
// The 2nd parameter can be read in from somewhere else, the 1st parameter is
// a hint as to what the 2nd parameter could be.
//
// To get a string from the string table, replace .getValue(idAsString) with
// .getResourceString(idAsString).
//
// If you don't want the code to assert if there is no match found, pass a false
// value to the `CMacroIdAsStringToValue` constructor.
//
// Adding multiple token names to search can be done by stringing multiple add()
// calls together with the dot operator.
//
//   - Written by Adrian Hawryluk, Jan 2015 and is hereby given to the public domain.

#ifdef UNICODE
// Expands x and converts it to a wide character array
#define T_STRINGIZE_IMPL(x) L#x
#else
// Expands x and converts it to a character array. NOT to be used directly.
#define T_STRINGIZE_IMPL(x) #x
#endif
// At this level x is not expanded, call IMPL version to have it expanded prior to operating on them.
#define T_STRINGIZE(x) T_STRINGIZE_IMPL(x)

// Token splice x and y together. NOT to be used directly.
#define CONCAT_IMPL(x, y) x##y
// At this level, x and y are not expanded, call IMPL version to have it expanded prior to operating on them.
#define CONCAT(x, y) CONCAT_IMPL(x,y)


class CMacroIdAsStringToValue
{
    struct IdToValue
    {
        CString id;     // This is a macro name stored as ASCII.
        int     value;  // This is the macro's value.
    };
    // List of macros to check against which got expanded into a number.
    std::vector<IdToValue> searchList;


#ifdef DEBUG
    struct IdFails
    {
        CString id;
        CString expandedId;
    };
    // List of macros to check against which failed to expand into a number.
    // Used only for debugging if something went wrong.  If a matching ID is
    // NOT found, and there is an item in here, you can see what the token
    // got expanded to.  If it looks ok, then you forgot to add a macro by 
    // that name into the resource table.
    //
    // See also CMacroIdAsStringToValue::testId()
    std::vector<IdFails> failedItems;
    bool assertOnNoMatch;
#endif

public:
    CMacroIdAsStringToValue(bool assertOnNoMatch = true)
#ifdef DEBUG
    :   assertOnNoMatch(assertOnNoMatch)
#endif
    {
    }

    // Test to see if the macro string got expanded correctly.  If an
    // assertion is tripped here then you probably attempted to get use
    // RIBBON_PANEL_TEXT_ICON() or RIBBON_CATEGORY_TEXT() macros without
    // defining CATEGORY macro or passed the wrong panel bareword..
    void testId(LPCTSTR macro)
    {
        static TCHAR const invalidId[] = _T("IDS_RIBBON_TAB_CATEGORY__GET_ELEMENT_CATEGORY");
        ASSERT(_tcsncmp(macro, invalidId, _tcslen(invalidId)));
    }

    // If macroValue has a length < 8, it is a number (macro names will be much greater than 7 characters long)
    template <int N>
    typename std::enable_if<(N<8), CMacroIdAsStringToValue&>::type add(LPCTSTR macro, TCHAR const (&macroValue)[N])
    {
        testId(macro);
        ASSERT(_istdigit(macroValue[0]));
        // macro results in a value
        searchList.push_back({ macro, _ttoi(macroValue) });
        return *this;
    }

    //  In Release mode, this function prevents the macroValue from being used and will stop it from being added to the .DATA segment.
    template <int N>
    typename std::enable_if<(N >= 8), CMacroIdAsStringToValue&>::type add(LPCTSTR macro, TCHAR const (&macroValue)[N])
    {
#ifdef DEBUG
        testId(macro);
        failedItems.push_back({ macro, macroValue });
#endif
        return *this;
    }


    // gets the macro's value given the macro's name as a string
    //
    // NOTE: id is empty?  Then the function to get the id returned nothing.
    int getValue(LPCTSTR id) const
    {
        auto result = find_if(searchList.begin(), searchList.end(), [id](IdToValue const& idToValue)
        {
            return _tcscmp(idToValue.id, id) == 0;
        });
        if (result == searchList.end())
        {
#ifdef DEBUG
            if (assertOnNoMatch)
            {
                ASSERT(FALSE);
            }
#endif
            return -1;
        }
        else
        {
            return result->value;
        }
    }

    // Gets the string associated and hotkey associated with the id seperated by LF ('\n').
    //
    // NOTE: id is empty?  Then the function to get the id returned nothing.
    CString getResourceString(LPCTSTR id) const
    {
        UINT stringId = getValue(id);
        CString string;
        VERIFY(string.LoadString(stringId));
        if (string == _T("*BLANK*"))
        {
            return "";
        }
        return string;
    }
};

// Takes a string and returns another string that consists of everything in 
// the first string that is to the left of the 1st '\n' character.  If no such
// character exists in the string, return the whole string.
inline CString leftOfLF(CString string)
{
    int index = string.Find('\n');
    if (index == -1)
    {
        return string;
    }
    return string.Left(index);
}

// Takes a string and returns another string that consists of everything in 
// the first string that is to the right of the 1st '\n' character.  If no such
// character exists in the string, return an empty string.
inline CString rightOfLF(CString string)
{
    int index = string.Find('\n');
    if (index == -1)
    {
        return "";
    }
    return string.Mid(index + 1);
}