Вы находитесь на странице: 1из 39

Visual C++ .

NET Tutorial
Using the ATL CImage Class

Visual Studio.NET introduced Managed C++, but that wasn't the only thing that was new in it. A number of interesting changes to MFC and ATL make life simpler for those who are working in unmanaged C++. In this column I'll introduce you to the CImage class, which has been added to ATL and adds enhanced bitmap support. Figure 1 shows a simple application that displays and converts images. This is an ordinary MFC dialog application. I added a textbox, two buttons captioned "Load" and "Save As JPG," and a picture control. Then I deleted the Cancel button, and changed the caption (but not the ID) of the OK button to Done.

Figure 1 - Displaying and converting images. Because this is a dialog application, AppWizard created a dialog class for me. I added variables by right-clicking on the surface of the dialog in the resource editor and choosing Add Variable. (ClassWizard is gone in this version of Visual Studio, so you have to learn how to get to the Add Member Variable Wizard dialog.) Here's a summary of the variables I added, all private:

Control ID IDC_PICTURE IDC_FILENAM E IDC_SAVE

Variable Name Control/Value m_picture m_filename m_savebutton Control Value Control

Variable Type CStatic CString Cbutton

I used the Properties Window to disable the "Save As JPG" button by default (there's code later to enable it.) I also changed the Type of the picture control to Bitmap. And finally, I edited the header file for the dialog class, adding a member variable called m_image of type CImage. At the top of the header file go these two #include statements:
#include <afxstr.h> #include <atlimage.h>

It's important that these include statements appear in this order. This is really the only indication you have that CImage is an ATL class rather than an MFC class. Here's how simple it is to load a GIF or JPG from the hard drive into a CImage object and show it in the picture control:
void CImageDlg::OnBnClickedLoad() { UpdateData(TRUE); if (m_filename == "") m_filename = "Please enter a file name first"; else { if (m_image.Load(m_filename) == S_OK) { m_picture.SetBitmap((HBITMAP)m_image); m_picture.Invalidate(); m_savebutton.EnableWindow(TRUE); } else { AfxMessageBox("File not found or invalid format"); } } UpdateData(FALSE); }

I even have room for a little error checking! I also enable the "Save As JPG" button once an image is successfully loaded. If you're adapting this code for your own use, don't skip the call to Invalidate() - it ensures the picture control will redraw itself. What about converting a GIF to a JPEG? All you have to do is save the image. The format is implied by the file name. Here's how to do it:
void CImageDlg::OnBnClickedSave() { CString filename = m_filename.Left( m_filename.Find('.')) + ".JPG"; m_image.Save(filename);

Two lines of code! How hard is that? What else can you do with a CImage object? Well, you don't have to load one from an existing picture file. You can draw on it by creating a device context associated with the bitmap inside the CImage:
CDC* pDC = CDC::FromHandle(image.GetDC());

Be sure to call ReleaseDC afterwards. CImage supports transparency, alpha blends and a variety of other cool effects, on reasonably recent versions of Windows. If you are writing unmanaged (Classic C++) code that needs to work with images, look into CImage. While it is part of ATL, you can't tell, can you? There's no sign of templates or anything tricky at all. And in my next column, I'll show you the Managed C++ equivalent for this application. Using the ATL CImage Class Simple Role-Based Security in Managed C++ <http://www.codeguru.com/system/KG101802.html> The .NET Framework makes things so much simpler for developers who care about security. There are a lot of useful ways to make sure that only authorized users update the database, place orders, or otherwise interact with your system. In addition, code access security ensures that only trusted code can perform certain tasks, no matter who runs the code. In this column I focus only on one small topic: how to establish that the user running your application is authorized to perform some subtask within the application. You can extend the examples here to any application you write in Managed C++.

Who's There?
Here's a simple console application:
using namespace System; using namespace System::Security::Principal; // This is the entry point for this application int _tmain(void) { WindowsIdentity* Identity = WindowsIdentity::GetCurrent(); WindowsPrincipal* Principal = new WindowsPrincipal(Identity); //Print the values. Console::WriteLine("Principal Values for current thread:"); Console::WriteLine("Name: {0}", Principal->Identity->Name); Console::WriteLine("Type: {0}", Principal->Identity->AuthenticationType); Console::WriteLine("IsAuthenticated: {0}", __box(Principal->Identity->IsAuthenticated));

Console::WriteLine(); Console::WriteLine(); Console::WriteLine("Identity Values for current thread:"); Console::WriteLine("Name: {0}", Identity->Name); Console::WriteLine("Type: {0}", Identity->AuthenticationType); Console::WriteLine("IsAuthenticated: {0}", __box(Identity->IsAuthenticated)); Console::WriteLine("IsAnonymous: {0}", __box(Identity->IsAnonymous)); Console::WriteLine("IsGuest: {0}", __box(Identity->IsGuest)); Console::WriteLine("IsSystem: {0}", __box(Identity->IsSystem)); Console::WriteLine("Token: {0}", Identity->Token.ToString()); return 0; }

The first line of this code gets the identity under which the application is running. That should be you, since the application doesn't contain any code to change the identity. Then it gets a principal based on that identity. The remainder of the code illustrates some useful properties of the identity and principal objects. Notice the use of __box to wrap up the Boolean values, and the ToString method to convert non-string values to strings. When I run this application, it prints out:
Principal Values for current thread: Name: GREGORY\kate Type: NTLM IsAuthenticated: True

Identity Values for current thread: Name: GREGORY\kate Type: NTLM IsAuthenticated: True IsAnonymous: False IsGuest: False IsSystem: False Token: 304

You could do a little security code of your own at this point, comparing the name of the identity or principal with a list of names of authorized users that you have stored somewhere. But that will require you to write code to manage user lists: adding and deleting users, changing their authority code, and so on. I prefer to leverage Windows groups. Even if they aren't in use by the people who'll be running the application, it's less work to teach people to use Windows groups than to write your own equivalent administration section.

What Group Are You In?


This code checks to see if a user is in a particular Windows group or not:
String* role = "GREGORY\\Domain Users"; if (Principal->IsInRole(role)) Console::WriteLine("You are a domain user"); else Console::WriteLine("You are not a domain user");

role = "BUILTIN\\Administrators"; if (Principal->IsInRole(role)) Console::WriteLine("You are an administrator"); else Console::WriteLine("You are not an administrator"); role = "JSERV\\CodeGuru"; if (Principal->IsInRole(role)) Console::WriteLine("You are a Code Guru"); else Console::WriteLine("You are not a Code Guru");

When I run this code, it prints out:


You are a domain user You are an administrator You are a Code Guru

For you, it almost certainly will not. Notice the three different prefixes in the role strings:

GREGORY is my domain. Use this prefix when you're referring to a group that has been created on your domain controller. BUILTIN refers to built-in groups such as Administrators, created when you install Windows 2000 or NT. JSERV is my machine name. Use this prefix when you're referring to a local group.

You can test this code by creating a local group, and adding yourself to it. If you've never done that before, you can use Computer Management. Right-click My Computer on your desktop and choose Manage. Expand the Local Users and Groups section, then click on Groups.

Click here for larger image Choose Actions, New Group to create a group. Name it CodeGuru, and click Add to add users to it. Add yourself. Then log off Windows and log back on again to update your security profile, and run the application again. Now you should be told you are a Code Guru. (And don't forget about the logging off and logging on. You'll do a lot of that when you're testing security code.)

You could use tests of IsInRole() to decide whether to enable or disable a button. Or you could throw a security exception when the wrong kind of user tried to do something. If you'd like to do that, you're better off using permissions sets.

Permission Sets
Permission sets can be used imperatively or declaratively, the documentation likes to say, which means you can either call a function or add an attribute. Let's start with the imperative way. I added a class to my console project. It isn't even a managed class, and it only has a constructor and destructor:
class AccessControlled { public: AccessControlled(void); ~AccessControlled(void); };

(I don't need a destructor, but I right-clicked the project in Class View and chose Add, Add Class and added a generic C++ class, and that gave me a constructor and destructor.) The implementation file looks like this:
#include "StdAfx.h" #using #include "accesscontrolled.h" using namespace System; using namespace System::Security::Permissions; using namespace System::Security::Principal; AccessControlled::AccessControlled(void) { AppDomain::CurrentDomain->SetPrincipalPolicy( PrincipalPolicy::WindowsPrincipal); //String* role = "BUILTIN\\Administrators"; String* role = "JSERV\\CodeGuru"; PrincipalPermission* p = new PrincipalPermission(0,role); p->Demand(); } AccessControlled::~AccessControlled(void) { }

The first line of this constructor is vital: without it the code will compile but will always react as though you are not in the group. The SetPrincipalPolicy instructs the framework to create WindowsPrincipal objects by default. The code creates a PrincipalPermission object that represents being in a particular role. (The role names used in this example follow the same rules as those used for IsInRole().) The first parameter is 0 here, to indicate that any user identity is acceptable. You can pass in a string representing a specific user if you wish. Having created a PrincipalPermissions object, you now demand that the individual running this application meets the conditions

in that object. If the demand fails, a security exception will be thrown. I added a few more lines to _tmain() to create an AccessControlled object, triggering a call to the constructor:
AccessControlled* ac = new AccessControlled(); Console::WriteLine("created the access controlled object");

When I am in the CodeGuru group, this works. When I am not, it throws a SecurityException. The other way to use PrincipalPermissions is as an attribute, declaratively. This requires the class to be garbage collected, since only managed classes can have attributes:
using namespace System::Security::Permissions; [PrincipalPermissionAttribute(SecurityAction::Demand, Role = "BUILTIN\\Administrators")] __gc class AccessControlled { public: AccessControlled(void); ~AccessControlled(void); };

Now the call to SetPrincipalPolicy must be before the attempt to construct the AccessControlled object:
AppDomain::CurrentDomain-> SetPrincipalPolicy(PrincipalPolicy::WindowsPrincipal); AccessControlled* ac = new AccessControlled(); Console::WriteLine("created the access controlled object");

The constructor itself no longer needs any security code. No-one can create or use an AccessControlled object unless they are in the specified role.

Conclusion
When you build .NET applications, you get a lot of security code without writing it yourself. It's easy to determine who a user is, and what role (Windows group) the user is in. You can deny access to specific methods or entire classes based on the user's identity. Now there's no excuse for skimping on security. Oh, and one more tip: if you're administrator on your own machine, stop it! Give yourself another account with less power, and find out what life is like for the users of those applications you write. I assure you, it will be an eye-opener.

Using PInvoke To Call An Unmanaged DLL From Managed C++

If you've been maintaining a large and complex C++ application for several years, it

probably serves as a mini-museum of technology all by itself. There will be code that interacts with the user, business logic, and data access logic - and I hope it's not all together in one giant source file. Chances are that you've used several different techniques to split that code into modules. One popular technique is COM, and creating and using COM components in Visual C++ 6 is relatively easy. But it's not the only way to reuse code: DLLs are relatively easy to write and use from Visual C++ 6, and many developers found them easier than COM for code that was only to be called from one place. Chances are your system has a few COM components and also a few DLLs. The COM Interop story is vital to the acceptance of .NET by today's programmers. You need access to the huge body of working tested code that is in production today, deployed as COM components. Your .NET code can call old COM code after someone (either the COM developer, or you if no-one else is available) creates a Runtime Callable Wrapper for the component. If you write a new component in .NET, it can be used by old COM code after someone (ok, probably you again) creates a COM Callable Wrapper and registers the interop assembly. These techniques are the same across the .NET languages, because they involve typing commands like tlbexp or tlbimp at a command prompt. Once the appropriate wrapper has been created, you're in It Just Works territory: the .NET code acts as though the COM component is really a .NET object, or the COM code calls the new exciting .NET object as though it were a traditional COM component. So let's go on past the COM Interop story to the PInvoke story. The P stands for platform and it's a reference to the underlying platform on which the Common Language Runtime is running. Today that will be some flavor of Windows, but in the future, who knows? There are projects underway to recreate the CLR on non-Windows platforms.

Calling a function in a DLL


I'm going to use examples of calling functions from the Win32 API, even though you would never actually call these functions from a managed C++ application - they are encapsulated very conveniently in the Base Class Library. It's just that these examples are sure to be on your machine, so using them spares you the trouble of writing a DLL just to call it. For my first example, I'm going to use GetSystemTime(). The documentation tells you that it takes an LPSYSTEMTIME, which it fills with the current UTC time. The documentation tells you the name of the function, the parameters it takes, and the fact that it's in kernel32.lib. (You can't use the version in the .lib file, but there's a corresponding kernel32.dll that you can use.) This is everything you need to declare the function prototype in an unmanaged application, like this:
using namespace System::Runtime::InteropServices; [DllImport("kernel32.dll")] extern "C" void GetSystemTime(SYSTEMTIME* pSystemTime);

The DllImport attribute tells the runtime that this function is in a DLL, and names the DLL. The extern C takes care of the rest. Now you need to teach the compiler what a SYSTEMTIME pointer is. By clicking on LPSYSTEMTIME on the help page for

GetSystemTime(), you can find a definition of the SYSTEMTIME struct:


typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME, *PSYSTEMTIME;

If you copy and paste this struct definition into a Managed C++ application, it won't compile - WORD is a typedef that isn't automatically usable in .NET applications. It just means a 16-bit integer, anyway, and that's a short in C++. So paste it in (before the prototype for GetSystemTime()) and change all the WORD entries to short. Now here's some code that calls this function:
SYSTEMTIME* pSysTime = new SYSTEMTIME(); GetSystemTime(pSysTime); Console::WriteLine("Current Month is {0}", __box(pSysTime->wMonth));

What could be simpler? When this code runs, it prints out:


Current Month is 9

The documention is clear that months are 1-based in the SYSTEMTIME structure, so yes indeed September is month 9. Remember, this example is here to show you how to declare the function type and the structures it uses, not how to get the current time and date. A .NET application gets the current time and date with DateTime::Now():
System::DateTime t = DateTime::Now; Console::WriteLine( "Current Month is {0}", __box(t.get_Month()));

(The trick here is to remember, or to be reminded by Intellisense, that DateTime is a value class, which means that C++ applications don't have to work with a pointer to a DateTime, but can just use a DateTime object as you see here. Also, Now is a property, not a function, of the DateTime class. Let Intellisense help you, because these things are non-trivial to keep track of yourself. It shows different symbols for properties and functions.)

Dealing with Strings


The GetSystemTime() function involved a struct, and once you made a managed-code version of the struct definition, it was simple enough to work with. Things are little trickier when the DLL function you want to call works with a string. That's because string manipulation is a bit of a technology museum itself. Strings come in two basic flavors: ANSI, or single-byte, and wide, or double-byte. Strings in .NET are all Unicode which are compatible with wide strings in the Win32

world. Many of the Win32 functions that use strings actually come in two versions: one for ANSI strings and one for wide strings. For example there is no MessageBox function really: there is a MessageBoxA and a MessageBoxW. This is hidden from you when you program in Visual C++ 6 by a #define that changes function names based on your Unicode settings. It's hidden from you in .NET by the Interop framework. If you wish, you can declare MessageBox with a DllImport attribute that only specifies the DLL. The declaration from the online help is:
int MessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

This translates into the following .NET signature:


MessageBox( int hWnd, String* lpText, String* lpCaption, unsigned int uType);

How do I know? Handles and the like generally map to integers. Pointers to various kinds of strings become String*. UINT is an unsigned int. If you're stuck, do a search in the online help for one of the many tables of data types that are scattered about. One of them will have the mapping you need. Here's the prototype for MessageBox:
[DllImport("user32.dll")] extern "C" void MessageBox( int hWnd, String* lpText, String* lpCaption, unsigned int uType);

And here's a simple way to use it:


MessageBox(0,"Hi there!","",0);

This is, however, a little wasteful. You know (or you should know) that "Hi There" is a Unicode string, and that MessageBoxW is the function you really want. Here's a revamp of the prototype that makes it clear:
[DllImport( "user32.dll", EntryPoint="MessageBoxW", CharSet=CharSet::Unicode)] extern "C" void MessageBox( int hWnd, String* lpText, String* lpCaption, unsigned int uType);

call the function the same way, but the DllImport attribute is now mapping directly to the right entry point, and using the right marshaling technique, without having to make these decisions on the fly every time you call this function from the DLL. Don't forget that MessageBox() is just an example of a function call that takes a string and that most developers are familiar with. If you really want to display a message box from within your code, use the MessageBox class in System::Windows::Forms (remember to add a #using statement to the top of your source code mentioning

System.Windows.Forms.dll, where this class is implemented. Here's an example:


System::Windows::Forms::MessageBox::Show(NULL, "Hi from the library!","", System::Windows::Forms::MessageBoxButtons::OK, System::Windows::Forms::MessageBoxIcon::Information);

Conclusion
Do you have useful, even vital functionality wrapped up in a DLL? Would you like to be able to access that DLL from your new .NET applications? Well, go ahead. All you have to do is declare a prototype of the function with a DllImport attribute and an extern C linkage. Declare any structures the function uses, and just call it as though it were managed code. (Remember that it isn't, though, and that means your application must have permission to execute unmanaged code.) Give the framework a hand and tell it you're using wide strings, and away you go. Old technologies can never die as long as they can be called from new ones.

Calling a Web Service Asynchronously

Web services are a terrific way to integrate disparate systems: cross-platforms, crossprogramming-languages, cross-everything. No matter what language you work in, you should be able to do an HTTP GET and parse a little XML. As I showed in an earlier column, it's incredibly simple if you're using Visual C++ to both create and consume Web Services. But to drill a little deeper into the problem, you mustn't forget that a Web service is accessed over the network. What if the network is slow? Or what if the Web service has to root around in a database and do all kinds of calculations before returning an answer? It can take quite a while to get a return value from the Web service, and you don't want your application to be blocked while you're waiting for that answer. Luckily the code that's generated for you when you mark a function with <WebMethod> goes beyond SOAP wrappers and HTTP listeners: you also get an asynchronous version of your web method, with no extra coding. This is an extension of a pattern set up throughout the Base Class Library (BCL) of the .NET Framework: potentially slow functions have a companion Begin method that calls the function asynchronously, so that your code is not blocked. The Begin method returns immediately and when the slow function is finished, a function you write (known as a callback) is called to notify your code that the process is complete. Your callback calls a companion End method that returns the value calculated for you.

A slow web service


I started with a copy of the Calculator project I used in my previous column, and added another Web method called SlowAdd(). I added the declaration of it to Calculator.h:

[System::Web::Services::WebMethod] double SlowAdd(double x, double y);

Then the implementation to Calculator.cpp:


double CalculatorService::SlowAdd(double x, double y) { System::Threading::Thread::Sleep(1000); // 1 second return x + y; }

This does the same job as Add(), but adds a one-second pause to imitate a slow connection or a slow calculation by the web service. Notice that I don't have to add any code to make this service available asynchronously.

Calling the web service


If you want to start with a copy of the CalcTest project I used in my previous column, you'll need to update the WSDL that Visual Studio uses to create a proxy for the web service. In Solution Explorer, right-click Calculator.wsdl and choose Update Web Reference. If you're starting from scratch, create a Managed C++ application, add a web reference to the Calculator web service, and you're ready to go. The main function starts out simply enough. Here's a version that only makes the synchronous call, as you saw last time:
int _tmain(void) { CalculatorService * Calc = new CalculatorService; System::Console::WriteLine("1 plus 1 synchronously is {0}", __box(Calc->Add(1,1))); return 0; }

Now for the asynchronous work. If I were to call SlowAdd(), it would be a synchronous call. To make an asynchronous call, I call BeginSlowAdd() instead, which was generated for me when I marked SlowAdd() as a Web method. The Begin methods need to be passed a callback object to indicate where the notification will go when the slow method has completed. That's what frees your application to go on and do something more excisting instead of being blocked waiting for the web method to return. You'll find lots of Visaul Basic and C# examples of creating, but C++ examples are a little thin on the ground, and that's a shame because it is a bit tricky. Instead of just passing the address of the function to the delegate constructor, you need to pass two parameters: a pointer to an object instance and a function pointer to the member function. Well here's problem number one: this is a console application, and it's object-free at the moment. That means you have to write an object whose only purpose in life is to hold this member function. It needs to be a managed object, so you'll need the __gc qualifier on the class definition. I added this code before _tmain():
__gc class CallBack { public: void AddCallback(System::IAsyncResult* ar); };

The signature of this callback method is pre-established for you. When you call a Begin method you must pass it not just any old function address, but the address of a function with this signature: it takes an IAsyncResult pointer and returns void. I'll show you the code for this function shortly, but in the meantime here are the lines I added to _tmain():
System::Console::WriteLine("adding 2 plus 2 asynchronously"); CallBack* callbackobject = new CallBack(); System::AsyncCallback* callback = new System::AsyncCallback(callbackobject, &CallBack::AddCallback); Calc->BeginSlowAdd(2,2,callback,Calc); for (int i= 0; i<20; i++) { System::Console::WriteLine("."); System::Threading::Thread::Sleep(100); // 0.1 second }

This code creates an instance of the class and makes an AsyncCallback from that instance. It then calls BeginSlowAdd(), passing in the two parameters that SlowAdd() expects, the callback, and the web service proxy itself - this last parameter will be given to the callback, as you'll see shortly. After the call to BeginSlowAdd(), just to prove that the application can do something else while the slow Web method proceeds, the code goes on to sleep for a tenth of a second at a time, and print out dots. This lets you know it's working. The callback itself is a member function of the CallBack() class I created. The implementation looks like this:
void CallBack::AddCallback(System::IAsyncResult* ar) { CalculatorService * Calc = (CalculatorService*) ar->AsyncState; double answer = Calc->EndAdd(ar); System::Console::WriteLine("result is {0}", __box(answer)); }

The first thing this code does is to get the AsyncState pointer out of the IAsyncResult that was passed to the callback and cast it back to a CalculatorService pointer. You know that's what it is, because that's what was passed as the last parameter to BeginSlowAdd(). I am using an old-fashioned C-style cast here for readability: it gets me a warning that I should use the static_cast template instead. Once this code has a CalculatorService pointer, it uses it to call EndAdd(), which returns the answer to the original question (2+2, in case you've forgotten). It then boxes the answer and passes it to Console::WriteLine(). When this code runs, the exact delay before the answer appears will vary, but generally you will see output like this:
1 plus 1 synchronously is 2 adding 2 plus 2 asynchronously . . . . .

. . . . . . result is 4 . . . . . . . . .

You can see that the result from the slow Web service just pops in among the regular stream of dots from the loop in _tmain(). This demonstrates that the Web service is being called asynchronously. There is no effort for a Web service developer to offer asynchronous versions of all the Web methods in the Web service at all, and little effort required to call a Web service asynchronously. It's worth looking into!

Working with XML in Managed C++


Using Visual C++ .NET... <http://www.codeguru.com/columns/Kate/index.html> August 23, 2002

In my last column, I showed you how to use MSXML4, the COM component that parses, searches, and generates XML, to work with XML in a "classic Visual C++" application. This time around I'm going to tackle the same tasks, but use the .NET Framework to process XML. The advantages of using the XML functionality that are built into the .NET class libraries include:

Nothing to install, deploy, or redistribute - your apps need the framework anyway, and once you have the framework you have everything you need Simpler code because you're not working with COM - for example you don't need to call CoInitialize() and CoUninitialize(). Extra functionality that wasn't in MSXML4.

Sample XML
I'm going to use the same sample XML as I did in the previous column. Here's how it looks:
<?xml version="1.0" encoding="utf-8" ?> <PurchaseOrder>

<Customer id="123"/> <Item SKU="1234" Price="4.56" Quantity="1"/> <Item SKU="1235" Price="4.58" Quantity="2"/> </PurchaseOrder>

Loading XML with XmlDocument The classes for working with XML are in the System::Xml namespace. The XmlDocument represents a DOM document, a tree into which your XML can be loaded. It's the same idea as the DOMDocument you worked with using MSXML4. Here's a simple Managed C++ application that loads a file of XML into memory:
#include "stdafx.h" #using <mscorlib.dll> #include <tchar.h> using namespace System; #using <System.Xml.dll> using namespace System::Xml; // This is the entry point for this application int _tmain(void) { XmlDocument* xmlDoc = new XmlDocument(); try { xmlDoc->Load("sample.xml"); System::Console::WriteLine("Document loaded ok." ); } catch (Exception *e) { System::Console::WriteLine("load problem"); System::Console::WriteLine(e->Message); } return 0; }

The #using statement is really important. Without it, you'll get strange compiler errors like 'Xml' : is not a member of 'System' or 'Xml' : a namespace with this name does not exist. When you're working in C# or VB.NET, there's a Project, Add References menu item that takes care of this for you, but as a C++ programmer, you just do it yourself. You can find the assembly to include in the online help for the class or namespace. Notice also that this code doesn't use a return value from Load() as the COM approach did. The Load() method will throw an exception if it can't load the XML. Simple arithmetic with the contents of a document In my last column, I wrote code to flip through all the elements in a document and determine the total price. Here's the equivalent code the .NET way:

xmlDoc->Load("sample.xml"); double total = 0; System::Console::WriteLine("Document loaded ok." ); XmlNodeList* items = xmlDoc->GetElementsByTagName("Item"); long numitems = items->Count; for (int i=0;i<numitems;i++) { XmlNode* item = items->Item(i); double price = Double::Parse(item->Attributes->GetNamedItem("Price")-> get_Value()); double qty = Double::Parse(item->Attributes->GetNamedItem("Quantity")-> get_Value()); total += price * qty; } System::Console::WriteLine("Purchase Order total is ${0}", __box(total));

I just added this code in the try block. If you're converting an application from COM and MSXML4 to .NET, notice that the capitalization is quite different, and the functions just return the values you need rather than taking a pointer to an object that they change for you. That makes it simpler to chain long strings of calls together as I've done here. Because Managed C++ is a little pickier about types, I've had to use Double::Parse() to change strings to numbers, and to box up total before passing it to WriteLine(). When this application runs, it prints out:
Document loaded ok. Purchase Order total is $13.72

If you want to dump the XML out, you just need one line of code:
System::Console::WriteLine(xmlDoc->InnerXml);

InnerXml is a String*, so there's no problem passing it to WriteLine() and no need to go through any intermediate variables.

Where does XML fit in .NET?


Throughout .NET you'll see places where XML belongs. For example, any class can serialize itself to and from XML if you just add the Serializable attribute to it when you declare it. Here's a sample class that you might want to dump out as XML, or fill from XML:
[Serializable] public __gc class PurchaseOrder { public: int customer; // other elements to be added PurchaseOrder(int id) {customer=id;} PurchaseOrder() {customer=0;} };

You mark the class with the Serializable element. Only public variables can be serialized

like this, and the class must have a default constructor. Once you've complied with those restrictions, life gets pretty simple. Here's how to dump out the object to a file:
PurchaseOrder* po = new PurchaseOrder(Int32::Parse(xmlDoc-> GetElementsByTagName("Customer")->Item(0)-> Attributes->GetNamedItem("id")->get_Value())); System::Xml::Serialization::XmlSerializer* ser = new System::Xml::Serialization::XmlSerializer(po->GetType()); System::IO::TextWriter* writer = new System::IO::StreamWriter("po.xml"); ser->Serialize(writer,po); writer->Close();

This would be a lot more readable if you used namespaces, but I wanted you to see the full namespaces associated with the classes I'm using here. The first line, which is very long, roots through the XML document looking for Customer tags, grabs the first one, looks for an attribute called id, and passes its value to the constructor for PurchaseOrder. Then I create a serializer and a writer, and called the Serialize() method of the serializer. The po.xml file comes out like this:
<?xml version="1.0" encoding="utf-8" ?> <PurchaseOrder xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <customer>123</customer> </PurchaseOrder>

If PurchaseOrder was a large and complex class, perhaps using one of the .NET collection classes to hold a collection of items, I would still need just this little block of code to write it out as XML: make a serializer and a writer, then call Serialize(). To rehydrate objects from XML, you just do the same work in reverse:
PurchaseOrder* po2 = new PurchaseOrder(); ser = new System::Xml::Serialization::XmlSerializer(po2->GetType()); System::IO::FileStream* fs = new System::IO::FileStream("po.xml", System::IO::FileMode::Open); po2 = (PurchaseOrder*) ser->Deserialize(fs); System::Console::WriteLine("Rehydrated customer is {0}.", __box(po2->customer));

If you're using DataGrids in either ASP.NET or Windows applications, you can load them from a file of XML (wonderful for quick prototypes) or as part of your debugging, have them dump themselves out to XML when you need to. Just make an empty DataSet object, call its ReadXml method to fill it with XML from a file, then make it the data source for your DataGrid and call DataBind(). Or grab the XML from the DataGrid like this:
xmlstring = DataGrid1->DataSource->GetXml();

There's so much more you can do with XML using the .NET class libraries. In just a line or two, you can generate XML, transform from one layout to another, or find subsets of a document. With the schema support in Visual Studio you can generate a schema that matches a sample XML file, or edit XML with Intellisense that knows the schema you're using and reminds you what elements or attributes are allowed where you're typing. XML is at the heart of the .NET revolution, so you should see what it's all about sooner

rather than later.

Working with XML in a Classic COM Application


Using Visual C++ .NET... <http://www.codeguru.com/columns/Kate/index.html> August 14, 2002

XML is at the heart of .NET. You can hardly read a single page of a .NET article, whitepaper, or help entry without coming across those three little letters. But XML was changing everything before .NET came along, and you can work with it from a "traditional" Win32 application. In this column, I'll cover how to read, write, and transform XML using the Microsoft COM component called MSXML4. Next time, I'll tackle the same tasks the .NET way. First, if you don't have MSXML4 installed, you're going to need it. You can get it from <http://msdn.microsoft.com/downloads/default.asp?url=/downloads/sample.asp?url=/msd n-files/027/001/766/msdncompositedoc.xml> and follow the instructions there to install it on your development machine. If you're planning to distribute applications that use MSXML4, you'll want to get the redistributable cab file too. Second, if you have no clue what XML is or why you should care, here's a quick laundry list of XML features. XML is a notation for representing structured and semi-structured data that:

is plain ASCII text that can be read and understood by people is plain ASCII text that can easily be hand-typed can be processed in a just a few lines of code using freely-available (often free) components for essentially any language and operating system can be generated in a just a few lines of code using those same components can be easily transformed into HTML, PDF, Postscript or a variety of other printfriendly and display-friendly formats can be persisted back and forth to almost any database format using widely available components features as few rules and pre-requisites as possible for maximum availability and flexibility

If you'd like to know more, check out www.xml.org <http://www.codeguru.com/columns/Kate/www.xml.org> or www.w3.org/XML/1999/XML-in-10-points <http://www.codeguru.com/columns/Kate/www.w3.org/XML/1999/XML-in-10-points> to see what all the fuss is about.

Sample XML

Here's a really simple file of XML to use in the sample application:


<?xml version="1.0" encoding="utf-8" ?> <PurchaseOrder> <Customer id="123"/> <Item SKU="1234" Price="4.56" Quantity="1"/> <Item SKU="1235" Price="4.58" Quantity="2"/> </PurchaseOrder>

Loading XML with COM


The simplest application to demonstrate working with XML is a console application. To create one in Visual Studio .NET, choose File, New, Project, Visual C++ Projects, Win32 application. Change the application settings to Console Application. (Still using Visual C++ 6? Make a console app by choosing File, New Project, Win32 application and change the settings to Console Application. You should be able to use this same code.) I named mine XMLCOM, so my _tmain() function is in XMLCOM.cpp. Here's what it looks like:
#include "stdafx.h" #import "msxml4.dll" using namespace MSXML2; #include <iostream> using std::cout; using std::endl; int _tmain(int argc, _TCHAR* argv[]) { CoInitialize(NULL); { //extra braces for scope only MSXML2::IXMLDOMDocumentPtr xmlDoc("MSXML2.DOMDocument.4.0"); xmlDoc->async = false; bool ret = xmlDoc->load("sample.xml"); if ( ret) { cout << "Document loaded ok." << endl; } else { cout << "load problem" << endl; } } CoUninitialize(); return 0; }

You can see that I'm using #import to bring in the COM library to make my coding as simple as possible. I call CoInitialize() at the start, to get COM ready for me, and CoUninitialize() at the end. In between I make an instance of the COM object on the stack. It looks like a pointer, but it's really an object using a template set up for me by the #import statement. It will clean up after itself when it goes out of scope, so I've wrapped

it up in an extra set of brace brackets just so that I can send it out of scope before calling CoUninitialize(). That part should be familiar to COM programmers who've used the #import timesavers before. Where's the XML part? The call to load(). This function takes a URL or a relative file name, and reads in the XML from that URL or in that file. It returns false if there's a problem, such as the file not being found or the XML in it not being well-formed. One line of code to get all that XML parsed and into memory. Now you can do things with it.

Simple arithmetic with the contents of a document


Here's a simple thing to do:
double total = 0; cout << "Document loaded ok." << endl; MSXML2::IXMLDOMNodeListPtr items = xmlDoc->getElementsByTagName("Item"); long numitems; items->get_length(&numitems); for (int i=0;i<numitems;i++) { MSXML2::IXMLDOMNodePtr item ; items->get_item(i, &item); double price = item->attributes->getNamedItem("Price")->GetnodeValue(); double qty = item->attributes->getNamedItem("Quantity")->GetnodeValue(); total += price * qty; } cout << "Purchase Order total is $" << total << endl;

I put this code in the if (ret) block, replacing the single output statement that was there before. You can see that it uses a variety of handy functions from the DOM API:

getElementsbyTagName() returns a list of all the <Item> elements. get_length() gets the length of the list of <Item> elements. get_item() gets an item from the list. attributes is a property of the item, and it's a list of attributes on the item. The list is held as a map, or lookup table. getNameItem() looks up entries in the map using the string provided getNodeValue() extracts the contents of the attribute so you can use it in simple arithmetic

How did I memorize all that? I didn't. Intellisense helps tremendously. I knew about getElementsbyTagName(), it's one of the standard XML functions that all the XML processors support. The help told me what it returns, an IXMLDOMNodeList, and I tacked Ptr on the end so I could get the helper class created from the #import. Then it was just a matter of typing -> and seeing what Intellisense offered.

There's an interesting convention at work in the helper classes that are created for you when you use #import. Function names that start get_ take an address of something you allocated, and put a value in it, like this:
items->get_length(&numitems);

Function names that start Get (with no underscore) just return what you're looking for, like this:
double qty = item->attributes->getNamedItem("Quantity")->GetnodeValue();

Quite often both sets of functions are supported in a class, but the C++ samples in the help only show you the kind that takes a pointer. I guess C++ people are not supposed to like having answers returned to us, or something. Anyway, remember that both sets are there. When this application runs, it prints out:
Document loaded ok. Purchase Order total is $13.72

That's how simple it is to run through a file of XML and do something with it. But why stop there? You can transform XML from one layout to another -- or to HTML, or to any other number of formats. You can write it out to a file or the screen pretty easily, too:
_bstr_t text = xmlDoc->Getxml(); char* printable = text; cout << printable << endl;

This code uses the helper class _bstr_t which wraps a BSTR and provides a conversion to a char*. You need to ask for the conversion, though - you can't just send the BSTR to cout. Still, this is pretty neat stuff. I encourage you to play with XML whenever you get the chance. It really is changing everything. If you never were a COM programmer, get hives from HRESULTs and shudder at the thought of a BSTR, take heart! Next time you'll see this same work the .NET way.

Creating and Using a Web Service in Managed C++


Using Visual C++ .NET... <http://www.codeguru.com/columns/Kate/index.html> July 25, 2002

Web Services are a really exciting part of the .NET initiative - but they are bigger than .NET itself. The concept behind them is simple. Almost any web server you can name has some mechanism for executing server-side code: you type a URL into your browser, and something runs on the server and then writes out HTML in response to your request. It might be ASP, ASP.NET, servlets, even a five-year-old Perl script triggered through CGI. So imagine that the running code returns XML instead of HTML, and that it's called not because a user typed a URL into a browser, but because some code somewhere did a GET over HTTP to ask the web server for that URL. Now you've got an application-toapplication connection happening. Just about every programming language has a class library that makes it easy to do a GET request over HTTP and some easy way of parsing

XML, so this approach gives you a cross-platform, cross-programming-language, crossvendor, cross-everything way to have code in one application call code on a completely different machine, as long as they're both on the Internet or otherwise connected. That's the basic concept behind Web Services. But thinking of that code as a function instead of a page, how do you pass parameters to the function? What if it returns a complex data type? What if a web server has more than one web service on it? How can you find out the names, specify which service you want, and so on? This is where the capital letters are earned. There are standards being developed with names like Web Services Description Language (say wizdle to sound ultra-cool) that cover these technical issues for you. If you build a Web Service with Visual Studio.NET, it will meet these standards. And if you consume a Web Service, no matter how it was built, with Visual Studio.NET, you'll see how amazingly easy it can be to leverage someone else's code.

Writing a Web Service


To write a Web Service, you write a class with at least one method. The class is marked with a [WebService] attribute, and the method with a [WebMethod] attribute. A class might represent a customer, for example, and have a method to get the customer's shipping information, or to add a new order to the customer's order list. Web methods can take and return any variable type, including instances of objects you define. And they can do anything: persist data to and from a database, make any kind of calculation, even call another web method to help get the job done. For this example, I'm going to write a CalculatorService class with a method called Add that - you guessed it - adds two numbers and returns the total. In Visual Studio.NET, I created a new project. From the Visual C++ project templates, I chose Managed C++ Web Service. I named the project Calculator. The code that is generated includes a class with the exciting name of Class1 - the first thing I did was change this name to CalculatorService in both the .cpp and .h files by hand, though I could have clicked on it in Class View and changed the name in the Properties Window. (I try to avoid having my class name and namespace name be the same, it can confuse Intellisense.) Whichever way I change the class name, I must also change it by hand in the file called Calculator.asmx, which I edit by double-clicking it in Solution Explorer. The edited Calculator.asmx looks like this:
<%@ WebService Class=Calculator.CalculatorService %>

I was given a method called HelloWorld() and it's simple enough to edit it into Add() - I just changed the name in both the .cpp and .h file, changed the signature so that it takes two floating-point numbers, and added code to return the total. The class declaration ends up looking like this:
using <System.Web.Services.dll> using namespace System; using namespace System::Web; using namespace System::Web::Services; namespace Calculator

{ public __gc class CalculatorService : public WebService { public: [System::Web::Services::WebMethod] double Add(double x, double y); }; }

The implementation looks like this:


#include "stdafx.h" #include "Calculator.h" #include "Global.asax.h" namespace Calculator { double CalculatorService::Add(double x, double y) { return x + y; } };

If you've been following along, you can test the code by choosing Start, Debug. You can't really run a web service, but this starts a web browser and loads Calculator.asmx into it. This is the runs-on-the-web-server file that actually uses your class and methods. If you prefer, open a browser and enter the URL yourself: <http://localhost/Calculator/Calculator.asmx> If you have any trouble, make sure that your web server is started, and that your browser isn't going through a proxy server. On the proxy server, localhost is the proxy server, not your machine. You can follow the link for Add to see some generated documentation for the method, and even test it by entering numbers in the edit boxes and clicking the Invoke button. When you do that, another browser window opens with the result of your call, wrapped up in XML. For example I entered 3 and 2 in the edit boxes, and got this XML:
<?xml version="1.0" encoding="utf-8" ?> <double xmlns="http://tempuri.org/">5</double>

Since 3 plus 2 does equal 5, the web service appears to be working.

Using a Web Service


Writing a web service was pretty easy: you just needed an attribute on the class, an attribute on the method, and the calculator.asmx file, and all three were generated for you by Visual Studio. But using one is even easier. I created a Managed C++ application called CalcTest to get started. Before you can use a web service, you need to teach your project where to find it. I right-clicked the CalcTest project in Solution Explorer and chose Add Web Reference. The Add Web Reference dialog has an edit box where you can type a URL. You can also use the UDDI directories to find Web Services throughout the Internet or test web services on your own machine. The simplest thing to do is to enter the URL to Calculator.asmx and press Enter. You will be shown the same

documentation you saw when you ran the web service project: click Add Reference to complete the process. Once the reference has been added, calling a web service is just like using any C++ class. Adding the reference creates a header file that you can include wherever you want to use the web service. I replaced the line that printed Hello World with a line to create the object that represents my web service and another to use it. The edited CalcTest.cpp looks like this:
#include "stdafx.h" #using <mscorlib.dll> #include <tchar.h> #include "WebService.h" using namespace System; // This is the entry point for this application int _tmain(void) { CalculatorService * Calc = new CalculatorService; System::Console::WriteLine("1 plus 1 is {0}", __box(Calc->Add(1,1))); return 0; }

(If the __box keyword doesn't ring a bell, check my previous column on boxing and unboxing fundamental types.) When this application runs, it prints out, not surprisingly I hope:
1 plus 1 is 2

And that's how simple it is to use a web method from within your own code. If you're wondering what happened to the XML and why you didn't have to parse the number out from inside it - that's just one of the cool things that Visual Studio took care of for your when you added the web reference. The possibilities for this are endless. Any code that can run on your server can, if you want, be accessed by other code through the Internet. Security, authentication, encryption and so on are all available to you and supported by SOAP, one of the standards involved in web services. Since those .asmx files are in fact part of ASP.NET, everything you know about ASP.NET pages applies to web services in .NET as well. Why not try your hand at a little application-to-application integration today?

Boxing Value Types in Managed C++


Using Visual C++ .NET... <http://www.codeguru.com/columns/Kate/index.html> July 12, 2002

Sometimes things that should be really simple don't feel simple at all when you try to do them. Take, for example, writing the value of a variable to the screen. You know how to do it in "classic" C++, say for example in Visual C++ 6:
int x = 3;

cout << "x is " << x << endl;

No problem. Whatever "Introduction to C++" you took, I'm willing to bet you saw something very much like these two lines of code less than 10% of the way into the course. Right?

Writing to the Screen


But what if you create a Managed C++ Application in Visual C++ .NET? Here's the little skeleton main() that's created for you:
int _tmain(void) { // TODO: Please replace the sample code below // with your own. Console::WriteLine(S"Hello World"); return 0; }

Now, you can paste your cout-using code into this main(), and it will work - after you add the usual include statement:
#include <iostream.h> // ... Console::WriteLine(S"Hello World"); int x = 3; cout << "x is " << x << endl;

You'll get a warning, though: warning C4995: '_OLD_IOSTREAMS_ARE_DEPRECATED': name was marked as #pragma deprecated To get rid of it, just use the iostream code from the STL, and bring in the std namespace:
#include <iostream> using namespace std;

The code will then build and run just fine. But to me, seeing that Console::WriteLine() in the same program as the cout line is a little confusing. What's more, Console::WriteLine() has neat features. It's more like printf in that it uses placeholders in a string to show where variable values should be put. For example, here's some working code from a C# console application:
int x = 3; Console.WriteLine("x is {0}",x);

The {0} is a placeholder, and the value of the second parameter will end up wherever that placeholder appears. So I want to use Console::WriteLine throughout my Managed C++ application, just as it's used in C#. But if you copy these lines into your Managed C++ application, and change the . to a ::, the application won't build. The compiler error is:
error C2665: 'System::Console::WriteLine' : none of the 19 overloads can convert parameter 2 from type 'int' boxpin.cpp(7): could be 'void System::Console::WriteLine( System::String __gc *,System::Object __gc *)' boxpin.cpp(7): or 'void System::Console::WriteLine( System::String __gc *,System::Object __gc * __gc[])'

while trying to match the argument list '(char [9], int)'

Now, I'm stubborn, and I expect C++ to be able to do everything the other .NET languages can do - and more. (Some of you may have noticed that already.) So why can't this simple little thing work? Well, if all else fails, read the error messages. I have given it an int for the second parameter, and it would like a pointer. In fact, a pointer to a System::Object (or some class derived from that, of course), a pointer to a __gc object. An int is none of those things. You could try passing &x instead of x, that at least would be a pointer, but it's no help. What WriteLine() wants is a pointer to an object. You can't hand the integer directly to WriteLine(), which (in the interests of genericity) has been written to handle pointers to garbage-collected objects, and nothing else. Why? Everything in the Base Class Library is designed to work with objects, because they can have member functions - not all .NET languages support the idea of casting or of overloading casting operators. For example, objects that inherit from System::Object all have a ToString() method. You don't want to write a class to hold this little not-an-object integer, and write a ToString() overload for it, plus deal with getting your integer into (and possibly out of) the class whenever you pass it to a Base Class Library method like WriteLine(). So how do you get your integer to WriteLine()?

The __box keyword


Managed C++ is also called Managed Extensions for C++. The word Extensions refers to the extra keywords, all starting with a double underscore, that have been added to the language. Like all keywords that start with a double underscore, they are compilerspecific - don't try these in Visual C++ 6 or a C++ compiler from any other vendor. You've just seen __gc in the error message when trying to compile the WriteLine() call. It stands for Garbage Collected and refers to an object that lives on the managed heap and is managed by the runtime. The __box keyword is the solution to the problem I've just presented about passing an integer to a Base Class Library method that's expecting a System::Object __gc * instead of an int. Here's how to use it:
Console::WriteLine("x is {0}",__box(x));

Boxing a value type means putting the value inside a temporary object, an instance of a class that inherits from System::Object and lives on the garbage collected heap, and passing the address of that temporary object to the method call. Whatever was in the original variable is bitwise-copied into the temporary object, and the temporary object provides all the functionality that WriteLine() will want. The __box keyword means that every service the Base Class Library provides will work with value types as well as with managed types.

Alternatives to Boxing
So boxing allows you to use both value types and managed types with Base Class Library methods that are expecting pointers to managed types. That naturally raises the question: what's the difference between a value type and a managed type? A managed type lives on the garbage collection heap and is managed by the runtime. Here's an example:
__gc class Foo

{ // internals omitted }; // ... Foo* f = new Foo();

The Foo class is a managed type. You can't create instances of it on the stack, like this:
Foo f2;

If you have a class already, perhaps from some pre-.NET application, it will not be a managed type. It doesn't have the __gc keyword. You could add the keyword (assuming the class meets all the rules for being a managed type - more on that in a future column) but then you would have to find all the places that created instances of the class and make sure they were creating the instance on the heap, like this:
OldClass* poc = new OldClass(); //maybe some parameters //to the constructor

And everywhere your code was calling methods of the class, you'd have to remember to change . to -> -- how tedious! Better to keep the class as an unmanaged class. You can allocate instances on the stack or the unmanaged heap as you prefer:
class notmanaged { private: int val; public: notmanaged(int v) : val(v) {}; }; // ... notmanaged nm(4); notmanaged *p = new notmanaged(5);

This is no big deal: it's just how C++ was before Managed Extensions were added with the release of Visual C++ .NET. But let's say you want to pass one of those instances to good old WriteLine():
Console::WriteLine("notmanaged holds {0}",nm);

You'll get the same error message as before: WriteLine() is no happier with this oldfashioned style class than it was with the integer. "Aha," you think, "I've learned a new keyword, I know what to do:"
Console::WriteLine("notmanaged holds {0}",__box(nm));

This gets you the rather obscure message "only value types can be boxed." Isn't notmanaged a value type? Well it isn't a managed type, but it doesn't inherit from the base class System::ValueType, and that's necessary to be boxed. Now, there is a handy __value keyword, which will cause the class to inherit from System::ValueType and be boxable, but for classes and structures I don't think boxing is the way to go. You don't just want the bits of data that are in your class or structure to be blurted out onto the screen. You want some control over the way your class is represented as a string - or to be accurate, as a System::String. In other words, you want to write a function so that you retain control of the way your class behaves. So why not add a ToString() method?

class notmanaged { private: int val; public: notmanaged(int v) : val(v) {}; String* ToString() {return __box(val)->ToString();} }; // ... notmanaged nm(4); Console::WriteLine("notmanaged holds {0}",nm.ToString()); notmanaged *p = new notmanaged(5); Console::WriteLine("notmanaged pointer has {0}",p->ToString());

Note the clever use of __box inside the ToString() method so that it can return a pointer to the managed type System::String* -- and WriteLine() is happy as can be with that as a parameter. You could easily expand this approach for a class with multiple member variables. Of course, you can call the method whatever you like, but calling it ToString will help other .NET programmers, since that's the name throughout the Base Class Library for a method that represents the internals of a class as a single System::String.

Unboxing
So far, you've only seen the __box keyword used in temporary circumstances, just to pass to a method. But you can create a longer-lived pointer to a managed type, like this:
__box int* bx = __box(x);

It's important to remember that this is a copy. If you change it, the original is unchanged. These four lines:
__box int* bx = __box(x); *bx = 7; Console::WriteLine("bx is {0}", bx); Console::WriteLine("x is still {0}", __box(x));

produce this output:


bx is 7 x is still 3

If you want changes in the boxed copy to go back to the original, just dereference the pointer:
x = *bx;

Then x will contain the new value (7, in this example) that you put into the boxed copy.

Summary
Well, that was a long walk in the park just to get a handful of integers onto the screen, wasn't it? Understanding the differences between managed and unmanaged types is vital to keeping your sanity when working in Managed C++. Boxing an unmanaged type is a useful way to convert it to a managed pointer, but when your unmanaged type is a class, remember that you're in control and can make managed pointers however you choose to do so - and a member function called ToString is an excellent approach. And you can unbox without any special keywords - just get the value back out. So don't let pesky

compiler errors keep you from exploring all the fun of the Base Class Library!

Building a .NET Windows Forms App in Visual C++ .NET


Using Visual C++ .NET... <http://www.codeguru.com/columns/Kate/index.html> June 28, 2002 From Kate Gregory's Codeguru <http://www.codeguru.com/> column, "Using Visual C++ .NET <http://www.codeguru.com/columns/Kate/index.shtml>. Most people will tell you that you can't build a Windows Forms application in Visual C++ .NET. However, that's not strictly true. What you can't do is use the visual designers to drag, drop, and arrange your buttons, labels, and so on if you're working in Visual C++. Old timers like me remember that the name Visual C++ comes from just that capability. But we also remember that there was always another way to make our user interfaces, creating buttons and labels and the like in code, and setting their size and position with lines of code. All the visual designers do is generate that code for you behind the scenes. And at the moment, they only generate C# or VB.NET code. In .NET, the code generated by the designers isn't hidden somewhere, or generated only when you build. It's easily accessible within files you probably have open anyway, and that means it's easy to copy. In this column, I'm going to show you how to build your user interface in C#, then do a little editing to turn it into a 100% C++ application. Along the way you'll get some insight into the differences between C# and C++. It's only fair to mention that MSDN has a sample of a Customization project called the ManagedCWinFormWiz. You copy some folders from the MSDN CD or DVD to specific places on your hard drive, and the next time you choose File, New Project you have an extra choice under C++: Managed C++ Windows Forms Project. Don't get too excited though: there are no steps to this wizard, and it doesn't do very much. It creates a form with a menu and a few controls, so you can see where to hand-add your own code. But you get no help in building your user interface at all. They even forgot to make the simple change that takes away the ugly black command prompt in the background behind your Windows application when it runs. You'll do better with the sample code I provide with this column. Remember also that the reality of .NET cross-language development means that you could just develop your user interface in C# and work with business objects written in C++. Still, I like to know that I can do it all in one project and all in one language if I choose. I don't like thinking of Visual C++ as limited in any way.

Getting Started
I'm going to start by building a C# application. If you want to follow along, open Visual Studio and choose File, New Project. Select Visual C# Projects on the left and Windows Application on the right. Name the project cs1 to have all your file names and so on match mine. In the forms designer, just quickly drag on a button and a label next to it. (You might have to choose View, Toolbox to have somewhere to drag them from.) The user interface looks like Figure 1.

Figure 1. Click here for larger image I changed the text (caption) of the button to Greet Me and the label to blank, but left their names as generated. For a real application, you would go to town here creating a complex and beautiful user interface. Next, double-click the button to generate an event handler for it. I just added this line of code:
label1.Text = "Hello!";

If you like, build and run it to assure yourself that it works. Click the button and the label text should change to "Hello!". That's all you need this project for, so exit the app and choose File, Close Solution to close it in Visual Studio.

Creating the C++ application


To create an empty C++ application that will grow to display your Windows Forms application, start by choosing File, New Project. Select Visual C++ projects on the left and Managed C++ Application on the right. Call it cpp1 if you want your file and variables names to match mine. Use notepad to open form1.cs from the C# application, and select and copy all the text, then paste it into cpp1.cpp, right before the _tmain() function.

Differences between C# and C++


Now it's time to set to work changing C# to C++. As you do this, remember the compiler is your friend, and will remind you of any changes you forget to make.The important differences include:

C# uses dots to separate namespaces from subnamespaces, namespaces from

classes, classes from static member functions, and object references from instance functions. C++ uses the scope resolution operator (::) for the first three, and the member dereference operator (->) for the last.

C# returns object references from new; C++ returns pointers. C++ cannot have an instance of a managed object, only a pointer to one, so declarations must be changed from Foo to Foo*.

C# allows you to add references to a project with menus; C++ requires a #using statement. (The online help for each class tells you what assembly it is in, such as System.dll, so you can add the appropriate #using statement.)

C# repeats the access qualifier (private, public, etc) on each declaration. C++ sets up blocks of members with a given access qualifier. C# allows you to initialize a member variable when you declare it. C++ does not. The C# keyword for a null reference is null; the C++ constant for the null pointer is NULL. In C# when you override a function from the base class, you indicate that you are doing so knowingly with the override keyword: this isn't necessary in C++. Although C# doesn't have a pre-processor, it does have some keywords that start with # that are instructions to the IDE more than to the compiler. These are generally not supported in C++ and must be removed. Two very common examples are #region and #endregion, used in outlining view.

C# makes it easy to declare arrays on the fly with brace brackets; C++ does not allow this as an argument to a function. Either create a temporary array to pass or look for another function that doesn't need an array passed to it, and call it multiple times.

C# talks to the base class as base. while C++ uses the actual name of the base class and ::. Some classes in the Base Class Library are actually value types, which means that C++ code doesn't create them with new or refer to them with pointers. Point and Size are good examples of these. If you aren't sure from the online help whether something is a value type or not, try compiling a line that uses it and the compiler will let you know if you've got it right.

The syntax for passing function pointers requires a & in C++ but not in C#. C# lets pointers-to-member functions be specified in a more concise syntax than C++.

Editing into C++


With that in mind, here are the steps to follow to edit the large mass of C# code you just pasted into your C++ source file so that it's valid C++:

Find the closing brace bracket at the end of the class definition (don't be tricked by the closing brace bracket at the end of the namespace block) and add a semicolon after it.

Change public class Form1 : System.Windows.Forms.Form to public __gc class Form1 : public System::Windows::Forms::Form Change the block of lines that start using System to start using namespace System Change all instances of . that separate namespaces such as System.Data to :: as in System::Data Add these lines immediately before the block of using namespace statements:
#using #using #using #using <System.dll> <System.Drawing.dll> <System.Windows.Forms.dll> <System.Data.dll>

Find fully qualified names like System.Windows.Forms.Button and change all the . characters to ::, or better still remove the fully qualified name, in this case just declaring a Button. (For Container, leave the fully qualified name to avoid compiler errors)

Change declarations of references, which would be solid objects in C++, to pointers. For example Button button1 becomes Button* button1. Remove the word private or public from individual declarations and instead place a private: or public: on a line of its own before the set of declarations. Remove the =null on the line that declares components, and add a line to the constructor to set components to NULL. Edit-and-replace this. to this-> or nothing at all, since there aren't any name collisions in this example. Remove any occurrences of the override keyword. Remove any #region and #endregion pre-processor directives Change the signature of the button1_Click() method to take pointers:
void button1_Click(Object* sender, System::EventArgs* e)

Change the single line: Controls.AddRange(new System.Windows.Forms.Control[] { label1, button1}); to the two lines

Controls->Add(label1); Controls->Add(button1);

In the Dispose method, change:


base.Dispose( disposing );

to
Form::Dispose( disposing );

Find the lines that create a new Point object and take away the new keyword, so that the line reads, for example, label1->Location = Point(184, 96); Do the same for the lines with Size, but call it by its full name, System::Drawing::Size, to avoid conflicts with the Size property of the form. Change the line in the middle of InitializeComponent() that adds button1_Click() to the list of handlers for a click on button1 from
this.button1.Click += new System.EventHandler(this.button1_Click);

to
button1->Click += new System::EventHandler(this, &Form1::button1_Click);

Take the single line in Form1::Main() and move it to _tmain(), editing it slightly so that it reads: Application::Run(new cs1::Form1()); (if you changed the namespace name from cs1, adjust this line.)

Remove Form1::Main() completely, it has no further work to do. Just to show off, change the declaration of _tmain to int __stdcall WinMain() so that you won't get a telltale black box in the background when you run your WinForms application.

That's it! Try building and running it. If you get compiler errors, it's probably because you missed a . somewhere -- let the messages guide you to the place you need to fix. Here's what my code looks like:
// This is the main project file for VC++ application project // generated using an Application Wizard. #include "stdafx.h" #using <mscorlib.dll> #include <tchar.h> #using #using #using #using using using using using using using <System.dll> <System.Drawing.dll> <System.Windows.Forms.dll> <System.Data.dll> namespace namespace namespace namespace namespace namespace System; System::Drawing; System::Collections; System::ComponentModel; System::Windows::Forms; System::Data;

namespace cs1 { /// <summary> /// Summary description for Form1. /// </summary>

public __gc class Form1 : public System::Windows::Forms::Form { private: Button* button1; Label* label1; /// <summary> /// Required designer variable. /// </summary> System::ComponentModel::Container* components; public: Form1() { // // Required for Windows Form Designer support // components = NULL; InitializeComponent(); // // TODO: Add any constructor code after // InitializeComponent call // } /// <summary> /// Clean up any resources being used. /// </summary> protected: void Dispose( bool disposing ) { if( disposing ) { if (components != NULL) { components->Dispose(); } } Form::Dispose( disposing ); } /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private: void InitializeComponent() { button1 = new Button(); label1 = new Label(); SuspendLayout(); // // button1 // button1->Location = Point(48, 96); button1->Name = "button1"; button1->TabIndex = 0;

button1->Text = "Greet Me"; button1->Click += new System::EventHandler(this, &Form1::button1_Click); // // label1 // label1->Location = Point(184, 96); label1->Name = "label1"; label1->TabIndex = 1; // // Form1 // AutoScaleBaseSize = System::Drawing::Size(5, 13); ClientSize = System::Drawing::Size(292, 273); Controls->Add(label1); Controls->Add(button1); Name = "Form1"; Text = "Form1"; ResumeLayout(false); }

private: void button1_Click(Object* sender, System::EventArgs* e) { label1->Text = "Hello from C++!"; } }; } // This is the entry point for this application int __stdcall WinMain() { Application::Run(new cs1::Form1()); return 0; }

What if you want to change your user interface later? Add another button and handler, or move one of the controls you already have? Well, you have two choices. Either you edit the C++ code you already have, or you go back to your starter C# app and edit your interface, then look at the code generated for you and do some careful copying and reediting in your C++ code. You can see the way that the position, name, and text of each control is set in InitializeComponent(), and you can edit these lines at will to rearrange your user interface, or copy them to add more controls and handlers. Well, that's all for now. I hope this column has shown you that C++ can do more than appears possible at first glance, and demonstrated some syntax differences between C# and C++. In future columns I'll talk more about managed types and value types, and some of the other subtle distinctions there wasn't space to discuss here.

What's Special about Visual C++ .NET?


Using Visual C++ .NET... <http://www.codeguru.com/columns/Kate/index.html> June 19, 2002

In the last 6 months, I've done two cross-Canada tours to tell developers about Visual Studio .NET and what makes it so amazing. I've delivered lines like these:

"All those things VB programmers couldn't do -- a service, a snapin, a multithreaded app, a console app -- VB.NET programmers can do." "Visual C++ programmers no longer have a privileged position with the class library that gets all the goodies first. There's one class library for everyone now" "Language is just syntax."

Now, from the perspective of the .NET Framework, those things are certainly true. But Visual C++ .NET has something none of the other Microsoft .NET languages has: it can make ordinary Windows applications. That's right, if a Visual Basic programmer wants to make a "classic" Windows app that runs on a machine without the .NET Framework installed, it's time to dig out that old copy of VB 6. And that means the VB programmer doesn't get to use the fun toys like code outlining, flyover windows, or the server explorer while developing that app. But for Visual C++ programmers like me, there's no need to go anywhere: Visual C++ .NET makes MFC and ATL applications, just like before. It has a privileged position compared to the other .NET languages after all. In fact, the developer team for Visual C++ had a mission statement that included something none of the other teams had: make this a great upgrade even for users who won't be building .NET applications. And they've achieved that mission. The user interface simplies some really common tasks, and reduces frustrations dramatically. (One way for me to measure this is to watch my staff as they switch back to a machine that doesn't have Visual Studio .NET on it yet, and have to do things the old way.) What's more, MFC and ATL have been improved, and so has C++ standards compliance. How do you upgrade a Visual C++ 6 application to a Visual Studio .NET application? Simple, just open your old project file in the new product. Confirm you want to convert to the new format, Visual Studio will make you a .sln file, and nothing else will change. You'll be using the fun new user interface to do your old work. Of course, you'll have to learn your way around that fun new user interface a bit. Some old favorites are gone, and others are hard to find. Probably the toughest thing is that ClassWizard is gone -- you use the Properties window to catch messages now.You can see a dramatically condensed explanation of catching messages with the Properties window in Figure 1. It goes like this (the large red numbers in the figure are the order of the steps):

Figure 2. - Click here for larger image 1. In the Class View, select the class that will catch the message. 2. Bring up the Properties Window if it's not up, and click the Events button -it's a lightning bolt. 3. Find the message of interest, click to expand it, and click next to COMMAND or UPDATE_COMMAND_UI as appropriate. You'll get a drop down with one choice, to add the function. You can't control the name of the function as you could in older versions of Visual C++. 4. After you select the Add choice, the function will appear in the source and header files for the class, and the Class View will be updated to show the function to you. You can then add appropriate message-handling code. What else is different? Well a lot of items have wandered from one menu to another, and some menu items only appear when you've selected the right thing in the Class View or Solution Explorer first. That can be frustrating, to say the least. The first thing you're going to want to do is to change your user profile to "Visual C++ Developer." That arranges the windows in a way that looks more like

Visual C++ 6, and gives you the familiar keyboard shortcuts instead of the new Visual Studio arrangements. Simply switch to the Start Page using the tabs across the top (if you closed the Start Page, bring it back by choosing Help, Show Start Page) and click My Profile at the bottom of the left-hand column. Set your profile to "Visual C++ Developer" and things should get a lot more comfortable right away. The first thing most Visual C++ developers ask me is "where has Project, Settings" gone to? Believe it or not, there are four ways to get to the Property Pages dialog that serves the same purpose as the old Settings dialog:

Select the project in Solution Explorer and choose Project, Properties Select the project in Solution Explorer and choose View, Property Pages Select the project in Class View and choose Project, Properties Select the project in Class View and choose View, Property Pages

There are two distractions to avoid here: don't select the solution, always the project. And if you're on the View menu, don't be fooled by the Properties item -- that's for the usual Properties window. You can do an incredible amount of things once you have the project property pages up: perhaps the most common is to add another directory for include files. To do that, expand the Resources tab and choose General under that. (See Figure 2.)

Figure 2. - Click here for larger image While there are other user interface differences between Visual C++ 6 and Visual C++ .NET, knowing how to catch messages and change your project settings should take you a long way towards feeling comfortable and productive with this

amazing new tool. In the columns to come, I'll be showing you more about how to use Visual C++ .NET to build both .NET applications and classic Windows applications. And for those of you who think that poor Visual C++ has a neglected air to it, or can't do some of the things that C# and VB can do, I have a few surprises in store. See you soon!

About the Author


Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com <http://www.gregcons.com>). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-theshelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET <http://books.internet.com/books/0789724669>.

Вам также может понравиться