Mobile Development - 'Support Side Story'

Broadcasting technical support to Windows Mobile\CE Application Developers to help realizing their potential

Broadcasting technical support to Windows Mobile\CE Application Developers to help realizing their potential

February, 2010

  • Mobile Development - 'Support Side Story'

    Fix released for WM6.x devices with HTC and Samsung drivers about UI slowdown

    • 0 Comments

    A very quick post: if you’re using devices powered with HTC or Samsung drivers (such as also the Sony-Ericsson Xperia X1, for example) and noticed periodic slowdowns of the UI, then you may benefit of the following fix: Some GUI interactions no longer work correctly on Windows Mobile 6.1 devices and Windows Mobile 6.5 devices. This is not strictly required, so make sure to read the Symptoms section. And please remember to fill out the feedback at the bottom of the page! Happy

    Cheers,
    ~raffaele

  • Mobile Development - 'Support Side Story'

    Silverlight for Windows Embedded CE 6.0: First sample codes (stress and multiple instances of a control in separate DLL)

    • 7 Comments
    • Intro about SWE (Silverlight for Windows Embedded)
    • Sample code for simple starting stress-test
    • Sample code showing multiple instances of a control, implemented as separate DLL

    sweUIs 

    Part of my job as Support Engineer is to handle requests coming from ISVs developing applications for Windows CE: this is not always an easy task because contrarily to Windows Mobile, for which OEMs must stick to some specifications (otherwise their OS would fail the “Windows Mobile Logo Test Kit”), when it comes to Windows CE (or “Windows Embedded CE”, you know what I mean…) the OEMs have the complete freedom of developing whatever module or driver to be included in their OS, so in some cases the only option to understand a problem’s cause or to reach a specific goal programmatically, is to engage the OEM. I surely have already talked about this in the past… Happy

    So, requests from ISVs targeting Windows CE are rare compared to the ones for Windows Mobile, but after the release of Windows Embedded CE 6.0 R3 (“Cashmere”) Microsoft did on late September 2009, I’ve started handling some very interesting cases about Silverlight for Windows Embedded (“SWE” from now on). During past months many have discussed about this topic and how “particular” it seems to be programming for Silverlight in C++ instead that in .NET… so I’d like to share some info and sample code that I’ve personally been using so far.

    Before doing that, even just because this is the very first post about SWE I’m writing, let me digress a bit so that we can all be on the same page. Above all, if you have no idea what SWE is at all about, then I think that nothing else than watching it in action would help on describing it, and apart from some other videos from latest conferences we may benefit of a 15' minutes-long demo recorded by Mike Hall back in June 2009 together with Jeff MacDuff and Todd Segal, available thru his post Creating compelling user interfaces for embedded devices (even if Jeff and Todd paid attention not to disclose the “name” of the underlying technology, we now know it’s SWE! Hot).

    So, the idea is very simple: just divide the job done by designers and by developers and let everyone do what is good at. Designers have their tools to develop the UI (for example, Expression Blend) and developers have theirs to develop the Business Logic (for example, Visual Studio). Developers won’t even need to know how the UI looks like in fact (my sample code below are an example for that). More about this idea is available through the document Introduction to Application Development with Silverlight for Windows Embedded.

    The important thing is that those out-of-browser applications are possible through a powerful engine, i.e. the XAML Runtime, that takes care of glueing the UI to the code-behind by supporting a subset of Silverlight 2 XAML. The object model to programmaticaly interact with such runtime is native.

    Other introductive videos, as reported by Olivier Bloch in his post:

    Now: imagine you’re a Windows Mobile developer or in any case not a CE6.0 OEM – this means you don’t have the tools to even start investigating how programming for SWE looks like. If that’s the case, remember that precisely as it was for its predecessors, also Windows Embedded CE 6.0 R3 has its own evaluation edition that you can freely download and that is fully functional for 6 months. Note that this is no longer a separate IDE as it was for Windows CE 5.0: it’s an addin that integrates with VS2005 SP1 – follow all the steps here. The evaluation copy includes the software tool for interaction design, Expression Blend 2.0 and gives you the ability to create a so-called “Private SDK” related to the XAML Runtime-powered CE image (the required component is SYSGEN_XAML_RUNTIME) and containing a Device Emulator image targetable from VS2008 directly.

    Still about tools, Olivier Bloch explained here why there’s a lack of really tied integration between the UI and the code-behind: “[…] Well here is the answer: there has been some decisions made to make this technology available as is because our customers were in urgent need of this technology.”. I’ve not yet personally tested it, but this missing functionality seems to be provided by the (Italian! Winking) Microsoft MVP Walter Minute through his XAML2CPP tool.

    So, after this quick intro, what I’d like to share here is:

    1. A starting sample code useful for testing purposes
    2. A sample showing how to use multiple instances of a SWE Custom User Control library (DLL separate from the SWE Application)

    The sample is very easy to read and I used it as starting poing to run specific tests, for example about automating resources-stressing. The code is based on the documentation Create a Silverlight for Windows Embedded Application:

    //global vars
    IXRStackPanelPtr m_pStackPanel; 
    IXRApplicationPtr m_pApp;
     
     
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
    {
        int exitCode                    = 0;
        HRESULT hr                      = S_FALSE;
        IXRVisualHostPtr vhost          = NULL;
        IXRFrameworkElementPtr root     = NULL;
     
        //Initialize Xaml Runtime
        if (!XamlRuntimeInitialize())
            return -1;
     
        //Get XRAppInstance
        hr = GetXRApplicationInstance(&m_pApp);
        CHR(hr);
     
        XRWindowCreateParams wp;
        ZeroMemory(&wp, sizeof(XRWindowCreateParams));
        wp.Style       = WS_OVERLAPPED | WS_SYSMENU;
        wp.pTitle      = L"SWE Testing";
        wp.Left        = 0;
        wp.Top         = 0;
     
        //Retrieve resource associated to the PAGE.XAML file
        XRXamlSource xamlsrc;
        xamlsrc.SetResource(hInstance,TEXT("XAML"), MAKEINTRESOURCE(IDR_XAML_PAGE));
     
        //Create host
        hr = m_pApp->CreateHostFromXaml(&xamlsrc, &wp, &vhost);
        CHR(hr);
     
        //Get Root Element of the XAML
        hr = vhost->GetRootElement(&root);
        CHR(hr);
        
        //Get the Stack Panel
        hr = root->FindName(TEXT("LayoutRoot"), &m_pStackPanel); //Grid = StackPanel
        CHR(hr);
     
        //TODO: Add test code here
        //For automation purposes, create a Timer
        //Automation testing code in TimerProc function
        Timer = SetTimer(0, 0, 250, TimerProc);
     
        UINT exitcode;
        hr = vhost->StartDialog(&exitcode);
        CHR(hr);
     
    Exit:
        XamlRuntimeUninitialize();
     
        return (int)hr;
    }
     
     
    void CALLBACK TimerProc(HWND hwnd, UINT, UINT, DWORD dwTime)
    {
        //TODO: Automation code here
        //... 
     
        //Just to see the UI refreshed
        InvalidateRect(GetActiveWindow(), NULL, TRUE);
    }

    In another case the ISV was interested on maintaining main application (or main page) separate from the control DLLs, so that he could re-use the control separately by other applications. Also, he was interested on having multiple instances of the same control within the same application.

    What I could learn through this Service Request is that the XAML Runtime takes care of (quite) everything for you! Love Struck I left many comments in the sample code (for me at least!) but probably the most important bit here is that a control class is derived from XRCustomUserControlImpl<Class [,Interface]>, therefore it’s an abstract class hence can’t be instantiated and needs to have static functions only. This means that you can’t really talk about “visual controls hosted in an application” – what happens instead is that the XAML Runtime executes and renders different bits of XAML code. So, look at “controls” as “chunks of XAML” (this is not really object-oriented…). This also means that before using the control, you need to initialize it becasue before calling IXRApplication::CreateHostFromXaml from the application EXE code (note closely where CreateHostFromXaml is called in WinMain), the XAML Runtime must know all the associations between Classes and XAML Namespaces.

    In other words, the “initialization” of a control should be done in2 steps:

    1. Set up the global var for the application instance so that Application’s EXE and Control’s DLL share the same and secondly invoke .Register on the control so that the XAML Runtime knows how to associate XAML Namespace and control class [in the sample, this is MyControl::MyControlPreInitialize]
    2. Initialization of the control, for example in terms of delegates associated to its events [in the sample, this is MyControl::MyControlInitialize]
    //CONTROL CLASS DECLARATION 
    class __declspec(uuid("{1F8F37C1-CA69-4894-A6AA-8667F9387E2E}")) //Change GUID!
    MyControl : public XRCustomUserControlImpl<MyControl>
    {
        public:
            static HRESULT GetXamlSource(XRXamlSource* pXamlSource);
            static HRESULT Register(HINSTANCE hInstance);
     
            //Added these functions so that they can be exported through a .LIB (created by adding a .DEF to the project)
            //Any exe application using this control will need to link the .LIB
            static HRESULT MyControlInitialize(IXRFrameworkElementPtr root, LPCTSTR pszControlClassName);
            static HRESULT MyControlPreInitialize(HINSTANCE hInstance);
    };
     
     
    //*************************************************************************************
     
    //Standard implementation of XRCustomUserControlImpl<>.GetXamlSource
    HRESULT MyControl::GetXamlSource(XRXamlSource* pXamlSource)
    {
        //Retrieve the resource (the XAML file) from the DLL Module
        pXamlSource->SetResource((HINSTANCE)GetModuleHandle(lpThisModule), TEXT("XAML"), MAKEINTRESOURCE(IDR_XAML_MYCTRL));
        return S_OK;
    }
     
    //Standard implementation of XRCustomUserControlImpl<>.Register
    HRESULT MyControl::Register(HINSTANCE hInstance)
    {
        HRESULT hr = S_FALSE;
        hr = XRCustomUserControlImpl::Register(__uuidof(MyControl), L"MyControl", L"clr-namespace:SLForEmbedded");
        CHR(hr);
     
    Exit:
        return hr;
    }
     
     
    // Setup the global var for the application instance and invoke .Register on the control.
    HRESULT MyControl::MyControlPreInitialize(HINSTANCE hInstance)
    {
        s_appInstance = hInstance;
     
        HRESULT hr = S_FALSE;
     
        /*LEFT FOR COMMENTING PURPOSES, IN CASE THE .XAML WILL CONTAIN IMAGE or FONT <Application.Resources>
        ////Initialize Xaml Runtime --> If needed, then add MyControl::UnInitialize
        //if (!XamlRuntimeInitialize())
        //    return -1;
        ////1. Get XRAppInstance 
        //IXRApplicationPtr app;
        //hr = GetXRApplicationInstance(&app);
        //CHR(hr);
        //2. Add control resource 
        /*This is needed when there are resources within the XAML of type XAML_RESOURCE
        //hr = app->AddResourceModule(hInstance);
        //CHR(hr);
        
        // ONLY IF <Application.Resources> IN THE APPLICATION's XAML:
        //The following code shows an example of XAML markup that must be parsed 
        //into a resource dictionary:
        //<Application.Resources>
        //    <SolidColorBrush x:Key="Text_DefaultColor" Color="#FF292F33"/>
        //</Application.Resources>
        //
        //IXRResourceDictionary* pResourceDictionary;
        //XRXamlSource Source;
        //Source.SetFile(MAKEINTRESOURCE(IDR_XAML_MYCONTROL));
        //hr = app->LoadResourceDictionary(&Source, &pResourceDictionary);
        //CHR(hr);
        //hr = app->GetResourceDictionary(&pResourceDictionary);
        //CHR(hr);
        */
     
     
        //3. Register control's Namespace
        hr = MyControl::Register(hInstance);
        CHR(hr);
     
    Exit:
        return hr;
    }
     
     
    //Initialize the control and event handlers
    HRESULT MyControl::MyControlInitialize(IXRFrameworkElementPtr root, LPCTSTR pszControlClassName)
    {
        HRESULT hr = S_FALSE;
     
        IXRControlPtr m_myUserControl;
        BtnEventHandler m_btnUserControlHandler;
        IXRDelegate<XRMouseButtonEventArgs>* m_btnUserControlDelegate;
        IXRButtonBasePtr m_btnUserControl;
     
        //hr = root->FindName(TEXT("MyUserControl"), &m_myUserControl);
        hr = root->FindName(pszControlClassName, &m_myUserControl);
        CHR(hr);
     
        //m_btnUserControlHandler.m_StoryboardPaused = FALSE;
        //BtnEventHandler::m_StoryboardPaused = FALSE;
        m_StoryboardPaused = FALSE;
     
        hr = m_myUserControl->FindName(TEXT("btnUserControl"), &m_btnUserControl);
        CHR(hr);
     
        hr = CreateDelegate(&m_btnUserControlHandler, &BtnEventHandler::btnUserControlOnClick,&m_btnUserControlDelegate);
        CHR(hr);
     
        hr = m_btnUserControl->AddClickEventHandler(m_btnUserControlDelegate);
        CHR(hr);
     
    Exit:
        return hr;
    }
     
     
    //TODO: Add event handler code
    class BtnEventHandler
    {
    public:
        HRESULT btnUserControlOnClick(IXRDependencyObject* pSender, XRMouseButtonEventArgs* pArgs)
        {
            MessageBox(GetActiveWindow(), L"User Control button clicked.", L"Event Handler", MB_OK | MB_ICONINFORMATION);
            return S_OK;
        }
    };

    For the application:

    //Application's MAIN Entry Pointint WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
    {
        int exitCode                    = 0;
        HRESULT hr                      = S_FALSE;
        IXRApplicationPtr app           = NULL;
     
        //(let's keep it simple here...)This is not needed when the XAML has not <Application.Resources>
        //IXRResourceManager* pResourceMgr = NULL;
        IXRVisualHostPtr vhost          = NULL;
        IXRFrameworkElementPtr root     = NULL;
        IXRButtonBasePtr btn            = NULL;
     
        //showing a page with its own button control
        //<Button Margin="200,200,200,200" Content="Button" x:Name="MyButton"/>
        IXRDelegate<XRMouseButtonEventArgs>* clickdelegate = NULL;
        PageBtnEventHandler btnHandler;
     
     
        //Initialize Xaml Runtime
        if (!XamlRuntimeInitialize())
            return -1;
     
        //Get XRAppInstance
        hr = GetXRApplicationInstance(&app);
        CHR(hr);
     
        ////An application should register an IXRResourceManager object for Silverlight
        ////before the application executes any functionality related to loading resources.
        ////<Image Source=foo.jpg" />
        ////<TextBlock x:Name="GridTextBlock10" Grid.Row="1" Grid.Column="0" FontSize="14" FontFamily="castelar.ttf#Castellar" FontStyle='Italic'  Text='Hello'/>
        // -->
        ////IXRResourceManager* pResourceMgr;
        //hr = app->RegisterResourceManager(pResourceMgr);
        //CHR(hr);
     
        //This is needed when there are resources within the XAML of type XAML_RESOURCE
        //hr = app->AddResourceModule(hInstance);
        //CHR(hr);
     
        XRWindowCreateParams wp;
        ZeroMemory(&wp, sizeof(XRWindowCreateParams));
        wp.Style       = WS_OVERLAPPED | WS_SYSMENU;
        wp.pTitle      = L"SWE Testing";
        wp.Left        = 0;
        wp.Top         = 0;
     
        //Retrieve resource associated to the PAGE.XAML file
        XRXamlSource xamlsrc;
        xamlsrc.SetResource(hInstance,TEXT("XAML"), MAKEINTRESOURCE(IDR_XAML_PAGE));
     
        //Register control with application instance
        //This method added so that the control code can do init and call Register on the control
        //so that the control's class is tied to the XAML Runtime's namespace
        //and this is done in control's DLL code
        hr = MyControl::MyControlPreInitialize(hInstance);
        CHR(hr);
     
        //Create host
        hr = app->CreateHostFromXaml(&xamlsrc, &wp, &vhost);
        CHR(hr);
     
        //Get Root Element of the XAML
        hr = vhost->GetRootElement(&root);
        CHR(hr);
     
        //MyButton is a control directly defined and used by the application
        //defined in PAGE.XAML, look at x.Name of the <Button> element
        //left here just to show a control example inside the same application
        hr = root->FindName(TEXT("MyButton"), &btn);
        CHR(hr);
        hr = CreateDelegate(&btnHandler,&PageBtnEventHandler::OnClick,&clickdelegate);
        CHR(hr);
        hr = btn->AddClickEventHandler(clickdelegate);
        CHR(hr);
     
     
        //MyControl is the control exported by SilverlightControl DLL project
        //deriving from XRCustomUserControlImpl<MyControl>, it's an abstract class
        //i.e. can't be instantiated and needs to have STATIC functions only
        hr = MyControl::MyControlInitialize(root, TEXT("MyUserControl"));
        CHR(hr);
     
        hr = MyControl::MyControlInitialize(root, TEXT("MyUserControl2"));
        CHR(hr);
     
     
        UINT exitcode;
        hr = vhost->StartDialog(&exitcode);
        CHR(hr);
     
    Exit:
        RELEASE_OBJ(clickdelegate);
        XamlRuntimeUninitialize();
     
        return (int)hr;
    }
     
     
    //Handler of a button included directly in the EXE
    class PageBtnEventHandler
    {
        public:
            HRESULT OnClick(IXRDependencyObject* source,XRMouseButtonEventArgs* args)
            {
                MessageBox(NULL,TEXT("Click!"),TEXT("Button"),MB_OK);
                return S_OK;
            }
    };

    Note how straightforward the XAML for the page (main application) is – quite intuitively, it just contains one line for each control:

    <UserControl
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="SLForEmbedded.Page"
        xmlns:SLForEmbedded="clr-namespace:SLForEmbedded"
        Width="640" Height="480">
     
      <Grid x:Name="LayoutRoot" Background="White">
        <Button Margin="200,200,200,200" Content="Button" x:Name="MyButton"/>
        <SLForEmbedded:MyControl HorizontalAlignment="Left" VerticalAlignment="Top" x:Name="MyUserControl"/>
        <SLForEmbedded:MyControl HorizontalAlignment="Right" VerticalAlignment="Top" x:Name="MyUserControl2"/>
      </Grid>
    </UserControl>

    The XAML for the control may be useless for this post, however for the sake of completeness:

    <UserControl
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        x:Class="SLForEmbedded.MyControl">
        <Grid x:Name="LayoutRoot">
            <Button Height="25" Width="125" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,10,0,0" Content="Click User Control" x:Name="btnUserControl"/>
        </Grid>
    </UserControl>

    As usual, macros were:

    #pragma once
     
    #ifndef MACROS
    #define MACROS
     
    #define _ExitLabel Exit
    #define _ExitCode exitCode
     
    #define CHR(hResult) \
        if(S_OK != hResult) { hr = (hResult); goto _ExitLabel;} 
     
    #define RELEASE_OBJ(s)  \
        if (NULL != s)      \
        {                   \
            s->Release();   \
            s = NULL;       \
        }
    #endif

     

     

     

     

    Hope this may help out developers learning SWE (like myself… Winking)

    Cheers,
    ~raffaele

  • Mobile Development - 'Support Side Story'

    Windows Mobile: programmatically intercepting phone calls

    • 3 Comments
    • Supported?? Expectations about cprog.exe
    • How Communicator Mobile works with “Work Phone calls
    • Sample code to “extend” the State&Notification Broker for IGNORED phone calls

    I recently worked with some ISVs asking support about this, then also answered to a MSDN Forum thread about the same, so I’d say it may be worth to mention it here as well for Community reference. This happens to be one of those common questions that fall under the umbrella of the “not supported as OEM-dependent” or “not supported as it’s not meant to work that way”. I’ve talked about this some times on my blog, for example regarding Wireless programming, Drivers, Kiosk applications, Hooks

    The issue is about “intercepting” incoming phone calls and prevent the native UI dialog provided by OS application cprog.exe to take care of it: there are some OEMs that customized the user interface for this phone application, so ISVs may think this could be a common task to achieve? One could say: “Wouldn’t be sufficient to kill cprog.exe?”. Or, in any case, is there a way for ISV Application Developers to programmatically control incoming and outgoing calls *before* or *instead* the Operating System itself?

    The answer is no, but you may possibly find “creative” ways to achieve the same visual effect your application’s final users may look for. The main point is that interfering with the OS Native Phone application (cprog.exe) is not supported for ISV Application Developers, contrarily to Device Manufacturers (OEMs) who actually build their own OS based on the Windows Mobile platform and therefore can develop and include whatever they want on their Operating Systems, as long as they fulfill the “Windows Mobile Logo Test Kit” (so that their OS is certified as “Windows Mobile”).

    As an example, a thing that the Windows Mobile platform doesn’t test or even consider is killing cprog.exe, which is the process underlying the Phone UI Application. Once the device is booted, cprog.exe executes and even when there is a Out-Of-Memory condition (so that the Shell starts asking every application to shut down, accordingly to this old post from WinMo Dev Team), cprog.exe is not terminated. So the current OS design does not expect it to be killed forcefully.

    In some cases, to meet this requirement you would need to cooperate with the Device OEM\OEMs so that he\they can sponsor you as their “Windows Mobile In-ROM ISV”. By doing so, you would have access to all the tools a Device Manufacturer has, including the knowledge on the RIL Driver (Radio Interface Layer), developed by the OEM, that is responsible to handle every radio-related communication of the device (phone call, GPRS data session, …). So, to intercept phone calls before cprog.exe does it, you should do it at RIL Driver level, and Microsoft has no idea about how the OEM implemented it.

    In other cases, you can let cprog.exe do its work, and play with Windows APIs to hide its window and take control of the phone call via TAPI (Telephony API): that is the “supported” way.

     

    You may ask: then how does Microsoft’s Communicator Mobile 2007 R2 implement "Work Phone calls”? How can it intercept phone calls, if this is OEM-dependent? Well, actually it doesn’t. CoMo is not working with the RIL to intercept phone-voice calls. Documentation is available through the following Microsoft Office Communications Server 2007 R2 – Technical Reference (section “Outside Voice Control”):

    […] For inbound calls, Office Communications Server 2007 R2 sends a SIP Invite to all registered SIP endpoints of the user including the user’s Communicator Mobile (2007 R2 release) client running on the phone, over the data channel.   Office Communications Server 2007 R2 subsequently initiates an outbound PSTN/mobile network call through Office Communications Server 2007 R2 Mediation Server to the user’s mobile phone number.

    So as you can see, when dealing with “Communicator phone-calls” we’re not precisely working with “Voice-Phone calls”: it’s about SIP requests sent over the Data Channel, not the Voice-one.

     

    Among the “supported” ways to deal with phone calls, developers can rely on the State&Notification Broker states like PhoneCallTalking and PhoneIncomingCall: a state that may be missing is about the last call being ignored. Here it is a managed sample code showing a possible way to achieve this (to be tested… as usual! Smile):

     

    public Form1()
    {
        InitializeComponent();
        _PhoneIncomingCall.Changed += new ChangeEventHandler(_PhoneIncomingCall_Changed);
    }
     
    bool bTalking = false;
    bool bIncoming = false;
    bool bIgnoring = false;
     
    SystemState _PhoneCallTalking = new SystemState(SystemProperty.PhoneCallTalking);
    SystemState _PhoneIncomingCall = new SystemState(SystemProperty.PhoneIncomingCall);
     
     
    void _PhoneCallTalking_Changed(object sender, ChangeEventArgs args)
    {
        bTalking = (0 != (int)args.NewValue);
     
        //_PhoneIncomingCall.Changed += new ChangeEventHandler(_PhoneIncomingCall_Changed);
        _PhoneCallTalking.Changed -= _PhoneCallTalking_Changed;
    }
     
    void _PhoneIncomingCall_Changed(object sender, ChangeEventArgs args)
    {
        bIncoming = (0 != (int)args.NewValue);
        if (bIncoming)
        {
            //_PhoneIncomingCall.Changed -= _PhoneIncomingCall_Changed;
            _PhoneCallTalking.Changed += new ChangeEventHandler(_PhoneCallTalking_Changed);
        }
        else
        {
            //answered: bTalking = true
            //ignored: bTalking = false
            bIgnoring = !bTalking;
            MessageBox.Show(string.Format("IgnoredCall: {0}", bIgnoring.ToString()));
     
            //reset vars
            bIncoming = false;
            bIgnoring = false;
            bTalking = false;
        }
    }

     

    Cheers,
    ~raffaele

Page 1 of 1 (3 items)