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

时间:2015-01-13 19:32:04

标签: c++ visual-studio winapi mfc

我想在获取字符串之前查询字符串是否在字符串表中,因为某些项目可能不在字符串表中。 为了明确这一点,该符号可能已经或可能未被声明,因此我不能只指定字符串id,因为它可能存在也可能不存在,并且如果它不存在则会导致编译错误

从控制台应用程序库中,我尝试了这个:

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()调用会返回NULL。我错过了什么?

修改

对于任何感兴趣的人,我写了一种方法来读取文件中的id符号名称,并将它们转换为符号的数值,只要符号读入是基于原始符号id即被质疑。

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

您可以看到解决方案below

3 个答案:

答案 0 :(得分:2)

您可以将资源(.rc)文件中的字符串表视为键/值对。 FindResource通过提供Key来工作,它返回值。

_T(&#34; IDS_TEST_Home_HERE&#34;)的正确语法是:

MAKEINTRESOURCE(IDS_TEST_Home_HERE)

您不需要报价。

答案 1 :(得分:2)

字符串资源不能通过名称标识,只能通过整数值标识。该值表示它们在字符串表中的位置。

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

Raymond Chen报道了internal format of string tables

答案 2 :(得分:1)

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

#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);
}