转换Unicode编码中的变量

3

我是一名JavaScript开发者,所以请对我宽容点!我正在尝试编写一小段C++代码来实现在框架上打印的功能。我正在使用Unicode进行编译,根据我的研究,这可能是导致问题的原因。

我认为这是一个相对简单的事情,但我可能把它复杂化了。应用程序包含一个std::string,其中包含当前打印机的名称。脚本首先检查是否未设置它(如果未设置,则使用GetDefaultPrinter输出一个LPTSTR)。最后,脚本将std::stringLPTSTR转换为LPCTSTR以供CreateDC使用。

以下是我的代码:

std::string PrinterName = window->getPrinter();
LPDWORD lPrinterNameLength;
LPWSTR szPrinterName;
LPCTSTR PrinterHandle;

if (PrinterName == "unset") {
    GetDefaultPrinter( szPrinterName, &lPrinterNameLength );
    PrinterHandle = szPrinterName; //Note sure the best way to convert here
} else {
    PrinterHandle = PrinterName.c_str();
}
HDC hdc = CreateDC( L"WINSPOOL\0", PrinterHandle, NULL, NULL);

在编译时,我只得到转换错误,例如
无法将参数2从LPDWORD *转换为LPDWORD(GetDefaultPrinter)
以及
从'const char *'无法转换为'LPCTSTR'(在PrinterHandle = PrinterName.c.str()行)
我已经在SO上进行了相当多的研究,但没有得出具体的解决方案。
非常感谢您的帮助!

1
我希望函数中为 szPrinterName 分配了内存。哦,算了,它没有。你需要修复它并传递你拥有的内存。 - chris
@chris 是的 - 我只是省略了它,因为它不是问题的范围之内。 :) - Jonathan
1
window->getPrinter() 是否有办法返回宽字符串而不是现在返回的窄字符串?如果可以,那么有一个比转换更合理的解决方案。 - chris
1
使用std::wstring代替std::string - ildjarn
@chris 你说得对,而且由于它是一个框架,很难修改这些函数。 - Jonathan
显示剩余3条评论
2个回答

2

首先,如评论中所述,正确的方法是创建一个DWORD并传递地址:

DWORD lpPrinterNameLength;
...
GetDefaultPrinter(..., &lpPrinterNameLength);

这是为了能够使用和更改数字:
在输入时,指定 pszBuffer 缓冲区的字符大小。在输出时,接收打印机名称字符串的字符大小(包括终止空字符)。
它只需要一个 DWORD,但函数会更改传入变量中的数字,因此函数需要更改变量地址,以便这些更改反映回调用者。
其次,由于 window->getPrinter() 返回窄字符串并且您正在使用 UNICODE,该函数需要宽字符串,因此应将窄字符串转换为宽字符串。有多种方法可以实现这一点(例如ildjarn评论中提到的非常简单的方法),即使使用 C++11,这个方法也略微好一些,但前面提到的注意事项适用得更好,但我将使用 MultiByteToWideChar 和 C++03:
std::wstring narrowToWide(const std::string &narrow) {
    std::vector<wchar_t> wide;

    int length = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, narrow.c_str(), -1, NULL, 0);
    if (!length) {
        //error
    }

    wide.resize(length);
    if (!MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, narrow.c_str(), -1, &wide[0], length)) {
        //error, should probably check that the number of characters written is consistent as well
    }

    return std::wstring(wide.begin(), wide.end());
}

...

std::wstring PrinterName = narrowToWide(window->getPrinter());
//rest is same, but should be L"unset"
CreateDC( L"WINSPOOL\0", PrinterHandle, NULL, NULL);

1
如果OP正在使用VC++ 2012,他们可以用std::wstring_convert::from_bytes()替换narrowToWide - ildjarn
@ildjarn,我不确定你在评论中具体指的是什么,但我想这可能只是一些简单的东西。如果你知道将要使用VS2012,那就非常有用了。 - chris
@ildjarn,非常正确。那些将非常有用,谢谢。 - chris
此外,当大家都关注编译器错误时,“PrinterName == 'unset'”也是错误的。 - Jesse Good
1
@JesseGood,我有什么遗漏吗?从我获取的内容来看,它正在与std::string进行比较,所以没问题,而且它是一个未知函数,因此在那种情况下很难证明返回值会有所不同。或者你是在谈论我在注释中提到将其更改为L"unset"的事实? - chris
显示剩余4条评论

2
即使您编译为“Unicode”(宽字符字符串),您仍然可以调用“ANSI”(窄字符字符串)版本的API函数。Windows会为您进行转换并在幕后调用宽字符版本。
例如,对于大多数Windows API(如CreateDC),实际上并没有名为该名称的函数。相反,有一个名为CreateDC的宏,它会扩展为CreateDCA或CreateDCW,这些是实际的函数名称。当您编译为“Unicode”时,宏会扩展为- W版本(在所有现代操作系统的本机版本中都是如此)。无论您是否编译为Unicode,都可以明确调用任一版本。在大多数情况下,-A版本将简单地为您将窄字符串转换为宽字符串,然后调用相应的-W版本。(这里与创建窗口相关的一些警告,但我认为它们不适用于DC。)
std::string PrinterName = window->getPrinter();
if (PrinterName == "unset") {
  char szPrinterName[MAX_PATH];  // simplified for illustration
  DWORD cchPrinterNameLength = ARRAYSIZE(szPrinterName);
  GetDefaultPrinterA(szPrinterName, &cchPrinterNameLength);
  PrinterName = szPrinterName;
}

HDC hdc = CreateDCA("WINSPOOL", PrinterName.c_str(), NULL, NULL);

刚刚重新编译了这个程序,现在解决方案好多了。谢谢。 - Jonathan

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