Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro Visual C++-CLI And The .NET 2.0 Platform (2006) [eng]-1

.pdf
Скачиваний:
70
Добавлен:
16.08.2013
Размер:
24.18 Mб
Скачать

768 C H A P T E R 1 8 A S S E M B L Y P R O G R A M M I N G

Listing 18-14. Very Simple Windows Form Code

void InitializeComponent(void)

{

this->lbHello = (gcnew System::Windows::Forms::Label()); this->SuspendLayout();

//

//lbHello

this->lbHello->Font =

(gcnew System::Drawing::Font(L"Microsoft Sans Serif", 12, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, static_cast<System::Byte>(0)));

this->lbHello->Location = System::Drawing::Point(12, 9); this->lbHello->Name = L"lbHello";

this->lbHello->Size = System::Drawing::Size(364, 23); this->lbHello->TabIndex = 0;

this->lbHello->Text = L"Hello, my name is Stephen";

//Form1

//

this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(390, 48); this->Controls->Add(this->lbHello);

this->Name = L"Form1"; this->Text = L"English"; this->ResumeLayout(false);

}

Okay, now let’s take this same code and make it localizable. To do this, simply set the Form’s Localizable property to true (see Figure 18-18).

Figure 18-18. Setting the Localizable flag to true

Now take a look at the code in the InitializeComponent() method (see Listing 18-15).

C H A P T E R 1 8 A S S E M B L Y P R O G R A M M I N G

769

Listing 18-15. Localizable Simple Application

void InitializeComponent(void)

{

System::ComponentModel::ComponentResourceManager^ resources =

(gcnew System::ComponentModel::ComponentResourceManager(Form1::typeid)); this->lbHello = (gcnew System::Windows::Forms::Label()); this->SuspendLayout();

//

//lbHello

resources->ApplyResources(this->lbHello, L"lbHello"); this->lbHello->Name = L"lbHello";

//Form1

//

resources->ApplyResources(this, L"$this");

this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->Controls->Add(this->lbHello);

this->Name = L"Form1"; this->ResumeLayout(false);

}

Where did all the code go? Don’t panic, every aspect of the label has now become a resource. As such, it can take on any look and feel you want based on the values you place within the resource file that populates this label. At this point, all the information about the label and the form is stored in a resource file called Form1.resx. Now, instead of hard coding everything, the application at runtime dynamically applies the look and feel using the ApplyResources() method of the

ComponentResourceManager class.

Currently, the resource file only contains all the default information about the Windows Form, as I pointed out as the first part of localization.

Now you’ll add a new culture, French (France), to the form. To do this you set the form’s Language property to French (France). Scrolling up and down in the Language property’s selection displays quite a few cultures, don’t you think?

Notice any difference in the Windows Form design? Nope, me neither. Here’s the fun part: Go wild and change any property of the label, but just don’t delete it. Now toggle between the default language and the French (France) language. Notice that they retain the information specific to each culture. (Well, apparently you can’t go too wild there, as it seems a few of the properties aren’t stored in the resource file automatically. Border and background color are two that surprised me by not working.)

Go ahead and do the same for the German (Germany) culture. Notice how everything reverts to the default culture look and feel again (if you were in the French language version anyway). Whenever you start a new culture, Visual Studio 2005 reverts back to the default so that you will always have a consistent starting point to make your culture-specific changes.

Anyway, now that you’ve created a French (France) culture and German (Germany) culture, notice there’s now a Form1.fr-fr.resx and a Form1.de-DE.resx resource file added to your Solution Explorer.

Now let’s see what happens when you compile the Windows Form application. After you compile the application, go ahead and open Windows Explorer and navigate to the directory structure where the application runs. There are now two directories, one for each culture using the culture’s RFC1766 code as a name. Also, in each directory is a file called [ApplicationName].resources.dll, as shown in Figure 18-19. These two new .dll files are your satellite assemblies.

770 C H A P T E R 1 8 A S S E M B L Y P R O G R A M M I N G

Figure 18-19. The Windows Explorer view of satellite assemblies

Run your new multicultured application. You see none of your French or German stuff, right? That is because your native culture is not French (France) or German (Germany), so the ResourceManager took the default resource values and not the French or German one. (Oh, of course, if you are reading this book in France or Germany and your machine is configured for French or German, then you would see the French or German. French or German readers might try some other culture for this example.)

As I stated previously, you need to change the CurrentThread class’s CurrentUICulture to the satellite assembly’s culture you want to access. Do this by adding the following lines before you call the InitializeComponent() method:

Thread::CurrentThread->CurrentCulture = gcnew CultureInfo("fr-fr"); Thread::CurrentThread->CurrentUICulture = Thread::CurrentThread->CurrentCulture;

Figure 18-20 shows MultiCulturalApp.exe French (France) culture in action.

Figure 18-20. The result of executing the MultiCulturalApp program

Building a Multicultural Console Application

When you build an assembly that isn’t a Windows application, things aren’t quite as easy. But it doesn’t take much to fool Visual Studio 2005 into believing it’s building Windows-like satellite assemblies.

Let’s create a simple little program called MulticultureConsole (see Listing 18-16) that writes four colors stored in a resource string table.

Listing 18-16. Writing Out Four Colors from a Resource

using namespace System;

using namespace System::Reflection; using namespace System::Resources; using namespace System::Threading; using namespace System::Globalization;

C H A P T E R 1 8 A S S E M B L Y P R O G R A M M I N G

771

void main()

{

Assembly ^assembly = Assembly::GetExecutingAssembly(); ResourceManager ^rmgr =

gcnew ResourceManager("MulticultureConsole.Colors", assembly);

Console::WriteLine(rmgr->GetObject("Color1")); Console::WriteLine(rmgr->GetObject("Color2")); Console::WriteLine(rmgr->GetObject("Color3")); Console::WriteLine(rmgr->GetObject("Color4"));

}

Add a new item of type Assembly Resource File (.resx) and name it Colors. Then add the string resources as shown in Figure 18-21. Finally, rename the generated resource file as $(IntDir)/ $(RootNamespace).Colors.resources.

Figure 18-21. The Colors assembly resource file

When you run MulticultureConsole.exe, you should get something like Figure 18-22. There is nothing new so far.

Figure 18-22. The first result of MulticultureConsole

Now let’s make the program multicultural. The first step is to add the code to the application so that it will display based on another culture or, in other words, you globalize the application. You do this by setting the CurrentThread CurrentUICulture to something else. Let’s change it to “fr-fr” or French (France), as shown in Listing 18-17.

772 C H A P T E R 1 8 A S S E M B L Y P R O G R A M M I N G

Listing 18-17. Writing Out Four Colors from a Resource Multiculturally

using namespace System;

using namespace System::Reflection; using namespace System::Resources; using namespace System::Threading; using namespace System::Globalization;

void main()

{

Assembly ^assembly = Assembly::GetExecutingAssembly(); ResourceManager ^rmgr =

gcnew ResourceManager("MulticultureConsole.Colors", assembly);

Console::WriteLine(rmgr->GetObject("Color1"));

Console::WriteLine(rmgr->GetObject("Color2"));

Console::WriteLine(rmgr->GetObject("Color3"));

Console::WriteLine(rmgr->GetObject("Color4"));

Thread::CurrentThread->CurrentUICulture = gcnew CultureInfo("fr-fr");

Console::WriteLine(rmgr->GetObject("Color1")); Console::WriteLine(rmgr->GetObject("Color2")); Console::WriteLine(rmgr->GetObject("Color3")); Console::WriteLine(rmgr->GetObject("Color4"));

}

The only new thing you did was change the CurrentUICulture. I just cut and pasted the four lines that display the colors.

Now it’s time to fool Visual Studio 2005. When Visual Studio 2005 created its resource files (which later became satellite assemblies) for the multiculture example, it did so in a very specific manner. The fortunate thing is that if you create your resource files in the same way, even in a console application, you will also get correctly built satellite assemblies.

Basically, here is how you do it. Create an assembly resource file (.resx) named WhatYouWant.resx that contains all the resource items for the default language. Also rename the auto-generated resource file as $(IntDir)/$(RootNamespace).WhatYouWant.resources. Notice that this is the same procedure you followed earlier when you embedded the standard resource file.

Now here’s the trick to add, let’s say, a French culture. Create a new assembly resource file (.resx) and name it WhatYouWant.fr-fr.resx. Add all the replacement values that you want for that culture. Then rename the auto-generated resource file as $(IntDir)/$(RootNamespace). WhatYouWant.fr-fr.resources. That’s it! Placing the culture just before the .resx and .resources files is enough to trick Visual Studio 2005 into creating a culture-specific satellite assembly.

So for the previous MulticultureConsole example, create an assembly resource file (.resx) named Colors.fr-fr.resx. Then add the string resources as shown in Figure 18-23.

As you can see, once you change the culture to French, the ResourceManager looks first in the French satellite assembly for the value. Because there is no Color2, the English (default) value is written.

C H A P T E R 1 8 A S S E M B L Y P R O G R A M M I N G

773

Figure 18-23. French Colors assembly resource file

Notice that it is important that the names of the name/value pairs match between the default and the French resource files. Finally, rename the generated resource file as $(IntDir)/ $(RootNamespace).fr-fr.Colors.resources.

When you run the revised MulticultureConsole.exe, you should get something like Figure 18-24. There is nothing new so far.

Figure 18-24. Revised result of MulticultureConsole

Summary

In this chapter, you looked at several ways to programmatically play with the .NET Framework assembly. You started by exploring reflection and then its counterpart, attributes. You moved on to look at shared assemblies. Next, you learned how to add more to assemblies than just code using resources. You finished off the chapter by looking at globalization and localization.

Programming with assemblies, like many of the other topics covered in this book, is a weighty topic. I feel the only way to really learn how to program the assembly is to do so yourself. This chapter should have opened up many doors on how to do this.

The programming world has gone security crazy—unfortunately, justifiably so. In the next chapter, we will look at what C++/CLI and the .NET Framework have to help secure your code.

C H A P T E R 1 9

■ ■ ■

Security

Even though this is the last chapter specifically addressing managed code, it is hardly the least important. In fact, to many developers it is one of the more important ones. The only reason it is placed here, instead of earlier, is that it is easier to understand security if you already have a good knowledge of both managed code and the .NET Framework—which you should have by now.

Another reason I placed the chapter here is because this is the last chapter that deals solely with managed code. To put it simply, .NET Security works only with managed code (and, as you’ll see in this chapter, managed data). So what you will be learning in the chapters following this one will not be bound by what is covered in this chapter. (Okay, that is not quite accurate. The code to access or call unsafe code still falls under the .NET security umbrella, but the unsafe code itself does not.)

In general, .NET security focuses on code that has an origin other than your local hard drive, or what is often called mobile code. Normally, code that originates on your local hard drive has authority to do anything on your computer that the operating system security allows. You can change this, but in most cases there is no need.

Security in .NET is a problematic topic when it comes to C++/CLI as you have the ability to very easily step outside the safe .NET sandbox if you are not paying attention. You may find that code that works just fine when run from your local hard drive continually throws exceptions when run as mobile code. The most probable reason for these exceptions is because of the code’s or the user’s lack of permission to execute a particular functionality or access a specific resource.

Understanding the reason for these exceptions and providing methods for solving them is the goal of this chapter.

Note If parts of your code are unsafe, it causes the common language runtime (CLR) to get upset and throw an exception tantrum. There is an easy way to combat accidentally introducing unsafe code: Always compile code that you want to be secure with the /clr:safe option. This option never compiles successfully if unsafe code is present. I discuss unsafe code in some detail in the last two chapters of this book.

This chapter will look at the two forms of security provided by .NET: role-based and code access security. I’ll start off with role-based security as I feel it is the easier of the two security types. Then I’ll move on to the more involved (though not much more complex) code access security.

The Security Namespaces

The .NET Framework breaks security functionality into two large namespaces: System::Web::Security for the ASP.NET and Web services worlds and System::Security for the Windows application, console, and Windows services worlds. Since the functionality of System::Security is so complex, the .NET Framework also breaks it up into the following:

775

776

C H A P T E R 1 9 S E C U R I T Y

System::Security is the primary namespace that provides the underlying structure of the

.NET security system.

System::Security::AccessControl provides security access information on objects like Active Directory, files, the Registry, mutex, and Semaphores.

System::Security::Authentication contains a set of enumerations that describe the security of a connection.

System::Security::Cryptography provides cryptographic services, including secure encoding and decoding of data.

System::Security::Permissions provides classes that control access to operations and resources based on policy.

System::Security::Policy contains code groups, membership conditions, and evidence.

System::Security::Principal defines a principal object that represents the security context under which code is running. In other words, it is a user, machine, or server that can be positively identified via authentication.

Which combination of namespaces you use depends mainly on the type of security your application is performing. For the most part, with role-based security you will use System::Security,

System::Security::Principal, and System::Security::Permissions, and for code access security you will use System::Security, System::Security::Policy, and System::Security::Permissions.

Role-Based Security

When someone traditionally thinks of securing their computer system, role-based is usually what they are thinking about. It is the process of specifying and then allowing a user to access specific resources and functionalities of your system based on the role that the user performs. Common roles are administrator, user, and guest. Each of these roles has a set of resources that the user can access and functionalities that they may perform. Roles are not mutually exclusive; in fact, it is a common practice to combine roles into a hierarchy where the top of the hierarchy provides unlimited access and functionality and as you navigate down the hierarchy the role’s rights become more restrictive. Of course, you can also build security in a haphazard way where roles have no interdependencies (though nearly always there is an administrative role that has the rights and privileges of all other roles).

.NET’s role-based security works well in conjunction with Windows’ user accounts and Active Directory (AD) users, but you are not restricted to either of these, since you can create roles dynamically at runtime that are neither a Windows user account nor an AD user.

To implement role-based security in .NET, you need two pieces: the user and the roles that the user belongs to. In .NET-speak, the user is represented by the identity object and the roles that the identity object belongs to are represented by the principal object. (I would have been quite happy with simply the user object and roles object, but hey, I didn’t write the .NET Framework.)

Identities

The .NET Framework provides two identity objects: WindowsIdentity and GenericIdentity. The WindowsIdentity object consists of Windows users that you maintain using the Computer Management administrative tool, as shown in Figure 19-1.

C H A P T E R 1 9 S E C U R I T Y

777

Figure 19-1. Users in the Computer Management administrative tool

The GenericIdentity, on the other hand, consists of users that you create dynamically at runtime. Both WindowsIdentity and GenericIdentity share the interface IIdentity, which makes things easier as methods need only use the interface to handle both types of identity.

Note You can create your own custom identities using the IIdentity interface, though personally I have found that GenericIdentity has provided all the functionality I’ve needed.

The IIdentity interface exposes three simple properties:

AuthenticationType is a string that indicates the type of authentication used by the identity object. When you are working with Windows, this value will be either Basic, Forms, Kerberos, NTLM, or Passport. (You will most likely find this value is NTLM as it is used by Windows for logon authentication on stand-alone systems.)

IsAuthenticated is a Boolean value that represents whether the identity object has been authenticated.

Name is, well, you know… the name associated with the identity object.

Principal

Like the identity, the .NET Framework provides two principal objects: WindowsPrincipal and GenericPrincipal. The WindowsPrincipal object more or less maps to the Windows group and is also maintained by the Computer Management tool, as shown in Figure 19-2. (Group, principal, and role are all basically the same thing when it comes to role-based security… hmmm, let’s just make things confusing, shall we?)