Custom Action Example
There is often a need to look information up in a database. The following example will show how
to set up a Custom Action dynamic link library. The call flow which the CA DLL will support is
shown below.
This example will focus on the PinNumber Module support. The other modules and their CA functions
may be mentioned, but will not be detailed here. The first issue will be our User Data. This will
be defined in a header of the Custom Action DLL project.
C++ Code
struct AccountInfo
{
LONG AccountNo;
LONG PinNo;
LONG StateCode;
LONG CityCode;
CHAR SocialSec[20];
LONG Age;
};
The LV_Call_Start function is a recommended function that will be called if found in the CA DLL. This
function will receive notification that a call is starting. This is a very good place to create and
store the User Data.
C++ Code
__declspec(dllexport) BOOL WINAPI LV_Call_Start(LONG nMsg, HANDLE hCA)
{
AccountInfo* pAI = new AccountInfo;
memset(pAI, 0, sizeof(AccountInfo));
if(!LVCA_SetUserData(hCA, pAI))
{
/*
error An Error is anything within the system
itself that creates a glitch in the Speech
Driven Information System, such as a missing
Vox or a Goto that doesn't go anywhere.handing;
*/
}
return true;
}
When the call ends the LV_Call_End function will be called. This is the logical place to clean up
the user data.
C++ Code
__declspec(dllexport) BOOL WINAPI LV_Call_End(LONG nMsg, HANDLE hCA)
{
AccountInfo* pAI;
if(!LVCA_GetUserData(hCA, (LPVOID *) &pAI))
{
//error handling;
}
delete pAI;
LVCA_SetUserData(hCA, NULL);
return true;
}
A definition file should be created for the project. This text file specifies the exported functions.
Here is an example of what the .def file would look like:
LIBRARY "CA_Test"
EXPORTS
LV_Call_Start
LV_Call_End
Here is how the PinNumber module will look in the Designer.
Notice there is no audio and the grammar has no concepts in it. The Custom DLL property has a Custom
Action DLL filename. The function that the module is going to look for is PinModule. This function will
need to be defined in the CA DLL.
C++ Code
__declspec(dllexport) BOOL WINAPI PinModule(LONG nEvent, HANDLE hCA)
{
switch(nEvent)
{
case CAMSG_POLL:
return TRUE;
case CAMSG_ENTERMODULE:
LVCA_AddMainLibraryAudio(hCA, "EnterPin");
LVCA_AddStandardGrammar(hCA, CA_GRAMMAR_DIGITS);
break;
case CAMSG_REC_BEFORE_CONFIRM:
CheckPin(hCA);
break;
}
return TRUE;
}
In the above example, notice that the CAMSG_ENTERMODULE has two functions being called. The
LVCA_AddMainLibraryAudio function is adding audio, defined in the Designer's Audio Manager.
LVCA_AddStandardGrammar is adding the built-in digits grammar. The event CAMSG_REC_BEFORE_CONFIRM
is calling CheckPin. The function needs to extract the raw concept and save it as a pin number
in the user data. Since it is possible for the concept to be a Designer defined grammar, the
LVCA_IsCustom function checks the response.
C++ Code
VOID CheckPin(HANDLE hCA)
{
LONG nCount;
LONG nSize = 1024;
LONG nConf = 0;
if (!LVCA_GetRawConceptCount(hCA, &nCount))
{
INT nErr = LVCA_GetLastCallError(hCA, m_szErrStr, &nSize);
if(!nErr)
nErr = LVCA_GetLastSystemError(m_szErrStr, &nSize);
ReportError(nErr);
return;
}
BOOL bIsCustom;
TCHAR szConcept[1024];
//for brevity just getting the first concept;
if(!LVCA_GetRawConcept(hCA, 0, szConcept, &nSize, &nConf))
{
//error handling
}
if(!LVCA_IsCustomConcept(hCA, &bIsCustom, szConcept))
{
//error handling
}
if(!bIsCustom) //this protects a global concept from being intercepted
return;
AccountInfo* pAI;
if(!LVCA_GetUserInfo(hCA, (LPVOID *) &pAI)
{
//error handling
}
pAI->PinNo = atol(szConcept);
//this function would perform a database lookup on the account and pin.
if(!Login(pAI))
{
LVCA_AddOneTimeLibraryAudio(hCA, "Bad Pin Number or Account");
LVCA_SetGoto(hCA, "AccountNo");
}
else
{
LVCA_SetGoto(hCA, "User Settings Needed");
}
}
Finally, the PinModule function needs to be added to the .def file.
LIBRARY "CA_Test"
EXPORTS
LV_Call_Start
LV_Call_End
PinModule