当使用C++可变长参数时,如何将\_TCHAR*转换为char*?

8

我们需要将一个格式为_TCHAR*的字符串和一些char *字符串传递到一个具有可变长度参数的函数中:

inline void FooBar(const _TCHAR *szFmt, const char *cArgs, ...) {
  //...
}

因此,它可以这样称呼:
char *foo = "foo";
char *bar = "bar";
LogToFileA(_T("Test %s %s"), foo, bar);

显然,一个简单的解决方法是使用_TCHAR代替char,但不幸的是我们没有这个奢侈条件。
我们需要和va_start一起使用,等等,这样我们才能格式化一个字符串:
va_list args;
_TCHAR szBuf[BUFFER_MED_SIZE];

va_start(args, cArgs);
_vstprintf_s(szBuf, BUFFER_MED_SIZE, szFmt, args);
va_end(args);

很遗憾,我们无法使用此功能,因为它会导致以下错误:

Unhandled exception at 0x6a0d7f4f (msvcr90d.dll) in foobar.exe:
0xC0000005: Access violation reading location 0x2d86fead.

我认为我们需要将char*转换成_TCHAR*,但是如何实现呢?

如果您有使用wchar而不是char的条件,我认为这将解决您的问题。 - David Menard
唔,很遗憾 - 当前系统是char和_TCHAR的混杂。 - Nick Bolton
太糟糕了...效率是个问题吗?如果不是,看看我的答案,它对我有用。 - David Menard
为什么允许TCHAR参数的...A()函数存在?只有当存在相应的Unicode的...W()函数时才有意义。 - Remy Lebeau
Remy,也许你可以看一下我的答案,并建议我应该如何改进它?请注意函数参数不能被更改。 - Nick Bolton
4个回答

3

使用%hs或%hS代替%s。这将强制参数在printf()风格函数的Ansi和Unicode版本中被解释为char*,例如:

inline void LogToFile(const _TCHAR *szFmt, ...)
{  
  va_list args;
  TCHAR szBuf[BUFFER_MED_SIZE];

  va_start(args, szFmt);
  _vstprintf_s(szBuf, BUFFER_MED_SIZE, szFmt, args);
  va_end(args);
}  

{
  char *foo = "foo"; 
  char *bar = "bar"; 
  LogToFile(_T("Test %hs %hs"), foo, bar); 
}

2
通常它看起来像下面这样:

char *foo = "foo";
char *bar = "bar";
#ifdef UNICODE
LogToFileW( L"Test %S %S", foo, bar); // big S
#else
LogToFileA( "Test %s %s", foo, bar);
#endif

您的问题并不是完全清楚的。您的功能是如何实现的,以及您如何使用它?


1
请使用%hs或%hS代替%s和%S。这将强制在Ansi和Unicode中使用char*。然后您可以删除UNICODE的检查。 - Remy Lebeau

1

这是我的解决方案 - 我欢迎改进建议!

inline void FooBar(const _TCHAR *szFmt, const char *cArgs, ...) {

    va_list args;
    _TCHAR szBuf[BUFFER_MED_SIZE];

    // Count the number of arguments in the format string.
    const _TCHAR *at = _tcschr(szFmt, '%');
    int argCount = 0;
    while(at) {
        argCount++;
        at = _tcschr(at + 1, '%');
    }

    CA2W *ca2wArr[100];
    LPWSTR szArgs[100];
    va_start(args, cArgs);
    for (int i = 1; i < argCount + 1; i++) {
        CA2W *ca2w = new CA2W(cArgs);
        szArgs[i] = ca2w->m_psz;
        ca2wArr[i] = ca2w;
        cArgs = va_arg(args, const char *);
    }
    va_end(args);

    // Use the new array we just created (skips over first element).
    va_start(args, szArgs[0]);
    _vstprintf_s(szBuf, BUFFER_MED_SIZE, szFmt, args);
    va_end(args);

    // Free up memory used by CA2W objects.
    for (int i = 1; i < argCount + 1; i++) {
        delete ca2wArr[i];
    }

    // ... snip ... - code that uses szBuf
}

你的循环计数参数没有考虑"%%"这类文字,而且使用va_arg()函数时不能处理超过32位的参数。另外,由于_vstprintf_s()已经同时支持Ansi和Unicode,因此根本不需要这样复杂的代码。之前我给你的代码有问题吗? - Remy Lebeau
啊,你关于计数器是正确的。关于你的代码,我认为它可以正常工作,我们将在实现新代码时使用它,但是对于现有的(有缺陷的)用法,我们必须使用我答案中提到的临时hack函数。 - Nick Bolton

0

这是我以前用过的一种方法,将TCHAR转换为char,希望能帮到你,虽然我并没有真正寻求优化,所以它不是最快的方式...但它确实有效!

    TCHAR tmp[255];
::GetWindowText(hwnd, tmp, 255);
std::wstring s = tmp;

//convert from wchar to char
const wchar_t* wstr = s.c_str();
size_t wlen = wcslen(wstr) + 1;
char newchar[100];
size_t convertedChars = 0;
wcstombs_s(&convertedChars, newchar, wlen, wstr, _TRUNCATE);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接