您的位置:首页 > 运维架构 > 网站架构

[转自微软网站]Making a Phone Call in C#

2005-05-25 21:11 471 查看

Accessing Phone APIs from the Microsoft .NET Compact Framework

  Derek Mitchell
DEVBUZZ.COM, Inc.
April 2003
Applies to:
    Microsoft® .NET Compact Framework 1.0
    Microsoft Visual Studio® .NET 2003
Summary: Learn how to access Phone APIs from your .NET Compact Framework-based application. (11 printed pages)
Download PhoneAPI.msi

Contents

Introduction
Making a Phone Call in C#
Making a Phone Call in VB.NET
Accessing SIM Information in C#
Accessing SIM Information in VB.NET
Code Use
Conclusion

Introduction

The purpose of this article is to provide another example in which calling native Microsoft® Win32® APIs is beneficial within the scope of managed code, written in both C# and VB.NET. Here, we look at common Phone APIs supported by both Pocket PC Phone Editions.

Making a Phone Call in C#

All of the code you will see referenced below is available for download. Making a call is a basic operation; we pass PhoneMakeCall (Win32 API), a string indicating the destination address and any options regarding whether or not we should ask for confirmation before placing the call. We have several declarations to make.private static long PMCF_DEFAULT = 0x00000001;
private static long PMCF_PROMPTBEFORECALLING = 0x00000002;
We next define a structure that will go largely unused for our purposes.private struct PhoneMakeCallInfo
{
public IntPtr cbSize;
public IntPtr dwFlags;
public IntPtr pszDestAddress;
public IntPtr pszAppName;
public IntPtr pszCalledParty;
public IntPtr pszComment;
}
cbSize indicates the size of the PhoneMakeCallInfo structure. dwFlags is an option bit that specifies whether the user should be prompted before the call is placed. pszDestAddress is a pointer to the number to be dialed. pszAppName is currently unsupported. pszCalledParty is optional and can indicate the name of the party to be called and is limited in size. pszComment is currently unsupported. Now we call forth the magic of PInvoke and make a DLLImport call to give us access to our API function PhoneMakeCall.[DllImport("phone.dll")]
private static extern IntPtr PhoneMakeCall(ref PhoneMakeCallInfo ppmci);
We include an auxiliary function for convenience that bypasses the need for a confirm-before-dial Boolean./// <summary>
/// Dials the specified phone number.
/// </summary>
/// <param name="PhoneNumber">Phone number to dial.</param>
public static void MakeCall(string PhoneNumber)
{
MakeCall(PhoneNumber, false);
}
Now let's take a look at what MakeCall does. In short, we break our PhoneNumber parameter (passed in as a string) into a character array.PhoneNumber += '/0';
char[] cPhoneNumber = PhoneNumber.ToCharArray();
We point to the memory address at which the character array appears in memory and decorate this variable with the fixed keyword to indicate we don't want the garbage collector to move its contents within the scope of the fixed brackets. PhoneMakeCallInfo info = new PhoneMakeCallInfo();
info.cbSize = (IntPtr)Marshal.SizeOf(info);
info.pszDestAddress = (IntPtr)pAddr;

if (PromptBeforeCall)
{
info.dwFlags = (IntPtr)PMCF_PROMPTBEFORECALLING;
}
else
{
info.dwFlags = (IntPtr)PMCF_DEFAULT;
}
We create a new PhoneMakeCallInfo structure instance and indicate whether or not we want confirmation before dialing and we insert the phone number into the pszDestAddress field. Finally, we pass the structure instance into PhoneMakeCall as a reference. This function is decorated with the unsafe keyword because it utilizes pointers and direct memory access.

Making a Phone Call in VB.NET

Our code for MakeCall in VB.NET ports with few major modifications from the C# code detailed above. We use IntPtr variables to hold value for most of our functionality. One difference is that in the forward declaration of MakeCall, we specify that the PhoneMakeCallInfo structure instance will be passed in as a reference. <System.Runtime.InteropServices.DllImport("phone.dll")> _
Private Shared Function PhoneMakeCall(ByRef ppmci As
PhoneMakeCallInfo) As IntPtr
End Function
We process the PhoneMakeCallInfo structure almost identically to as before. We break the PhoneNumber string into a character array then write to memory using iPhoneNumber as a memory alignment pointer.PhoneNumber.Insert(PhoneNumber.Length, " ")
Dim cPhoneNumber() As Char = PhoneNumber.ToCharArray()
Dim pAddr() As Char = cPhoneNumber

Dim info As PhoneMakeCallInfo = New PhoneMakeCallInfo
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info)
Dim iPhoneNumber As IntPtr = Marshal.AllocHLocal(cPhoneNumber.Length)
System.Runtime.InteropServices.Marshal.Copy(cPhoneNumber, 0,
iPhoneNumber, cPhoneNumber.Length)
info.pszDestAddress = iPhoneNumber
After we point the pszDestAddress member to this memory space and set the confirm before dial option, we pass the structure instance into PhoneMakeCall.If PromptBeforeCall Then
info.dwFlags = PMCF_PROMPTBEFORECALLING
Else
info.dwFlags = PMCF_DEFAULT
End If
res = PhoneMakeCall(info)

Accessing SIM Information in C#

Though by no means an exhaustive exploration of the SIM access functions available on the Pocket PC Phone Edition platform, we will take a look at how you can retrieve the SIM owner's primary phone number and service provider name, to further drive home the usefulness of P/Invoke. First we define a structure to hold the values received from pinging the SIM card as follows. [StructLayout(LayoutKind.Sequential)]
private struct SimRecord
{
public IntPtr cbSize;
public IntPtr dwParams;
public IntPtr dwRecordType;
public IntPtr dwItemCount;
public IntPtr dwSize;
}
Because only sequential-layout structures can be marshaled automatically between native and managed code, we decorate our structure with the sequential layout tag. cbSize is the size of the structure pas
4000
sed. dwParams are parameter values we won't worry about here. dwRecordType indicates the form of the record. Options include:
ValueDescription
SIM_RECORDTYPE_UNKNOWNAn unknown file type.
SIM_RECORDTYPE_TRANSPARENTA single variable-length record.
SIM_RECORDTYPE_CYCLICA cyclic set of records, each of the same length.
SIM_RECORDTYPE_LINEARA linear set of records, each of the same length.
SIM_RECORDTYPE_MASTEREvery SIM has a single master record, effectively the head node.
SIM_RECORDTYPE_DEDICATEDEffectively a "directory" file that is a parent of other records.
dwItemCount is the number of items in the record. dwSize is the size of each item in the record. Next we have a bunch of function declarations that utilize DLLImport. [DllImport("sms.dll")]
private static extern IntPtr SmsGetPhoneNumber(IntPtr psmsaAddress);
Retrieves SIM owner's phone number. [DllImport("cellcore.dll")]
private static extern IntPtr SimInitialize(IntPtr dwFlags, IntPtr
lpfnCallBack, IntPtr dwParam, out IntPtr lphSim);
This function is required to call any of the SIM access functions. A pointer to an HSIM handle is returned upon successful execution, to be used with subsequent calls. [DllImport("cellcore.dll")]
private static extern IntPtr SimGetRecordInfo(IntPtr hSim, IntPtr
dwAddress, ref SimRecord lpSimRecordInfo);
This function takes in the HSIM handle returned from SimInitialize along with a SIM address (dwAddress) and a SIM record structure and fills the structure with the requested information. [DllImport("cellcore.dll")]
private static extern IntPtr SimReadRecord(IntPtr hSim, IntPtr
dwAddress, IntPtr dwRecordType, IntPtr dwIndex, byte[] lpData,
IntPtr dwBufferSize, ref IntPtr lpdwBytesRead);
This function takes in the HSIM handle returned from SimInitialize along with a SIM address (dwAddress), a dwRecordType (see table above), dwIndex (if SIM_RECORDTYPE_CYCLIC or SIM_RECORDTYPE_LINEAR are used), lpData pointing to the data buffer, dwBufferSize indicating the buffer size, and lpdwBytesRead, indicating the number of bytes to read. [DllImport("cellcore.dll")]
private static extern IntPtr SimDeinitialize(IntPtr hSim );
This function releases the resources of the HSIM handle created in SimInitialize.
We will implement two important Phone API calls: obtain the phone number of the current SIM owner and get the service provider name currently being accessed by the SIM.
In GetPhoneNumber, all we do is define a large byte buffer and pass it into SmsGetPhoneNumber. Byte[] buffer = new Byte[516];
fixed (byte* pAddr = buffer)
{
IntPtr res = SmsGetPhoneNumber((IntPtr)pAddr);
if (res != IntPtr.Zero)
throw new Exception("Could not get phone number from SIM");
And then we pick off both the returned PhoneAddress type and PhoneAddress phone number. byte *pCurrent = pAddr;
phoneaddr.AddressType =
(AddressType)Marshal.ReadInt32((IntPtr)pCurrent);
pCurrent += Marshal.SizeOf(phoneaddr.AddressType);
phoneaddr.Address = Marshal.PtrToStringUni((IntPtr)pCurrent);
}
This function is declared unsafe as we're directly reading from memory from managed code.
In our next function, GetServiceProvider, we first initialize the SIM res = SimInitialize(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out
hSim);
if (res != IntPtr.Zero)
throw new Exception("Could not initialize SIM");
Then, declare a SIM record structure instance and request that we retrieve a handle to the SimRecord holding the value of the SERVICE_PROVIDER when we call
SimGetRecordInfo.
SimRecord rec = new SimRecord();
rec.cbSize = (IntPtr)Marshal.SizeOf(rec);

res = SimGetRecordInfo(hSim, (IntPtr)SERVICE_PROVIDER, ref rec);
if (res != IntPtr.Zero)
throw new Exception("Could not read the service provider
information from the SIM");
We then allocate a byte array to hold the contents of the SIM record with the SERVICE_PROVIDER details. byte[] bData = new byte[(int)rec.dwSize + 1];
IntPtr dwBytesRead = IntPtr.Zero;
Then, request to read the contents of the SERVICE_PROVIDER record. res = SimReadRecord(hSim, (IntPtr)SERVICE_PROVIDER,
rec.dwRecordType, IntPtr.Zero, bData, (IntPtr)bData.Length, ref
dwBytesRead);
if (res != IntPtr.Zero)
throw new Exception("Could not read the service provider from the
SIM");
Then, go through the structure returned and remove any non-standard ASCII characters that could break our code when we display the final result. byte[] bScrubbed = new byte[(int)dwBytesRead];
int nPos = 0;

// Scrub the non-ascii characters
for (int i = 0; i < (int)dwBytesRead; i ++)
{
if (((int)bData[i] > 19) && ((int)bData[i] < 125))
{
bScrubbed[nPos] = bData[i];
nPos++;
}
}
Then, we de-initialize the SIM and free up its resources. SimDeinitialize(hSim);
And finally, we return the string representation of our "scrubbed" byte buffer.

Accessing SIM Information in VB.NET

Our initial class declarations are the same as what we saw in C#: Private Shared SERVICE_PROVIDER As Long = &H6F46

<StructLayout(LayoutKind.Sequential)> _
Public Structure SimRecord
Public cbSize As IntPtr
Public dwParams As IntPtr
Public dwRecordType As IntPtr
Public dwItemCount As IntPtr
Public dwSize As IntPtr
End Structure

<System.Runtime.InteropServices.DllImport("sms.dll")> _
Private Shared Function SmsGetPhoneNumber(ByVal psmsaAddress As IntPtr) As
IntPtr
End Function

<System.Runtime.InteropServices.DllImport("cellcore.dll")> _
Private Shared Function SimInitialize(ByVal dwFlags As IntPtr, ByVal
lpfnCallBack As IntPtr, ByVal dwParam As IntPtr, ByRef lphSim As IntPtr)
As IntPtr
End Function

<System.Runtime.InteropServices.DllImport("cellcore.dll")> _
Private Shared Function SimGetRecordInfo(ByVal hSim As IntPtr, ByVal
dwAddress As IntPtr, ByRef lpSimRecordInfo As SimRecord) As IntPtr
End Function

<System.Runtime.InteropServices.DllImport("cellcore.dll")> _
Private Shared Function SimReadRecord(ByVal hSim As IntPtr, ByVal
dwAddress As IntPtr, ByVal dwRecordType As IntPtr, _
ByVal dwIndex As IntPtr, ByVal lpData() As Byte, ByVal dwBufferSize As
IntPtr, ByRef lpdwBytesRead As IntPtr) As IntPtr
End Function

<System.Runtime.InteropServices.DllImport("cellcore.dll")> _
Private Shared Function SimDeinitialize(ByVal hSim As IntPtr) As IntPtr
End Function
Our GetPhoneNumber function parallels well with the C# implementation. First we create our buffer space and call the SmsGetPhoneNumber function using P/Invoke. Dim phoneaddr As PhoneAddress = New PhoneAddress
Dim buffer(512) As Byte
Dim pAddr() As Byte = buffer
Dim ipAddr As IntPtr = Marshal.AllocHLocal(pAddr.Length)
Dim res As IntPtr = IntPtr.Zero

Try
res = SmsGetPhoneNumber(ipAddr)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try

If (res.ToInt32 <> 0) Then
Throw New Exception("Could not get phone number from SIM")
End If
Next, we pick off the address type information from the returned structure. phoneaddr.AddressType =
System.Runtime.InteropServices.Marshal.ReadInt32(ipAddr)
And cast the returned phone number buffer into a string and we return the complete PhoneAddress structure.
The GetServiceProvider function is also quite similar in behavior to our C# version. Dim hSim, res As IntPtr
hSim = IntPtr.Zero
Dim temp As Long

res = SimInitialize(IntPtr.Zero, Nothing, IntPtr.Zero, hSim)
If (res.ToInt32 <> 0) Then
Throw New Exception("Could not initialize SMS.")
End If
First, we initialize the SIM so we can retrieve data from it. Dim rec As SimRecord = New SimRecord
rec.cbSize =
Marshal.AllocHLocal(System.Runtime.InteropServices
.Marshal.SizeOf(temp))
rec.cbSize = IntPtr.op_Explicit(System.Runtime.InteropServices
.Marshal.SizeOf(rec))
We create a new SimRecord structure instance and set only the cbSize member (and fill it with the size of the SimRecord structure). res = SimGetRecordInfo(hSim, IntPtr.op_Explicit(SERVICE_PROVIDER), rec)
If (res.ToInt32 <> 0) Then
Throw New Exception("Could not read service provider info from
SMS.")
End If
We call SimGetRecordInfo to get a handle to the SIM record containing the SERVICE_PROVIDER data. Dim bData((rec.dwSize).ToInt32 + 1) As Byte
Dim dwBytesRead As IntPtr = IntPtr.Zero

res = SimReadRecord(hSim, IntPtr.op_Explicit(SERVICE_PROVIDER),
rec.dwRecordType, IntPtr.Zero, bData,
IntPtr.op_Explicit(bData.Length), dwBytesRead)
If (res.ToInt32 <> 0) Then
Throw New Exception("Could not read service provider from SMS.")
End If
Then, as we had to do in the C# code, we must strip any non-ASCII characters from the resulting byte buffer before we convert to string and return that string value. Dim bScrubbed(dwBytesRead.ToInt32) As Byte
Dim nPos As Int32 = 0
Dim i As Int32

'Scrub the non-ascii characters
For i = 0 To dwBytesRead.ToInt32
If bData(i) > 19 And bData(i) < 125 Then
bScrubbed(nPos) = bData(i)
nPos = nPos + 1
End If
Next i

SimDeinitialize(hSim)

Return System.Text.ASCIIEncoding.ASCII.GetString(bScrubbed, 0,
bScrubbed.Length)

Code Use

To make a phone call from your code using C#, call
Microsoft.Wireless.Phone.MakeCall("Phone number");

To get the SIM user's phone number from your code using C#, call:
Microsoft.Wireless.Sim.GetPhoneNumber()
(which returns a string)
To get the service provider of the SIM user from your code using C#, call:
Microsoft.Wireless.Sim.GetServiceProvider()
(which returns a string)
To make a phone call from your code using VB.NET, call
Phone.MakeCall("Phone number");

To get the SIM user's phone number from your code using VB.NET, call:
Sim.GetPhoneNumber()
(which returns a string)
To get the service provider of the SIM user from your code using VB.NET, call:
Sim.GetServiceProvider()
(which returns a string)

Conclusion

This document served to further illustrate the usefulness and ease of use with which one can call important classes of Win32 API functions from managed code. Here, we illustrated the use of the Phone API class, though we only scratched the surface. It should be apparent that by using P/Invoke, one can leverage the power of Win32 API programming to a high degree.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息