Search code examples
c++c++buildertmemo

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();

Solution

  • 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;