I send the following c++ application request:
string data_to_send = "Hello World";
PCSTR lpszString = data_to_send.c_str();
COPYDATASTRUCT cds;
cds.dwData = 0; // can be anything
cds.cbData = sizeof(TCHAR) * (data_to_send.size());
cds.lpData = &lpszString;
cout << lpszString << endl;
SendMessage(Output, WM_COPYDATA, (WPARAM)Output, (LPARAM)(PVOID)&cds);
I get the structure using the following code in Delphi
var
p : PCopyDataStruct;
s : UTF8String;
begin
p := PCopyDataStruct(Message.lParam);
if (p <> nil) then
begin
SetString(s, PAnsiChar(p^.lpData), p^.cbData);
ShowMessage(s);
end else
inherited;
end;
The string looks wrong.
In the debugger, it is equal to the following
'l'#$FE#$F6#2'Hello World'#0#$10#3#$88'u'#$B#0
We see 22 bytes with an extra 4 bytes before the message.
If we use CHAR instead of TCHAR, then we see 11 bytes, but again with an offset of 4 bytes
#$18#$F9#$1B#3'Hello W'
Please, help!!!
UPDATE:
Thanks to Remy Lebeau for his help, his code does everything as it was originally intended and David Heffernan for the correct remark! They saved me. Here is the working code.
On the C++ side:
cds.dwData should not be 0. Use a more unique value, such as the result of calling RegisterWindowMessage(). Many apps, and even the VCL internally, use WM_COPYDATA for different purposes, so you don't want to get confused with someone else's message by mistake.
sizeof(TCHAR) should be sizeof(char) instead (or omitted entirely, since sizeof(char) is always 1).
cds.lpData = &lpszString; needs to be cds.lpData = lpszString; instead. You are sending the address of the lpszString variable itself, not the address of the character data it points at. That is why you are seeing garbage on the other end - you are seeing random bytes from the call stack where lpszString resides, which in your case includes the std::string object (whose internal members happen to include a Short-String-Optimization buffer, which is why you are also seeing your characters, too).
On the Delphi side:
you should validate p^.dwData is your unique number before processing the message any further. If the number does not match what you are expecting, pass the message to the inherited handler and move on.
UTF8String should be AnsiString, unless the sender's std::string is actually UTF-8 encoded.
Try this:
const UINT uMyDataID = RegisterWindowMessage(TEXT("MyDataID"));
...
if (uMyDataID != 0)
{
string data_to_send = u8"Hello World";
COPYDATASTRUCT cds;
cds.dwData = uMyDataID;
cds.cbData = sizeof(char) * data_to_send.size();
cds.lpData = const_cast<char*>(data_to_send.c_str());
// or: cds.lpData = data_to_send.data(); in C++17 and later
SendMessage(Output, WM_COPYDATA, reinterpret_cast<WPARAM>(Output), reinterpret_cast<LPARAM>(&cds));
}
var
uMyDataID: UINT = 0;
...
procedure TMyForm.WMCopyData(var Message: TMessage);
var
p : PCopyDataStruct;
s : UTF8String;
begin
p := PCopyDataStruct(Message.lParam);
if (uMyDataID <> 0) and (p <> nil) and (p^.dwData = uMyDataID) then
begin
SetString(s, PAnsiChar(p^.lpData), p^.cbData);
ShowMessage(s);
end else
inherited;
end;
...
initialization
uMyDataID := RegisterWindowMessage('MyDataID');
Related
**
SOLVED
**
Thanks to the Rob for bringing light to my problem.
Create a global var on component unit fDict
Implement 3 methods, one to add, one to remove and one to retrive;
making the main class create , clear and destroy the global var to avoid memory leaks
making the connectin function of TDispositivo to feed the fDict
making the callback to retrive and move on.
making the disconnect function of TDispositivo to remove the reference in fDict keeping it updated
**
EDIT
**
I found my crash error, i´m implementing the dll inside a component class and try to declare the callback procedures inside the class using
TSnapRev = procedure(XXXXX) of object stdcall;
...
private
procedure callbacksnapshot(XXXXX); stdcall;
...
CLIENT_SetSnapRevCallBack(callbackSnapshot,FSDKUSer);
the callbacksnapshot was triggered but raising a accesse violation after.
O realize the order of variables returned was always misplaced, then i change my approuch moving the callbacksnapshot procedure out of the class and changing type declaration like this:
Type
TSnapRev = procedure(XXXXX); stdcall;
TDispositivo = class
private
end;
procedure callbacksnapshot(XXXXX); stdcall;
...
CLIENT_SetSnapRevCallBack(#callbackSnapshot,FSDKUSer);
...
now the callback triggeing, the order of variables are right and no access violation after, BUT, i need to invoke some functions (events, etc) from my TDispositivo instance or from the owner of TDispositivo instance, in a outside procedure i can´t.
How can i do this into my class ?
i had tryed declare a variable FSDKSnap : TRevSap (when TRevSap is a type of object) but got the same erros or miplaced vars and access violations
**
ORIGINAL
**
Keep working on my Dahua dll convertion to Delphi. Now i'm stuck in a callback, actually in a definition of the type used.
There is the cpp code in header
#define BYTE unsigned char
#define UINT unsigned int
// Snapshot parameter structure
typedef struct _snap_param
{
unsigned int Channel; // Snapshot channel
unsigned int Quality; // Image quality:level 1 to level 6
unsigned int ImageSize; // Video size;0:QCIF,1:CIF,2:D1
unsigned int mode; // Snapshot mode;0:request one frame,1:send out requestion regularly,2: Request consecutively
unsigned int InterSnap; // Time unit is second.If mode=1, it means send out requestion regularly. The time is valid.
unsigned int CmdSerial; // Request serial number
unsigned int Reserved[4];
} SNAP_PARAMS, *LPSNAP_PARAMS;
// Snapshot callback function original shape
// Encode Type 10: jpeg 0: number i frame of mpeg4
typedef void (CALLBACK *fSnapRev)(LLONG lLoginID, BYTE *pBuf, UINT RevLen, UINT EncodeType, DWORD CmdSerial, LDWORD dwUser);
// Set snapshot callback function
CLIENT_NET_API void CALL_METHOD CLIENT_SetSnapRevCallBack(fSnapRev OnSnapRevMessage, LDWORD dwUser);
// Snapshot request
CLIENT_NET_API BOOL CALL_METHOD CLIENT_SnapPicture(LLONG lLoginID, SNAP_PARAMS par);
Looking to the cpp code, it defined BYTE as unsigned char, so mapping to Delphi (Byte), UINT mapping to Cardinal.
The structure was mapped to Cardinal as well.
---> First question HERE
The callback fSnapRev has (BYTE *pBuf) so i suppose is a pointer to byte (Delphi PByte) and i suspect here is the problem. A buffer with so little memory is not right, maybe a array of bytes or array of pansichar. How can i do know that ? like try and miss ? I accept suggestions.
The RevLen and Encode Type was mapped to Cardinal.
There is the cpp code from demo application.
//First part of the method to initialize the dll,
BOOL ret = CLIENT_Init(DisConnectFunc, (LDWORD)this);
if (ret)
{
CLIENT_SetSnapRevCallBack(SnapPicRet,(LDWORD)this);
//here defining the callback
CLIENT_SetAutoReconnect(ReConnectFunc, (LDWORD)this);
}
else
{
MessageBox(ConvertString("initialize SDK failed!"), ConvertString("prompt"));
}
//the callback
void CALLBACK SnapPicRet(LLONG ILoginID, BYTE *pBuf, UINT RevLen, UINT EncodeType, DWORD CmdSerial,
LDWORD dwUser)
{
CCapturePictureDlg *pThis = (CCapturePictureDlg*)dwUser;
pThis->OnOnePicture(ILoginID,pBuf,RevLen,EncodeType,CmdSerial);
}
void CCapturePictureDlg::OnOnePicture(LLONG ILoginID, BYTE *pBuf, UINT RevLen, UINT EncodeType, UINT
CmdSerial)
{
//Get file path
char path[1000];
int filelen = GetModuleFileName(NULL,path,1000);
int i = filelen;
while(path[i]!='\\')
{
i--;
}
path[i + 1] = '\0';
//Get file name
CString filepath(path);
CString filename = "mpeg4.JPG";
CString strfile = filepath + filename;
char *pFileName = strfile.GetBuffer(200);
/* Save image original file */
FILE *stream;
if( (stream = fopen((const char*) pFileName, "wb")) != NULL )
{
int numwritten = fwrite( pBuf, sizeof( char ), RevLen, stream );
fclose( stream );
}
/*Veirfy image encode type. If it is an I frame of mpeg4,then call I frame to decode to BMP to display.*/
if ( EncodeType == 0)
{
//int iRet = IFrameToBmp("tmp.bmp",pFileName);
//if (iRet == 1)
//{
// ShowBitmap("tmp.bmp");
//}
}
else if (EncodeType == 10)
{
//ShowBitmap(pFileName);
ShowSnapImage(pFileName);
}
}
In OnOnePicture the first code is to define the path to image file be stored, then write the buffer in a file stream. if the buffer and size comes from pBuf and RevLen, then i can write this on a TMemoryStream and assign to TJpegImage but still do not know what pBuf really is.
Another ocuard thing is the data, revLen returns value 10 and is too small to be a byte length of the image.
My Delphi Code
TSnapRev = procedure(lLoginID: Integer; pBuf: PByte; RevLen: Cardinal; EncodeType: Cardinal; CmdSerial: DWORD; dwUser: DWORD) of object stdcall;
// Snapshot parameter structure
PSnapParams = ^TSnapParams;
TSnapParams = record
Channel: Cardinal; // Snapshot channel
Quality: Cardinal; // Image quality:level 1 to level 6
ImageSize: Cardinal; // Video size;0:QCIF,1:CIF,2:D1
mode: Cardinal; // Snapshot mode;0:request one frame,1:send out requestion regularly,2: Request consecutively
InterSnap: Cardinal; // Time unit is second.If mode=1, it means send out requestion regularly. The time is valid.
CmdSerial: Cardinal; // Request serial number
Reserved: array[0..3] of Cardinal;
end;
// Snapshot request
function CLIENT_SnapPicture(lLoginID: Integer; par: TSnapParams): Boolean; stdcall; external 'dhnetsdk.dll';
procedure callbackSnapshot(lLoginID: Integer; pBuf: PByte; RevLen: Cardinal; EncodeType: Cardinal; CmdSerial: DWORD; dwUser: DWORD); stdcall;
//Part of my initDll function
FInicializado := CLIENT_Init(callbackDis,FSDKUser);
if FInicializado then
begin
CLIENT_SetSnapRevCallBack(callbackSnapshot,FSDKUSer);
end;
Result := FInicializado;
//Snapshot wrapper procedure
procedure TDispositivo.SDKSnapshot;
var
sIn: TSnapParams;
begin
ZeroMemory(#sIn, SizeOf(sIn));
With sIn do
begin
Channel := 1;
mode := 0;
CmdSerial := 0;
if not CLIENT_SnapPicture(FLoginHandler,sIn) then
begin
doErro(999,'Não foi possível tirar o snapshot');
end;
end;
end;
//the callback implementation
procedure TDispositivo.callbackSnapshot(lLoginID: Integer; pBuf: PByte; RevLen: Cardinal; EncodeType: Cardinal; CmdSerial: DWORD; dwUser: DWORD);
var
F: TMemoryStream;
jpg : TJpegImage;
begin
Showmessage('Tamanho do buffer: ' + SizeOf(pBuf).ToString + #13 +
'RevLen: ' + RevLen.ToString + #13 +
'Tipo: ' + EncodeType.ToString + #13 +
'CmdSerial: ' + CmdSerial.ToString
);
F := TMemoryStream.Create;
jpg := TJPEGImage.Create;
Try
F.Write(pBuf,RevLen);
//F.Write(pBuf^,RevLen);
F.Position:= 0;
jpg.LoadFromStream(F);
fOwner.doSnapshot(self,jpg);
Finally
FreeAndNil(F);
FreeAndNil(jpg);
End;
end;
When i call SDKSnapshot, i got the "showmessage" placed in callbackfunction with the values, but im debug mode i got infinite access violation, on Release mode the application close after the showmessage.
I try to do nothing on callback but still getting access violation and termination.
I´m wondering if my problem is the declaration of the callback procedure, pBuf.
I am posting a different answer because this is a different question now.
Your callback is a standard procedure and does not accept an object reference (and can't be called as an object method which it wasn't when I checked your previous post as this would be one of the key reasons why variables don't match up).
You need to be able to look up the object from some information that you have that will be unique in the callback. Without knowing more details about the API you're calling it looks like that would either be LLoginId or dwUser in your case.
I would probably implement a TDictionary with a lookup to match the unique code to the object you're using. Remember to remove objects from the TDictionary when you're not using them!
Assuming you can use dwUser your code may be something like:
implementation
uses Systm.Generics.Collections;
var
gClassDict: TDictionary<DWORD, TDispositivo>;
procedure RegisterInDictionary(dwUser: DWORD, class: TDispitivo);
begin
if(gClassDict=nil) then
gClassDict:=TDictionary<DWORD, TDispositivo>.Create;
gClassDict.AddOrSetValue(DWORD, class);
end;
function GetClassFromDictionary(dwUser: DWORD): TDispotivo;
begin
if( (gClassDict=nil) Or
(not(gClassDict.TryGetValue(dwUser, Result))) ) then
Result:=nil;
end;
Then in your callback call GetClassFromDictionary to obtain your object.
I suspect your LLoginId shoud be an Int64 .. LLONG is going to be defined in the compiler dialect / system includes for your source platform. (See: long long in C/C++)
Your errors indicate that the parameters are holding garbage (see below) which would happen if they are out of sequence - which probably means that you have the wrong parameter sizes (hence LLoginId not reading all of the value from the stack, all variables that follow it will be corrupted).
You can try making LLogin Int64, or if you want to invesitigate further you can try the following:
It seems that you are successfully calling the callback routine, but that the variables you receive in the routine are incorrect. (You say that you have revLen only 10, and the access violation errors indicate that pBuf is probably pointing at garbage).
You have declared the routine as stdcall which should be correct, and parameters will be passed on the stack in the normal "C" convention.
If you break on the entry to the routine and look in CPU view you should see that the variables are read from the stack, and determine that each one is set correctly. You should be able to put the address of pBuf into the memory window and determine if it is pointing at JPEG data from the JPEG ssignature.
You can also look at the memory dump of the stack to check what has been passed to you and see if you can identify where the variables you need really are.
I am deriving from CEdit, to make a custom control. It would be nice, if like the MFC Feature Pack controls (Mask, Browsable) that I could change GetWindowText to actually report back not what is normally displayed on the control (for example, convert the data between hex and decimal, then return back that string).
Is it this possible in a derived CEdit?
Add message map entries for WM_GETTEXT and WM_GETTEXTLENGTH to your derived CEdit class:
BEGIN_MESSAGE_MAP( CMyEdit, CEdit )
ON_WM_GETTEXT()
ON_WM_GETTEXTLENGTH()
END_MESSAGE_MAP()
As we are overriding these messages we need a method of getting the original text of the edit control without going into endless recursion. For this we can directly call the default window procedure which is named DefWindowProc:
CStringW CMyEdit::GetTextInternal()
{
CStringW text;
LRESULT len = DefWindowProcW( WM_GETTEXTLENGTH, 0, 0 );
if( len > 0 )
{
// WPARAM = len + 1 because the length must include the null terminator.
len = DefWindowProcW( WM_GETTEXT, len + 1, reinterpret_cast<LPARAM>( text.GetBuffer( len ) ) );
text.ReleaseBuffer( len );
}
return text;
}
The following method gets the original window text and transforms it. Anything would be possible here, including the example of converting between hex and dec. For simplicity I just enclose the text in dashes.
CStringW CMyEdit::GetTransformedText()
{
CStringW text = GetTextInternal();
return L"--" + text + L"--";
}
Now comes the actual handler for WM_GETTEXT which copies the transformed text to the output buffer.
int CMyEdit::OnGetText( int cchDest, LPWSTR pDest )
{
// Sanity checks
if( cchDest <= 0 || ! pDest )
return 0;
CStringW text = GetTransformedText();
// Using StringCchCopyExW() to make sure that we don't write outside of the bounds of the pDest buffer.
// cchDest defines the maximum number of characters to be copied, including the terminating null character.
LPWSTR pDestEnd = nullptr;
HRESULT hr = StringCchCopyExW( pDest, cchDest, text.GetString(), &pDestEnd, nullptr, 0 );
// If our text is greater in length than cchDest - 1, the function will truncate the text and
// return STRSAFE_E_INSUFFICIENT_BUFFER.
if( SUCCEEDED( hr ) || hr == STRSAFE_E_INSUFFICIENT_BUFFER )
{
// The return value is the number of characters copied, not including the terminating null character.
return pDestEnd - pDest;
}
return 0;
}
The handler for WM_GETTEXTLENGTH is self-explanatory:
UINT CMyEdit::OnGetTextLength()
{
return GetTransformedText().GetLength();
}
Thanks to everyone for pointing me in the right direction. I tried OnGetText, but the problem seemed to be I couldn't get the underlying string or it would crash when calling GetWindowText (or just called OnGetText again...and couldn't find the underlying string).
After seeing what they did on masked control, I did a simpler answer like this. Are there any drawbacks? It seemed to not cause any issues or side effects...
Derive directly from GetWindowText
void CConvertibleEdit::GetWindowText(CString& strString) const
{
CEdit::GetWindowText(strString);
ConvertibleDataType targetDataType;
if (currentDataType == inputType)
{
}
else
{
strString = ConvertEditType(strString, currentDataType, inputType);
}
}
I am trying to get the text of the currently chosen tab in CTabCtrl.
int tabCurSel = currentTabCtrl->GetCurSel();
TCITEM tcItem;
tcItem.mask = TCIF_TEXT;
tcItem.cchTextMax = 256; //Do I need this?
CString tabCurrentCString;
currentTabCtrl->GetItem(tabCurSel, &tcItem);
tabCurrentCString = tcItem.pszText;
CT2A tabCurrentChar(tabCurrentCString);
std::string tabCurrentStr(tabCurrentChar);
return tabCurrentStr;
I clearly have some unnecessary string conversions and currently this returns a "Error reading characters of the string" in
tcItem.pszText;
How can I get the string from the CTabCtrl? I ultimately am trying to get an std::string but the main question is how to get the text from the tab.
tcItem.pszText is pointing to 0. To fill it with text, it has to point to a buffer before a call is made to GetItem:
Documentation for: CTabCtrl::GetItem
pszText
Pointer to a null-terminated string containing the tab text if the
structure contains information about a tab. If the structure is
receiving information, this member specifies the address of the buffer
that receives the tab text.
Example:
TCITEM tcItem { 0 };
tcItem.mask = TCIF_TEXT;
const int len = 256;
tcItem.cchTextMax = len;
TCHAR buf[len] = { 0 };
tcItem.pszText = buf;
currentTabCtrl->GetItem(tabCurSel, &tcItem);
Both tcItem.pszText and buf will point to the same text. Or use CString with CString::GetBuffer()/CString::ReleaseBuffer()
CString tabCurrentCString;
TCITEM tcItem;
tcItem.mask = TCIF_TEXT;
tcItem.cchTextMax = 256;
tcItem.pszText = tabCurrentCString.GetBuffer(tcItem.cchTextMax);
BOOL result = currentTabCtrl->GetItem(tabCurSel, &tcItem);
tabCurrentCString.ReleaseBuffer();
if (result)
MessageBox(tabCurrentCString); //success
It looks like you are using the recommended Unicode settings. Avoid converting UNICODE to ANSI (std::string). This conversion will work for Latin languages, most of the time, but it's not needed. You can use std::wstring if you need to use that in STL, or convert to UTF-8 if you want to send data to internet etc.
std::string str = CW2A(tabCurrentCString, CP_UTF8);
I am a newbie to C++. I want to get the content of the clipboard, which might contain Unicode chars, append a div tag with some content formatted in HTML and set that back to clipboard.
I have achieved successfully in getting the content and appending it. But could not set it back to the clipboard as an HTML text. I have achieved setting as simple text. Here is my code:
#include <shlwapi.h>
#include <iostream>
#include <conio.h>
#include <stdio.h>
using namespace std;
wstring getClipboard(){
if (OpenClipboard(NULL)){
HANDLE clip = GetClipboardData(CF_UNICODETEXT);
WCHAR * c;
c = (WCHAR *)clip;
CloseClipboard();
return (WCHAR *)clip;
}
return L"";
}
bool setClipboard(wstring textToclipboard)
{
if (OpenClipboard(NULL)){
EmptyClipboard();
HGLOBAL hClipboardData;
size_t size = (textToclipboard.length()+1) * sizeof(WCHAR);
hClipboardData = GlobalAlloc(NULL, size);
WCHAR* pchData = (WCHAR*)GlobalLock(hClipboardData);
memcpy(pchData, textToclipboard.c_str(), size);
SetClipboardData(CF_UNICODETEXT, hClipboardData);
GlobalUnlock(hClipboardData);
CloseClipboard();
return true;
}
return false;
}
int main (int argc, char * argv[])
{
wstring s = getClipboard();
s += std::wstring(L"some extra text <b>hello</b>");
setClipboard(s);
getch();
return 0;
}
I did try using the code described here and read the doc here. But I couldn't make it work. What I tried could be way off track or completely wrong.
Update: The code below is what I tried after the modifications suggested by Cody Gray to the original code presented here:
bool CopyHTML2(WCHAR *html ){
wchar_t *buf = new wchar_t [400 + wcslen(html)];
if(!buf) return false;
static int cfid = 0;
if(!cfid) cfid = RegisterClipboardFormat("HTML Format");
// Create a template string for the HTML header...
wcscpy(buf,
L"Version:0.9\r\n"
L"StartHTML:00000000\r\n"
L"EndHTML:00000000\r\n"
L"StartFragment:00000000\r\n"
L"EndFragment:00000000\r\n"
L"<html><body>\r\n"
L"<!--StartFragment -->\r\n");
// Append the HTML...
wcscat(buf, html);
wcscat(buf, L"\r\n");
// Finish up the HTML format...
wcscat(buf,
L"<!--EndFragment-->\r\n"
L"</body>\r\n"
L"</html>");
wchar_t *ptr = wcsstr(buf, L"StartHTML");
wsprintfW(ptr+10, L"%08u", wcsstr(buf, L"<html>") - buf);
*(ptr+10+8) = L'\r';
ptr = wcsstr(buf, L"EndHTML");
wsprintfW(ptr+8, L"%08u", wcslen(buf));
*(ptr+8+8) = '\r';
ptr = wcsstr(buf, L"StartFragment");
wsprintfW(ptr+14, L"%08u", wcsstr(buf, L"<!--StartFrag") - buf);
*(ptr+14+8) = '\r';
ptr = wcsstr(buf, L"EndFragment");
wsprintfW(ptr+12, L"%08u", wcsstr(buf, L"<!--EndFrag") - buf);
*(ptr+12+8) = '\r';
// Open the clipboard...
if(OpenClipboard(0)) {
EmptyClipboard();
HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE |GMEM_DDESHARE, wcslen(buf)+4);
wchar_t *ptr = (wchar_t *)GlobalLock(hText);
wcscpy(ptr, buf);
GlobalUnlock(hText);
SetClipboardData(cfid, hText);
CloseClipboard();
GlobalFree(hText);
}
// Clean up...
delete [] buf;
return true;
}
This code compiles successfully, But I get the following error at SetClipboardData : HEAP[Project1.exe]: Heap block at 007A8530 modified at 007A860A past requested size of d2
Project1.exe has triggered a breakpoint.
Please guide me on how to proceed. I am using Visual Studio Express 2012 on Windows 8. Thanks.
You're mismatching ANSI (narrow) and Unicode (wide) strings.
Unlike the wcscpy function, the w in the wsprintf function doesn't stand for "wide", it stands for "Windows". It is part of the Win32 API, rather than the C runtime library. All of the Win32 API functions that work with strings have two versions, one suffixed with an A that deals with ANSI strings and another suffixed with a W that deals with wide strings. The headers hide all of this from you with macros. I explain all of this in more detail here—recommended reading.
Anyway, the simple fix here is to explicitly call the wide variant of that function, since you're correctly using wide strings everywhere else. Make all the calls to wsprintf look like this:
wchar_t *ptr = wcsstr(buf, L"StartHTML");
wsprintfW(ptr+10, L"%08u", wcsstr(buf, L"<html>") - buf);
*(ptr+10+8) = L'\r';
Alternatively, you could use the swprintf function provided by the C runtime library instead of the Win32 version. This one works just like the wcsstr and wcscpy functions you're using elsewhere. The w in the name means "wide". The documentation for this series of functions is here.
Note also that when you use character or string literals, they also need to be wide characters. You accomplish that by prepending them with an L. You do that some places, but miss doing it others. Make sure that you do it consistently.
The compiler should warn you about all this, though. You just need to make sure you turn your warning level up and don't ignore any of the warnings. Also make sure that both the UNICODE and _UNICODE preprocessor symbols are defined globally for your project. That will ensure that you are always calling the Unicode/wide versions of functions. Although that should be the default for all new projects.
This is the function I came up with the help of Jochen Arndt at codeproject.com. Hope this helps somebody. Here is a complete working code, if you are interested in checking this out.
It still has one problem. That is when pasted to onenote alone, it pastes gibberish after a anchor tag. It does not happen with Word, PowerPoint or Excel. And it does not have this problem for normal English language texts. If you have a solution for this, please do let me know. The problem seems to be with OneNote. Not with the code.
bool setClipboard(LPCWSTR lpszWide){
int nUtf8Size = ::WideCharToMultiByte(CP_UTF8, 0, lpszWide, -1, NULL, 0, NULL, NULL);
if (nUtf8Size < 1) return false;
const int nDescLen = 105;
HGLOBAL hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, nDescLen + nUtf8Size);
if (NULL != hGlobal)
{
bool bErr = false;
LPSTR lpszBuf = static_cast<LPSTR>(::GlobalLock(hGlobal));
LPSTR lpszUtf8 = lpszBuf + nDescLen;
if (::WideCharToMultiByte(CP_UTF8, 0, lpszWide, -1, lpszUtf8, nUtf8Size, NULL, NULL) <= 0)
{
bErr = true;
}
else
{
LPCSTR lpszStartFrag = strstr(lpszUtf8, "<!--StartFragment-->");
LPCSTR lpszEndFrag = strstr(lpszUtf8, "<!--EndFragment-->");
lpszStartFrag += strlen("<!--StartFragment-->") + 2;
int i = _snprintf(
lpszBuf, nDescLen,
"Version:1.0\r\nStartHTML:%010d\r\nEndHTML:%010d\r\nStartFragment:%010d\r\nEndFragment:%010d\r\n",
nDescLen,
nDescLen + nUtf8Size - 1, // offset to next char behind string
nDescLen + static_cast<int>(lpszStartFrag - lpszUtf8),
nDescLen + static_cast<int>(lpszEndFrag - lpszUtf8));
}
::GlobalUnlock(hGlobal);
if (bErr)
{
::GlobalFree(hGlobal);
hGlobal = NULL;
}
// Get clipboard id for HTML format...
static int cfid = 0;
cfid = RegisterClipboardFormat("HTML Format");
// Open the clipboard...
if(OpenClipboard(0)) {
EmptyClipboard();
HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE |GMEM_DDESHARE, strlen(lpszBuf)+4);
char *ptr = (char *)GlobalLock(hText);
strcpy(ptr, lpszBuf);
GlobalUnlock(hText);
::SetClipboardData(cfid, hText);
CloseClipboard();
GlobalFree(hText);
}
}
return NULL != hGlobal;
}
Your problem comes from the use of wchar_t instead of char in the cited example which makes you wrong on the offset computations.
I would however recommend you avoiding the use of wchar_t for transfering UNICODE text to the clipboard. Indeed, UTF-8 char could coded with a sequence of bytes comprised between 1 and 4 bytes, while wchar_t on Windows is a fixed 2 bytes type.
As explained in the Microsoft doc refered in your email, the content of the clipboard shall be UNICODE, which happens to be the same as ASCII for the characters contained in the header of the clipboard memory.
To transfert UNICODE in the clipboard, you can do it using the standard char C++ functions to prepare the content sent to clipboard (std::string for eg.)
While the cited example works, please find here another code sample using C++ framework that can actually copy UTF-8 chars to the clipboard in HTML format:
void copyHTMLtoClipboard(const std::string& html) {
std::string contextStart("Version:0.9\r\nStartHTML:0000000000\r\nEndHTML:0000000000\r\nStartFragment:0000000000\r\nEndFragment:0000000000\r\n<html><body>\r\n<!--StartFragment -->\r\n");
std::string contextEnd("\r\n<!--EndFragment -->\r\n</body></html>");
std::stringstream aux;
aux << contextStart << html << contextEnd;
std::string res = aux.str();
size_t htmlStart = 105 * sizeof(char);
size_t fragmentStart = 119 * sizeof(char);
size_t htmlEnd = res.size() * sizeof(char);
size_t fragmentEnd = htmlEnd - 35 * sizeof(char);
aux.fill('0');
aux.width(10);
aux.seekp(23);
aux << htmlStart;
aux.seekp(43);
aux.fill('0');
aux.width(10);
aux << htmlEnd;
aux.seekp(69);
aux.fill('0');
aux.width(10);
aux << fragmentStart;
aux.seekp(93);
aux.fill('0');
aux.width(10);
aux << fragmentEnd;
res = aux.str();
HGLOBAL hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, htmlEnd + sizeof(char));
LPSTR dst = (LPSTR)GlobalLock(hdst);
memcpy(dst, res.c_str(), htmlEnd);
dst[htmlEnd] = 0;
GlobalUnlock(hdst);
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(RegisterClipboardFormat(L"HTML Format"), hdst);
CloseClipboard();
GlobalFree(hdst);
}
Note that this code was compiled defining the macros _UNICODE and UNICODE.
I am filling a combobox:
while((pHPSet = pHPTable->GetNext()) != NULL)
{
CString str = pHPSet->GetName();
// I am normally using str but to proove that this is
// not the problem I am using "a"
m_comboBaseHP.AddString(_T("a"));
}
Now I am trying to read the combobox:
if(m_comboBaseHP.GetCount() > 0)
{
CString csHPName = _T("");
m_comboBaseHP.GetLBText(0, csHPName);
// This is the ms way but ReleaseBuffer causes a crash
//CString str = _T("");
//int n = m_comboBaseHP.GetLBTextLen( 0 );
//m_comboBaseHP.GetLBText( 0, str.GetBuffer(n) );
//str.ReleaseBuffer();
// Do whatever with csHPName
}
The problem is that csHPName shows in the Debugger some Chinese signs. I am assuming this is memory garbage. This happens in the same Method. This happens pre draw. Post draw the same issue. This happens in Debug and Release. I don't understand how this can happen since I am not actually working with pointers.
Apparently it is necessary to set the property Has Strings of the combobox to True.