I have a TS3 plugin in creation right here, which uses
System::Speech::Recognition for its SpeechRecognitinEngine. Now, I create an EventHandler for the SpeechRecognized event.
Well, my logging (and the actions of the plugin - None) tell me, that the Event is actually never triggered, even if you start and stop talking with the (somewhat) valid (parts of the) grammar.
I have no idea why this is. It happens in a DLL written in C++ CLI.
Now, my theory was that DLLs do not support event handerls... Might that be possible?
void recogn_speech() {
uint64 schid = ts3Functions.getCurrentServerConnectionHandlerID();
SpeechRecognitionEngine^ recognizer = gcnew SpeechRecognitionEngine();
System::Speech::Recognition::Grammar^ g = assembleGrammar();
recognizer->LoadGrammar(g);
recognizer->SetInputToDefaultAudioDevice();
char pluginPath[PATH_BUFSIZE];
ts3Functions.getPluginPath(pluginPath, PATH_BUFSIZE, pluginID);
String^ a = gcnew String(pluginPath);
a = a + "vctest_cpp_ts3\\signal_vc_start.wav";
char* newPath = (char*)(void*)Marshal::StringToHGlobalAnsi(a);
ts3Functions.playWaveFile(schid, newPath);
Marshal::FreeHGlobal((IntPtr)newPath);
recognizer->SpeechRecognized +=
gcnew EventHandler<SpeechRecognizedEventArgs^>(this, &tsapi::sre_SpeechRecognized);
}
void sre_SpeechRecognized(Object^ sender, SpeechRecognizedEventArgs^ e)
{
uint64 schid = ts3Functions.getCurrentServerConnectionHandlerID();
String^ recognRes = e->Result->Text->ToString();
interpretCommand(recognRes);
}
Full Sourcecode on
GitHub
Your event handler looks correct. There's no issue with having events & event handlers across DLLs: It's all managed code, the DLL boundary doesn't matter here.
However, there is one possible issue:
void recogn_speech() {
...
SpeechRecognitionEngine^ recognizer = gcnew SpeechRecognitionEngine();
...
}
You might want to save your SpeechRecognitionEngine object somewhere so it doesn't get garbage collected.
Also, you may want to switch to the more C++-style of having class definitions in .h files, and implementations in .cpp files. As your code gets more complex and the classes need to refer to each other, having everything in one file will start to become an issue.
Related
Anybody got this problem, anyway I didn't find an answer. The code is simple:
void CbDlg::OnBnClickedOk()
{
for(int i=0; i<1000; i++)
{
HRSRC hRes = ::FindResource(NULL, MAKEINTRESOURCE(IDR_MAINFRAME), RT_GROUP_ICON);
HGLOBAL hResLoad = ::LoadResource(NULL, hRes);
BYTE* pIconBytes = (BYTE*)::LockResource(hResLoad);
int nId = ::LookupIconIdFromDirectory(pIconBytes, TRUE);
hRes = ::FindResource(NULL, MAKEINTRESOURCE(nId), RT_ICON);
DWORD read = ::SizeofResource(NULL ,hRes);
hResLoad = ::LoadResource(NULL, hRes);
pIconBytes = (BYTE*)::LockResource(hResLoad);
if(pIconBytes != NULL)
{
HICON hIcon = ::CreateIconFromResource(pIconBytes, read, TRUE, 0x00030000);
DWORD e = ::GetLastError();
if(hIcon != NULL)
{
::DestroyIcon(hIcon);
}
}
}
}
If I click the Ok button four times (On my computer), CreateIconFromResource start to return NULL (It worked fine before and I could even draw out the icon). As to the GetLastError, it's always return 6 whatever CreateIconFromResource return NULL or not.
When this problem happened, if I drag the title bar to move, UI crashed, see the pictrue.
Of course you can understand this piece of code is just a demo, my real business need to call CreateIconFromResource thousands of times just like this.
UPDATE:
According to Hans' suggestion, I keep tracking the Handles/USER Objects/GDI objects, and found that USER Objects grows 1000 and GDI objects grows 2000 against each clicking to OK button (handles didn't grow), and GDI objects is 9999 when problem happens. But how to release them correctly, when I finish to use? I didn't use that much at one time, but need to load, release, load again, release again... Just like this demo. As MSDN document, I called DestroyIcon for every HICON. What else do I need to do, to finally release the USER/GDI objects?
I found the answer. The success or failure is all due to MSDN.
It says:
"The CreateIconFromResource function calls CreateIconFromResourceEx passing LR_DEFAULTSIZE|LR_SHARED as flags" AND "Do not use this function(DestroyIcon) to destroy a shared icon"
But It also says:
"When you are finished using the icon, destroy it using the DestroyIcon function" in CreateIconFromResource's document.
Actually, the second statement is WRONG.
So, the solution is, using CreateIconFromResourceEx without LR_SHARED, and DestroyIcon every HICON after using.
I'm debugging Qt5.3.1 on Mac, because my program freezes sometimes (intermittent ). I discovered that it is because the QTimer can't work properly.
In Qt code, they use the following two lines to trigger function activateTimersSourceCallback
CFRunLoopSourceSignal(d->activateTimersSourceRef);
CFRunLoopWakeUp(mainRunLoop());
void QCocoaEventDispatcherPrivate::activateTimersSourceCallback(void *info)
{
static int counter = 0;
NSLog(#"finished activeteTimersSourceCallback %d", counter++);
}
but sometimes, these two lines doesn't work, activateTimersSourceCallback won't get called.
I googled, but I couldn't find any solution? is this a known OS bug?
the initialization details:
// keep our sources running when modal loops are running
CFRunLoopAddCommonMode(mainRunLoop(), (CFStringRef) NSModalPanelRunLoopMode);
CFRunLoopSourceContext context;
bzero(&context, sizeof(CFRunLoopSourceContext));
context.info = d;
context.equal = runLoopSourceEqualCallback;
// source used to activate timers
context.perform = QCocoaEventDispatcherPrivate::activateTimersSourceCallback;
d->activateTimersSourceRef = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
Q_ASSERT(d->activateTimersSourceRef);
CFRunLoopAddSource(mainRunLoop(), d->activateTimersSourceRef, kCFRunLoopCommonModes);
Such behavior very likely can occur when UI event loop is overloaded with events or some business logic takes too long time. You should to check your business logic and move it to separate thread or run asynchronous.
We have multiple dialogs in our MFC program that are very similar. Each one of these dialogs contain similar controls (i.e., they all contain a name, date, address, etc). Because of this, we've had to code out the display code multiple times for the windows despite the fact that the processing of these controls is identical. I'm looking for suggestions on how to change up our guis so that i have to only do the processing at one spot and not have to do it multiple times.
My thought was to have a class that would do the processing and pass pointers to the controls to display to that class, though i feel that is not a very good OO design.
Thoughts?
Create a base class derived from CDialog (say CMyDlgBase), place all your common functions there and derive your dialog classes from CMyDlgBase instead of CDialog.
You can now call the functions in CMyDlgBase as if they were declared directly in your dialog classes.
EDIT sample code to validate an item common to dialogs (CDlg1 and CDlg2 are derived from CMyDlgBase), error checking code not included:
BOOL CMyDlgBase::ValidateName(UINT nID)
{ CString ss;
CEdit *pEdit = GetDlgItem(nID);
pEdit->GetWindowText(ss);
if (ss.Find(_T("A")) < 0) // some kind of validation
{ MessageBox(_T("Name should contain the character 'A'"));
pEdit->SetFocus();
return FALSE;
}
return TRUE;
}
CDlg1::OnOK()
{ if (!ValidateName(IDC_DLG1_NAME)) // resource id value = 101
return;
CDialog::OnOK(); // This will close the dialog and DoModal will return.
}
CDlg2::OnOK()
{ if (!ValidateName(IDC_DLG2_NAME)) // resource id value = 102
return;
CDialog::OnOK(); // This will close the dialog and DoModal will return.
}
I am trying to use an edit control along with a spin control using MFC visual studio .net 2003. I have carried out the basic settings for the spin control like setting the "AutoBuddy" property and "SetBuddyInteger" property to True so that the Spin control works in coordination with the edit control next to it. In my Spin control's event handler, I am facing a problem when I am trying to call my Invalidate() function. The float value in my edit control does not update and stays zero. If I remove the Invalidate(), then the value increments but my paint function is not updated obviously. A code of the following is given below:
void CMyDlg::OnSpinA(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
UpdateData();
m_A = m_ASpinCtrl.GetPos(); // m_A is my edit control float value variable
Invalidate(); // Invalidate is to be called to update my paint function to redraw the drawing
UpdateData(false);
*pResult = 0;
}
I have carried out the tab order correctly as well for the two controls.
Any suggestions on where I am going wrong?
Thanks in advance.
If you just want to have a spinning integer, you don't have to override anything.
The spin control has to be right next to the edit control in the tab order. With AutoBuddy that's all you have to do.
m_A when getting the position back would do something weird and would not return you the correct value. Try using the pointer to get your position and value and then carry out the invalidate().
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
UpdateData();
CString tempStr;
m_A += pNMUpDown->iDelta;
tempStr.Format("%f",m_A);
m_ACtrl.SetWindowText(tempStr); // Like a CEdit m_ACtrl to display your string
Invalidate();
UpdateData(false);
*pResult = 0;
}
This should work perfectly well. Let me know if you still get any problems.
What?
I have a DLGTEMPLATE loaded from a resource DLL, how can I change the strings assigned to the controls at runtime programmatically?
I want to be able to do this before the dialog is created, such that I can tell that the strings on display came from the resource DLL, and not from calls to SetWindowText when the dialog is initialized.
Google has found examples of creating DLGTEMPLATE in code, or twiddling simple style bits but nothing on editing the strings in memory.
How?
I am doing this by hooking the Dialog/Property Sheet creation API's. Which gives me access to the DLGTEMPLATE before the actual dialog is created and before it has a HWND.
Why?
I want to be able to do runtime localization, and localization testing. I already have this implemented for loading string (including the MFC 7.0 wrapper), menus and accelerator tables, but I am struggling to handle dialog/property sheet creation.
Code examples would be the perfect answer, ideally a class to wrap around the DLGTEMPLATE, if I work out my own solution I will post it.
You can't edit the strings in memory. The DLGTEMPLATE structure is a direct file mapping of the relevent bytes of the resource dll. Thats read only.
You are going to need to process the entire DLGTEMPLATE structure and write out a new one with the altered length strings.
It will frankly be easier to just hook the WM_INITDIALOG and alter the strings by interacting with the controls than building a DLGTEMPLATE writer. Because there arn't a lot of those around. Unless you have an additional requirement to actually save altered dialog resources to disk as raw .res files (or attempt to modify the .dll inplace) Id really recommend you avoid this approach.
You say you are already doing this for accellerator tables and menu strings - if you can guarantee that the patched in strings are going to be shorter, then just make a binary copy of the DLGTEMPLATE struct, and write the non trivial scanning code necessary to find each string so you can patch the copy in place.
There is a file out there somewhere (which I think originated at Microsoft but I am not completely sure) called RESFMT.ZIP which explains this with some code examples. Raymond Chen also does some excellent explanations of this on his blog. Note that the format of DIALOGEX and DIALOG controls are different.
As noted in some other answers you would need to create the structure again from the start. This isn't all bad as you already have the basic information. Adding the controls is where is gets hard.
Basically, allocate a largish block of memory into a WORD *lpIn. Then add the structure up on top of that. adding the basic information for the DIALOG (see DLGTEMPLATE) and the controls is pretty obvious as the information is there in MSDN.
The two biggest problems you will encounter are: Making sure that the various part start on an alignment boundary, and interpreting the values of DIALOG controls, especially when to add a just a string or, a string or ordinal. Each control needs to start on an even boundary.
For the first (borrowed from somewhere I think RESFMT.ZIP):
WORD *AlignDwordPtr (WORD *lpIn)
{
ULONG ul;
ul = (ULONG) lpIn;
ul +=3;
ul >>=2;
ul
What I did was build a series of functions like this one following that allowed me to assemble DIALOGS in memory. (My need was so I could have some common code that didn't need an associated RC file for some very basic messages).
Here is an example...
WORD *AddStringOrOrdinalToWordMem( WORD *lpw, char *sz_Or_Ord )
{
LPWSTR lpwsz;
int BufferSize;
if (sz_Or_Ord == NULL)
{
*lpw++ = 0;
}
else
{
if (HIWORD(sz_Or_Ord) == 0) //MAKEINTRESOURCE macro
{
*lpw++ = 0xFFFF;
*lpw++ = LOWORD(sz_Or_Ord);
}
else
{
if (strlen(sz_Or_Ord))
{
lpwsz = ( LPWSTR ) lpw;
BufferSize = MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, 0 );
MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, BufferSize );
lpw = lpw + BufferSize;
}
else
{
*lpw++ = 0;
}
}
}
return( lpw );
}
The header file to the complete module included these functions:
WORD *AddControlToDialogTemplateEx(MTDialogTemplateType *dlgtmp,
char *Title,
WORD Id,
char *WinClass,
DWORD Style,
short x,
short y,
short cx,
short cy,
DWORD ExStyle,
int HelpID);
int DestroyDlgTemplateEx(MTDialogTemplateType *dlgtmp);
MTDialogTemplateType *CreateDlgTemplateEx( char *Name, // We use name just for reference, so it can be NULL
short x,
short y,
short cx,
short cy,
DWORD ExtendedStyle,
DWORD Style,
char *Menu,
char *WinClass,
char *Caption,
char *FontTypeFace,
int FontSize,
int FontWeigth,
int FontItalic,
int Charset,
int HelpID,
int NumberOfControls);
Which allowed me to assemble whole dialogs easily from code.
See the API function ::EnumChildWindows( HWND, WNDENUMPROC, LPARAM )
You can call this in a CFormView::Create or CDialog::OnInitDialog to give yourself a chance to replace the control captions. Don't worry, the old strings don't flicker up before you replace them.
In your dialog resource, set the control captions to a key in some kind of dictionary. If you're compiling /clr you can use a managed string table resource. In your callback, look up the translated string in your dictionary and set the control's caption to the translation. Another benefit of /clr and managed string table is that you can automatically look up the right language by Windows (or you) having already set System::Threading::Thread::CurrentThread->CurrentUICulture.
Something like this
CMyDialog::OnInitDialog()
{
::EnumChildWindows(
this->GetSafeHwnd(),
CMyDialog::UpdateControlText,
(LPARAM)this )
}
BOOL CALLBACK CMyDialog::UpdateControlText( HWND hWnd, LPARAM lParam )
{
CMyDialog* pDialog = (CMyDialog*)lParam;
CWnd* pChildWnd = CWnd::FromHandle( hWnd );
int ctrlId = pChildWnd->GetDlgCtrlID();
if (ctrlId)
{
CString curWindowText;
pChildWnd->GetWindowText( curWindowText );
if (!curWindowText.IsEmpty())
{
CString newWindowText = // some look up
pChildWnd->SetWindowText( newWindowText );
}
}
}
You'll have to locate the string you want to modify in the mem buffer that represents the template. The only way to do that is to traverse the whole template. Which is not easy.
Once you've done that, either insert bytes in the buffer if your new string is longer than the original one. Or shrink the buffer if the new string is shorter.
As Chris wrote, it would be much easier to modify the text in WM_INITDIALOG and try to re-phrase you requirement that says you may not call SetWindowText().
Thanks all, I actually had 24 hours rest on the problem, then went with a global windows hook filtering WM_INITDIALOG which was a much simpler method, worked out just fine, no API hooking required, 2 pages of code down to just a few lines.
Thanks for all the answers.