how to change Text to Speech voice and how to insert characters into char array - c++

I need to change the voice of the Text To Speech engine. When a menu is selected (ID_SPEAK_PLAY), I get the text of an edit box and simply read it.
My situation can be solved in two ways :
insert the XML code at the begining of ptrData without using strncat or other functions that involve creating other wchar_t* buffers (memory issues ). StringCchPrintf is not working.
change the voice in some other way that i don't know.
Here is my code :
case ID_SPEAK_PLAY:
text_size = SendMessage(h_edit, WM_GETTEXTLENGTH, 0, 0);
text_size += 100;
ptrData = new wchar_t[text_size];
SendMessage(h_edit, WM_GETTEXT, text_size, (LPARAM)ptrData);
StringCchPrintf(ptrData, text_size, L"<voice required = \"Gender=Female;Age=Teen\"> %s", ptrData);
pVoice->Speak(ptrData, SPF_ASYNC | SPF_IS_XML, NULL);
delete [] ptrData;
break;

StringCchPrintf is not working.
That is because you ignored the warning in the documentation:
Behavior is undefined if the strings pointed to by pszDest, pszFormat, or any argument strings overlap.
You are specifying ptrData as both pszDest and an argument string, so your code has undefined behavior. You must use separate buffers when using StringCchPrintf():
case ID_SPEAK_PLAY:
text_size = SendMessage(h_edit, WM_GETTEXTLENGTHW, 0, 0) + 1;
ptrData = new wchar_t[text_size];
SendMessage(h_edit, WM_GETTEXTW, text_size, (LPARAM)ptrData);
speak_size = text_size + 100;
speakData = new wchar_t[speak_size];
StringCchPrintf(speakData, speak_size, L"<voice required = \"Gender=Female;Age=Teen\"> %s", ptrData);
pVoice->Speak(speakData, SPF_ASYNC | SPF_IS_XML, NULL);
delete [] speakData;
delete [] ptrData;
break;
Alternatively, just skip StringCchPrintf() and let WM_GETTEXT populate your single buffer directly:
case ID_SPEAK_PLAY:
{
const wchar_t *xml = L"<voice required = \"Gender=Female;Age=Teen\"> ";
const int xml_size = lstrlenW(xml);
text_size = SendMessage(h_edit, WM_GETTEXTLENGTHW, 0, 0);
ptrData = new wchar_t[text_size + xml_size + 1];
lstrcpyW(ptrData, xml);
SendMessage(h_edit, WM_GETTEXTW, text_size+1, (LPARAM)(ptrData+xml_size));
pVoice->Speak(ptrData, SPF_ASYNC | SPF_IS_XML, NULL);
delete [] ptrData;
break;
}
change the voice in some other way that i don't know.
Instead of inserting XML in front of your text, you can call the ISpVoice::SetVoice() method before calling ISpVoice::Speak(). Use SpEnumTokens() to know which voices are installed, or use SpFindBestToken() to search for a voice that matches the criteria you need.

Related

Printing different documents silently in C++

I have folder of different documents like: pdf, txt, rtf, images.
My case is to send all documents to the printer (print it). Used framework is MFC and WinAPI. Current implementation has dialog box for choose documents and another dialog for choose printer.
Then question appears, how to print it all? Do I need to convert every documents to PDF, then merge it and print one pdf document? I will appreciate any advice in that field.
void CMultipleDocsPrintTestDlg::OnBnClickedButton1()
{
TCHAR strFilter[] = { _T("Rule Profile (*.pdf)||") };
// Create buffer for file names.
const DWORD numberOfFileNames = 100;
const DWORD fileNameMaxLength = MAX_PATH + 1;
const DWORD bufferSize = (numberOfFileNames * fileNameMaxLength) + 1;
CFileDialog fileDlg(TRUE, _T("pdf"), NULL, OFN_ALLOWMULTISELECT, strFilter);
TCHAR* filenamesBuffer = new TCHAR[bufferSize];
// Initialize beginning and end of buffer.
filenamesBuffer[0] = NULL;
filenamesBuffer[bufferSize - 1] = NULL;
// Attach buffer to OPENFILENAME member.
fileDlg.GetOFN().lpstrFile = filenamesBuffer;
fileDlg.GetOFN().nMaxFile = bufferSize;
// Create array for file names.
CString fileNameArray[numberOfFileNames];
if (fileDlg.DoModal() == IDOK)
{
// Retrieve file name(s).
POSITION fileNamesPosition = fileDlg.GetStartPosition();
int iCtr = 0;
while (fileNamesPosition != NULL)
{
fileNameArray[iCtr++] = fileDlg.GetNextPathName(fileNamesPosition);
}
}
// Release file names buffer.
delete[] filenamesBuffer;
CPrintDialog dlg(FALSE);
dlg.m_pd.Flags |= PD_PRINTSETUP;
CString printerName;
if (dlg.DoModal() == IDOK)
{
printerName = dlg.GetDeviceName();
}
// What next ???
}
You could make use of ShellExecute to do this. The parameter lpOperation can be set to print. To quote:
Prints the file specified by lpFile. If lpFile is not a document file, the function fails.
As mentioned in a similar discussion here on StackOverflow (ShellExecute, "Print") you should keep in mind:
You need to make sure that the machine's associations are configured to handle the print verb.
You referred to pdf, txt, rtf, images which should all be supported I would think by this mechanism.
ShellExecute(NULL, "print", fileNameArray[0], nullptr, nullptr, SW_SHOWNORMAL);
The last parameter might have to be changed (SW_SHOWNORMAL). This code would be put in a loop so you could call it for each file. And note that the above code snippet has not been tested.

How to get the current Tab Item name from CTabCtrl in MFC?

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

Using buffer or pszText member of LVITEM structure upon LVM_GETITEM

If I want to read a value from a certain item and sub-item in a list-view I do the following:
const int MAX_SIZE = 256;
char szBuffer[MAX_SIZE];
LVITEM lvItem = {0};
lvItem.iItem = rowIndex;
lvItem.iSubItem = subItemIndex;
lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
lvItem.cchTextMax = MAX_SIZE;
lvItem.pszText = szBuffer;
// Get item
SendMessage(hListView, LVM_GETITEM, 0, (LPARAM)&lvItem);
// Print value, which one always guaranteed to work?
std::cout << lvItem.pszText << std::endl;
std::cout << lvItem.szBuffer << std::endl;
Now, reading the MSDN Documentation on LVM_GETITEM we see that it says
Applications should not assume that the text will necessarily be placed in the specified buffer. The control may instead change the pszText member of the structure to point to the new text, rather than place it in the buffer.
How do we know when it is safe to use our buffer (szBuffer in this example) rather than using
lvItem.pszText
directly?
Maybe we always have to do (or similar):
if(lvItem.pszText != szBuffer)
snprintf(szBuffer, MAX_SIZE, "%s", lvItem.pszText);
to copy the value from the pszText of the LVITEM structure into
our buffer (after we have sent the LVM_GETITEM message of course).

How to set HTML Unicode text to clipboard in VC++?

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.

ListView_GetItem() macro & LV_GETITEMTEXT returns empty STRING while getting another APP listview items

I am fully aware if i try to get buffer of another app listview items that i need to solve memory space boundary so What i am doing is hooking which returns well, no error, success then
enter code here
LVITEM lvi;
lvi.cchTextMax = 552;
lvi.mask = LVIF_TEXT;
_tfreopen(path,_T("w"),stdout);
for (int nItem = 0; nItem < nMaxItems; nItem++) {
// Get the name and position of a ListView item.
lvi.iSubItem = 0;
lvi.iItem = nItem;
lvi.pszText = szName;
ListView_GetItem(hWndLV, &lvi);
wprintf(L"%s, ", szName);
wprintf(L"%s, ", lvi.pszText);
for ( int subitem = 0; subitem < columns; subitem++)
{
lvi.iSubItem = subitem;
lvi.iItem = nItem;
lvi.pszText = szName;
ListView_GetItem(hWndLV, &lvi);
wprintf(L"%s, ", szName);
wprintf(L"%s, ", lvi.pszText);
}
wprintf(L"%s", "\n");
}
wprintf(L"%s", GetLastError());
fclose (stdout);
No error with getlasterror and this works perfect on any other listview i tried before except this app, is it possible to still get item text somehow ?
Maybe it is an owner-drawn listview (LVS_OWNERDRAWFIXED)? If this is the case it will not be possible to get the text without private knowledge of the app that owns it.
You don't need to use hooking to get across the process boundary. You can use WriteProcessMemory/ReadProcessMemory as described in the following Code Project article: http://www.codeproject.com/KB/threads/int64_memsteal.aspx
What you have to watch out for is crossing a 32/64 bit boundary. I know of now way to achieve that.