Programmatically getting the phone number of a device

One question that is often asked is "How do I get the phone number of my device?"  Usually the person asking doesn't really need the phone number, they just need some number that uniquely identifies the device.  While there does exist an IOCTL to get a unique ID (not the phone number), only apps that are privileged are allowed to call it.  (On a closed Smartphone, that means only apps that have been signed with a certificate that is in the device's privileged certificate store.)   As an alternative, the code below shows how to get the phone number (without needing to be privileged).



#include "stdafx.h"
#include "windows.h"
#include "tapi.h"
#include "tsp.h"

#define EXIT_ON_NULL(_p)     \
    if (_p == NULL)    \
{   \
    hr = E_OUTOFMEMORY; \
    goto FuncExit; \

#define EXIT_ON_FALSE(_f)     \
    if (!(_f))    \
{   \
    hr = E_FAIL; \
    goto FuncExit; \

#define MAX(i, j)   ((i) > (j) ? (i) : (j))

#define TAPI_API_LOW_VERSION   0x00020000
#define TAPI_API_HIGH_VERSION  0x00020000

#define CAPS_BUFFER_SIZE    512


HRESULT SHReadLineAddressCaps(LPTSTR szNumber, UINT cchNumber, PDWORD pdwCallFwdModes, UINT nLineNumber)
    HRESULT  hr = E_FAIL;
    LRESULT  lResult = 0;
    HLINEAPP hLineApp;
    DWORD    dwNumDevs;

    DWORD dwTAPILineDeviceID;
    const DWORD dwAddressID = nLineNumber - 1;

    liep.dwTotalSize = sizeof(liep);

    if (SUCCEEDED(lineInitializeEx(&hLineApp, 0, 0, TEXT("ExTapi_Lib"), &dwNumDevs, &dwAPIVersion, &liep)))

        BYTE* pCapBuf = NULL;
        DWORD dwCapBufSize = CAPS_BUFFER_SIZE;
        LINEEXTENSIONID  LineExtensionID;
        LINEDEVCAPS*     pLineDevCaps = NULL;
        LINEADDRESSCAPS* placAddressCaps = NULL;

        pCapBuf = new BYTE[dwCapBufSize];

        pLineDevCaps = (LINEDEVCAPS*)pCapBuf;
        pLineDevCaps->dwTotalSize = dwCapBufSize;

        // Get TSP Line Device ID
        dwTAPILineDeviceID = 0xffffffff;
        for (DWORD dwCurrentDevID = 0 ; dwCurrentDevID < dwNumDevs ; dwCurrentDevID++)
            if (0 == lineNegotiateAPIVersion(hLineApp, dwCurrentDevID, TAPI_API_LOW_VERSION, TAPI_API_HIGH_VERSION,
                &dwAPIVersion, &LineExtensionID))
                lResult = lineGetDevCaps(hLineApp, dwCurrentDevID, dwAPIVersion, 0, pLineDevCaps);

                if (dwCapBufSize < pLineDevCaps->dwNeededSize)
                    delete[] pCapBuf;
                    dwCapBufSize = pLineDevCaps->dwNeededSize;
                    pCapBuf = new BYTE[dwCapBufSize];

                    pLineDevCaps = (LINEDEVCAPS*)pCapBuf;
                    pLineDevCaps->dwTotalSize = dwCapBufSize;

                    lResult = lineGetDevCaps(hLineApp, dwCurrentDevID, dwAPIVersion, 0, pLineDevCaps);

                if ((0 == lResult) &&
                    (0 == _tcscmp((TCHAR*)((BYTE*)pLineDevCaps+pLineDevCaps->dwLineNameOffset), CELLTSP_LINENAME_STRING)))
                    dwTAPILineDeviceID = dwCurrentDevID;

        placAddressCaps = (LINEADDRESSCAPS*)pCapBuf;
        placAddressCaps->dwTotalSize = dwCapBufSize;

        lResult = lineGetAddressCaps(hLineApp, dwTAPILineDeviceID, dwAddressID, dwAPIVersion, 0, placAddressCaps);

        if (dwCapBufSize < placAddressCaps->dwNeededSize)
            delete[] pCapBuf;
            dwCapBufSize = placAddressCaps->dwNeededSize;
            pCapBuf = new BYTE[dwCapBufSize];

            placAddressCaps = (LINEADDRESSCAPS*)pCapBuf;
            placAddressCaps->dwTotalSize = dwCapBufSize;

            lResult = lineGetAddressCaps(hLineApp, dwTAPILineDeviceID, dwAddressID, dwAPIVersion, 0, placAddressCaps);

        if (0 == lResult)
            if (szNumber)
                szNumber[0] = _T('\0');

                EXIT_ON_FALSE(0 != placAddressCaps->dwAddressSize);

                // A non-zero dwAddressSize means a phone number was found
                ASSERT(0 != placAddressCaps->dwAddressOffset);
                PWCHAR tsAddress = (WCHAR*)(((BYTE*)placAddressCaps)+placAddressCaps->dwAddressOffset);

                StringCchCopy(szNumber, cchNumber, tsAddress);

            // Record the allowed forwarding modes
            if (pdwCallFwdModes)
                *pdwCallFwdModes = placAddressCaps->dwForwardModes;

            hr = S_OK;

        delete[] pCapBuf;
    } // End if ()


    return hr;


// szNumber - Out Buffer for the phone number

//cchNumber - size of sznumber in characters

// nLineNumber - which phone line (1 or 2) to get the number for
HRESULT SHGetPhoneNumber(LPTSTR szNumber, UINT cchNumber, UINT nLineNumber)
    return SHReadLineAddressCaps(szNumber, cchNumber, NULL, nLineNumber);

Comments (22)

  1. meep says:

    How about some bluetooth things using the WinSock stack new BT APIs? Is Bluetooth being wrapped in 2.0 Base Class Libraries in .NET for this? If not, do you know a good .NET wrapper for Winsock bluetooth APIs?

  2. peterept says:

    Does this only work with the SIM cards that have the owners phone number stored?

  3. Jim Coburn says:

    Great. Now can you tell how to get current info from the embedded GPS device?

  4. Jeffrey Siegel says:

    It seems like it takes a lot of code in this article to get the phone number. What is the difference with just making a single call to SmsGetPhoneNumber()?

    If you don’t post a reply, is there any chance you could email it to me? – thanks!

  5. Lagur says:

    hi, im not sure why it doesn’t work so i debug it myself and everything is success but ends to EXIT_ON_FALSE(0 != placAddressCaps->dwAddressSize);

    what causes this?

    thanks (progy at

  6. Kyriakos Lakkas says:


    I am new to this stuff, and I just spent all Monday trying to get the code above to compile and link successfully. I am mostly using VB, both 6.0 and .NET, but I also have basic C/C++ knowledge.

    I have on my PC eVC++ 4.0 sp2, and the SDKs for Windows Mobile 2003 based Smartphones and PPC’s.

    After many many moving around the .h files, I managed to compile this code, but I get 6 link errors like the one below:

    error LNK2019: unresolved external symbol _lineShutdown referenced in function "long __cdecl SHReadLineAddressCaps(unsigned short *,unsigned int,unsigned long *,unsigned int)" (?SHReadLineAddressCaps@@YAJPAGIPAKI@Z)

    Any help would be GREATLY appreciated.


  7. Kyriakos Lakkas says:

    Adding to the message above,

    please do contact me personally via email if you can help me with this program.


  8. hq says:

    I don’t know why this code runs not stable. Some is good, but some is wrong.

    I got "" always on a Motorola MPX220, and I tried it not 2 lines, but 5 lines. nLineNumber = 1->5.

    Please help me!

  9. Aaron says:

    I can’t get the phone number. I also always got "" on a Motorola MPX220, and I also tried SmsGetPhoneNumber(), it also gave me "" for phone number. Could you tell me why and how to get the phone number correctly.

    BTW, I can get the deviceID using lineOpen(), lineGetGeneralInfo(), but have problem in getting the phone number. It could be great if you can help me for that.

    Please contact me at:



  10. kashish says:


    i have a problem . I am trying to dial up a number using pocket pcs through tapi .

    the problem is everything is going well if i run the application thorough pc the line is dialed and connected and all the callback messages are trapped properly.

    But when i run the similar application on pocket pc using evc++ with all the relevant changes the line is dialed , reaches to call proceeding and then replies Unreachable, which means the remote host is unreachable ,

    messages to callback function.

    plzz reply asap i .thansk in advance

  11. sri2uk says:

    Hi Jason

    IT will be very helpful for me if you can post the full source as a zip file so that I can convert it into a DLL and call it from my managed code can you please do it for me



  12. sridhar says:

    hi jason,

    we r developing an telephony application on windows mobile 5.0 using  .net cf 2.0.we r able to control the speaker phone volume but not able to control the headset volume.

    IT will be very helpful for us if u can help us in this.

  13. SAMEER KARIWAL says:

    HI GUYZ!

    This code works fine on windows mobile smartphone 5.0 emulator.But not working on device.Same case is also happening with SmsGetPhoneNumber().

    can anybody suggest any way out.

  14. bloggerdad says:


    I have problem with enter to this site via GPRS.

    Please, help me.


    Best regards.

  15. Huy says:


    I try to use the code

    but the result is always empty

    could please explain me the reason of this result

  16. Shelly says:

    I tried it. But the result is also always empty. Can someone please help?

  17. Shelly says:

    My email is I tried above sample and the sample from msdn, always get empty phone number. Help is greatly appreciated.

  18. lyndon says:


    I met the question exactly as Kyriakos met.If someone can help me about this,I will be Great appreaciated.

    Thank all very much

  19. Juan says:

    I tried this program in a HP iPAQ Bussines Navigator and always the result is empty. Anyone knows another way to obtain the own phone number?

    I tried the program with the same SIM Card in a HP 6915 and works fine.

  20. Ike says:

    thanks ,starting from your code I can get the imei code using lineGetGeneralInfo().. 😉

  21. Jan says:

    Hello, the own phone number (MSISDN) is assigned to mobile station by operator during outgoing call, therefore you can get the number out of SIM only if operator stored it there.


Skip to main content