12

I know this has already been discussed in several questions on SO, but none of those solutions have worked for me.

I start with a char* because this is for a DLL that will be called from VBA, and char* is necessary for VBA to pass a string to the DLL.

I need to return a LPCWSTR because that's the input parameter for the API function I'm trying to call, and I can't enable casting by switching from Unicode to multi-byte character set in the Properties window, because the API has this code:

#if !defined(UNICODE) && !defined(NOUNICODE)
#error UNICODE is not defined. UNICODE must be defined for correct API arguments.
#endif

I tried this:

LPCWSTR convertCharArrayToLPCWSTR(char* charArray)
    {
        const char* cs=charArray;
        wchar_t filename[4096] = {0};
        MultiByteToWideChar(0, 0, cs[1], strlen(cs[1]), filename, strlen(cs[1]));
    }

which gave these errors:

error C2664: 'strlen' : cannot convert parameter 1 from 'const char' to 'const char *'
error C2664: 'MultiByteToWideChar' : cannot convert parameter 3 from 'const char' to 'LPCCH'

I tried this (same function header), loosely adapted from this post:

size_t retVal;
const char * cs = charArray;    
size_t length=strlen(cs);
wchar_t * buf = new wchar_t[length]();  // value-initialize to 0 (see below)
size_t wn = mbsrtowcs_s(&retVal,buf,20, &cs, length + 1, NULL);
return buf;

This compiled ok, but when I passed it an example string of "xyz.xlsx", mbsrtowcs_s() set buf to an empty string: L""

So, how do I make this conversion?

Community
  • 1
  • 1
sigil
  • 9,370
  • 40
  • 119
  • 199
  • 2
    MultiByteToWideChar(CP_ACP, 0, cs, -1, filename, 4096); – Hans Passant Oct 31 '13 at 19:43
  • Why don't you make the function take a const char* and avoid the pointless pointer copying. Do you really mean to skip the first char? Why don't you use warring for the conversion? – David Heffernan Oct 31 '13 at 19:54
  • @HansPassant, this works, but when I try to handle several char* in a row, e.g. `LPCWSTR str1=convertCharArrayToLPCWSTR(str1);LPCWSTR str2=convertCharArrayToLPCWSTR(str2);` then the second pointer gets assigned to the same address as the first pointer (i think), because both `LPCWSTR` end up with the same value. – sigil Oct 31 '13 at 20:07
  • @DavidHeffernan, how do I use "warring" for the conversion? I googled that but didn't find anything useful. – sigil Oct 31 '13 at 20:08
  • 1
    Sure, that happens when you write illegal C code. You can't return a pointer to a local variable. You struggling with the very basics of C, you really do need to start at "Hello world" and do all the exercises. – Hans Passant Oct 31 '13 at 20:09
  • Sorry. Auto correct fail. My bad. It is wstring. – David Heffernan Oct 31 '13 at 20:12
  • @HansPassant, thanks for the encouragement. I've been reading about pointers and trying to figure out how to preserve the value referenced by the pointer once it goes out of scope. I tried passing in a `wchar_t` parameter so that the pointer could reference that instead of a local variable, but that didn't work. How can I keep that value outside of the function? – sigil Oct 31 '13 at 22:09
  • @HansPassant, ok, I think I got it, answer posted below. – sigil Oct 31 '13 at 22:51
  • Possible duplicate of [Convert char \* to LPWSTR](http://stackoverflow.com/questions/6858524/convert-char-to-lpwstr) – Rusty Nail May 04 '16 at 10:28

2 Answers2

20

Following Hans Passant's advice regarding pointers to local variables, I worked out this approach, which seems to work well:

wchar_t *convertCharArrayToLPCWSTR(const char* charArray)
{
    wchar_t* wString=new wchar_t[4096];
    MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096);
    return wString;
}

I'm aware that the use of new requires memory management, which I perform in the function that calls this one.

sigil
  • 9,370
  • 40
  • 119
  • 199
  • 3
    Yup, that works. Well, as long as the string isn't bigger than 4096 characters. You can fix that too, read the docs. Using std::wstring as a return value is the next step ahead. – Hans Passant Oct 31 '13 at 23:55
  • Best solution. Thanks – f4d0 Aug 07 '21 at 19:42
4

Since cs is a const char*, cs[1] is a const char. C++ won't convert it to a pointer for you, because in most cases that doesn't make sense.

You could instead say &cs[1] or cs+1 if the intent is to skip the first char. (That's what you're doing when you pass a pointer to the 1th element; in C++, indexes start at 0.) If the intent is to pass the whole string, then just pass cs.

cHao
  • 84,970
  • 20
  • 145
  • 172