1. Trang chủ
  2. » Công Nghệ Thông Tin

Professional Microsoft Smartphone Programming phần 7 ppt

53 216 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Nội dung

When throwing a user-defined exception, you have the option to use one of the available constructors. In the preceding example code, you can use either the default constructor, or the one that accepts a string parameter ( User-defined exception: Invalid Zip Code), or the one that accepts the string parameter and an exception instance ( new ArgumentException()). For example, the following two throw calls can also be used in the preceding example: //throw new InvalidZipCodeException(); //throw new InvalidZipCodeException(“User-defined exception: Invalid Zip Code. “); Any code that calls ProcessZipCodeInput() should be put into a try-catch block. For example, if a string “239d3” is passed to the ProcessZipCodeInput() method, an exception will be thrown and should be handled: try { ProcessZipCodeInput(“239d3”); } catch (InvalidZipCodeException e) { Console.WriteLine(e.ToString()); } Best Practices of Exception Handling As shown in the sample code discussed so far, exception handling can act as a method for execution event notification across method/function calls. Note the following issues regarding when, where, and how to use exception handling to improve the robustness of an application: ❑ Exceptions vs. condition checking. Some errors can be discovered by checking whether the variable truly holds the expected value or by checking the return value of a method. Without using exceptions, the code can simply return some error code in the case of a specific type of error. The key is to determine whether this kind of error occurs quite rarely in the code path. On the one hand, if the error is indeed exceptional, using exceptions is recommended because you won’t waste CPU cycles on that condition-checking code in normal cases. On the other hand, if the error is almost certain to occur every time in the current method, you may prefer condition checking to exception handling because the latter usually results in significant overhead of stack unwinding and exception creation and deletion. ❑ Exceptions vs. return error code. A method that may incur some error can either return a spe- cific error code or raise some exception. The general guideline is to return a common error code (for example, null for an object, -1 for an integer, false for a Boolean) when a common error occurs, or define your return codes for your application logic, but throw an exception when an unusual, critical error occurs. ❑ Exception classes vs. user-defined exception classes. Use the .NET Compact Framework’s exception classes for all general exceptions. Write your user-defined exception classes only for a special error in your application. For example, if the application requires numeric input follow- ing a pattern, then a user-defined exception can be created for invalid input that does not con- form to the pattern. 292 Chapter 11 16_762935 ch11.qxp 11/20/06 7:58 AM Page 292 Debugging in Visual Studio 2005 Chapter 3 talked briefly about debugging Smartphone applications. This section first describes the debugging features in Visual Studio 2005, and then we turn to some advanced topics regarding debug- ging support in the .NET Compact Framework. Debugging Windows Basic debugging functions supported by Visual Studio 2005 include the following: place breakpoints (F9 to toggle the breakpoint), step into (F11), step over (F10), or step out of the code (Shift+F11), and attach to a process on a device or the emulator. In addition, you can use a handful of debug windows to view and change variables, and to evaluate expressions and function calls. The following debugging windows are listed under Debug ➪Windows: ❑ Immediate window — You can enter an expression or a statement to inspect or change its value. Two modes are supported in the immediate window. The first is command mode, in which you can enter a Visual Studio command prefixed by a > character. For example, to open the com- mands window, enter >cmd. The other mode is immediate mode, in which you can enter a vari- able or a statement. To evaluate an expression, prefix the expression with a ? (such as ? 3+5). ❑ Watch windows — These windows enable you to enter and edit a variable, object, or expression, which will be evaluated automatically while debugging. The display of an object enables you to quickly view the properties and fields. ❑ Locals window — This window automatically displays local variables and objects. You can change the values as well. ❑ Autos window — This window automatically displays local variables, objects, and expressions of the current line and preceding lines. You can change the values as well. ❑ Call stack window — This window displays the method call stack, including all methods, parameters, and return values. ❑ Modules window–This window lists loaded modules. ❑ Process window — This window lists currently running processes in the debugger. ❑ Threads window — This window lists all the threads of the application. ❑ Breakpoint window — This window lists all breakpoints. You can set conditional breakpoints and configure a macro or print a message for a breakpoint. Visual Studio 2005 debugger’s “Attach to process” support is disabled by default for processes running on the emulator or Smartphone devices. To enable this feature, add the following DWORD key to the registry of the device or the emulator: HKLM\Software\Microsoft\.NetCompactFramework\Managed Debugger\AttachEnabled = 1 You can use the WinCE Remote Registry Editor (in Visual Studio Remote Tools of the Visual Studio 2005) to do this. 293 Exception Handling and Debugging 16_762935 ch11.qxp 11/20/06 7:58 AM Page 293 In addition, when you hover the mouse over the variable or object while the code breaks at a statement or an exception, you will see the DataTip window, which enables you to inspect and change complex data types. You can even expand properties or fields, which themselves are other types. Note that managed code based on the .NET Compact Framework does not support disassembly debugging. Debugging Setting The C# compiler (csc.exe) supports a /debug option, which you can set to one of the following two values: ❑ full — This setting allows source code debugging when the program is run in a debugger and when a debugger is attached to the running program. This is the default setting for a project’s “Debug” configuration. ❑ pdbonly — This setting allows source code debugging when the program is run in a debugger but only enables assembly-level debugging when a debugger is attached. This is the default set- ting for a project’s “Release” configuration. To change this setting in Visual Studio 2005, go to Project ➪Project Properties➪Build➪Advanced➪Debug Info. On the command line, you can also use /debug+ or /debug to get the same result as /debug:full. Conversely, /debug- will disable debugging, just as /debug is not included in the compiler options. The following line is an example of using /debug:full for the file MyExample.cs: csc /debug:full MyExample.cs Using /debug:full will have some impact on the performance of the program because the JIT code size will increase and the .NET Compact Framework CLR will take longer time to JIT-compile the MSIL code. The programmatic way to control these settings is to use System.Diagnostics.DebuggableAttribute in your code. A related compiler option is /optimize (or /o), which specifies whether to optimize the code for better performance. Usually, code under debugging should not use this option. The same setting in Visual Studio 2005 is available under Project ➪Project Properties➪Debug. Deploying and Debugging in Visual Studio In Chapter 3, you learned how to deploy your Smartphone application onto a device or an emulator. Basically, select Project ➪Project Properties➪Device and select the device from the Target device box. Or, if you have the Device toolbar selected, you can directly select the device there. After this step, debugging an application running on the device or the emulator can be as easy as debugging a desktop application: You will be able to perform source code debugging, put breakpoint in the code, and so on. Of course, the user’s input must be done on the device or the emulator, but anything else is done on your development PC. See Chapter 3 for a detailed discussion of the device debugging features in Visual Studio. Defining Symbols Sometimes conditional compilation is needed to quickly separate code for debugging purposes from release code. The common way to do this is to use a conditional check on a symbol, as shown in the fol- lowing example: 294 Chapter 11 16_762935 ch11.qxp 11/20/06 7:58 AM Page 294 #ifdef DEBUG // Some debug code goes here #endif This piece of code checks whether the DEBUG symbol is defined either in your code (#define DEBUG) or on the compiler command line ( csc /define:DEBUG MyExample.cs). If so, statements following ifdef will be compiled. Otherwise, the compiler will skip the ifdef-endif enclosed code. You can set the DEBUG symbol as a compiler option with /define:DEBUG. The code segment can be placed any- where in the code. Another symbol that is often used for debugging and tracing is TRACE. The compiler option is /define: TRACE . In fact, you can define whatever symbols you like using /define: (or /d:), followed by your symbol on the compiler command line. Note that in C#, you can’t assign a value to a defined symbol. Multiple symbols can be defined and separated by commas. In Visual Studio 2005, these settings are found under Project ➪Project Properties➪Build. Limitations of Debugging Despite the powerful functionality of Smartphone application debugging, it has the following limita- tions (as of the .NET Compact Framework 2.0 and Visual Studio 2005): ❑ Just-My-Code debugging is not supported. Just-My-Code debugging is a feature that, once enabled, allows the programmer to see only the user code while debugging; library class meth- ods and system code will not be displayed. The debugger will search for symbols files to deter- mine whether a piece of code is “My Code.” If symbols for the code exist, it is considered “My Code.” This feature is not available for .NET Compact Framework debugging. ❑ Edit-and-Continue is not supported. In the .NET Framework application development, you can edit the code and continue to run in a debugging session, thus saving the time for recompila- tion. This feature is not supported in .NET Compact Framework debugging. ❑ The next statement is not supported. You cannot set the instruction point when debugging .NET Compact Framework applications. 295 Exception Handling and Debugging AppVerifier Microsoft provides another free tool, called AppVerifier for Windows Mobile 5.0, that you can use to test Smartphone applications against common native coding mistakes such as memory leaks, handle leaks, GDI resource leaks, and some heap corruptions. For managed code debugging, it is also useful for testing problems in intensive I/O applications that may stem from the .NET Compact Framework CLR. For more information about AppVerifier for Windows Mobile, visit www.microsoft .com/downloads/details.aspx?FamilyId=D275348A-D937-4D88-AE25-28702 C78748D&displaylang=en or search for “Application Verifier Tool for Windows Mobile” at download.Microsoft.com. 16_762935 ch11.qxp 11/20/06 7:58 AM Page 295 Multithreaded Debugging Applications can have multiple threads to perform different tasks in parallel and in sync. The .NET Compact Framework has done a good job encapsulating multithreading details into many classes such that you don’t need to create threads yourself. For example, the BeginInvoke() method of a Control internally uses the ThreadPool thread to perform the specified task asynchronously. The BeginRead() and BeingWrite() methods of the Stream class are two other examples of multithreading in the .NET Compact Framework. There are still cases where you need to create and manage multiple threads in a Smartphone application. For instance, for an application that retrieves web pages from the Internet and caches them locally, net- work access, local file access, and UI updates can be performed with three threads simultaneously; thus, one will not block others. A major debugging topic involves multiple threads running concurrently and interoperating with each other. Common issues in multithreaded applications include race condition (the execution of multiple threads depending on the timing of events), deadlock (two threads waiting for each other to release a resource), and access violations (a thread accessing a resource that has been released). AV (access violation) can be fairly easy to detect, as the .NET Compact Framework runtime will throw exceptions in these cases. For the other two problems of concurrency, you need the debugger to help. Managed Threads A managed thread in the .NET Compact Framework CLR is not directly mapped to an operating system thread. The CLR may schedule some managed threads using a single operating system thread. A managed thread may be migrated from one operating system thread to another, but to application developers this is completely transparent. If you have debugged applications that use multiple operating system threads in Visual Studio .NET, you will find that debugging managed multithreaded applications is very similar. Before discussing the details, let’s go over the threading support in the .NET Compact Framework. To create a managed thread, use System.Threading.Thread. You need to define a thread procedure and pass it to the constructor of the Thread object. Table 11-4 lists some notable properties and methods of the Thread class. Table 11-4 Thread Class Members Member Description Thread::Start() Starts the thread. Thread::Abort() Terminates the thread. This method will throw a ThreadAbort Exception . Thread::Join() Blocks the calling thread until the thread being joined terminates. Thread.CurrentThread Returns the current running thread. Thread.Sleep() Put the current thread into sleep. Thread::ManagedThreadId Returns the unique thread ID. Thread::Name Gets or sets a thread name. Once set, Name cannot be changed. 296 Chapter 11 16_762935 ch11.qxp 11/20/06 7:58 AM Page 296 Member Description Thread::Priority Gets or sets thread priority, which is one of the values defined in the ThreadPriority enumeration. Thread::IsBackground Gets or sets a value indicating whether the thread is a back- ground thread. This setting determines whether the process can terminate. A process cannot terminate until all its foreground threads terminate. Once all the foreground threads have termi- nated, the CLR terminates the process and stops all background threads of the process. The following code snippet shows how to create and start a thread: Thread newThread = new Thread(ThreadProc); // Set the Name property of the new thread newThread.Name = “A new thread other than the main thread”; newThread.Start(); // Thread procedure private static void ThreadProc() { // Current thread’s Name string name = Thread.CurrentThread.Name; // Sleep for 1 second Thread.Sleep(1000); } As shown in this example, in the .NET Compact Framework 2.0, you can pass the method name for the new thread to the constructor of a Thread object. You can certainly also use the “old” scheme — that is, pass a new ThreadStart delegate that is created with the thread method to the constructor: Thread newThread = new Thread(new ThreadStart(ThreadProc)); If the thread method is fairly simple, you can put it inline in the constructor call, as follows: Thread newThread = new Thread(delegate() { // ThreadProc statements } ); The .NET Compact Framework CLR provides another facility for multithreaded applications: the thread pool. The thread pool consists of a set of worker threads (background threads) managed by the CLR for each application. You can post asynchronous I/O tasks and short callbacks to the thread pool using the Thread Pool.QueueUserWorkerItem() method or the ThreadPool.RegisterWaitForSingleObject() method. That way, you don’t need to create and manage a new thread yourself. The disadvantage of using a thread pool worker thread is that the tasks can’t take too long to finish. Otherwise, the thread pool may become fully occupied and can’t accommodate new worker thread requests. In addition, you can’t change a thread pool thread’s priority, and they are all background threads. 297 Exception Handling and Debugging 16_762935 ch11.qxp 11/20/06 7:58 AM Page 297 Race Condition Multiple threads may need to change the same object. If the code that accesses the object is not properly protected, you will see garbled object data. This is often called a race condition. The code block that modi- fies the shared object is called a critical section. Applications must ensure that at any given time there is only one thread in the critical section. Critical sections can be protected by a lock. Only a thread that acquires the lock can enter the critical sec- tion. Other threads waiting for the lock will be blocked. The locking mechanism is implemented as a lock construct in C# that is built on top of the Monitor class in the .NET Compact Framework. If there are multiple resources to protect, a mutex can be used for each resource. A mutex is a named synchro- nization object that provides exclusive access to a resource. Once a System.Threading.Mutex object is created, a thread can call WaitOne() to obtain the mutex, and any other threads calling WaitOne() will be blocked. A thread should call ReleaseMutex() when it finishes the access. You can use the lock construct of C# as follows: lock(lockObject) { // Enter critical section } The lockObject can be a simple object type. It is suggested that this object should be a private member of the class. Locking on a public type may lead to deadlock because other code may also lock this type. The following RaceCondition class demonstrates a first-come-first-take procedure of a number of work items. The class manages a private variable numItems that can be changed by the public method TakeWorkItem(). In the TakeWorkItem() method, we check to see if the number of items are greater than zero. If so, the method will let the underlying thread sleep for some random time to simulate the time for that work item. After the sleep, it will check the number of work items again. If multiple threads are executing in this method, there will be a chance that when the thread comes out of sleep, the number of work items have already be decremented by another thread. An exception is thrown when a thread wakes up and identifies zero or a negative number of work items. class RaceCondition { private Object myLock = new object(); private int numItems = 0; Random r = new Random(); public RaceCondition(int items) { numItems = items; } public void TakeWorkItem() { if (numItems > 0) { // There are still work items; so take one. Thread.Sleep(r.Next(100,1000)); 298 Chapter 11 16_762935 ch11.qxp 11/20/06 7:58 AM Page 298 if(numItems <= 0) throw new ApplicationException(“Race condition: negative number of items!”); numItems ; } else if (numItems == 0) { } else { throw new ApplicationException(“Race condition: negative number of items!”); } } } You can test the race condition situation in the preceding class by simply creating a RaceCondition object with x work items and then creating more than x threads that execute the RaceCondition:: TakeWorkItem() class: RaceCondition rc = new RaceCondition(sw,5); // 5 work items Thread[] workers = new Thread[10]; // 10 workers for (int i = 0; i < 10; i++) { Thread t = new Thread(rc.TakeWorkItem); workers[i] = t; workers[i].Start(); } The unhandled ApplicationException will be raised when a thread sees zero or a negative number of work items. When the application breaks, you can see all the currently running threads in the Threads window. You can view each thread’s current statement by switching to that thread. Note that you will not see all ten threads because some of the earlier ones are already finished when the exception is raised. A lock is applied to protect the TakeWorkItem() method. This guarantees that only one thread can enter the critical section (i.e., to change the numItems variable). Therefore, no thread will see zero or a negative number of work items once they have entered the critical section: lock (myLock) { if (numItems > 0) { // There are still work items; so take one. Thread.Sleep(r.Next(100, 1000)); if (numItems <= 0) throw new ApplicationException(“Race condition: negative number of items!”); numItems ; } else if (numItems == 0) 299 Exception Handling and Debugging 16_762935 ch11.qxp 11/20/06 7:58 AM Page 299 { } else { throw new ApplicationException(“Race condition: negative number of items!”); } } Deadlock A mutex guarantees mutually exclusive access to a resource. You have to be cautious, however, when using a mutex for thread synchronization. Deadlock can occur if two or more threads are holding some resource and waiting for others to unlock other resources, and all resources can’t be shared among threads. Because no thread can preempt other threads to forcibly obtain a requested resource, these threads end up in a cyclical wait state. The following code shows an example of deadlock — the famous philosopher’s dinner problem. In this simplified scenario, three philosophers, John, Jack, and Joe, are sitting around a table in the middle of which is a bowl of spaghetti. As illustrated in Figure 11-3, the table has been set with a number of forks equal to the number of philosophers. Eating the spaghetti requires two forks, however, so a philosopher must pick up both the fork to his left and the fork to his right. If each philosopher takes the fork to his left and then waits for the fork on his right to become available, nothing can happen; therefore, deadlock occurs. Figure 11-3 class Deadlock { private StreamWriter sw = new StreamWriter(@”\Storage Card\Philosopher.txt”); Mutex[] forks = new Mutex[3]; public void Dinner() { // Create three forks (mutex) for(int i = 0 ; i < 3; i ++) { forks[i] = new Mutex(); John Joe Fork 2Fork 0 Jack Fork 1 300 Chapter 11 16_762935 ch11.qxp 11/20/06 7:58 AM Page 300 } // Tell the philosopher which forks to grab // Philosopher John = new Philosopher(sw, forks[2], forks[0]); // Solution to the deadlock problem: let the philosopher try the smaller fork ID first Philosopher John = new Philosopher(sw, forks[0], forks[2]); Philosopher Jack = new Philosopher(sw, forks[0], forks[1]); Philosopher Joe = new Philosopher(sw, forks[1], forks[2]); Thread t1 = new Thread(John.Eat); t1.Name = “John”; Thread t2 = new Thread(Jack.Eat); t2.Name = “Jack”; Thread t3 = new Thread(Joe.Eat); t3.Name = “Joe”; t1.Start(); t2.Start(); t3.Start(); t1.Join(); t2.Join(); t3.Join(); sw.WriteLine(“Main thread exits.\n”); sw.Close(); } } class Philosopher { private StreamWriter sw = null; private Mutex lfork; // First fork to grab private Mutex rfork; // Second fork to grab public Philosopher(StreamWriter logfile, Mutex fork_left, Mutex fork_right) { sw = logfile; lfork = fork_left; rfork = fork_right; } public void Eat() { lfork.WaitOne(); Log(Thread.CurrentThread.Name + “\t acquired “ + lfork.GetHashCode()); Thread.Sleep(1000); rfork.WaitOne(); Log(Thread.CurrentThread.Name + “\t acquired “ + rfork.GetHashCode()); // The philosopher starts to eat Log(Thread.CurrentThread.Name + “\t is eating”); rfork.ReleaseMutex(); lfork.ReleaseMutex(); } private void Log(string s) 301 Exception Handling and Debugging 16_762935 ch11.qxp 11/20/06 7:58 AM Page 301 [...]... John, and Jack Depending on the timing, the eating sequence may vary over multiple runs Joe John Joe Joe acquired 878 386 acquired 878 385 acquired 878 3 87 is eating 303 16 _76 2935 ch11.qxp 11/20/06 7: 58 AM Page 304 Chapter 11 John acquired 878 3 87 John is eating Jack acquired 878 385 Jack acquired 878 386 Jack is eating Main thread exits Summar y A good computer program should perform as expected even under abnormal... well as C#’s threading support 304 17_ 762935 pt03.qxp 11/20/06 7: 58 AM Page 305 Part III Advanced Topics Chapter 12: Device and Application Security Chapter 13: Data and Communication Security Chapter 14: Globalization and Localization Chapter 15: Graphics Chapter 16: Performance 17_ 762935 pt03.qxp 11/20/06 7: 58 AM Page 306 18 _76 2935 ch12.qxp 11/20/06 7: 59 AM Page 3 07 Device and Application Security... this change A sample output (in the file philosopher.txt on the storage card if a Smartphone is used or in the shared folder on the desktop machine if the Smartphone emulator is used) is shown in the following code The hash codes of the three mutexes are 878 385 (between John and Jack), 878 386 (between Jack and Joe), and 878 3 87 (between John and Joe) In this run, the eating sequence is Joe, John, and Jack... about how to better defend themselves 308 18 _76 2935 ch12.qxp 11/20/06 7: 59 AM Page 309 Device and Application Security Glossar y of Terms Microsoft has defined a number of terms to describe the device and application security features in Windows Mobile 5.0 development and deployment Having a good understanding of terms will greatly help you to develop and ship Smartphone applications with enhanced security... most cases, you can simply let the wizard find a proper place for you automatically 316 18 _76 2935 ch12.qxp 11/20/06 7: 59 AM Page 3 17 Device and Application Security Figure 12-6 Once the certificate is imported, the Manage Certificates window will reappear with a new certificate shown in the window (see Figure 12 -7) Now close the Manage Certificates window The Select Certificate window appears again This... Certificates window The Select Certificate window appears again This time, a certificate is available in the selection list to enable you to sign your application (see Figure 12-8) Figure 12 -7 3 17 18 _76 2935 ch12.qxp 11/20/06 7: 59 AM Page 318 Chapter 12 Figure 12-8 Once you have selected the certificate to sign your application, detailed information about the certificate is shown in the project’s properties... detailed information about a certificate, such as who issued it and when it expires, is also displayed on the screen, as shown in Figure 12-11 319 18 _76 2935 ch12.qxp 11/20/06 7: 59 AM Page 320 Chapter 12 Figure 12-10 Figure 12-11 320 18 _76 2935 ch12.qxp 11/20/06 7: 59 AM Page 321 Device and Application Security This applet allows you to view certificates from two certificate stores only; it is not capable... 313 18 _76 2935 ch12.qxp 11/20/06 7: 59 AM Page 314 Chapter 12 ❑ Two-Tier-Prompt As with One-Tier Prompt mode, users have control over unsigned applications Unlike One-Tier Prompt mode, however, those unsigned applications can be executed only in normal mode and therefore have no access to privileged APIs and protected registry settings Two-Tier Prompt mode is an ideal security configuration for Smartphone. .. (http://msdn .microsoft. com/mobility/windowsmobile/partners/ mobile2market/default.aspx) Mobile2Market partners provide certificate authority specifically for Windows Mobile devices In addition to obtaining certificates to sign your application, if you are willing to pay more, your application logo can be certified This is not required to deploy your application but may be 314 18 _76 2935 ch12.qxp 11/20/06 7: 59... digital signature If you publish an application without signing it, that application is considered to be an unsigned application 309 18 _76 2935 ch12.qxp 11/20/06 7: 59 AM Page 310 Chapter 12 Privileged and Unprivileged Applications and Certificate Stores Certificates saved on Smartphone devices are organized into certificate stores, the two most important of which are the privileged certificate store and . runs. Joe acquired 878 386 John acquired 878 385 Joe acquired 878 3 87 Joe is eating 303 Exception Handling and Debugging 16 _76 2935 ch11.qxp 11/20/06 7: 58 AM Page 303 John acquired 878 3 87 John is eating Jack. visit www .microsoft .com/downloads/details.aspx?FamilyId=D 275 348A-D9 37- 4D88-AE25-2 870 2 C7 874 8D&displaylang=en or search for “Application Verifier Tool for Windows Mobile” at download .Microsoft. com. 16 _76 2935. Localization Chapter 15: Graphics Chapter 16: Performance 17_ 762935 pt03.qxp 11/20/06 7: 58 AM Page 305 17_ 762935 pt03.qxp 11/20/06 7: 58 AM Page 306 Device and Application Security This chapter

Ngày đăng: 12/08/2014, 23:23

TỪ KHÓA LIÊN QUAN