////////////////////////////////////////////////////////////////////////////// // // ACDCLNT.C // // ACDClient app // ////////////////////////////////////////////////////////////////////////////// #include #include #include "acdclnt.h" #include "resource.h" ////////////////////////////////////////////////////////////////////////////// // PROTOTYPES ////////////////////////////////////////////////////////////////////////////// static BOOL CreateMainWindow (int nCmdShow); static LRESULT CALLBACK MainWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK AgentStateDlgProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); VOID CALLBACK LineCallback (DWORD hDevice, DWORD dwMsg, DWORD dwCallbackInstance, DWORD dwParam1, DWORD dwParam2, DWORD dwParam3); BOOL InitTapi(); BOOL CloseTapi(); BOOL RedoWindow(); BOOL SetStatusMessage(LPTSTR lpszMessage); BOOL SetButton(DWORD dwAddress, BOOL bAnswer, BOOL bEnable); LRESULT WaitForLineReply(); LONG ThreadRoutine(LPVOID lpv); ////////////////////////////////////////////////////////////////////////////// // // GLOBALS // ////////////////////////////////////////////////////////////////////////////// HINSTANCE ghInstance; // main instance HWND ghMainWnd; // main window PADDRESSINFO pAddressInfo = NULL; // array of info about each address HLINEAPP ghLineApp; // hlineapp DWORD gdwAddresses; // number of addresses on our line DWORD gdwDeviceID; // our device HLINE ghLine; // our line HANDLE ghCompletionPort; // tapi message completionport CRITICAL_SECTION csLineReply; // using global variables to keep track of line // replies, since the main thread will only have at most one outstanding // line reply at a time BOOL gbReply; LONG glResult; ////////////////////////////////////////////////////////////////////////////// // // WinMain() // ////////////////////////////////////////////////////////////////////////////// int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; ghInstance = hInstance; if(!InitTapi()) { MessageBox(NULL, TEXT("Failed to initialize TAPI"), TEXT("Cannot start ACDClient"), MB_OK); return 0; } if (!CreateMainWindow(nCmdShow)) { return 0; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 1; } /////////////////////////////////////////////////////////////////////////////// // // CreateMainWindow() // /////////////////////////////////////////////////////////////////////////////// BOOL CreateMainWindow (int nCmdShow) { // main window ghMainWnd = CreateDialog(ghInstance, MAKEINTRESOURCE(IDD_MAINDLG), NULL, MainWndProc); if (ghMainWnd == NULL) { return FALSE; } SetStatusMessage(TEXT("Waiting for call")); // create buttons RedoWindow(); ShowWindow(ghMainWnd, nCmdShow); UpdateWindow(ghMainWnd); return TRUE; } ///////////////////////////////////////////////////////////////////////////////// // // BOOL SetStatusMessage(LPTSTR lpszMessage) // // Sets text in the static control at the bottom of the main window to // lpszMessage // ///////////////////////////////////////////////////////////////////////////////// BOOL SetStatusMessage(LPTSTR lpszMessage) { return (SetWindowText(GetDlgItem(ghMainWnd, IDC_STATIC1), lpszMessage)); } ///////////////////////////////////////////////////////////////////////////////// // // BOOL ClearCall(HCALL hCall) // // Called when a CALLSTATE_IDLE message is recieved. Looks for the call in the // global pAddressInfo array. If it finds it, is clears the appropriate members // of the structure // ///////////////////////////////////////////////////////////////////////////////// BOOL ClearCall(HCALL hCall) { DWORD dwCount; for (dwCount = 0; dwCount < gdwAddresses; dwCount++) { if (pAddressInfo[dwCount].hCall == hCall) { pAddressInfo[dwCount].hCall = NULL; pAddressInfo[dwCount].bCall = FALSE; SetButton(dwCount, TRUE, FALSE); return TRUE; } } return FALSE; } ////////////////////////////////////////////////////////////////////////////////// // // BOOL SetButton() // // Sets the status and text of the answer/drop button for a specific address // ////////////////////////////////////////////////////////////////////////////////// BOOL SetButton(DWORD dwAddress, BOOL bAnswer, BOOL bEnable) { if (dwAddress >= gdwAddresses) return FALSE; if (bAnswer) { SetWindowText(pAddressInfo[dwAddress].hAnswer, TEXT("Answer")); } else { SetWindowText(pAddressInfo[dwAddress].hAnswer, TEXT("Hang Up")); } EnableWindow(pAddressInfo[dwAddress].hAnswer, bEnable); return TRUE; } /////////////////////////////////////////////////////////////////////////////// // // VOID CALLBACK LineCallback () // // TAPI callback function. Handles all tapi messages // /////////////////////////////////////////////////////////////////////////////// VOID CALLBACK LineCallback (DWORD hDevice, DWORD dwMsg, DWORD dwCallbackInstance, DWORD dwParam1, DWORD dwParam2, DWORD dwParam3) { LPLINECALLINFO pLCI; LPLINECALLSTATUS pLCS; TCHAR szBuffer[64]; switch (dwMsg) { case LINE_REPLY: { EnterCriticalSection(&csLineReply); if (dwParam1 == (DWORD)glResult) { gbReply = TRUE; glResult = dwParam2; } LeaveCriticalSection(&csLineReply); } break; case LINE_CALLSTATE: { if (dwParam1 == LINECALLSTATE_OFFERING) { // get the call privilege // note note note the new LINE_APPNEWCALL // give call privilege pLCS = LineGetCallStatus((HCALL)hDevice); if (!pLCS) return; if (!(pLCS->dwCallPrivilege & LINECALLPRIVILEGE_OWNER)) { // not our call GlobalFree(pLCS); return; } GlobalFree(pLCS); // we're getting offered a call // first get the address pLCI = LineGetCallInfo((HCALL)hDevice); if (!pLCI) { // error return; } // set the status message text wsprintf(szBuffer, TEXT("Incoming call on address %lu"), pLCI->dwAddressID); pAddressInfo[pLCI->dwAddressID].hCall = (HCALL)hDevice; SetStatusMessage(szBuffer); // set the button to answer SetButton(pLCI->dwAddressID, TRUE, TRUE); GlobalFree(pLCI); break; } if (dwParam1 == LINECALLSTATE_IDLE) { // see if we have this call ClearCall((HCALL)hDevice); // dealloc no matter what lineDeallocateCall((HCALL)hDevice); break; } } break; } } /////////////////////////////////////////////////////////////////////////////// // // BOOL GetAddressFromhWnd() // /////////////////////////////////////////////////////////////////////////////// BOOL GetAddressFromhWnd(HWND hWnd, LPDWORD pdwAddress, LPBOOL pbStatus) { DWORD dwAddress; // go through the array of addressinfo and see // if the hwnd matches for (dwAddress = 0; dwAddress < gdwAddresses; dwAddress++) { if (pAddressInfo[dwAddress].hStatus == hWnd) { *pdwAddress = dwAddress; *pbStatus = TRUE; return TRUE; } if (pAddressInfo[dwAddress].hAnswer == hWnd) { *pdwAddress = dwAddress; *pbStatus = FALSE; return TRUE; } } return FALSE; } ///////////////////////////////////////////////////////////////////////////// // // BOOL DoLineAnswerDrop(DWORD dwAddress) // // Handles what happens when the answer/drop button is pressed // ///////////////////////////////////////////////////////////////////////////// BOOL DoLineAnswerDrop(DWORD dwAddress) { // if we have a call, then we want to drop it if (pAddressInfo[dwAddress].bCall) { SetStatusMessage(TEXT("Hanging up call ...")); EnterCriticalSection(&csLineReply); glResult = lineDrop(pAddressInfo[dwAddress].hCall, NULL, 0); if (glResult < 0) { LeaveCriticalSection(&csLineReply); // error } else if (WaitForLineReply()) { // error } // error or not, deallocate and set button lineDeallocateCall(pAddressInfo[dwAddress].hCall); SetButton(dwAddress, TRUE, FALSE); pAddressInfo[dwAddress].hCall = NULL; pAddressInfo[dwAddress].bCall = FALSE; SetStatusMessage(TEXT("Waiting for a call")); } else { BOOL bError = FALSE; // answer SetStatusMessage(TEXT("Answering call...")); EnterCriticalSection(&csLineReply); glResult = lineAnswer(pAddressInfo[dwAddress].hCall, NULL, 0); if (glResult < 0) { LeaveCriticalSection(&csLineReply); bError = TRUE; //error } else if (WaitForLineReply()) { bError = TRUE; // error } if (bError) { SetStatusMessage(TEXT("Hanging up call ...")); lineDeallocateCall(pAddressInfo[dwAddress].hCall); pAddressInfo[dwAddress].hCall = NULL; SetButton(dwAddress, TRUE, FALSE); SetStatusMessage(TEXT("Waiting for a call")); return FALSE; } SetStatusMessage(TEXT("On a call")); pAddressInfo[dwAddress].bCall = TRUE; SetButton(dwAddress, FALSE, TRUE); } return TRUE; } ////////////////////////////////////////////////////////////////////// // // LRESULT DoCommand(WPARAM wParam, // LPARAM lParam) // // Handles WM_COMMAND messages for the main window // ////////////////////////////////////////////////////////////////////// LRESULT DoCommand(WPARAM wParam, LPARAM lParam) { DWORD dwAddress; BOOL bStatus; // check to see if a button is being clicked if (HIWORD(wParam) == BN_CLICKED) { // check to see if it is a button we care about if (GetAddressFromhWnd((HWND)lParam, &dwAddress, &bStatus)) { // if it's the status button, display the status // dialog if (bStatus) { DialogBoxParam(ghInstance, MAKEINTRESOURCE(IDD_AGENTSTATE), ghMainWnd, AgentStateDlgProc, (LPARAM)dwAddress); } // else it's the answer/drop button else { DoLineAnswerDrop(dwAddress); } } return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////// // // MainWndProc() // //////////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK MainWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: return 1; case WM_COMMAND: return DoCommand(wParam, lParam); break; case WM_CLOSE: case WM_DESTROY: CloseTapi(); PostQuitMessage(0); return 1; } return 0; } /////////////////////////////////////////////////////////////////////// // // BOOL InitTapi() // // Initializes TAPI. For this sample, we assume that the "agent" (the person // logged on) can only have access to _one_ hLine. Also, they have access to // every address on that line. This may not be true for many ACD situations. // // As soon as we find a device that the agent has access to, we quit // looking, and use that device // /////////////////////////////////////////////////////////////////////// BOOL InitTapi() { LONG lResult; LINEINITIALIZEEXPARAMS exparams; LPLINEAGENTCAPS pLAC; LPLINEDEVCAPS pLDC; DWORD dwDeviceID, dwNumDevs, dwAPIVersion, dwThreadID; // initialize completion port to receive TAPI notifications ghCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); InitializeCriticalSection(&csLineReply); // fill in lineinitex parameters exparams.dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS); exparams.dwOptions = LINEINITIALIZEEXOPTION_USECOMPLETIONPORT; exparams.Handles.hCompletionPort = ghCompletionPort; dwAPIVersion = TAPI_CURRENT_VERSION; lResult = lineInitializeEx(&ghLineApp, ghInstance, NULL, SZAPPNAME, &dwNumDevs, &dwAPIVersion, &exparams); if (lResult) { return FALSE; } // if no devices, don't continue if (dwNumDevs == 0) { lineShutdown(ghLineApp); return FALSE; } // kick off completion port thread CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadRoutine, NULL, 0, &dwThreadID); // loop through all devices for (dwDeviceID = 0; dwDeviceID < dwNumDevs; dwDeviceID++) { // Get the Agent Caps. If this succeedes, this is // a device we can use pLAC = LineGetAgentCaps(ghLineApp, dwDeviceID, 0); if (pLAC) { // this is a device we can use gdwDeviceID = dwDeviceID; // get the number of addresses pLDC = LineGetDevCaps(ghLineApp, dwDeviceID); if (pLDC) { gdwAddresses = pLDC->dwNumAddresses; GlobalFree(pLDC); } GlobalFree(pLAC); break; } } // open the line in owner mode lResult = lineOpen(ghLineApp, gdwDeviceID, &ghLine, TAPI_CURRENT_VERSION, 0, 0, LINECALLPRIVILEGE_OWNER, LINEMEDIAMODE_INTERACTIVEVOICE, NULL); // if line failed, don't continue if (lResult) { lineShutdown(ghLineApp); return FALSE; } return TRUE; } ////////////////////////////////////////////////////////////////////// // // ThreadRoutine // // Thread dedicated to checking completion port for TAPI events // ////////////////////////////////////////////////////////////////////// LONG ThreadRoutine(LPVOID lpv) { LPLINEMESSAGE pMsg; DWORD dwNumBytesTransfered, dwCompletionKey; // just wait for tapi notifications while (GetQueuedCompletionStatus(ghCompletionPort, &dwNumBytesTransfered, &dwCompletionKey, (LPOVERLAPPED *) &pMsg, INFINITE)) { if (pMsg) { // when we get one, call the handling function LineCallback(pMsg->hDevice, pMsg->dwMessageID, pMsg->dwCallbackInstance, pMsg->dwParam1, pMsg->dwParam2, pMsg->dwParam3); LocalFree (pMsg); } else { break; } } ExitThread(0); return 0; } /////////////////////////////////////////////////////////////////////////// // // BOOL CloseTapi() // // Close tapi and free resources // /////////////////////////////////////////////////////////////////////////// BOOL CloseTapi() { GlobalFree(pAddressInfo); CloseHandle(ghCompletionPort); lineClose(ghLine); lineShutdown(ghLineApp); return TRUE; } // static information for the status dialog static DWORD dwAgentStates[] = { LINEAGENTSTATE_LOGGEDOFF, LINEAGENTSTATE_NOTREADY, LINEAGENTSTATE_READY, LINEAGENTSTATE_BUSYACD, LINEAGENTSTATE_BUSYINCOMING, LINEAGENTSTATE_BUSYOUTBOUND, LINEAGENTSTATE_BUSYOTHER, LINEAGENTSTATE_WORKINGAFTERCALL, LINEAGENTSTATE_UNKNOWN, LINEAGENTSTATE_UNAVAIL, 0 }; static LPTSTR lpszStates[] = { TEXT("Logged Off"), TEXT("Not Ready"), TEXT("Ready"), TEXT("Busy ACD"), TEXT("Busy Incoming"), TEXT("Busy Outbound"), TEXT("Busy Other"), TEXT("Working after call"), TEXT("Unknown"), TEXT("Unavail"), NULL }; /////////////////////////////////////////////////////////////////////////// // // BOOL InitAgentDlg() // // Handles initialization of the status dialog // // Gets the group list and puts groups in multiselect list box // these are the groups that the agent _can_ log into // the groups they are logged into will be selected // Creates comboboxes of states and nextstates, and select // the agent's current state/nextstate // Gets the activity list and puts each item into a combobox // the current activity will be selected // /////////////////////////////////////////////////////////////////////////// BOOL InitAgentDlg(HWND hwnd, DWORD dwAddress, LPLINEAGENTGROUPLIST * ppLAG) { LPLINEAGENTCAPS pLAC; LPLINEAGENTSTATUS pLAS; LPLINEAGENTACTIVITYLIST pLAA; LPLINEAGENTGROUPENTRY pEntry, pLoggedInEntry; LPLINEAGENTACTIVITYENTRY pActivityEntry; DWORD dwEntries, dwCount; LONG item; // first, get the status // this information will be used to know which items to select // in each of the listbox/comboboxes pLAS = LineGetAgentStatus(ghLine, dwAddress); if (!pLAS) { return FALSE; } // get the group list if (!(*ppLAG = LineGetAgentGroupList(ghLine, dwAddress))) { return FALSE; } // get the first groupentry pEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)*ppLAG)+(*ppLAG)->dwListOffset); // loop through the group entries for (dwEntries = 0; dwEntries < (*ppLAG)->dwNumEntries; dwEntries++) { // add group to list box item = SendDlgItemMessage(hwnd, IDC_GROUPS, LB_ADDSTRING, 0, (LPARAM)(LPTSTR)(((LPBYTE)*ppLAG) + pEntry->dwNameOffset)); // save the entry SendDlgItemMessage(hwnd, IDC_GROUPS, LB_SETITEMDATA, (WPARAM)item, (LPARAM)pEntry); // now get list of groups currently logged into from the agent status structure // loop through them. if any of them match the group we are currently adding // select that group pLoggedInEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pLAS) + pLAS->dwGroupListOffset); for (dwCount = 0; dwCount < pLAS->dwNumEntries; dwCount++) { if ((pLoggedInEntry->GroupID.dwGroupID1 == pEntry->GroupID.dwGroupID1) && (pLoggedInEntry->GroupID.dwGroupID2 == pEntry->GroupID.dwGroupID2) && (pLoggedInEntry->GroupID.dwGroupID3 == pEntry->GroupID.dwGroupID3) && (pLoggedInEntry->GroupID.dwGroupID4 == pEntry->GroupID.dwGroupID4)) { SendDlgItemMessage(hwnd, IDC_GROUPS, LB_SETSEL, (WPARAM)TRUE, (LPARAM)item); } pLoggedInEntry++; } pEntry++; } // get the agent caps if (pLAC = LineGetAgentCaps(ghLineApp, gdwDeviceID, dwAddress)) { dwCount = 0; // loop through all possbile agent states. if the agent state // is selected in the agent caps, add that state to the list box while (dwAgentStates[dwCount]) { if (dwAgentStates[dwCount] & pLAC->dwStates) { item = SendDlgItemMessage(hwnd, IDC_STATE, CB_ADDSTRING, 0, (LPARAM)lpszStates[dwCount]); SendDlgItemMessage(hwnd, IDC_STATE, CB_SETITEMDATA, (WPARAM)item, (LPARAM)dwAgentStates[dwCount]); // if the state matches the current one from the agent status // select it if (pLAS->dwState == dwAgentStates[dwCount]) { SendDlgItemMessage(hwnd, IDC_STATE, CB_SETCURSEL, (WPARAM)item, 0); } } dwCount ++; } dwCount = 0; // now do the same for the next states while (dwAgentStates[dwCount]) { if (dwAgentStates[dwCount] & pLAC->dwNextStates) { item = SendDlgItemMessage(hwnd, IDC_NEXTSTATE, CB_ADDSTRING, 0, (LPARAM)lpszStates[dwCount]); SendDlgItemMessage(hwnd, IDC_NEXTSTATE, CB_SETITEMDATA, (WPARAM)item, dwAgentStates[dwCount]); if (pLAS->dwNextState == dwAgentStates[dwCount]) { SendDlgItemMessage(hwnd, IDC_NEXTSTATE, CB_SETCURSEL, (WPARAM)item, 0); } } dwCount++; } GlobalFree(pLAC); } // get the activity list pLAA = LineGetAgentActivityList(ghLine, gdwDeviceID, dwAddress); if (pLAA) { dwCount = pLAA->dwNumEntries; pActivityEntry = (LPLINEAGENTACTIVITYENTRY)(((LPBYTE)pLAA) + pLAA->dwListOffset); // go through all the possible activities and add them to the list while (dwCount) { item = SendDlgItemMessage(hwnd, IDC_ACTIVITY, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)(((LPBYTE)pLAA) + pActivityEntry->dwNameOffset)); SendDlgItemMessage(hwnd, IDC_ACTIVITY, CB_SETITEMDATA, (WPARAM)item, (LPARAM)pActivityEntry->dwID); // if this is the current activity (from agent status) // select it if (pLAS->dwActivityID == pActivityEntry->dwID) { SendDlgItemMessage(hwnd, IDC_ACTIVITY, CB_SETCURSEL, (WPARAM)item, 0); } dwCount--; pActivityEntry++; } GlobalFree(pLAA); } } ////////////////////////////////////////////////////////////////////////////////////////////// // // BOOL SaveAgentStatus(HWND hwnd) // // Saves information from the status dialog // ////////////////////////////////////////////////////////////////////////////////////////////// BOOL SaveAgentStatus(HWND hwnd, DWORD dwAddress) { LPLINEAGENTGROUPENTRY pGroupEntry, pNewGroupEntry; LPLINEAGENTGROUPLIST pNewLAG; DWORD dwCount; LPINT pItems; DWORD item; DWORD dwState, dwNextState, dwActivity; // get the number of groups selected in the group // list box. each selected group is a group this // agent will be logged into dwCount = SendDlgItemMessage(hwnd, IDC_GROUPS, LB_GETSELCOUNT, 0, 0); // allocate an array to hold the selected item's indexes pItems = (LPINT)GlobalAlloc(GPTR, sizeof(int) * dwCount); // get the item's indexes SendDlgItemMessage(hwnd, IDC_GROUPS, LB_GETSELITEMS, dwCount, (LPARAM)pItems); // alloc a LINEAGENTGROUP array for groups pNewLAG = (LPLINEAGENTGROUPLIST)GlobalAlloc(GPTR, sizeof(LINEAGENTGROUPLIST) + dwCount * sizeof(LINEAGENTGROUPENTRY)); // fill in sizes pNewLAG->dwTotalSize = sizeof(LINEAGENTGROUPLIST) + dwCount * sizeof(LINEAGENTGROUPENTRY); pNewLAG->dwUsedSize = pNewLAG->dwTotalSize; pNewLAG->dwNeededSize = pNewLAG->dwTotalSize; pNewLAG->dwListSize = sizeof(LINEAGENTGROUPENTRY) * dwCount; pNewLAG->dwListOffset = sizeof(LINEAGENTGROUPLIST); // count pNewLAG->dwNumEntries = dwCount; // get pointer to first entry in array pNewGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pNewLAG) + pNewLAG->dwListOffset); // loop though all selected item while (dwCount) { // get the item data associated with the item. this data // is a group entry struct pGroupEntry = (LPLINEAGENTGROUPENTRY)SendDlgItemMessage(hwnd, IDC_GROUPS, LB_GETITEMDATA, (WPARAM)pItems[dwCount-1], 0); // copy the GroupID to the new array CopyMemory(&pNewGroupEntry->GroupID, &pGroupEntry->GroupID, sizeof(pGroupEntry->GroupID)); // these fields are not used pNewGroupEntry->dwNameSize = 0; pNewGroupEntry->dwNameOffset = 0; // next entry pNewGroupEntry++; dwCount--; } // now that we've created the AGENTGROUPLIST, set it EnterCriticalSection(&csLineReply); glResult = lineSetAgentGroup(ghLine, dwAddress, pNewLAG); if (glResult < 0) { LeaveCriticalSection(&csLineReply); //error } else if (WaitForLineReply()) { //error } GlobalFree(pNewLAG); // now get the current state item = SendDlgItemMessage(hwnd, IDC_STATE, CB_GETCURSEL, 0, 0); // get item data. this is the state flag dwState = SendDlgItemMessage(hwnd, IDC_STATE, CB_GETITEMDATA, (WPARAM)item, 0); // same for next state item = SendDlgItemMessage(hwnd, IDC_NEXTSTATE, CB_GETCURSEL, 0, 0); dwNextState = SendDlgItemMessage(hwnd, IDC_NEXTSTATE, CB_GETITEMDATA, (WPARAM)item, 0); // set it EnterCriticalSection(&csLineReply); glResult = lineSetAgentState(ghLine, dwAddress, dwState, dwNextState); if (glResult < 0) { LeaveCriticalSection(&csLineReply); //error } else if (WaitForLineReply()) { //error } // get the activity selected item = SendDlgItemMessage(hwnd, IDC_ACTIVITY, CB_GETCURSEL, 0, 0); // get the item data. this is the activity ID dwActivity = SendDlgItemMessage(hwnd, IDC_ACTIVITY, CB_GETITEMDATA, (WPARAM)item, 0); // set it EnterCriticalSection(&csLineReply); glResult = lineSetAgentActivity(ghLine, dwAddress, dwActivity); if (glResult < 0) { LeaveCriticalSection(&csLineReply); //error } else if (WaitForLineReply()) { //error } return TRUE; } /////////////////////////////////////////////////////////////////////////////// // // LRESULT WaitForLineReply() // // waiting for a line reply. // // 2 issues: // - using global variables for line reply information. only recommended // in the most simple situations // // - using completion ports to demonstrate the completion port mechanism. // since this app has ui, the wait loop has a message loop and a sleep()!! // /////////////////////////////////////////////////////////////////////////////// LRESULT WaitForLineReply() { MSG msg; gbReply = FALSE; LeaveCriticalSection(&csLineReply); while (!gbReply) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } Sleep(5); } return glResult; } /////////////////////////////////////////////////////////////////////////////////// // // LRESULT CALLBACK AgentStateDlgProc () // // Dialog proc for the agent status dialog // /////////////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK AgentStateDlgProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static DWORD dwAddress; static LPLINEAGENTGROUPLIST pLAG; switch (uMsg) { case WM_INITDIALOG: dwAddress = (DWORD)lParam; InitAgentDlg(hwnd, dwAddress, &pLAG); SetFocus(GetDlgItem(hwnd, IDC_GROUPS)); return 1; case WM_COMMAND: if (LOWORD(wParam) == IDOK) { SaveAgentStatus(hwnd, dwAddress); GlobalFree(pLAG); EndDialog(hwnd, 1); return 1; } if (LOWORD(wParam) == IDCANCEL) { GlobalFree(pLAG); EndDialog(hwnd, 1); return 1; } } return 0; } /////////////////////////////////////////////////////////////////////////////// // // **************TAPI WRAPPER FUNCTIONS************** // /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // LineGetAgentGroupList() // /////////////////////////////////////////////////////////////////////////////// LINEAGENTGROUPLIST * LineGetAgentGroupList (HLINE hLine, DWORD dwAddressID) { LINEAGENTGROUPLIST * pLineAgentGroupList; static DWORD dwMaxNeededSize = sizeof(LINEAGENTGROUPLIST); // Allocate an initial block of memory for the LINEAGENTGROUPLIST structure, // which may or may not be big enough to hold all of the information. // pLineAgentGroupList = GlobalAlloc(GPTR, dwMaxNeededSize); while (TRUE) { BOOL bError = FALSE; if (pLineAgentGroupList == NULL) { return NULL; } pLineAgentGroupList->dwTotalSize = dwMaxNeededSize; // Try (or retry) to get the LINEAGENTGROUPLIST information // EnterCriticalSection(&csLineReply); glResult = lineGetAgentGroupList(hLine, dwAddressID, pLineAgentGroupList); if (glResult < 0) { LeaveCriticalSection(&csLineReply); bError = TRUE; //error } else if (WaitForLineReply()) { bError = TRUE; //error } if (bError) { GlobalFree((HLOCAL)pLineAgentGroupList); return NULL; } // If the currently allocated LINEAGENTGROUPLIST memory block was big // enough, we're all done, else we need to realloc the memory block // and try again. // if (pLineAgentGroupList->dwNeededSize <= dwMaxNeededSize) { return pLineAgentGroupList; } else { dwMaxNeededSize = pLineAgentGroupList->dwNeededSize; pLineAgentGroupList = GlobalReAlloc((HLOCAL)pLineAgentGroupList, dwMaxNeededSize, GMEM_MOVEABLE); } } } /////////////////////////////////////////////////////////////////////////////// // // LineGetAgentStatus() // /////////////////////////////////////////////////////////////////////////////// LINEAGENTSTATUS * LineGetAgentStatus (HLINE hLine, DWORD dwAddressID) { LINEAGENTSTATUS * pLineAgentStatus; static DWORD dwMaxNeededSize = sizeof(LINEAGENTSTATUS); // Allocate an initial block of memory for the LINEAGENTSTATUS structure, // which may or may not be big enough to hold all of the information. // pLineAgentStatus = GlobalAlloc(GPTR, dwMaxNeededSize); while (TRUE) { BOOL bError = FALSE; if (pLineAgentStatus == NULL) { return NULL; } pLineAgentStatus->dwTotalSize = dwMaxNeededSize; // Try (or retry) to get the LINEAGENTSTATUS information // EnterCriticalSection(&csLineReply); glResult = lineGetAgentStatus(hLine, dwAddressID, pLineAgentStatus); if (glResult < 0) { LeaveCriticalSection(&csLineReply); bError = TRUE; //error } else if (WaitForLineReply()) { bError = TRUE; //error } if (bError) { GlobalFree((HLOCAL)pLineAgentStatus); return NULL; } // If the currently allocated LINEAGENTSTATUS memory block was big // enough, we're all done, else we need to realloc the memory block // and try again. // if (pLineAgentStatus->dwNeededSize <= dwMaxNeededSize) { return pLineAgentStatus; } else { dwMaxNeededSize = pLineAgentStatus->dwNeededSize; pLineAgentStatus = GlobalReAlloc((HLOCAL)pLineAgentStatus, dwMaxNeededSize, GMEM_MOVEABLE); } } } /////////////////////////////////////////////////////////////////////////////// // // LineGetAgentCaps() // /////////////////////////////////////////////////////////////////////////////// LINEAGENTCAPS * LineGetAgentCaps (HLINEAPP hLineApp, DWORD dwDeviceID, DWORD dwAddressID) { LINEAGENTCAPS * pLineAgentCaps; static DWORD dwMaxNeededSize = sizeof(LINEAGENTCAPS); // Allocate an initial block of memory for the LINEAGENTCAPS structure, // which may or may not be big enough to hold all of the information. // pLineAgentCaps = GlobalAlloc(GPTR, dwMaxNeededSize); while (TRUE) { BOOL bError = FALSE; if (pLineAgentCaps == NULL) { return NULL; } pLineAgentCaps->dwTotalSize = dwMaxNeededSize; // Try (or retry) to get the LINEAGENTCAPS information // EnterCriticalSection(&csLineReply); glResult = lineGetAgentCaps(hLineApp, dwDeviceID, dwAddressID, TAPI_CURRENT_VERSION, pLineAgentCaps); if (glResult < 0) { bError = TRUE; LeaveCriticalSection(&csLineReply); //error } else if (WaitForLineReply()) { bError = TRUE; //error } if (bError) { GlobalFree((HLOCAL)pLineAgentCaps); return NULL; } // If the currently allocated LINEAGENTCAPS memory block was big // enough, we're all done, else we need to realloc the memory block // and try again. // if (pLineAgentCaps->dwNeededSize <= dwMaxNeededSize) { return pLineAgentCaps; } else { dwMaxNeededSize = pLineAgentCaps->dwNeededSize; pLineAgentCaps = GlobalReAlloc((HLOCAL)pLineAgentCaps, dwMaxNeededSize, GMEM_MOVEABLE); } } } /////////////////////////////////////////////////////////////////////////////// // // LineGetAgentActivityList() // /////////////////////////////////////////////////////////////////////////////// LPLINEAGENTACTIVITYLIST LineGetAgentActivityList (HLINE hLine, DWORD dwDeviceID, DWORD dwAddressID) { LINEAGENTACTIVITYLIST * pLineAgentActivityList; static DWORD dwMaxNeededSize = sizeof(LINEAGENTACTIVITYLIST); // Allocate an initial block of memory for the LINEAGENTACTIVITYLIST structure, // which may or may not be big enough to hold all of the information. // pLineAgentActivityList = GlobalAlloc(GPTR, dwMaxNeededSize); for (;;) { BOOL bError = FALSE; if (pLineAgentActivityList == NULL) { return NULL; } pLineAgentActivityList->dwTotalSize = dwMaxNeededSize; // Try (or retry) to get the LINEAGENTACTIVITYLIST information // EnterCriticalSection(&csLineReply); glResult = lineGetAgentActivityList(hLine, dwAddressID, pLineAgentActivityList); if (glResult < 0) { LeaveCriticalSection(&csLineReply); bError = TRUE; //error } else if (WaitForLineReply()) { bError = TRUE; //error } if (bError) { GlobalFree((HLOCAL)pLineAgentActivityList); return NULL; } // If the currently allocated LINEAGENTACTIVITYLIST memory block was big // enough, we're all done, else we need to realloc the memory block // and try again. // if (pLineAgentActivityList->dwNeededSize <= dwMaxNeededSize) { return pLineAgentActivityList; } else { dwMaxNeededSize = pLineAgentActivityList->dwNeededSize; pLineAgentActivityList = GlobalReAlloc((HLOCAL)pLineAgentActivityList, dwMaxNeededSize, GMEM_MOVEABLE); } } } /////////////////////////////////////////////////////////////////////////////// // // LineGetAddressCaps() // /////////////////////////////////////////////////////////////////////////////// LINEADDRESSCAPS * LineGetAddressCaps (HLINEAPP hLineApp, DWORD dwDeviceID, DWORD dwAddressID) { LONG lRetVal; LINEADDRESSCAPS * pLineAddressCaps; static DWORD dwMaxNeededSize = sizeof(LINEADDRESSCAPS); // Allocate an initial block of memory for the LINEADDRESSCAPS structure, // which may or may not be big enough to hold all of the information. // pLineAddressCaps = GlobalAlloc(GPTR, dwMaxNeededSize); for (;;) { if (pLineAddressCaps == NULL) { return NULL; } pLineAddressCaps->dwTotalSize = dwMaxNeededSize; // Try (or retry) to get the LINEADDRESSCAPS information // lRetVal = lineGetAddressCaps(hLineApp, dwDeviceID, dwAddressID, TAPI_CURRENT_VERSION, 0, pLineAddressCaps); if (lRetVal < 0) { GlobalFree((HLOCAL)pLineAddressCaps); return NULL; } // If the currently allocated LINEADDRESSCAPS memory block was big // enough, we're all done, else we need to realloc the memory block // and try again. // if (pLineAddressCaps->dwNeededSize <= dwMaxNeededSize) { return pLineAddressCaps; } else { dwMaxNeededSize = pLineAddressCaps->dwNeededSize; pLineAddressCaps = GlobalReAlloc((HLOCAL)pLineAddressCaps, dwMaxNeededSize, GMEM_MOVEABLE); } } } /////////////////////////////////////////////////////////////////////////////// // // LineGetCallInfo() // /////////////////////////////////////////////////////////////////////////////// LINECALLINFO * LineGetCallInfo (HCALL hCall) { LONG lRetVal; LINECALLINFO * pLineCallInfo; static DWORD dwMaxNeededSize = sizeof(LINECALLINFO); // Allocate an initial block of memory for the LINECALLINFO structure, // which may or may not be big enough to hold all of the information. // pLineCallInfo = GlobalAlloc(GPTR, dwMaxNeededSize); for (;;) { if (pLineCallInfo == NULL) { return NULL; } pLineCallInfo->dwTotalSize = dwMaxNeededSize; // Try (or retry) to get the LINECALLINFO information // lRetVal = lineGetCallInfo(hCall, pLineCallInfo); if (lRetVal < 0) { GlobalFree((HLOCAL)pLineCallInfo); return NULL; } // If the currently allocated LINECALLINFO memory block was big // enough, we're all done, else we need to realloc the memory block // and try again. // if (pLineCallInfo->dwNeededSize <= dwMaxNeededSize) { return pLineCallInfo; } else { dwMaxNeededSize = pLineCallInfo->dwNeededSize; pLineCallInfo = GlobalReAlloc((HLOCAL)pLineCallInfo, dwMaxNeededSize, GMEM_MOVEABLE); } } } /////////////////////////////////////////////////////////////////////////////// // // LineGetDevCaps() // /////////////////////////////////////////////////////////////////////////////// LINEDEVCAPS * LineGetDevCaps (HLINEAPP hLineApp, DWORD dwDeviceID) { LONG lRetVal; LINEDEVCAPS * pLineDevCaps; static DWORD dwMaxNeededSize = sizeof(LINEDEVCAPS); pLineDevCaps = GlobalAlloc(GPTR, dwMaxNeededSize); for (;;) { if (pLineDevCaps == NULL) { return NULL; } pLineDevCaps->dwTotalSize = dwMaxNeededSize; lRetVal = lineGetDevCaps(hLineApp, dwDeviceID, TAPI_CURRENT_VERSION, 0, pLineDevCaps); if (lRetVal < 0) { GlobalFree((HLOCAL)pLineDevCaps); return NULL; } if (pLineDevCaps->dwNeededSize <= dwMaxNeededSize) { return pLineDevCaps; } else { dwMaxNeededSize = pLineDevCaps->dwNeededSize; pLineDevCaps = GlobalReAlloc((HLOCAL)pLineDevCaps, dwMaxNeededSize, GMEM_MOVEABLE); } } } /////////////////////////////////////////////////////////////////////////////// // // LineGetID() // /////////////////////////////////////////////////////////////////////////////// VARSTRING * LineGetID (HLINE hLine, DWORD dwAddressID, HCALL hCall, DWORD dwSelect, LPCTSTR lpszDeviceClass) { LONG lRetVal; VARSTRING * pVarString; static DWORD dwMaxNeededSize = sizeof(VARSTRING); // Allocate an initial block of memory for the VARSTRING structure, // which may or may not be big enough to hold all of the information. // pVarString = GlobalAlloc(GPTR, dwMaxNeededSize); for (;;) { if (pVarString == NULL) { return NULL; } pVarString->dwTotalSize = dwMaxNeededSize; // Try (or retry) to get the VARSTRING information // lRetVal = lineGetID(hLine, dwAddressID, hCall, dwSelect, pVarString, lpszDeviceClass); if (lRetVal < 0) { GlobalFree((HLOCAL)pVarString); return NULL; } // If the currently allocated VARSTRING memory block was big // enough, we're all done, else we need to realloc the memory block // and try again. // if (pVarString->dwNeededSize <= dwMaxNeededSize) { return pVarString; } else { dwMaxNeededSize = pVarString->dwNeededSize; pVarString = GlobalReAlloc((HLOCAL)pVarString, dwMaxNeededSize, GMEM_MOVEABLE); } } } /////////////////////////////////////////////////////////////////////////////// // // LineGetCallStatus() // /////////////////////////////////////////////////////////////////////////////// LINECALLSTATUS * LineGetCallStatus (HCALL hCall) { LONG lRetVal; LINECALLSTATUS * pLineCallStatus; static DWORD dwMaxNeededSize = sizeof(LINECALLSTATUS); // Allocate an initial block of memory for the LINECALLSTATUS structure, // which may or may not be big enough to hold all of the information. // pLineCallStatus = GlobalAlloc(GPTR, dwMaxNeededSize); while (TRUE) { if (pLineCallStatus == NULL) { return NULL; } pLineCallStatus->dwTotalSize = dwMaxNeededSize; // Try (or retry) to get the LINECALLSTATUS information // lRetVal = lineGetCallStatus(hCall, pLineCallStatus); if (lRetVal < 0) { GlobalFree((HLOCAL)pLineCallStatus); return NULL; } // If the currently allocated LINECALLSTATUS memory block was big // enough, we're all done, else we need to realloc the memory block // and try again. // if (pLineCallStatus->dwNeededSize <= dwMaxNeededSize) { return pLineCallStatus; } else { dwMaxNeededSize = pLineCallStatus->dwNeededSize; pLineCallStatus = GlobalReAlloc((HLOCAL)pLineCallStatus, dwMaxNeededSize, GMEM_MOVEABLE); } } } //////////////////////////////////////////////////////////////////////////// // // // constants for creating buttons in the main window // #define YSTART 8 #define XSTART 8 #define STATICX 57 #define BUTTONX 50 #define BUTTONGAP 20 #define BUTTONY 14 #define LINEGAP 8 ///////////////////////////////////////////////////////////////////////////// // // BOOL RedoWindow() // // Creates the buttons and static controls in the main window // For each address on the line, create a static control with the name off // the address, a button to get/set status, and a button to answer/drop // // Right now, this should only be done when the app is starting. It does // not check to see if pAddressInfo has already been allocated // ///////////////////////////////////////////////////////////////////////////// BOOL RedoWindow() { DWORD dwAddress; LPLINEADDRESSCAPS pLAC; TCHAR szBuffer[64]; LONG lBaseUnits, lxbase, lybase; HFONT hFont; HWND hWnd; int x,y,w,h,xstart,ystart,buttonx,buttony,staticx,buttongap,linegap; // alloc for address info pAddressInfo = (PADDRESSINFO)GlobalAlloc(GPTR, sizeof(ADDRESSINFO) * gdwAddresses); if (!pAddressInfo) { return FALSE; } // get conversions lBaseUnits = GetDialogBaseUnits(); lxbase = (LONG)LOWORD(lBaseUnits); lybase = (LONG)HIWORD(lBaseUnits); // convert dialog units to pixels xstart = (XSTART * lxbase) / 4; ystart = (YSTART * lybase) / 8; buttonx = (BUTTONX * lxbase) / 4; buttony = (BUTTONY * lybase) / 8; staticx = (STATICX * lxbase) / 4; buttongap = (BUTTONGAP * lxbase) / 4; linegap = (LINEGAP * lybase) / 8; // init x = xstart; y = ystart; w = buttonx; h = buttony; // get the font used by the static control hFont = (HFONT)SendDlgItemMessage(ghMainWnd, IDC_STATIC1, WM_GETFONT, 0, 0); // loop through all addressed for (dwAddress = 0; dwAddress < gdwAddresses; dwAddress++) { // get the name of the address pLAC = LineGetAddressCaps(ghLineApp, gdwDeviceID, dwAddress); if (!pLAC || !pLAC->dwAddressSize) { wsprintf(szBuffer, TEXT("Address %lu"), dwAddress); } else { lstrcpy(szBuffer, (LPTSTR)(((LPBYTE)pLAC)+pLAC->dwAddressOffset)); } if (pLAC) { GlobalFree(pLAC); } w = staticx; x = xstart; // create the static control hWnd = CreateWindow(TEXT("STATIC"), szBuffer, WS_CHILD | SS_LEFT | WS_VISIBLE, x,y+(buttony/3),w,h, ghMainWnd, NULL, ghInstance, NULL); // set the font SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, 0); x += staticx; w = buttonx; // create the status button pAddressInfo[dwAddress].hStatus = CreateWindow(TEXT("BUTTON"), TEXT("Set Status..."), WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, x,y,w,h, ghMainWnd, NULL, ghInstance, NULL); // set the font SendMessage(pAddressInfo[dwAddress].hStatus, WM_SETFONT, (WPARAM)hFont, 0); x += buttonx + buttongap; // create the answer/drop button pAddressInfo[dwAddress].hAnswer = CreateWindow(TEXT("BUTTON"), TEXT("Answer"), WS_CHILD | WS_DISABLED | BS_PUSHBUTTON | WS_VISIBLE, x,y,w,h, ghMainWnd, NULL, ghInstance, NULL); // set the font SendMessage(pAddressInfo[dwAddress].hAnswer, WM_SETFONT, (WPARAM)hFont, 0); y += buttony + linegap; } // adjust position of message static control SetWindowPos(GetDlgItem(ghMainWnd, IDC_STATIC1), NULL, xstart,y,0,0, SWP_NOZORDER | SWP_NOSIZE); // adjust the size of th main window SetWindowPos(ghMainWnd, NULL, 0,0,xstart+staticx+buttonx+buttonx+buttongap+50,y+50, SWP_NOZORDER | SWP_NOMOVE); return TRUE; }