From b891fe19743c1437d1b17565b901c2aa608c83c2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 23 Dec 2018 22:01:50 +0100 Subject: [PATCH 1/4] Remove misleading warning from the ipc sample DDE code translates wxIPC_PRIVATE to wxIPC_TEXT internally since a change done in 9d86099269660fb9eab4d146e625cb2a5fff9c14, but same commit also added a warning to the ipc sample stating that wxIPC_PRIVATE doesn't work, which isn't really the case. Remove the warning to avoid the confusion. See #7470. --- samples/ipc/server.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/samples/ipc/server.cpp b/samples/ipc/server.cpp index 54d2763898..25a4420cc1 100644 --- a/samples/ipc/server.cpp +++ b/samples/ipc/server.cpp @@ -273,12 +273,6 @@ void MyServer::Advise() const wxString s = now.FormatTime() + " " + now.FormatDate(); m_connection->Advise(m_connection->m_advise, s.mb_str(), wxNO_LEN); -#if wxUSE_DDE_FOR_IPC - wxLogMessage("DDE Advise type argument cannot be wxIPC_PRIVATE. " - "The client will receive it as wxIPC_TEXT, " - " and receive the correct no of bytes, " - "but not print a correct log entry."); -#endif char bytes[3] = { '1', '2', '3' }; m_connection->Advise(m_connection->m_advise, bytes, 3, wxIPC_PRIVATE); } From c657fd3d61828e0507c9dae5522f6c007e87628c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 24 Dec 2018 01:42:12 +0100 Subject: [PATCH 2/4] Fix passing Unicode strings via wxIPC when using DDE wxIPC API doesn't map well onto DDE, as we don't have wxIPCFormat parameter in StartAdvise() but do allow specifying the format when calling Advise() itself, whereas DDE requires specifying the format when establishing the advise loop and the data always must use this format later. Because of this, we have to pass the actual format with the data itself instead of relying on DDE formats support. This has the advantage of allowing wxIPC_UTF8TEXT to work, while previously it didn't and couldn't, as DDE only supports the standard (or custom, but registered) clipboard formats and it wasn't one of them. Of course, this also has a disadvantage of having to make another copy of the data, but this seems unavoidable. This change allow Advise() overload taking wxString to work, including for non-ASCII strings, as shown by the update to the IPC sample. It also makes wxDDEConnection::m_dataType unnecessary, as we must always use the format passed to DDE callback anyhow when handling XTYP_ADVREQ. Closes #17900. --- docs/changes.txt | 4 ++++ include/wx/msw/dde.h | 1 - samples/ipc/server.cpp | 6 +++++- src/msw/dde.cpp | 32 ++++++++++++++++++++++---------- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 16cb9613f5..404da6917a 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -124,6 +124,10 @@ All (GUI): - Allow changing tooltip text for button allowing to enter a new string in wxPGArrayEditorDialog. +wxMSW: + +- Fix passing Unicode strings via wxIPC when using DDE. + 3.1.2: (released 2018-12-10) ---------------------------- diff --git a/include/wx/msw/dde.h b/include/wx/msw/dde.h index 91fdd06f96..a7cd04ccda 100644 --- a/include/wx/msw/dde.h +++ b/include/wx/msw/dde.h @@ -70,7 +70,6 @@ public: WXHCONV m_hConv; const void* m_sendingData; int m_dataSize; - wxIPCFormat m_dataType; wxDECLARE_NO_COPY_CLASS(wxDDEConnection); wxDECLARE_DYNAMIC_CLASS(wxDDEConnection); diff --git a/samples/ipc/server.cpp b/samples/ipc/server.cpp index 25a4420cc1..8058eb983a 100644 --- a/samples/ipc/server.cpp +++ b/samples/ipc/server.cpp @@ -268,7 +268,11 @@ void MyServer::Advise() { const wxDateTime now = wxDateTime::Now(); - m_connection->Advise(m_connection->m_advise, now.Format()); + m_connection->Advise + ( + m_connection->m_advise, + wxString::FromUTF8("\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82") + ); const wxString s = now.FormatTime() + " " + now.FormatDate(); m_connection->Advise(m_connection->m_advise, s.mb_str(), wxNO_LEN); diff --git a/src/msw/dde.cpp b/src/msw/dde.cpp index ccea317306..11199fad2f 100644 --- a/src/msw/dde.cpp +++ b/src/msw/dde.cpp @@ -748,12 +748,20 @@ bool wxDDEConnection::DoAdvise(const wxString& item, size_t size, wxIPCFormat format) { + // Unfortunately we currently always use the same CF_TEXT in StartAdvise() + // but allow calling Advise() with different formats. This doesn't map well + // to the DDE API, so the price to pay for it is that we need to send the + // actual format used as part of the data, even if this means making + // another copy of it. + wxCharBuffer buf; + buf.extend(size + 1); + *buf.data() = format; + memcpy(buf.data() + 1, data, size); + HSZ item_atom = DDEGetAtom(item); HSZ topic_atom = DDEGetAtom(m_topicName); - m_sendingData = data; // mrf: potential for scope problems here? - m_dataSize = size; - // wxIPC_PRIVATE does not succeed, so use text instead - m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format; + m_sendingData = buf.data(); + m_dataSize = size + 1; bool ok = DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0; if ( !ok ) @@ -986,11 +994,12 @@ _DDECallback(UINT wType, connection->m_dataSize, 0, hsz2, - connection->m_dataType, + wFmt, 0 ); connection->m_sendingData = NULL; + connection->m_dataSize = 0; return (DDERETURN)data; } @@ -1008,18 +1017,21 @@ _DDECallback(UINT wType, DWORD len = DdeGetData(hData, NULL, 0, 0); - void *data = connection->GetBufferAtLeast(len); + BYTE* const data = static_cast(connection->GetBufferAtLeast(len)); wxASSERT_MSG(data != NULL, wxT("Buffer too small in _DDECallback (XTYP_ADVDATA)") ); - DdeGetData(hData, (LPBYTE)data, len, 0); + DdeGetData(hData, data, len, 0); DdeFreeDataHandle(hData); + + // Our code in DoAdvise() prepends the actual format of the + // data as the first byte, extract it back now. if ( connection->OnAdvise(connection->m_topicName, item_name, - data, - (int)len, - (wxIPCFormat) wFmt) ) + data + 1, + (int)len - 1, + (wxIPCFormat)*data) ) { return (DDERETURN)(DWORD)DDE_FACK; } From 09bf235a59b47b886c4ab6321daf1a2cf46be211 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 26 Dec 2018 23:20:52 +0100 Subject: [PATCH 3/4] Revert "Fix passing Unicode strings via wxIPC when using DDE" This reverts commit c657fd3d61828e0507c9dae5522f6c007e87628c because changing the format of DDE advise requests/replies is not a good idea: other applications (those using previous versions of wxWidgets or even not using wxWidgets at all) may rely on getting data in real CF_TEXT format rather than in one of text formats preceded by the extra byte containing the actual format and the previous commit would have silently broken this. Another fix for #17900 will be implemented instead. --- docs/changes.txt | 4 ---- include/wx/msw/dde.h | 1 + samples/ipc/server.cpp | 6 +----- src/msw/dde.cpp | 32 ++++++++++---------------------- 4 files changed, 12 insertions(+), 31 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 404da6917a..16cb9613f5 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -124,10 +124,6 @@ All (GUI): - Allow changing tooltip text for button allowing to enter a new string in wxPGArrayEditorDialog. -wxMSW: - -- Fix passing Unicode strings via wxIPC when using DDE. - 3.1.2: (released 2018-12-10) ---------------------------- diff --git a/include/wx/msw/dde.h b/include/wx/msw/dde.h index a7cd04ccda..91fdd06f96 100644 --- a/include/wx/msw/dde.h +++ b/include/wx/msw/dde.h @@ -70,6 +70,7 @@ public: WXHCONV m_hConv; const void* m_sendingData; int m_dataSize; + wxIPCFormat m_dataType; wxDECLARE_NO_COPY_CLASS(wxDDEConnection); wxDECLARE_DYNAMIC_CLASS(wxDDEConnection); diff --git a/samples/ipc/server.cpp b/samples/ipc/server.cpp index 8058eb983a..25a4420cc1 100644 --- a/samples/ipc/server.cpp +++ b/samples/ipc/server.cpp @@ -268,11 +268,7 @@ void MyServer::Advise() { const wxDateTime now = wxDateTime::Now(); - m_connection->Advise - ( - m_connection->m_advise, - wxString::FromUTF8("\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82") - ); + m_connection->Advise(m_connection->m_advise, now.Format()); const wxString s = now.FormatTime() + " " + now.FormatDate(); m_connection->Advise(m_connection->m_advise, s.mb_str(), wxNO_LEN); diff --git a/src/msw/dde.cpp b/src/msw/dde.cpp index 11199fad2f..ccea317306 100644 --- a/src/msw/dde.cpp +++ b/src/msw/dde.cpp @@ -748,20 +748,12 @@ bool wxDDEConnection::DoAdvise(const wxString& item, size_t size, wxIPCFormat format) { - // Unfortunately we currently always use the same CF_TEXT in StartAdvise() - // but allow calling Advise() with different formats. This doesn't map well - // to the DDE API, so the price to pay for it is that we need to send the - // actual format used as part of the data, even if this means making - // another copy of it. - wxCharBuffer buf; - buf.extend(size + 1); - *buf.data() = format; - memcpy(buf.data() + 1, data, size); - HSZ item_atom = DDEGetAtom(item); HSZ topic_atom = DDEGetAtom(m_topicName); - m_sendingData = buf.data(); - m_dataSize = size + 1; + m_sendingData = data; // mrf: potential for scope problems here? + m_dataSize = size; + // wxIPC_PRIVATE does not succeed, so use text instead + m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format; bool ok = DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0; if ( !ok ) @@ -994,12 +986,11 @@ _DDECallback(UINT wType, connection->m_dataSize, 0, hsz2, - wFmt, + connection->m_dataType, 0 ); connection->m_sendingData = NULL; - connection->m_dataSize = 0; return (DDERETURN)data; } @@ -1017,21 +1008,18 @@ _DDECallback(UINT wType, DWORD len = DdeGetData(hData, NULL, 0, 0); - BYTE* const data = static_cast(connection->GetBufferAtLeast(len)); + void *data = connection->GetBufferAtLeast(len); wxASSERT_MSG(data != NULL, wxT("Buffer too small in _DDECallback (XTYP_ADVDATA)") ); - DdeGetData(hData, data, len, 0); + DdeGetData(hData, (LPBYTE)data, len, 0); DdeFreeDataHandle(hData); - - // Our code in DoAdvise() prepends the actual format of the - // data as the first byte, extract it back now. if ( connection->OnAdvise(connection->m_topicName, item_name, - data + 1, - (int)len - 1, - (wxIPCFormat)*data) ) + data, + (int)len, + (wxIPCFormat) wFmt) ) { return (DDERETURN)(DWORD)DDE_FACK; } From 20cb47c1c4d2bce13eee87f51f919de94baaacf9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 27 Dec 2018 00:21:49 +0100 Subject: [PATCH 4/4] Send Unicode data as UTF-8 text when using DDE-based IPC This is a more hackish but more compatible solution to the problem of data sent using wxIPC_UTF8TEXT format being simply lost when using DDE for IPC classes. We must use CF_TEXT for the DDE to pass our data, but we can try to decode it as UTF-8 in the client and assume it was sent in this format if it worked. This obviously suffers from false positives as any ASCII string will still be assumed to be UTF-8, but there shouldn't be any real harm coming from this. This change also makes sending data in wxIPC_UTF{16,32}TEXT formats work as well by converting it to UTF-8. Update the sample to call Advise() with both wxIPC_UTF{8,16}TEXT formats and remove the now unnecessary wxDDEConnection::m_dataType member. Closes #17900. --- docs/changes.txt | 4 ++ include/wx/msw/dde.h | 1 - samples/ipc/server.cpp | 9 ++++- src/msw/dde.cpp | 83 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 88 insertions(+), 9 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 16cb9613f5..404da6917a 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -124,6 +124,10 @@ All (GUI): - Allow changing tooltip text for button allowing to enter a new string in wxPGArrayEditorDialog. +wxMSW: + +- Fix passing Unicode strings via wxIPC when using DDE. + 3.1.2: (released 2018-12-10) ---------------------------- diff --git a/include/wx/msw/dde.h b/include/wx/msw/dde.h index 91fdd06f96..a7cd04ccda 100644 --- a/include/wx/msw/dde.h +++ b/include/wx/msw/dde.h @@ -70,7 +70,6 @@ public: WXHCONV m_hConv; const void* m_sendingData; int m_dataSize; - wxIPCFormat m_dataType; wxDECLARE_NO_COPY_CLASS(wxDDEConnection); wxDECLARE_DYNAMIC_CLASS(wxDDEConnection); diff --git a/samples/ipc/server.cpp b/samples/ipc/server.cpp index 25a4420cc1..cbaf7586e9 100644 --- a/samples/ipc/server.cpp +++ b/samples/ipc/server.cpp @@ -268,8 +268,15 @@ void MyServer::Advise() { const wxDateTime now = wxDateTime::Now(); - m_connection->Advise(m_connection->m_advise, now.Format()); + wxString str = wxString::FromUTF8("\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82"); + m_connection->Advise(m_connection->m_advise, str + " (using UTF-8)"); + str += " (using wchar_t)"; + m_connection->Advise(m_connection->m_advise, + str.wc_str(), (str.length() + 1)*sizeof(wchar_t), + wxIPC_UNICODETEXT); + + // This one uses wxIPC_TEXT by default. const wxString s = now.FormatTime() + " " + now.FormatDate(); m_connection->Advise(m_connection->m_advise, s.mb_str(), wxNO_LEN); diff --git a/src/msw/dde.cpp b/src/msw/dde.cpp index ccea317306..245a1f3de2 100644 --- a/src/msw/dde.cpp +++ b/src/msw/dde.cpp @@ -742,6 +742,14 @@ bool wxDDEConnection::StopAdvise(const wxString& item) return ok; } +// Small helper function converting the data from the format used by the given +// conversion to UTF-8. +static +wxCharBuffer ConvertToUTF8(const wxMBConv& conv, const void* data, size_t size) +{ + return wxConvUTF8.cWC2MB(conv.cMB2WC((const char*)data, size, NULL)); +} + // Calls that SERVER can make bool wxDDEConnection::DoAdvise(const wxString& item, const void *data, @@ -750,10 +758,55 @@ bool wxDDEConnection::DoAdvise(const wxString& item, { HSZ item_atom = DDEGetAtom(item); HSZ topic_atom = DDEGetAtom(m_topicName); - m_sendingData = data; // mrf: potential for scope problems here? - m_dataSize = size; - // wxIPC_PRIVATE does not succeed, so use text instead - m_dataType = format == wxIPC_PRIVATE ? wxIPC_TEXT : format; + + // Define the buffer which, if it's used at all, needs to stay alive until + // after DdePostAdvise() call which uses it. + wxCharBuffer buf; + + // As we always use CF_TEXT for XTYP_ADVSTART, we have to use wxIPC_TEXT + // here, even if a different format was specified for this value. Of + // course, this can only be done for just a few of the formats, so check + // that we have one of them here. + switch ( format ) + { + case wxIPC_TEXT: + case wxIPC_OEMTEXT: + case wxIPC_UTF8TEXT: + case wxIPC_PRIVATE: + // Use the data directly. + m_sendingData = data; + m_dataSize = size; + break; + + case wxIPC_UTF16TEXT: + case wxIPC_UTF32TEXT: + // We need to convert the data to UTF-8 as UTF-16 or 32 would + // appear as mojibake in the client when received as CF_TEXT. + buf = format == wxIPC_UTF16TEXT + ? ConvertToUTF8(wxMBConvUTF16(), data, size) + : ConvertToUTF8(wxMBConvUTF32(), data, size); + + m_sendingData = buf.data(); + m_dataSize = buf.length(); + break; + + case wxIPC_INVALID: + case wxIPC_BITMAP: + case wxIPC_METAFILE: + case wxIPC_SYLK: + case wxIPC_DIF: + case wxIPC_TIFF: + case wxIPC_DIB: + case wxIPC_PALETTE: + case wxIPC_PENDATA: + case wxIPC_RIFF: + case wxIPC_WAVE: + case wxIPC_ENHMETAFILE: + case wxIPC_FILENAME: + case wxIPC_LOCALE: + wxFAIL_MSG( "Unsupported IPC format for Advise()" ); + return false; + }; bool ok = DdePostAdvise(DDEIdInst, topic_atom, item_atom) != 0; if ( !ok ) @@ -986,7 +1039,7 @@ _DDECallback(UINT wType, connection->m_dataSize, 0, hsz2, - connection->m_dataType, + wFmt, 0 ); @@ -1008,18 +1061,34 @@ _DDECallback(UINT wType, DWORD len = DdeGetData(hData, NULL, 0, 0); - void *data = connection->GetBufferAtLeast(len); + char* const data = (char *)connection->GetBufferAtLeast(len); wxASSERT_MSG(data != NULL, wxT("Buffer too small in _DDECallback (XTYP_ADVDATA)") ); DdeGetData(hData, (LPBYTE)data, len, 0); DdeFreeDataHandle(hData); + + // We always get data in CF_TEXT format, but it could + // actually be UTF-8, so try recovering the original format + // if possible. + if ( wFmt != CF_TEXT ) + { + wxLogDebug("Unexpected format %02x in XTYP_ADVDATA", wFmt); + return (DDERETURN)DDE_FNOTPROCESSED; + } + + wxIPCFormat format; + if ( wxConvUTF8.ToWChar(NULL, 0, data, len) != wxCONV_FAILED ) + format = wxIPC_UTF8TEXT; + else + format = wxIPC_TEXT; + if ( connection->OnAdvise(connection->m_topicName, item_name, data, (int)len, - (wxIPCFormat) wFmt) ) + format) ) { return (DDERETURN)(DWORD)DDE_FACK; }