www.pudn.com > Acd.rar > AcdTapi.c


/////////////////////////////////////////////////////////////////////////////////// 
// 
//  ACDTAPI.C 
// 
//  This file handles all tapi functionality in the ACD sample 
// 
// 
// 
//////////////////////////////////////////////////////////////////////////////////// 
#include  
#include  
#include "acdsmpl.h" 
 
VOID CALLBACK LineCallback (DWORD hDevice, 
                            DWORD dwMsg, 
                            DWORD dwCallbackInstance,  
                            DWORD dwParam1, 
                            DWORD dwParam2,  
                            DWORD dwParam3); 
 
 
#define LogTapiError(__lResult__, __szString__) 
#define LogError(__szString__) 
 
extern ACDGLOBALS       g; 
 
 
//////////////////////////////////////////////////////////////////////////////////// 
// 
//  BOOL InitializeTapi() 
// 
//    Whatever is needed to init TAPI for the application.  This is called 
//    before the main window is created. 
// 
//////////////////////////////////////////////////////////////////////////////////// 
BOOL InitializeTapi() 
{ 
    DWORD                       dwAPIVersion; 
    LINEINITIALIZEEXPARAMS      exparams; 
    LONG                        lResult; 
    DWORD                       i; 
    LPLINEDEVCAPS               pLDC; 
 
 
    // fill in lineinitex parameters 
    exparams.dwTotalSize             = sizeof(LINEINITIALIZEEXPARAMS); 
    exparams.dwOptions               = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW; 
     
    dwAPIVersion = TAPI_CURRENT_VERSION; 
 
    // line init 
    if ((lResult = lineInitializeEx(&g.hLineApp, 
                                    g.hInstance, 
                                    LineCallback, 
                                    SZAPPNAME, 
                                    &g.dwNumDevs, 
                                    &dwAPIVersion, 
                                    &exparams)) < 0) 
    { 
        LogTapiError(lResult, "lineInitializeEx"); 
        return FALSE; 
    } 
 
    // if there are no tapi devices, should probably 
    // not continue 
    if (g.dwNumDevs == 0) 
    { 
        LogError("No TAPI devices installed"); 
        lineShutdown(g.hLineApp); 
        return FALSE; 
    } 
 
    // need to get the permanent device IDs to map from 
    // an .ini file being read in 
    g.pdwPermIDs = (LPDWORD)ACDAlloc(g.dwNumDevs * sizeof(DWORD)); 
 
    if (!g.pdwPermIDs) 
    { 
        return FALSE; 
    } 
     
    for (i = 0; i < g.dwNumDevs; i++) 
    { 
        pLDC = LineGetDevCaps(g.hLineApp, 
                              i); 
 
        if (pLDC) 
        { 
            g.pdwPermIDs[i] = pLDC->dwPermanentLineID; 
            ACDFree(pLDC); 
        } 
    } 
 
    return TRUE; 
} 
 
 
////////////////////////////////////////////////////////////////////// 
// 
//  BOOL CleanUp() 
// 
//  Called while shutting down.  free memory, close down tapi 
// 
////////////////////////////////////////////////////////////////////// 
BOOL CleanUp() 
{ 
    PAGENT      pAgent, pAgentNext; 
    PGROUP      pGroup, pGroupNext; 
 
    // remove agents 
    pAgent = g.pAgents; 
    while(pAgent) 
    { 
        pAgentNext = pAgent->pNext; 
        DeleteAgent(pAgent); 
        pAgent = pAgentNext; 
    } 
 
    // remove groups 
    pGroup = g.pGroups; 
    while (pGroup) 
    { 
        pGroupNext = pGroup->pNext; 
        DeleteGroup(pGroup); 
        pGroup = pGroupNext; 
    } 
 
    // free id array 
    ACDFree(g.pdwPermIDs); 
 
    // shutdown 
    lineShutdown(g.hLineApp); 
 
    return TRUE; 
     
} 
//////////////////////////////////////////////////////////////////////////////// 
// 
//  LRESULT MakeGroupList(PAGENT pAgent, 
//                        LPLINEAGENTGROUPLIST pGroupList) 
// 
//    Creates a LINEAGENTGROUPLIST for pAgent - group that the agent 
//      is allowed to log into 
//    Assumption:  don't care about address for group list 
// 
//////////////////////////////////////////////////////////////////////////////// 
LRESULT MakeGroupList(PAGENT pAgent, 
                      LPLINEAGENTGROUPLIST pGroupList) 
{ 
    PGROUP                  pGroup; 
    DWORD                   dwTotalSizeNeeded, dwNameOffset, dwNumEntries; 
    LPLINEAGENTGROUPENTRY   pEntry; 
    LPTSTR                  pName; 
 
 
    pGroup = g.pGroups; 
    dwTotalSizeNeeded = sizeof(LINEAGENTGROUPLIST); 
    pGroupList->dwNumEntries = 0; 
    dwNumEntries = 0; 
 
    // walk list of groups 
    while (pGroup) 
    { 
        if (IsAgentInList(pGroup->pAgentList, 
                          pAgent)) 
        // if found the agent, add the group to the group list 
        { 
            // incrememt number of entries 
            dwNumEntries++; 
 
            // add to total size needed 
            dwTotalSizeNeeded += sizeof(LINEAGENTGROUPENTRY); 
            dwTotalSizeNeeded += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR); 
        } 
 
        pGroup = pGroup->pNext; 
    } 
 
    pGroupList->dwNeededSize = dwTotalSizeNeeded; 
     
    if (pGroupList->dwTotalSize < dwTotalSizeNeeded) 
    { 
        pGroupList->dwUsedSize = sizeof(LINEAGENTGROUPLIST); 
 
        return 0; 
//        return LINEERR_STRUCTURETOOSMALL; 
    } 
 
    pGroupList->dwNumEntries = dwNumEntries; 
 
    // set the list info 
    pGroupList->dwListSize = sizeof(LINEAGENTGROUPENTRY) * pGroupList->dwNumEntries; 
    pGroupList->dwListOffset = sizeof(LINEAGENTGROUPLIST); 
 
    // get the first agentgroup entry struct 
    pEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset); 
 
    dwNameOffset = pGroupList->dwListOffset + pGroupList->dwListSize; 
    pGroup = g.pGroups; 
 
    // loop through the groups again, and fill in the structure 
    while (pGroup) 
    { 
        if (IsAgentInList(pGroup->pAgentList, 
                          pAgent)) 
        { 
            // ID is just PGROUP 
            pEntry->GroupID.dwGroupID1 = (DWORD)pGroup; 
            pEntry->GroupID.dwGroupID2 = 0; 
            pEntry->GroupID.dwGroupID3 = 0; 
            pEntry->GroupID.dwGroupID4 = 0; 
 
            // set name of group 
            pName = (LPTSTR)(((LPBYTE)pGroupList) + dwNameOffset); 
                 
            pEntry->dwNameSize = (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR); 
            pEntry->dwNameOffset = dwNameOffset; 
            lstrcpy(pName, 
                    pGroup->lpszName); 
 
            dwNameOffset += pEntry->dwNameSize; 
 
            // get next entry 
            pEntry++; 
        } 
 
        pGroup = pGroup->pNext; 
    } 
 
    pGroupList->dwUsedSize = dwTotalSizeNeeded; 
     
    return 0; 
} 
 
 
 
//////////////////////////////////////////////////////////////////////////// 
// 
//  LRESULT SetGroupList() 
// 
//   Sets the groups that the agent is logged into. 
//     This does not change the groups that the agent _can_ log into 
// 
//////////////////////////////////////////////////////////////////////////// 
LRESULT SetGroupList(PAGENT pAgent, 
                     DWORD dwAddress, 
                     LPLINEAGENTGROUPLIST pGroupList) 
{ 
    LPLINEAGENTGROUPENTRY   pGroupEntry; 
    PLISTITEM               pListEntry; 
    DWORD                   i; 
    PGROUP  *               ppGroups = NULL; 
    PGROUP                  pGroup; 
 
    ppGroups = (PGROUP*)ACDAlloc(sizeof(PGROUP) * pGroupList->dwNumEntries); 
     
    // get to the group entry struct 
    pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset); 
 
    // loop through all entries 
    for (i = 0; i < pGroupList->dwNumEntries; i++) 
    { 
        // get the group in entry 
        // NOTE! NOTE! NOTE! 
        // should protect here against bad pointers !!! 
        pGroup = (PGROUP)pGroupEntry->GroupID.dwGroupID1; 
 
        if (pGroup->dwKey != GROUPKEY) 
        { 
            return LINEERR_INVALAGENTGROUP; 
        } 
         
        pListEntry = pGroup->pAgentList; 
 
        // walk list of agents in that group 
        if (!IsAgentInList(pGroup->pAgentList, 
                           pAgent)) 
        { 
            ACDFree(ppGroups); 
            return LINEERR_INVALAGENTGROUP; 
        } 
 
        // save group for easy access 
        ppGroups[i] = pGroup; 
         
        // get the next entry (after the variable portion of 
        // the previous entry struct) 
        pGroupEntry++; 
    } 
 
    // now we know that the groups to be set are valid 
    // walk through the list of groups again, and 
    // set the status to logged in/ not logged in 
    // for every group that the agent is a member of 
 
    pGroup = g.pGroups; 
     
    // walk list of all groups 
    while (pGroup) 
    { 
        if (pListEntry = IsAgentInList(pGroup->pAgentList, 
                                       pAgent)) 
        { 
            // default to not logged in 
            pListEntry->bLoggedIn = FALSE; 
 
            // loop through groups being set 
            for (i = 0; i < pGroupList->dwNumEntries; i++) 
            { 
                // if this group is in list, set agent to logged in 
                if (pGroup == ppGroups[i]) 
                { 
                    pListEntry->bLoggedIn = TRUE; 
                    // assumption:  agent can only log into a group on one address. 
                    pListEntry->dwAddress = dwAddress; 
                    break; 
                } 
                 
            } // for 
             
        }  // agent in list 
 
        // next group 
        pGroup = pGroup->pNext; 
         
    } // while 
 
 
    ACDFree(ppGroups); 
     
    return 0; 
} 
 
 
///////////////////////////////////////////////////////////////////////// 
// 
//  BOOL MakeAgentActivityList() 
// 
//    Creates a LINEAGENTACTIVITYLIST for pAgent 
// 
//    for the sample, just generic names are used 
//    "Activity 1", "Activity 2".... 
// 
///////////////////////////////////////////////////////////////////////// 
LRESULT MakeAgentActivityList(PAGENT pAgent, 
                              LPLINEAGENTACTIVITYLIST pActivityList) 
{ 
    TCHAR                       szBuffer[64]; 
    DWORD                       dwTotalSize, dwNameOffset, i, dwNumEntries; 
    LPTSTR                      pName; 
    LPLINEAGENTACTIVITYENTRY    pEntry; 
 
    // init 
    dwTotalSize = sizeof(LINEAGENTACTIVITYLIST); 
    pActivityList->dwNumEntries = 0; 
    dwNumEntries = 0; 
     
    // just a static list of activities 
    for (i = 0; i < TOTALACTIVITIES; i++) 
    { 
        dwNumEntries++; 
 
        // create a name 
        wsprintf(szBuffer, TEXT("Activity %lu"), i); 
 
        // determine size of this entry 
        dwTotalSize += sizeof(LINEAGENTACTIVITYENTRY); 
        dwTotalSize += (lstrlen(szBuffer) + 1) * sizeof(TCHAR); 
    } 
 
    pActivityList->dwNeededSize = dwTotalSize; 
     
    // verify size 
    if (pActivityList->dwTotalSize < dwTotalSize) 
    { 
        pActivityList->dwUsedSize = sizeof(LINEAGENTACTIVITYLIST); 
 
        return 0; 
//        return LINEERR_STRUCTURETOOSMALL; 
    } 
 
    pActivityList->dwNumEntries = dwNumEntries; 
     
    // set list stuff 
    pActivityList->dwListSize = sizeof(LINEAGENTACTIVITYENTRY) * pActivityList->dwNumEntries; 
    pActivityList->dwListOffset = sizeof(LINEAGENTACTIVITYLIST); 
 
    // get first activityentry 
    pEntry = (LPLINEAGENTACTIVITYENTRY)(((LPBYTE)pActivityList) + pActivityList->dwListOffset); 
    dwNameOffset = pActivityList->dwListOffset + pActivityList->dwListSize; 
 
    // loop through activities again 
    for (i = 0; i < TOTALACTIVITIES; i++) 
    { 
        // fill in members 
        pEntry->dwID = i; 
 
        // create a name 
        wsprintf(szBuffer, TEXT("Activity %lu"), i); 
 
        pName = (LPTSTR)(((LPBYTE)pActivityList) + dwNameOffset); 
         
        pEntry->dwNameSize = (lstrlen(szBuffer) + 1) * sizeof(TCHAR); 
        pEntry->dwNameOffset = dwNameOffset; 
        lstrcpy(pName, 
                szBuffer); 
 
        dwNameOffset += pEntry->dwNameSize; 
         
        pEntry++; 
 
    } // for 
 
    // fill in used size 
    pActivityList->dwUsedSize = dwTotalSize; 
     
    return 0; 
} 
 
 
#define DWAGENTFEATURES         LINEAGENTFEATURE_SETAGENTGROUP | \ 
                                LINEAGENTFEATURE_SETAGENTSTATE | \ 
                                LINEAGENTFEATURE_SETAGENTACTIVITY | \ 
                                LINEAGENTFEATURE_GETAGENTACTIVITYLIST | \ 
                                LINEAGENTFEATURE_GETAGENTGROUP 
 
#define DWSTATES                LINEAGENTSTATE_LOGGEDOFF | \ 
                                LINEAGENTSTATE_NOTREADY | \ 
                                LINEAGENTSTATE_READY | \ 
                                LINEAGENTSTATE_BUSYACD | \ 
                                LINEAGENTSTATE_BUSYINCOMING | \ 
                                LINEAGENTSTATE_BUSYOUTBOUND | \ 
                                LINEAGENTSTATE_BUSYOTHER | \ 
                                LINEAGENTSTATE_WORKINGAFTERCALL | \ 
                                LINEAGENTSTATE_UNKNOWN | \ 
                                LINEAGENTSTATE_UNAVAIL 
 
#define DWNEXTSTATES            LINEAGENTSTATE_LOGGEDOFF | \ 
                                LINEAGENTSTATE_NOTREADY | \ 
                                LINEAGENTSTATE_READY | \ 
                                LINEAGENTSTATE_BUSYACD | \ 
                                LINEAGENTSTATE_BUSYINCOMING | \ 
                                LINEAGENTSTATE_BUSYOUTBOUND | \ 
                                LINEAGENTSTATE_BUSYOTHER | \ 
                                LINEAGENTSTATE_WORKINGAFTERCALL | \ 
                                LINEAGENTSTATE_UNKNOWN | \ 
                                LINEAGENTSTATE_UNAVAIL 
 
#define DWSTATUSMESSAGES        LINEAGENTSTATUS_GROUP | \ 
                                LINEAGENTSTATUS_STATE | \ 
                                LINEAGENTSTATUS_NEXTSTATE | \ 
                                LINEAGENTSTATUS_ACTIVITY | \ 
                                LINEAGENTSTATUS_ACTIVITYLIST | \ 
                                LINEAGENTSTATUS_GROUPLIST | \ 
                                LINEAGENTSTATUS_CAPSCHANGE | \ 
                                LINEAGENTSTATUS_VALIDSTATES | \ 
                                LINEAGENTSTATUS_VALIDNEXTSTATES 
 
 
//////////////////////////////////////////////////////////////////// 
// 
//  BOOL IsValidState(DWORD dwState) 
// 
//////////////////////////////////////////////////////////////////// 
BOOL IsValidState(DWORD dwState) 
{ 
    if (!dwState) 
    { 
        return TRUE; 
    } 
 
    if ((dwState) & (dwState - 1)) 
    { 
        // more than one bit set 
        return FALSE; 
    } 
 
    // make sure it's one of the valid states 
    return (dwState & DWSTATES); 
 
} 
 
 
//////////////////////////////////////////////////////////////////// 
// 
//  BOOL IsValidNextState(DWORD dwState) 
// 
//////////////////////////////////////////////////////////////////// 
BOOL IsValidNextState(DWORD dwState) 
{ 
    if (!dwState) 
    { 
        return TRUE; 
    } 
 
    if ((dwState) & (dwState - 1)) 
    { 
        // more than one bit set 
        return FALSE; 
    } 
 
    // make sure it's one of the valid states 
    return (dwState & DWNEXTSTATES); 
 
} 
 
 
/////////////////////////////////////////////////////////////////////// 
// 
//  BOOL IsValidActivityID(DWORD dwActivityID) 
// 
/////////////////////////////////////////////////////////////////////// 
BOOL IsValidActivityID(DWORD dwActivityID) 
{ 
    return (dwActivityID <= TOTALACTIVITIES); 
} 
 
 
 
//////////////////////////////////////////////////////////////////////// 
// 
//  LRESULT MakeAgentCaps(PAGENT pAgent, 
//                        LPLINEAGENTCAPS pAgentCaps) 
// 
//    Creates a LINEAGENTCAPS for pAgent 
//    Features/states/messages are hardcoded 
//     for this example 
// 
//////////////////////////////////////////////////////////////////////// 
LRESULT MakeAgentCaps(PAGENT pAgent, 
                      LPLINEAGENTCAPS pAgentCaps) 
{ 
    DWORD       dwStringSize; 
     
    dwStringSize = (lstrlen(SZAPPNAME) + 1) * sizeof(TCHAR); 
 
    pAgentCaps->dwNeededSize = sizeof(LINEAGENTCAPS) + dwStringSize; 
 
    if (pAgentCaps->dwTotalSize < pAgentCaps->dwNeededSize) 
    { 
        pAgentCaps->dwUsedSize = sizeof(LINEAGENTCAPS); 
        return 0; 
//        return LINEERR_STRUCTURETOOSMALL; 
    } 
 
     
    pAgentCaps->dwAgentHandlerInfoSize = dwStringSize; 
    pAgentCaps->dwAgentHandlerInfoOffset = sizeof(LINEAGENTCAPS); 
 
    pAgentCaps->dwCapsVersion = TAPI_CURRENT_VERSION; 
 
    // these features are hardcoded here. 
    // a real implementation may set specific features 
    // per agent or line or address 
    pAgentCaps->dwFeatures              = DWAGENTFEATURES; 
    pAgentCaps->dwStates                = DWSTATES; 
    pAgentCaps->dwNextStates            = DWNEXTSTATES; 
    pAgentCaps->dwMaxNumGroupEntries    = NUMGROUPENTRIES; 
    pAgentCaps->dwAgentStatusMessages   = DWSTATUSMESSAGES; 
 
    // no extensions 
    pAgentCaps->dwNumAgentExtensionIDs = 0; 
    pAgentCaps->dwAgentExtensionIDListSize = 0; 
    pAgentCaps->dwAgentExtensionIDListOffset = 0; 
 
 
    pAgentCaps->dwUsedSize = pAgentCaps->dwNeededSize; 
     
    return 0; 
} 
 
 
////////////////////////////////////////////////////////////////////////// 
// 
//  LRESULT GetAgentStatus() 
// 
//  Creates a LINEAGENTSTATUS for pAgent 
// 
////////////////////////////////////////////////////////////////////////// 
LRESULT GetAgentStatus(PAGENT pAgent, 
                       DWORD dwAddress, 
                       LPLINEAGENTSTATUS pAgentStatus) 
{ 
    PGROUP                  pGroup; 
    LPLINEAGENTGROUPENTRY   pGroupEntry; 
    DWORD                   dwTotalSize, dwNameOffset, dwCount; 
    TCHAR                   szActivityName[NAMESIZE]; 
    PGROUP *                ppGroups; 
    PLISTITEM               pEntry; 
 
    // init total size 
    dwTotalSize = sizeof(LINEAGENTSTATUS); 
 
    if (dwAddress >= pAgent->dwNumAddresses) 
    { 
        return LINEERR_INVALADDRESSID; 
    } 
     
    // set know members 
    // for valid states / next states / agent features, just setting it to 
    // generic stuff.  a real implementation may want to set these 
    // field depending on current state / agent / hline 
    pAgentStatus->dwState           = pAgent->pAddressInfo[dwAddress].dwState; 
    pAgentStatus->dwNextState       = pAgent->pAddressInfo[dwAddress].dwNextState; 
    pAgentStatus->dwActivityID      = pAgent->pAddressInfo[dwAddress].dwActivity; 
    pAgentStatus->dwAgentFeatures   = DWAGENTFEATURES; 
    pAgentStatus->dwValidStates     = DWSTATES; 
    pAgentStatus->dwValidNextStates = DWNEXTSTATES; 
 
    // create the activity name 
    wsprintf(szActivityName, TEXT("Activity %lu"), pAgent->pAddressInfo[dwAddress].dwActivity); 
    dwTotalSize += (lstrlen(szActivityName) + 1) * sizeof(TCHAR); 
 
    ppGroups = (PGROUP *)ACDAlloc(sizeof(PGROUP) * g.dwNumGroups); 
 
    pGroup = g.pGroups; 
 
    pAgentStatus->dwNumEntries = 0; 
 
    // walk list of groups 
    while (pGroup) 
    { 
        pEntry = pGroup->pAgentList; 
 
        // walk each agent in each group 
        while (pEntry) 
        { 
            if (pEntry->pAgent == pAgent) 
            { 
                if ((!pEntry->bLoggedIn) || 
                    (pEntry->dwAddress != dwAddress)) 
                { 
                    break; 
                } 
 
                // save group 
                ppGroups[pAgentStatus->dwNumEntries] = pGroup; 
 
                // adjust total size / entries 
                pAgentStatus->dwNumEntries++; 
                dwTotalSize += sizeof(LINEAGENTGROUPENTRY); 
                dwTotalSize += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR); 
 
                break; 
            } 
 
            pEntry = pEntry->pNext; 
             
        }  // while (pEntry) 
 
        pGroup = pGroup->pNext; 
         
    } // while (pGroup) 
 
    // set needed size 
    pAgentStatus->dwNeededSize = dwTotalSize; 
 
    // do we have enough room? 
    if (pAgentStatus->dwTotalSize < dwTotalSize) 
    { 
        // if not, return 
        pAgentStatus->dwUsedSize = sizeof(LINEAGENTSTATUS); 
        ACDFree(ppGroups); 
 
        return 0; 
//        return LINEERR_STRUCTURETOOSMALL; 
    } 
 
 
    // set the group entries... 
 
    // first get the offset to the first entry 
    pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pAgentStatus) + sizeof(LINEAGENTSTATUS)); 
    pAgentStatus->dwGroupListOffset = sizeof(LINEAGENTSTATUS); 
 
    // figure out where the names can go (after all the fixed structures) 
    dwNameOffset = sizeof(LINEAGENTSTATUS) + 
                   sizeof(LINEAGENTGROUPENTRY) * pAgentStatus->dwNumEntries; 
 
    // loop through all the group that the agent is logged into 
    for (dwCount = 0; dwCount < pAgentStatus->dwNumEntries; dwCount++) 
    { 
        // set the it (just the pGroup) 
        pGroupEntry->GroupID.dwGroupID1 = (DWORD)ppGroups[dwCount]; 
        pGroupEntry->GroupID.dwGroupID2 = 0; 
        pGroupEntry->GroupID.dwGroupID3 = 0; 
        pGroupEntry->GroupID.dwGroupID4 = 0; 
 
        // set name size and offset 
        pGroupEntry->dwNameSize = (lstrlen(ppGroups[dwCount]->lpszName) + 1) * sizeof(TCHAR); 
        pGroupEntry->dwNameOffset = dwNameOffset; 
 
        // copy name 
        lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset), 
                ppGroups[dwCount]->lpszName); 
 
        // fix the name offset 
        dwNameOffset += pGroupEntry->dwNameSize; 
 
        // next entry 
        pGroupEntry++; 
         
    } 
 
    pAgentStatus->dwGroupListSize = dwNameOffset - pAgentStatus->dwGroupListOffset; 
     
    // put the activity name at the end 
    pAgentStatus->dwActivitySize = (lstrlen(szActivityName) + 1) * sizeof(TCHAR); 
    pAgentStatus->dwActivityOffset = dwNameOffset; 
 
    lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset), 
            szActivityName); 
 
     
    ACDFree(ppGroups); 
 
    pAgentStatus->dwUsedSize = pAgentStatus->dwNeededSize; 
    // return success 
    return 0; 
} 
 
 
///////////////////////////////////////////////////////////////////// 
// 
//  LRESULT SetAgentState() 
// 
//    Sets the current and next state for pAgent 
//    on that specific address 
// 
// 
///////////////////////////////////////////////////////////////////// 
LRESULT SetAgentState(PAGENT pAgent, 
                      DWORD dwAddress, 
                      DWORD dwState, 
                      DWORD dwNextState) 
{ 
    // make sure valid 
    if ((!IsValidState(dwState)) || 
        (!IsValidNextState(dwNextState))) 
    { 
        return LINEERR_INVALAGENTSTATE; 
    } 
 
    // check address 
    if (dwAddress >= pAgent->dwNumAddresses) 
    { 
        return LINEERR_INVALADDRESSID; 
    } 
 
    // set the state if specified 
    if (dwState) 
    { 
        pAgent->pAddressInfo[dwAddress].dwState = dwState; 
    } 
 
    if (dwNextState) 
    { 
        pAgent->pAddressInfo[dwAddress].dwNextState = dwNextState; 
    } 
     
    return 0; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  LRESULT SetAgentActivity() 
// 
//   Sets the activity for pAgent 
// 
/////////////////////////////////////////////////////////////////////////////// 
LRESULT SetAgentActivity(PAGENT pAgent, 
                         DWORD dwAddress, 
                         DWORD dwActivityID) 
{ 
    if (dwAddress >= pAgent->dwNumAddresses) 
    { 
        return LINEERR_INVALADDRESSID; 
    } 
 
    if (!IsValidActivityID(dwActivityID)) 
    { 
        return LINEERR_INVALAGENTACTIVITY; 
    } 
 
    pAgent->pAddressInfo[dwAddress].dwActivity = dwActivityID; 
     
    return 0; 
} 
 
 
//////////////////////////////////////////////////////////////////////////////// 
// 
//  BOOL HandleOffering(HCALL hCall) 
// 
//    Handles a LINECALLSTATE_OFFERING message 
//      Determines the group associated with the address 
//      that the call came in on, finds an available 
//      agent in that group, and transfers the call to that 
//      agent. 
// 
//////////////////////////////////////////////////////////////////////////////// 
BOOL HandleOffering(HCALL hCall) 
{ 
    LPLINECALLINFO      pLCI; 
    PGROUP              pGroup; 
    PLISTITEM           pEntry; 
    LONG                lResult; 
     
    pLCI = LineGetCallInfo(hCall); 
 
    if (!pLCI) 
    { 
        return FALSE; 
    } 
 
    pGroup = g.pGroups; 
 
    // find the group that this call came in on 
    // walk all the groups 
    while (pGroup) 
    { 
        // if the line and address match, it's the 
        // correct address 
        if ((pGroup->hLine == pLCI->hLine) && 
            (pGroup->dwAddress == pLCI->dwAddressID)) 
        { 
            break; 
        } 
 
        pGroup = pGroup->pNext; 
    } 
 
    // couldn't find the group 
    if (!pGroup) 
    { 
        // error! 
        ACDFree(pLCI); 
        return FALSE; 
    } 
 
    // OK - found the group that this call is for.  Now transfer to 
    // an agent that is available. 
    pEntry = pGroup->pAgentList; 
 
    while (pEntry) 
    { 
        if (pEntry->bLoggedIn && 
            (pEntry->pAgent->pAddressInfo[pEntry->dwAddress].dwState == 
                LINEAGENTSTATE_READY)) 
        { 
            // found someone 
            // doing a blind transfer here 
            // other implementations may need to 
            // do lineSetupTransfer / lineDial / lineCompleteTransfer 
            if (lResult = lineBlindTransfer(hCall, 
                                            (LPCWSTR)pEntry->pAgent->lpszNumber, 
                                            0)) 
            { 
                //LogTapiError(TEXT("lineBlindTransfer"), lResult); 
                // don't break - try the next agent 
            } 
            else 
            { 
                // set the state to reflect that 
                // a call is being handled 
                SetAgentState(pEntry->pAgent, 
                              pEntry->dwAddress, 
                              LINEAGENTSTATE_BUSYACD, 
                              LINEAGENTSTATE_READY); 
 
                break; 
            } 
 
        } 
         
        pEntry = pEntry->pNext; 
    } 
 
    if (!pEntry) 
    { 
        // couldn't find an available agent 
 
        // NOTE! NOTE! NOTE! NOTE! NOTE! 
        // something should be done here with this call.  put into 
        // a queue on hold or something.  For this sample, we are just 
        // ignoring it 
    } 
 
    ACDFree(pLCI); 
 
    return TRUE; 
} 
 
 
////////////////////////////////////////////////////////////////////////// 
// 
//  BOOL HandleIdle(HCALL hCall) 
// 
//    Handles LINECALLSTATE_IDLE 
//     Should always always always deallocate when 
//     getting an IDLE message.  Also, determine if this is a call 
//     that we know about and set the agent state appropriatly 
// 
////////////////////////////////////////////////////////////////////////// 
BOOL HandleIdle(HCALL hCall) 
{ 
    LPLINECALLINFO      pLCI; 
    PAGENT              pAgent; 
 
    pLCI = LineGetCallInfo(hCall); 
 
    // always deallocate the call 
    lineDeallocateCall(hCall); 
 
    if (!pLCI) 
    { 
        return FALSE; 
    } 
 
    // get the agent associated with the line 
    pAgent = GetAgentFromhLine(pLCI->hLine); 
 
    if (!pAgent) 
    { 
        ACDFree(pLCI); 
        return FALSE; 
    } 
               
 
    // set that agent to their next state 
    // Assumption:  only calls that the ACD app know about 
    // occur.  For example, if an agent made an outgoing call 
    // and it transitioned to idle, this code would still be executed. 
    // May make more sense to not handle NextState in the ACD app, and let 
    // the client app handle it. 
    SetAgentState(pAgent, 
                  pLCI->dwAddressID, 
                  pAgent->pAddressInfo[pLCI->dwAddressID].dwNextState, 
                  0); 
 
    ACDFree(pLCI); 
     
    return TRUE; 
     
} 
 
 
//////////////////////////////////////////////////////////////////////////// 
// 
//  void HandleLineProxyRequest(HLINE hLine, 
//                              LPLINEPROXYREQUEST pProxyRequest) 
// 
//    Handles LINE_PROXYREQUEST message 
//     Just dispatches to appropriate functions 
// 
//////////////////////////////////////////////////////////////////////////// 
void HandleLineProxyRequest(HLINE hLine, 
                            LPLINEPROXYREQUEST pProxyRequest) 
{ 
    PAGENT       pAgent; 
    LRESULT      lResult; 
 
    pAgent = GetAgentFromName((LPTSTR)(((LPBYTE)pProxyRequest) + 
                                       pProxyRequest->dwClientUserNameOffset)); 
     
    if (!pAgent) 
    { 
        lineProxyResponse(hLine, 
                          pProxyRequest, 
                          LINEERR_INVALAGENTID); 
 
        return; 
    } 
     
    switch (pProxyRequest->dwRequestType) 
    { 
        case LINEPROXYREQUEST_SETAGENTGROUP: 
 
            lResult = SetGroupList(pAgent, 
                                   pProxyRequest->SetAgentGroup.dwAddressID, 
                                   &pProxyRequest->SetAgentGroup.GroupList); 
 
            lineProxyResponse(hLine, 
                              pProxyRequest, 
                              lResult); 
 
            return; 
             
        case LINEPROXYREQUEST_SETAGENTSTATE: 
 
            lResult = SetAgentState(pAgent, 
                                    pProxyRequest->SetAgentState.dwAddressID, 
                                    pProxyRequest->SetAgentState.dwAgentState, 
                                    pProxyRequest->SetAgentState.dwNextAgentState); 
 
            lineProxyResponse(hLine, 
                              pProxyRequest, 
                              lResult); 
             
            break; 
             
        case LINEPROXYREQUEST_SETAGENTACTIVITY: 
 
            lResult = SetAgentActivity(pAgent, 
                                       pProxyRequest->SetAgentActivity.dwAddressID, 
                                       pProxyRequest->SetAgentActivity.dwActivityID); 
 
            lineProxyResponse(hLine, 
                              pProxyRequest, 
                              lResult); 
             
            break; 
             
        case LINEPROXYREQUEST_GETAGENTSTATUS: 
 
            lResult = GetAgentStatus(pAgent, 
                                     pProxyRequest->GetAgentStatus.dwAddressID, 
                                     &pProxyRequest->GetAgentStatus.AgentStatus); 
 
            lineProxyResponse(hLine, 
                              pProxyRequest, 
                              lResult); 
             
            break; 
             
        case LINEPROXYREQUEST_GETAGENTCAPS: 
 
            if ((hLine == pAgent->hLine) && 
                (pProxyRequest->GetAgentCaps.dwAddressID < pAgent->dwNumAddresses)) 
            { 
                lResult = MakeAgentCaps(pAgent, 
                                        &pProxyRequest->GetAgentCaps.AgentCaps); 
            } 
            else 
            { 
                lResult = LINEERR_BADDEVICEID; 
            } 
 
            lineProxyResponse(hLine, 
                              pProxyRequest, 
                              lResult); 
            break; 
             
        case LINEPROXYREQUEST_GETAGENTACTIVITYLIST: 
 
            lResult = MakeAgentActivityList(pAgent, 
                                            &pProxyRequest->GetAgentActivityList.ActivityList); 
 
            lineProxyResponse(hLine, 
                              pProxyRequest, 
                              lResult); 
             
            break; 
             
        case LINEPROXYREQUEST_GETAGENTGROUPLIST: 
 
            lResult = MakeGroupList(pAgent, 
                                    &pProxyRequest->GetAgentGroupList.GroupList); 
 
            lineProxyResponse(hLine, 
                              pProxyRequest, 
                              lResult); 
            return; 
             
 
    } 
    return; 
} 
 
///////////////////////////////////////////////////////////// 
// 
//  void HandleLineCallState(DWORD dwDevice, 
// 
//  Handles callstate messages we are interested in 
// 
///////////////////////////////////////////////////////////// 
void HandleLineCallState(DWORD dwDevice, 
                         DWORD dwParam1, 
                         DWORD dwParam2, 
                         DWORD dwParam3) 
{ 
    switch (dwParam1) 
    { 
    case LINECALLSTATE_OFFERING: 
    { 
        LPLINECALLSTATUS        pLCS; 
 
        // get the call privilege. 
        // NOTE:  the new LINE_APPNEWCALL message notifies applications 
        // of their priv for new calls not created by app 
        pLCS = LineGetCallStatus((HCALL)dwDevice); 
        if (!pLCS) 
        { 
            break; 
        } 
         
        if (pLCS->dwCallPrivilege & LINECALLPRIVILEGE_OWNER) 
        { 
            HandleOffering((HCALL)dwDevice); 
        } 
 
        ACDFree(pLCS); 
         
        break; 
    } 
    case LINECALLSTATE_CONNECTED: 
        break; 
 
    case LINECALLSTATE_DISCONNECTED: 
        break; 
 
    case LINECALLSTATE_IDLE: 
 
        HandleIdle((HCALL)dwDevice); 
         
        break; 
 
    case LINECALLSTATE_BUSY: 
        break; 
 
    default: 
        break; 
    } 
} 
 
 
/////////////////////////////////////////////////////////////////////// 
// TAPI message handlers.  For this sample, they don't 
// do anything 
/////////////////////////////////////////////////////////////////////// 
void HandleLineDevState(DWORD dwParam1, 
                        DWORD dwParam2, 
                        DWORD dwParam3) 
{ 
} 
 
 
void HandleLineReply(DWORD dwParam1, 
                     DWORD dwParam2, 
                     DWORD dwParam3) 
{ 
} 
void HandleLineCallInfo(DWORD dwParam1, 
                             DWORD dwParam2, 
                             DWORD dwParam3) 
{ 
} 
 
void HandleLineClose(DWORD dwParam1, 
                          DWORD dwParam2, 
                          DWORD dwParam3) 
{ 
} 
 
 
////////////////////////////////////////////////////////////////////////////////// 
// 
// LineCallback() - TAPI callback function 
// 
////////////////////////////////////////////////////////////////////////////////// 
VOID CALLBACK LineCallback (DWORD hDevice, 
                            DWORD dwMsg, 
                            DWORD dwCallbackInstance,  
                            DWORD dwParam1, 
                            DWORD dwParam2,  
                            DWORD dwParam3) 
{ 
    switch(dwMsg) 
    { 
      case LINE_PROXYREQUEST: 
           HandleLineProxyRequest((HLINE) hDevice, 
                                  (LPLINEPROXYREQUEST)dwParam1); 
           return; 
            
      case LINE_LINEDEVSTATE: 
          HandleLineDevState(dwParam1, 
                             dwParam2, 
                             dwParam3); 
          return; 
      case LINE_REPLY: 
          HandleLineReply(dwParam1, 
                          dwParam2, 
                          dwParam3); 
          return; 
      case LINE_CALLSTATE: 
          HandleLineCallState(hDevice, 
                              dwParam1, 
                              dwParam2, 
                              dwParam3); 
          return;  
      case LINE_CALLINFO: 
          HandleLineCallInfo(dwParam1, 
                             dwParam2, 
                             dwParam3); 
          return; 
      case LINE_CLOSE: 
          HandleLineClose(dwParam1, 
                          dwParam2, 
                          dwParam3); 
          return; 
 
      default: 
          return; 
   } 
}