Jordan Houston
Jordan Houston

Reputation: 67

Convert a Vcl::Controls::TCaption aka (System::UnicodeString) to a const char *

I'm currently using C++Builder to create an application that copies text to the user's clipboard. I've placed a TMemo control and I want to contain that in a const char * variable as seen in the code below:

const char* output = TMemo1->Text;

When I compile the program it throws the error

no viable conversion from 'Vcl::Controls::TCaption' (aka 'System::UnicodeString') to 'const char *'

Here's the code that copies text to the clipboard:

const char* output = TMemo1->Text; // Error here
const size_t len = strlen(output) + 1;
HGLOBAL hMem =  GlobalAlloc(GMEM_MOVEABLE, len);
memcpy(GlobalLock(hMem), output, len);
GlobalUnlock(hMem);
OpenClipboard(0);
EmptyClipboard();
SetClipboardData(CF_TEXT, hMem);
CloseClipboard();

Upvotes: 4

Views: 749

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 597941

The Text property returns a UnicodeString object, not a const char* pointer. And there is no implicit conversion from UnicodeString to const char* (nor do you want one). So you would have to convert the data manually, such as with WideCharToMultiByte() (or equivalent), eg:

UnicodeString text = TMemo1->Text;
const size_t len = WideCharToMultiByte(CP_ACP, 0, text.c_str(), -1, NULL, 0, NULL, NULL);
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len);
if (hMem)
{
    char *output = (char*) GlobalLock(hMem);
    WideCharToMultiByte(CP_ACP, 0, text.c_str(), -1, output, len, NULL, NULL);
    GlobalUnlock(hMem);
    if (OpenClipboard(0))
    {
        EmptyClipboard();
        if (SetClipboardData(CF_TEXT, hMem))
            hMem = NULL;
        CloseClipboard();
    }
    if (hMem)
        GlobalFree(hMem);
}

Alternatively, you can save the TMemo's text to an AnsiString and let the RTL handle the conversion for you, eg:

AnsiString output = TMemo1->Text; // <-- automatic conversion from UTF-16 to ANSI
const size_t len = (output.Length() + 1) * sizeof(System::AnsiChar);
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len);
if (hMem)
{
    memcpy(GlobalLock(hMem), output.c_str(), len);
    GlobalUnlock(hMem);
    if (OpenClipboard(0))
    {
        EmptyClipboard();
        if (SetClipboardData(CF_TEXT, hMem))
            hMem = NULL;
        CloseClipboard();
    }
    if (hMem)
        GlobalFree(hMem);
}

However, since you are dealing with Unicode text, you should be using the CF_UNICODETEXT format instead of CF_TEXT. That way, you don't need to convert the UnicodeString data at all, you can just store it as-is (if anybody requests CF_TEXT from the clipboard afterwards, the clipboard itself will convert the text for you), eg:

#include <System.SysUtils.hpp> // for ByteLength()

UnicodeString output = TMemo1->Text;
const size_t len = ByteLength(output) + sizeof(System::WideChar);
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len);
if (hMem)
{
    memcpy(GlobalLock(hMem), output.c_str(), len);
    GlobalUnlock(hMem);
    if (OpenClipboard(0))
    {
        EmptyClipboard();
        if (SetClipboardData(CF_UNICODETEXT, hMem))
            hMem = NULL;
        CloseClipboard();
    }
    if (hMem)
        GlobalFree(hMem);
}

That being said, you are making things harder for yourself then you need to. The VCL has a TClipboard class that handles all of these details for you, eg:

#include <Vcl.Clipbrd.hpp>

Clipboard()->AsText = TMemo1->Text;

Upvotes: 5

Related Questions