Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

492 lines
12 KiB

  1. //
  2. // sptask.cpp
  3. //
  4. // implements a notification callback ISpTask
  5. //
  6. // created: 12/1/99
  7. //
  8. //
  9. #include "private.h"
  10. #include "globals.h"
  11. #include "sptask.h"
  12. #include "candui.h"
  13. #include "ids.h"
  14. #include "computil.h"
  15. //
  16. // ctor
  17. //
  18. //
  19. CSpTask::CSpTask(CCandidateUI *pcui)
  20. {
  21. // CSpTask is initialized with an TFX instance
  22. // so store the pointer to the TFX
  23. // init data members here
  24. m_pcui = pcui;
  25. m_fSapiInitialized = FALSE;
  26. m_fActive = FALSE;
  27. m_fInCallback = FALSE;
  28. }
  29. CSpTask::~CSpTask()
  30. {
  31. _ReleaseGrammars();
  32. }
  33. //
  34. // CSpTask::_InitializeSAPIObjects
  35. //
  36. // initialize SAPI objects for SR
  37. // later we'll get other objects initialized here
  38. // (TTS, audio etc)
  39. //
  40. HRESULT CSpTask::InitializeSAPIObjects(void)
  41. {
  42. #ifdef _WIN64
  43. return E_NOTIMPL;
  44. #else
  45. if (m_fSapiInitialized == TRUE)
  46. return m_cpRecoCtxt ? S_OK : E_FAIL;
  47. // do not try again even in failure
  48. m_fSapiInitialized = TRUE;
  49. // m_xxx are CComPtrs from ATL
  50. //
  51. HRESULT hr = _GetSapilayrEngineInstance(&m_cpRecoEngine);
  52. // create the recognition context
  53. if( S_OK == hr )
  54. {
  55. hr = m_cpRecoEngine->CreateRecoContext( &m_cpRecoCtxt );
  56. }
  57. if ( hr == S_OK )
  58. {
  59. SPRECOGNIZERSTATUS stat;
  60. if (S_OK == m_cpRecoEngine->GetStatus(&stat))
  61. {
  62. m_langid = stat.aLangID[0];
  63. }
  64. }
  65. return hr;
  66. #endif // _WIN64
  67. }
  68. //
  69. // CSpTask::NotifyCallback
  70. //
  71. // INotifyControl object calls back here
  72. // returns S_OK when it handles notifications
  73. //
  74. //
  75. HRESULT CSpTask::NotifyCallback( WPARAM wParam, LPARAM lParam )
  76. {
  77. USES_CONVERSION;
  78. // we can't delete reco context while we're in this callback
  79. //
  80. m_fInCallback = TRUE;
  81. // also we can't terminate candidate UI object while in the callback
  82. m_pcui->AddRef();
  83. {
  84. CSpEvent event;
  85. while ( m_cpRecoCtxt && event.GetFrom(m_cpRecoCtxt) == S_OK )
  86. {
  87. switch (event.eEventId)
  88. {
  89. case SPEI_RECOGNITION:
  90. _OnSpEventRecognition(event);
  91. break;
  92. default:
  93. break;
  94. }
  95. }
  96. }
  97. m_fInCallback = FALSE;
  98. m_pcui->Release();
  99. return S_OK;
  100. }
  101. HRESULT CSpTask::_OnSpEventRecognition(CSpEvent &event)
  102. {
  103. HRESULT hr = S_OK;
  104. ISpRecoResult *pResult = event.RecoResult();
  105. if (pResult)
  106. {
  107. static const WCHAR szUnrecognized[] = L"<Unrecognized>";
  108. SPPHRASE *pPhrase;
  109. hr = pResult->GetPhrase(&pPhrase);
  110. if (S_OK == hr)
  111. {
  112. if (pPhrase->ullGrammarID == GRAM_ID_CANDCC)
  113. {
  114. if (SUCCEEDED(hr) && pPhrase)
  115. {
  116. // retrieve LANGID from phrase
  117. LANGID langid = pPhrase->LangID;
  118. hr = _DoCommand(pPhrase, langid);
  119. }
  120. }
  121. else if(pPhrase->ullGrammarID == GRAM_ID_DICT)
  122. {
  123. if (m_pcui->_ptim != NULL) {
  124. // Windows bug#508709
  125. // Ignore dictation event during SPTip is in commanding mode
  126. DWORD dwSpeechGlobalState;
  127. GetCompartmentDWORD(m_pcui->_ptim, GUID_COMPARTMENT_SPEECH_GLOBALSTATE, &dwSpeechGlobalState, TRUE);
  128. if (dwSpeechGlobalState & TF_DICTATION_ON) {
  129. hr = _DoDictation(pResult);
  130. }
  131. }
  132. }
  133. ::CoTaskMemFree( pPhrase );
  134. }
  135. }
  136. return hr;
  137. }
  138. const WCHAR c_szRuleName[] = L"ID_Candidate";
  139. //
  140. // CSpTask::_DoCommand
  141. //
  142. // review: the rulename may need to be localizable?
  143. //
  144. HRESULT CSpTask::_DoCommand(SPPHRASE *pPhrase, LANGID langid)
  145. {
  146. HRESULT hr = S_OK;
  147. if ( wcscmp(pPhrase->Rule.pszName, c_szRuleName) == 0)
  148. {
  149. if (m_pcui)
  150. {
  151. hr = m_pcui->NotifySpeechCmd(pPhrase,
  152. pPhrase->pProperties[0].pszValue,
  153. pPhrase->pProperties[0].ulId);
  154. }
  155. }
  156. return hr;
  157. }
  158. //
  159. // CSpTask::_DoDictation
  160. //
  161. // support spelling
  162. //
  163. HRESULT CSpTask::_DoDictation(ISpRecoResult *pResult)
  164. {
  165. HRESULT hr = E_FAIL;
  166. BYTE bAttr; // no need?
  167. Assert(pResult);
  168. // this cotaskmemfree's text we get
  169. CSpDynamicString dstr;
  170. hr = pResult->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstr, &bAttr);
  171. if (S_OK == hr)
  172. {
  173. WCHAR sz[2]={0};
  174. StringCchCopyW(sz, ARRAYSIZE(sz), dstr);
  175. Assert(m_pcui);
  176. hr = m_pcui->FHandleSpellingChar(sz[0]);
  177. }
  178. return hr;
  179. }
  180. //
  181. // CSpTask::InitializeCallback
  182. //
  183. //
  184. HRESULT CSpTask::InitializeCallback()
  185. {
  186. #ifdef _WIN64
  187. return E_NOTIMPL;
  188. #else
  189. // set recognition notification
  190. CComPtr<ISpNotifyTranslator> cpNotify;
  191. HRESULT hr = cpNotify.CoCreateInstance(CLSID_SpNotifyTranslator);
  192. // set this class instance to notify control object
  193. if (SUCCEEDED(hr))
  194. {
  195. hr = cpNotify->InitSpNotifyCallback( (ISpNotifyCallback *)this, 0, 0 );
  196. }
  197. if (SUCCEEDED(hr))
  198. {
  199. hr = m_cpRecoCtxt->SetNotifySink(cpNotify);
  200. }
  201. // set the events we're interested in
  202. if( SUCCEEDED( hr ) )
  203. {
  204. const ULONGLONG ulInterest = SPFEI(SPEI_RECOGNITION);
  205. hr = m_cpRecoCtxt->SetInterest(ulInterest, ulInterest);
  206. }
  207. else
  208. {
  209. m_cpRecoCtxt.Release();
  210. }
  211. if( SUCCEEDED( hr ) )
  212. {
  213. hr = _LoadGrammars();
  214. }
  215. return hr;
  216. #endif // _WIN64
  217. }
  218. //
  219. // _LoadGrammars
  220. //
  221. // synopsis - load CFG for dictation and commands available during dictation
  222. //
  223. HRESULT CSpTask::_LoadGrammars()
  224. {
  225. // do not initialize grammars more than once
  226. //
  227. if (m_cpDictGrammar || m_cpCmdGrammar)
  228. return S_OK;
  229. HRESULT hr = E_FAIL;
  230. if (m_cpRecoCtxt)
  231. {
  232. //
  233. // create grammar object
  234. //
  235. if ( m_langid != 0x0804 ) // Chinese Engine doesn't support spelling grammar.
  236. {
  237. hr = m_cpRecoCtxt->CreateGrammar(GRAM_ID_DICT, &m_cpDictGrammar);
  238. if (S_OK == hr)
  239. {
  240. // specify spelling mode
  241. hr = m_cpDictGrammar->LoadDictation(L"Spelling", SPLO_STATIC);
  242. }
  243. if (SUCCEEDED(hr))
  244. {
  245. hr = m_cpRecoCtxt->CreateGrammar(GRAM_ID_CANDCC, &m_cpCmdGrammar);
  246. }
  247. }
  248. else
  249. hr = m_cpRecoCtxt->CreateGrammar(GRAM_ID_CANDCC, &m_cpCmdGrammar);
  250. // load the command grammar
  251. //
  252. if (SUCCEEDED(hr) )
  253. {
  254. // Load it from the resource first to speed up the initialization.
  255. if (m_langid == 0x409 || // English
  256. m_langid == 0x411 || // Japanese
  257. m_langid == 0x804 ) // Simplified Chinese
  258. {
  259. hr = m_cpCmdGrammar->LoadCmdFromResource(
  260. g_hInst,
  261. (const WCHAR*)MAKEINTRESOURCE(ID_DICTATION_COMMAND_CFG),
  262. L"SRGRAMMAR",
  263. m_langid,
  264. SPLO_DYNAMIC);
  265. }
  266. // in case LoadCmdFromResource returns wrong.
  267. if (!SUCCEEDED(hr))
  268. {
  269. if(!_GetCmdFileName(m_langid))
  270. {
  271. hr = E_FAIL;
  272. }
  273. if (m_szCmdFile[0])
  274. {
  275. hr = m_cpCmdGrammar->LoadCmdFromFile(_GetCmdFileName(m_langid), SPLO_DYNAMIC);
  276. }
  277. if (!SUCCEEDED(hr))
  278. {
  279. m_cpCmdGrammar.Release();
  280. }
  281. }
  282. hr = S_OK;
  283. }
  284. }
  285. return hr;
  286. }
  287. void CSpTask::_ReleaseGrammars(void)
  288. {
  289. if (!m_fInCallback)
  290. {
  291. m_cpDictGrammar.Release();
  292. m_cpCmdGrammar.Release();
  293. if (m_cpRecoCtxt)
  294. {
  295. m_cpRecoCtxt->SetNotifySink(NULL);
  296. m_cpRecoCtxt.Release();
  297. }
  298. }
  299. }
  300. WCHAR * CSpTask::_GetCmdFileName(LANGID langid)
  301. {
  302. if (!m_szCmdFile[0])
  303. {
  304. // now we only have a command file for English/Japanese
  305. // when cfgs are available, we'll get the name of cmd file
  306. // and the rule names from resources using findresourceex
  307. //
  308. if (PRIMARYLANGID(langid) == LANG_ENGLISH
  309. || PRIMARYLANGID(langid) == LANG_JAPANESE
  310. || PRIMARYLANGID(langid) == LANG_CHINESE)
  311. {
  312. char szFilePath[MAX_PATH];
  313. char *pszExt;
  314. char szCp[MAX_PATH];
  315. int ilen;
  316. if (!GetModuleFileName(g_hInst, szFilePath, ARRAYSIZE(szFilePath)))
  317. return NULL;
  318. // find extension
  319. // is this dbcs safe?
  320. pszExt = strrchr(szFilePath, (int)'.');
  321. if (pszExt)
  322. {
  323. *pszExt = '\0';
  324. }
  325. ilen = lstrlen(szFilePath);
  326. if (!pszExt)
  327. {
  328. pszExt = szFilePath+ilen;
  329. }
  330. LoadStringA(g_hInst, IDS_CMD_EXT, pszExt, ARRAYSIZE(szFilePath)-ilen);
  331. if (GetLocaleInfo(langid, LOCALE_IDEFAULTANSICODEPAGE, szCp, ARRAYSIZE(szCp))>0)
  332. {
  333. int iACP = atoi(szCp);
  334. if (MultiByteToWideChar(iACP, NULL, szFilePath, -1, m_szCmdFile, ARRAYSIZE(m_szCmdFile)) == 0) {
  335. m_szCmdFile[0] = 0;
  336. return NULL;
  337. }
  338. }
  339. }
  340. }
  341. return m_szCmdFile;
  342. }
  343. HRESULT CSpTask::_Activate(BOOL fActive)
  344. {
  345. HRESULT hr = E_FAIL;
  346. if (m_cpRecoCtxt)
  347. {
  348. // Need SAPI bug# for this workaround.
  349. //
  350. m_fActive = fActive;
  351. //
  352. // Is the NULL rulename fine?
  353. //
  354. if (m_cpCmdGrammar)
  355. hr = m_cpCmdGrammar->SetRuleState(NULL, NULL, m_fActive ? SPRS_ACTIVE : SPRS_INACTIVE);
  356. if (m_cpDictGrammar)
  357. hr = m_cpDictGrammar->SetDictationState(m_fActive? SPRS_ACTIVE : SPRS_INACTIVE);
  358. }
  359. return hr;
  360. }
  361. HRESULT CSpTask::InitializeSpeech()
  362. {
  363. HRESULT hr = E_FAIL;
  364. #ifdef _WIN64
  365. hr = E_NOTIMPL;
  366. #else
  367. hr = InitializeSAPIObjects();
  368. // set callback
  369. if (hr == S_OK)
  370. hr = InitializeCallback();
  371. // activate grammars
  372. if (hr == S_OK)
  373. hr = _Activate(TRUE);
  374. #endif // _WIN64
  375. return hr;
  376. }
  377. //
  378. // _GetSapilayrEngineInstance
  379. //
  380. //
  381. //
  382. HRESULT CSpTask::_GetSapilayrEngineInstance(ISpRecognizer **ppRecoEngine)
  383. {
  384. #ifdef _WIN64
  385. return E_NOTIMPL;
  386. #else
  387. HRESULT hr = E_FAIL;
  388. CComPtr<ITfFunctionProvider> cpFuncPrv;
  389. CComPtr<ITfFnGetSAPIObject> cpGetSAPI;
  390. // we shouldn't release this until we terminate ourselves
  391. // so we don't use comptr here
  392. if (m_pcui->_ptim != NULL) {
  393. hr = m_pcui->_ptim->GetFunctionProvider(CLSID_SapiLayr, &cpFuncPrv);
  394. if (S_OK == hr)
  395. {
  396. hr = cpFuncPrv->GetFunction(GUID_NULL, IID_ITfFnGetSAPIObject, (IUnknown **)&cpGetSAPI);
  397. }
  398. if (S_OK == hr)
  399. {
  400. hr = cpGetSAPI->Get(GETIF_RECOGNIZERNOINIT, (IUnknown **)ppRecoEngine);
  401. }
  402. }
  403. return hr;
  404. #endif
  405. }