C++ Clipboard entries keep overwriting instead of having separate entries

110 views Asked by At

I am writing a program that takes all objects from a JSON array and copies them into the clipboard. However, when I open the Clipboard history only the last object from the file is shown, therefore they keep overwriting each other.

This is my Code:

void loadOldClipboard() {
    // Load the JSON data from our File
    std::ifstream file("pastEntries.json");
    Json::Value root;
    file >> root;
    file.close();
    OpenClipboard(0);
    EmptyClipboard();
    for (const auto& obj : root) {
        std::string entry = obj["clipboardEntry"].asString();
        std::cout << "clipboardEntry: " << entry << std::endl;

        // Allocate global memory for the clipboard data
        HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, entry.size() + 1);
        if (hMem == NULL) {
            // Handle memory allocation error
            CloseClipboard();
            return;
        }

        // Copy the entry to the global memory
        char* pMem = static_cast<char*>(GlobalLock(hMem));
        if (pMem == NULL) {
            // Handle memory locking error
            GlobalFree(hMem);
            CloseClipboard();
            return;
        }
        strcpy_s(pMem, entry.size() + 1, entry.c_str());
        GlobalUnlock(hMem);

        // Set the clipboard data for the current entry
        SetClipboardData(CF_TEXT, hMem);
    }
    CloseClipboard();
}

just in case I also added my current JSON File:

[
    {
        "clipboardEntry": "Entry 1"
    },
    {
        "clipboardEntry": "Entry 2"
    },
    {
        "clipboardEntry": "Entry 3"
    }
]

UPDATE:

I tried opening and closing the clipboard on each loop iteration, but it still doesn't work.

void loadOldClipboard() {
    // Load the JSON data from our File
    std::ifstream file("pastEntries.json");
    Json::Value root;
    file >> root;
    file.close();

    // Allocate global memory for each string and copy it to the clipboard
    for (const auto& text : root) {
        if (!OpenClipboard(0)) {
            std::cerr << "Failed to open clipboard\n";
            return;
        }

        // Empty the clipboard
        EmptyClipboard();

        std::string entry = text["clipboardEntry"].asString();
        std::cout << "clipboardEntry: " << entry << std::endl;
        // Allocate global memory for the clipboard data
        HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, entry.size() + 1);
        if (hMem == NULL) {
            // Handle memory allocation error
            CloseClipboard();
            return;
        }

        // Copy the text to the global memory
        char* pMem = static_cast<char*>(GlobalLock(hMem));
        if (pMem == NULL) {
            // Handle memory locking error
            GlobalFree(hMem);
            CloseClipboard();
            return;
        }
        strcpy_s(pMem, entry.size() + 1, entry.c_str());
        GlobalUnlock(hMem);

        // Set the clipboard data
        SetClipboardData(CF_TEXT, hMem);

        // Close the clipboard
        CloseClipboard();
    }
}

UPDATE:

I got it to work, it turns out that similar to what @Halfix mentioned, I needed to put in a Sleep() delay of around 1/2 - 1 second. Otherwise, the program couldn't process it properly.

2

There are 2 answers

1
Chasper On BEST ANSWER

Since @MarioRütsche didn't show the final code himself, I took it upon myself. I quickly recreated it and ended up with this new function. It looks like the problem was indeed with the built-in Clipboard Viewer from Windows not properly receiving the Data, so I added a Sleep() of 1 second.

void loadOldClipboard() {
    // Load the JSON data from our File
    std::ifstream file("pastEntries.json");
    Json::Value root;
    file >> root;
    file.close();


    // Allocate global memory for each string and copy it to the clipboard
    for (const auto& text : root) {
        if (!OpenClipboard(0)) {
            std::cerr << "Failed to open clipboard\n";
            return;
        }

        // Empty the clipboard
        EmptyClipboard();

        std::string entry = text["clipboardEntry"].asString();
        std::cout << "clipboardEntry: " << entry << std::endl;
        // Allocate global memory for the clipboard data
        HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, entry.size() + 1);
        if (hMem == NULL) {
            // Handle memory allocation error
            CloseClipboard();
            return;
        }

        // Copy the text to the global memory
        char* pMem = static_cast<char*>(GlobalLock(hMem));
        if (pMem == NULL) {
            // Handle memory locking error
            GlobalFree(hMem);
            CloseClipboard();
            return;
        }
        strcpy_s(pMem, entry.size() + 1, entry.c_str());
        GlobalUnlock(hMem);

        // Set the clipboard data
        SetClipboardData(CF_TEXT, hMem);

        HANDLE hData = GetClipboardData(CF_TEXT);
        char* pszData = static_cast<char*>(GlobalLock(hData));
        std::string clipboardContent = pszData;

        // Close the clipboard
        CloseClipboard();
        Sleep(1000); // Wait for 1000ms before checking again
    }
 }
3
Remy Lebeau On

This is expected behavior. Until you call CloseClipboard(), the data is not fully committed (ie, other apps can't access it). The clipboard allows code to call SetClipboardData() multiple times with different types of data, and then commit them in one go. But, since you are setting only CF_TEXT on every iteration, CloseClipboard() will commit only the last item that you set.

If you want the clipboard history to record every CF_TEXT item individually, you will have to (re-)open/close the clipboard on each iteration.