views:

3194

answers:

3

I have a Windows Mobile application using the compact framework (NETCF) that I would like to respond to someone pressing the send key and have the phone dial the number selected in my application. Is there a way using the compact framework to trap the send key? I have looked at several articles on capturing keys, but I have not found one that includes the "Send" key.

Update:

I found an article describing SetWindowsHookEx as an undocumented API on Windows Mobile. If this is the case then I really don't want to use it.

SetWindowsHookEx on Windows Mobile

After doing more searching I found out that the "Send" key is called the "Talk" key in Windows Mobile lingo. I then found a blog post about using the SHCMBM_OVERRIDEKEY message to signal the OS to send my app a WM_HOTKEY message when the user presses the Talk key.

Jason Fuller Blog post about using the Talk button

The blog post and the documentation it points to seem like exactly what I'm looking for. I'm unable to find a working example, and I find a lot of people unable to make it work. It also looks like VK_TTALK is not supported in SmartPhones. I would love to hear from someone that actually has this working on both Smartphones and PocketPC phones.

+2  A: 

You can catch all keys in teh worlds (apart from CTRL+ALT+DEL on desktop) via a keyhook:

static extern IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod, uint dwThreadId);

You can use this (or one of the other overrides) in CE via coredll.dll (instead of user32).

However this is not in the .NET Compact Framework and requires a P/Invoke. The best joy with "free" key pressery is Form.KeyPreview = true; and that only gives you the keys that get hit when the form is focused and sometimes nothing when dealing with devices that have special keys (possibly such as your "send" key, as I had a similar situation with "Application1" key on an Intermec unit a while back).

There is however one fantastic alternative, I believe OpenNETCF either already does or will very soon roll with a keyhook (via the WinAPI call above) exposing a delightful managed wrapper. Joy!

If that's not ready yet there is an abundance of articles and code kicking around that demonstrate how to use this via Google so in the worst case scenario you should be able to find something if you perform a search like this. Perhaps something like this?

Quibblesome
Intermecs, oy. There's a little lightbulb button on the CN3 that doubles as the zero numeric key, and I could never figure out a way of accessing its event. Do you know if SetWindowsHook would catch that?
MusiGenesis
Possibly, that's how I shut up the app keys to put one into a "kiosk mode".Intermec seem to have a habit of choosing odd keycodes, on the 730 the top right key "Action" is Enter followed by an F key a bit later, Enter is bottom right so using the keys to do different things is.... erm... fun.
Quibblesome
A: 

Is there some particular reasoning behind not using the designated PhoneMakeCall? It's available for Smartphone and up to Windows Mobile 6 Professional.

Edit: I misread the question a bit. I see now that you wanted to trap the Send event. I let my answer stay, though, for reference.

Johann Gerell
+2  A: 

I can confirm that using SHCMBM_OVERRIDEKEY works on both PPC and SP devices. I have tested it on WM5 PPC, WM5 SP, WM6 PPC, WM6 SP. I have not tried WM6.1 or WM6.5 yet but I kind-of assume that they work since WM6 works.

Also you may need to support DTMF during the call as well?

Since I was writing a LAP dll I followed the following page which you may find useful: LAP Implementation Issues

These examples are in C so you will have to translate them into C#.

To setup trapping of the "talk" key for a specific window you need to do:

SendMessage(SHFindMenuBar(window_hwnd), 
            SHCMBM_OVERRIDEKEY, 
            VK_TTALK, 
            MAKELPARAM((SHMBOF_NODEFAULT|SHMBOF_NOTIFY), (SHMBOF_NODEFAULT|SHMBOF_NOTIFY));

You can turn on/off the trap at any time. To turn the trap off it easy as well:

SendMessage(SHFindMenuBar(window_hwnd), 
            SHCMBM_OVERRIDEKEY, 
            VK_TTALK, 
            MAKELPARAM(0, (SHMBOF_NODEFAULT|SHMBOF_NOTIFY));

To detect when the 'Talk' key is pressed you need to trap the WM_HOTKEY window message on the window proc:

case WM_HOTKEY:
    switch(HIWORD(lParam))
    {
    case VK_TTALK:
        // make ph call
        break;
    }
    return TRUE;

To make a phone call you need to use the "PhoneMakeCall" API:

#include <phone.h>

void MakePhoneCall(const wchar_t* number)
{
    PHONEMAKECALLINFO call;
    memset(&call, 0x0, sizeof(PHONEMAKECALLINFO));
    call.cbSize = sizeof(PHONEMAKECALLINFO);
    call.dwFlags = PMCF_DEFAULT;
    call.pszDestAddress = number;
    PhoneMakeCall(&call);
}

To support DTMF during a phone call you need to track the phone call using SNAPI (I believe there is a C# library to help you out there SystemProperty).

Setup after starting the call:

  #include <snapi.h>
  RegistryNotifyWindow(SN_PHONEACTIVECALLCOUNT_ROOT, SN_PHONEACTIVECALLCOUNT_PATH, SN_PHONEACTIVECALLCOUNT_VALUE, window_hwnd, callback_window_msg_number /*e.g. WM_APP */, 0, NULL, &phone_call_notify_handle);

You will be called back with the window message you supply when the call count changes. You need to read the registry and check that the call count drops to zero. If it does you need to close the SNAPI handle:

RegistryCloseNotification(phone_call_notify_handle);

While in the call send a message to the cprog application with the key that was pressed by the user:

#define WM_CPROG_SEND_VKEY_DTMF (WM_APP+3) // Sends the DTMF tone(s) through to the current call (converting from VKEY to DTMF chars)

  BOOL PhoneSendDTMF(UINT uvKey)
  {
    BOOL bRet = FALSE;
    static HWND s_hwndCProg = NULL;
    TCHAR chDTMF = MapVKeyToChar(uvKey);

    // Attempt to find the cprog window (MSCprog).
    // Try to keep this window handle cached.
    if(NULL == s_hwndCProg || !IsWindow(s_hwndCProg))
    {
      s_hwndCProg = FindWindow(TEXT("MSCprog"), NULL);
    }

    // Send WM_CPROG_SEND_VKEY_DTMF to the CProg window.
    if(NULL != s_hwndCProg)
    {
      bRet = BOOLIFY(PostMessage(s_hwndCProg,
                              WM_CPROG_SEND_VKEY_DTMF, (WPARAM)chDTMF, 0));
    }

    return bRet;
  }
Shane Powell
Thanks, it would be nice if you could post an example.
bruceatk