Architecture - DevSource
DevSource: Microsoft Developer Resource DevSource Home Sponsored by Microsoft Home Add Ons Architecture Languages Techniques Using VS Forums
Home arrow Architecture arrow Page 4 - Creating a custom Phone App on Windows Mobile (Part 1)
Creating a custom Phone App on Windows Mobile (Part 1)
By Jeff Cogswell

Rate This Article: Add This Article To:

Creating a custom Phone App on Windows Mobile (Part 1) - The Main Form
( Page 4 of 5 )

The Main Form that calls the TAPI functions

Now you need a user interface. I’m keeping this one very rudimentary for now. In the next article I’ll take it to the next level. Go back to the form and remove the button and code you added before. (Delete the button itself, and delete the entire button1_Click function, as well as the using line that you added.) Your code will be back to this:

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace MobilePhone
{
    public partial class Form1 : Form
    {
        public Form1() {
            InitializeComponent();
        }
        }
    }
}
ADVERTISEMENT

And your form will be blank again. Now add two new buttons (I’m using different names for them; that’s why I had you delete the original button), and two text boxes, one above the buttons (for the phone number) and one below the buttons. Set the upper text box’s Name property to NumberTextbox. Set the lower text box’s Name property to DebugTextbox, its Multiline property to true, and its Scrollbars property to both. Set the left button’s Name to CallButton and its Text to Call. Set the right button’s Name to HangupButton and its Text to Hang up. Your form should look like this:

The lower textbox will be used to output useful debug information for us programmers. (An actual final application shouldn’t have that, of course. I shouldn’t have to say this, but please don’t confuse your users by including debugging data like this in the final program.)

Now without further ado, here’s the code. This code includes an initialization function that sets up all the lines and what-not; I called the function initPhone. There’s also a FormClose handler where I put the code to close up the TAPI functions.

Then I added a handler to each button; one to make the call, and the other to hang up.

Finally, you’ll notice there’s a callback function that’s really not being used in this version of the program. The function is required by the Tapi functions, and it receives messages from the Tapi system. In the next version of this program (and article) I’ll be making more use of this callback. Note also that to pass the callback, I had to create a C# delegate.

Here’s the code:

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace MobilePhone
{
    public partial class Form1 : Form
    {
        // These must be public or the DllImport calls will fail

        public Form1() {
            InitializeComponent();
        }

        public void linecallback(int device, int Msg,
            int callbackInst, int param1, int param2, int param3) {
        }

        private void logstring(string str) {
            DebugTextbox.Text += str + "\r\n";
        }

        private bool haveAppHandle = false;
        private IntPtr appHandle;
        private int VoiceLineId = -1;
        private IntPtr callHandle;
        private int version;

        private void initPhone() {
            // Don't pass linecallback function directly: make a delegate
            MobileTapi.TapiFunctions.lineCallbackFunc mycallback =
                new MobileTapi.TapiFunctions.lineCallbackFunc(linecallback);

            // Initialize Tapi
            int numdevs;
            int result;
            result = MobileTapi.TapiFunctions.lineInitialize(out appHandle,
                MobileTapi.TapiFunctions.GetModuleHandle(0),
                mycallback, "DevSource Phone", out numdevs);
            if (result != 0) {
                MessageBox.Show(result.ToString());
            }
            else {
                haveAppHandle = true;
                MobileTapi.LineExtensionId extid = new MobileTapi.LineExtensionId();
                MobileTapi.LineCaps caps = new MobileTapi.LineCaps();
                //caps.extra = new StringBuilder(500);
                logstring(String.Format("There are {0} devices", numdevs));
                for (int i = 0; i < numdevs; i++) {
                    result = MobileTapi.TapiFunctions.lineNegotiateAPIVersion(appHandle,
                        i, 0x00020000, 0x00020000, out version, ref extid);
                    if (result != 0) {
                        logstring("Error: " + result.ToString());
                    }
                    else {
                        logstring("===============");
                        // Device capabilities
                        caps.dwTotalSize = 792;
                        result = MobileTapi.TapiFunctions.lineGetDevCaps(
                            appHandle, i, version, 0, ref caps);

                        logstring("provider:" + caps.getProviderInfo());
                        logstring("switch:" + caps.getSwitchInfo());
                        logstring("line:" + caps.getLineName());
                        logstring("devClasses:" + caps.getDeviceClasses());
                        logstring("MediaModes:" + caps.getMediaModeAsHex());

                        //logstring(caps.dwMediaModes.ToString());
                        if ((caps.dwMediaModes & 
                            MobileTapi.TapiFunctions.LINEMEDIAMODE_INTERACTIVEVOICE)
                            == MobileTapi.TapiFunctions.LINEMEDIAMODE_INTERACTIVEVOICE) {
                            logstring("Found voice line");
                            VoiceLineId = i;
                        }
                    }
                }
            }
        }

        private void Form1_Closed(object sender, EventArgs e) {
            if (appHandle != null) {
                MobileTapi.TapiFunctions.lineShutdown(appHandle);
            }
        }

        private void CallButton_Click(object sender, EventArgs e) {
            if (haveAppHandle == false) {
                initPhone();
            }
            // If the initialization worked, then call
            if (VoiceLineId > -1) {
                IntPtr hline;
                int result = MobileTapi.TapiFunctions.lineOpen(
                    appHandle, VoiceLineId, out hline, 
                    version, 0, 0, 6, // 6 = all privileges
                    MobileTapi.TapiFunctions.LINEMEDIAMODE_INTERACTIVEVOICE, 0);
                if (result != 0) {
                    MessageBox.Show("lineOpen error: " + result.ToString());
                }
                else {

                    result = MobileTapi.TapiFunctions.lineMakeCall(
                        hline, ref callHandle, NumberTextbox.Text, 0, 0);
                    if (result < 0) {
                        MessageBox.Show("Call error: " + result.ToString());
                    }
                }
            }
        }

        private void HangupButton_Click(object sender, EventArgs e) {
            // Hang up
            MobileTapi.TapiFunctions.lineDrop(callHandle, 0, 0);
        }
    }
}

Make sure you understand what the initPhone function is doing, as that’s the heart of the TAPI calls. I’m following the order outlined earlier, starting with a call to lineInitialize. That function initializes the system and fills in a variable that tells me how many line devices are available. (See my numdevs variable.) I then iterate through each line, calling lineGetDevCaps until I locate the voice one. (Meanwhile, I display some interesting debugging information about the lines. Note that the structure I’m using has to have some extra space that the TAPI system fills in primarily with text characters; this is what I display as debugging information.)

And that’s it; once I have a line with the voice features I need, I can make a call. This happens in the CallButton’s handler. (This handler is what calls my initPhone function as well.) The handler opens a line, and then places the call.

The handler for the other button ends the call by simply calling LineDrop. That’s it.



 
 
>>> More Architecture Articles          >>> More By Jeff Cogswell
 



HD VOIP Has Arrived (with Tony Konstner)

Play Video >

All Videos >

Google and blonde jokes?

Read now >

Favorite books!

Read now >

View Now
DevSource RSS FEEDS
XML Want an easy way to keep up with breaking tech news? And the Get DevSource headlines delivered to your desktop with RSS.