C++ At Work

IRegistrar, Finding Submenus, and More

Paul DiLascia

Code download available at:C++atWork2006_10.exe(174 KB)

Q When I generate a DLL with automation support using Visual C++® 6.0, some functions for registration are generated, but not for unregistering my DLL. I wrote a DllUnregisterServer that looks like this:

STDAPI DllUnregisterServer(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); if (!COleObjectFactory::UnregisterAll()) return ResultFromScode(SELFREG_E_CLASS); return NOERROR; }

COleObjectFactory::UnregisterAll returns TRUE, but my DLL is still registered. How should I write the code?

Q When I generate a DLL with automation support using Visual C++® 6.0, some functions for registration are generated, but not for unregistering my DLL. I wrote a DllUnregisterServer that looks like this:

STDAPI DllUnregisterServer(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); if (!COleObjectFactory::UnregisterAll()) return ResultFromScode(SELFREG_E_CLASS); return NOERROR; }

COleObjectFactory::UnregisterAll returns TRUE, but my DLL is still registered. How should I write the code?

Ivan Pavlik

A

A

Alas, you stumbled onto a small wormhole in the MFC universe. UnregisterAll would indeed appear to be the function you'd call to unregister your DLL. If you peer inside olefact.cpp, where UnregisterAll is implemented, you'll discover it looks something like this:

for (/* pFactory = all factories */) { pFactory->Unregister(); }

That is, it loops over all your module's factories and calls Unregister for each one. So far, so good. What does COleObjectFactory::Unregister do? The code tells the story:

BOOL COleObjectFactory::Unregister() { return TRUE; }

Oops. As you can see, COleObjectFactory::Unregister doesn't do anything! It just returns TRUE. Which is most peculiar, since COleObjectFactory::Register does actually register your COM class by calling ::CoRegisterClassObject. On the other hand, there's another function, COleObjectFactory::Revoke, that calls ::CoRevokeClassObject to unregister your COM class. MFC calls Revoke automatically from your COleObjectFactory destructor. So what the heck is going on here?

The problem is an unfortunate confusion in terminology, which stems from the different ways of registering COM classes for DLLs and EXEs. For DLLs, you register your class by adding keys in the Windows registry (CLSID, ProgID, and so on). For EXEs, you must call CoRegisterClassObject to register your class with the COM system at runtime. The issue is further confused by the fact that for EXEs, the opposite of register is not unregister, but revoke (CoRevokeClassObject).

When the Redmondtonians added COM support to MFC, they did their best to make life as easy as possible, but they didn't always succeed perfectly. COleObjectFactory::Register looks like this:

// in olefact.cpp BOOL COleObjectFactory::Register() { if (!afxContextIsDLL) { ::CoRegisterClassObject(..., CLSCTX_LOCAL_SERVER, ..); } }

You can see right away it does nothing for DLLs; it only registers EXEs using CLSCTX_LOCAL_SERVER (context = local server, EXE running on the local machine). Following the underlying C API, the Redmondtonians used the same name Revoke for the function that unregisters the EXE: COleObjectFactory::Revoke (which is called automatically from your class factory's destructor).

So what's COleObjectFactory::Unregister for, the function that doesn't do anything? Perhaps the Redmondtonians intended to one day make Register/Unregister work for DLLs, too. But as it stands today, they don't. To register your DLL, you need yet another completely different function: COleObjectFactory::UpdateRegistry. This function takes a Boolean argument that tells MFC whether you want to register or unregister your COM class. There's also UpdateRegistryAll, which loops over all the class factories, calling UpdateRegistry for each one. So here is the proper way to implement your DllUnregisterServer::

STDAPI DllUnregisterServer(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); return COleObjectFactory::UpdateRegistryAll(FALSE) ? S_OK : SELFREG_E_CLASS; }

DllRegisterServer should look the same, except you should pass TRUE instead of FALSE to UpdateRegistryAll.

The default implementation for UpdateRegistry does its best to add or delete the appropriate registry keys in HKCR/CLSID–InprocServer32, ProgID, Insertable, ThreadingModel, and so on, but sometimes you need additional keys specific to your COM class. For example, you may need to register categories, which are a kind of extension of the old "Insertable" key (which means your COM class can be dropped into a form in design mode). In that case, you need to override UpdateRegistry to add or remove your own keys.

Way back in the November and December 1999 issues of Microsoft Systems Journal (now known as MSDN®Magazine), I showed how to build a Band Object for Internet Explorer using the Active Template Library (ATL) IRegistrar interface. (Band Objects need to register a special category CATID_DeskBand.) IRegistrar is a really cool tool that lets you write a registration script (.RGS file) to add your registry entries, instead of calling registry functions like RegOpenKey, RegSetValue, and the rest. Figure 1 shows a typical script.

Figure 1 IRegistrar

HKCR { NoRemove CLSID { ForceRemove %CLSID% = s ‘%ClassName%’ { InprocServer32 = s ‘%MODULE%’ { val ThreadingModel = s ‘%ThreadingModel%’ } ProgID = s ‘%ProgID%’ } } }

As you can see, IRegistrar lets you define variables like %ClassName% and %ThreadingModel%, then replace them with actual values at runtime. With IRegistrar, you never have to call the registry API again. You can write a script that more resembles the actual registry entries and you can even use the same script to register or unregister your DLL. That's right, IRegistrar is smart enough to unregister. IRegistrar isn't officially documented anywhere I can find, but the code is there (find it in atliface.h). If you're not using IRegistrar to register and unregister your COM DLLs, you really should check it out—it'll save you tons of work. For details see my November 1999 article.

Q My main app has a normal menu with an Edit submenu of commands (Cut, Copy, Paste, and so on.). I'd like to show the Edit submenu as a context menu when the user right-clicks in the main window. The problem is how to get this submenu from the main menu. There doesn't seem to be any command ID associated with a submenu. I can use the zero-based index, but because of customization the Edit menu may not always be the second menu. I can't search for the text "Edit" because we support multiple languages and the actual word may change. How can I find the Edit submenu in all these cases?

Q My main app has a normal menu with an Edit submenu of commands (Cut, Copy, Paste, and so on.). I'd like to show the Edit submenu as a context menu when the user right-clicks in the main window. The problem is how to get this submenu from the main menu. There doesn't seem to be any command ID associated with a submenu. I can use the zero-based index, but because of customization the Edit menu may not always be the second menu. I can't search for the text "Edit" because we support multiple languages and the actual word may change. How can I find the Edit submenu in all these cases?

Brian Manlin

A Well, let me point out that the Edit menu should be the second submenu, if you have one. The Official Windows® GUI Guidelines call for the first three menu items to be File, Edit, View, in that order—if you support them. See The Windows Interface Guidelines for Software Design (Microsoft Press®, 1995). That said, I can help you find your Edit submenu or any other submenu for that matter.

A Well, let me point out that the Edit menu should be the second submenu, if you have one. The Official Windows® GUI Guidelines call for the first three menu items to be File, Edit, View, in that order—if you support them. See The Windows Interface Guidelines for Software Design (Microsoft Press®, 1995). That said, I can help you find your Edit submenu or any other submenu for that matter.

You are indeed correct, there's no command ID for a submenu. That's because internally Windows uses the command ID field to hold the submenu's HMENU, if the menu item is a submenu. Not to worry, assuming the submenu always holds a specific command (for example Edit | Cut), and assuming the command always has the same ID (for example, ID_EDIT_CUT), it's easy enough to write a function that finds the submenu that contains a given command. Figure 2 shows the code. CSubmenuFinder really acts as a namespace to hold the static function FindCommandID, which calls itself recursively to deep-search the menu and all submenus for a menu item whose command ID matches the one sought.

Figure 2 Finding a Submenu

SubmenuFinder.h

#pragma once ////////////////// // Class to find a submenu that contains a given command ID. // This class is really just a namespace for the function FindCommandID. // class CSubmenuFinder { public: CSubmenuFinder() { } ~CSubmenuFinder() { } static CMenu* FindCommandID(CMenu* pMenu, UINT nID); };

SubmenuFinder.cpp

#include "StdAfx.h" #include "SubmenuFinder.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ////////////////// // Find the submenu within a given menu that contains a given command // ID. // Iterate the menu items looking for the command ID. Recurse // into submenus if necessary. // CMenu* CSubmenuFinder::FindCommandID(CMenu* pMenu, UINT nID) { if (pMenu) { for (UINT i=0; i<pMenu->GetMenuItemCount(); i++) { if (pMenu->GetMenuItemID(i)==nID) return pMenu; // found command--return menu // Recurse! Note this returns NULL if item is not a submenu. CMenu* pSubMenu = CSubmenuFinder::FindCommandID( pMenu->GetSubMenu(i),nID); if (pSubMenu) return pSubMenu; // found submenu -- return it! } } return NULL; } You can use CSubmenuFinder to find your edit menu, like so: void CMainFrame::OnContextMenu(..) { CMenu* pMenu = CSubmenuFinder::FindCommandID( GetMenu(),ID_EDIT_CUT); if (pMenu) pMenu->TrackPopupMenu(...); }

This snippet searches the main menu for a submenu containing a menu item whose command ID is ID_EDIT_CUT, and returns the submenu if found. I wrote a little program called EdMenu to test CSubmenuFinder. EdMenu uses the previous code to handle WM_CONTEXTMENU to display the very same edit submenu as the one in the main menu, when the user right-clicks in the main window. You'll have to believe me that CSubmenuFinder finds the edit menu wherever it appears within the main menu. I did in fact test it by temporarily moving the Edit menu ahead of the View menu in one of my builds. To try it yourself, download the source.

Q How do I convert MFC CString to String in managed C++? For example, I have the following code in C++:

void GetString(CString& msg) { msg = // build a string }

How do I rewrite this function using managed C++ and replacing the CString parameter with a managed String? The problem is that GetString changes the caller's CString, and I want to do the same thing using a managed String.

Q How do I convert MFC CString to String in managed C++? For example, I have the following code in C++:

void GetString(CString& msg) { msg = // build a string }

How do I rewrite this function using managed C++ and replacing the CString parameter with a managed String? The problem is that GetString changes the caller's CString, and I want to do the same thing using a managed String.

Sumit Prakash

A Well, the answer depends on whether you're using the new C++/CLI syntax or the old Managed Extensions. Both flavors of syntax can be confusing, but never fear, I'm here to clear your coding confusion.

A Well, the answer depends on whether you're using the new C++/CLI syntax or the old Managed Extensions. Both flavors of syntax can be confusing, but never fear, I'm here to clear your coding confusion.

Let's start with the new syntax, since it is 2006. The trick to getting the syntax right is to remember two things. First, in C++/CLI, managed objects wear hats. So CString becomes String^. Second, remember that the managed equivalent to reference (&) is tracking reference, which in C++/CLI is %. I know, you can barely wrap your head around ^ but with enough practice, % will soon seem normal too. So the new functions looks like this:

// using C++/CLI void GetString(String^% msg) { msg = // build a string }

Does that make sense? To show that it actually works, I wrote a little program strnet.cpp (see Figure 3). It implements two classes, CFoo1 and CFoo2, each with a member GetName. The first uses CString& and the second uses String^%. If you compile and run this program (using /clr of course), you'll see that in both cases—the one using CString and the one using String—the object passed (native or managed) is modified by the member function.

Figure 3 Using a Tracking Reference

#include "stdafx.h" #include "resource.h" using namespace System; ////////////////// // Passing a reference to a native MFC CString // class CFoo1 { public: void GetName(CString& name) { name = _T("Rumplestilskin"); } }; ////////////////// // Passing a reference to a managed String^ // class CFoo2 { public: void GetName(String^% name) { name = _T("Rumplestilskin"); } }; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { CFoo1 foo1; CFoo2 foo2; CString name; foo1.GetName(name); tprintf(_T("GetName returns %s\n"), name); String^ s = gcnew String(""); foo2.GetName(s); Console::WriteLine("GetName returns {0}\n",s); }

By the way, you always want to use a tracking reference (%) for managed objects, instead of utilizing a native reference (&, which will also compile) because the tracking reference will track the reference even if the garbage collector moves the object within its managed heap.

If you're using the old-style syntax (/clr:oldSyntax), how come you're still living in the dark ages? Never fear, you can use a reference even if you're retro. If you're using the Managed Extensions, just remember that managed objects are __gc pointers, so CString becomes String __gc *. And since the compiler already knows String is a managed type, you don't even need the __gc; a simple pointer (*) will do. The reference is the same. So the old-style syntax conversion looks like this:

// __gc omitted void GetString(String*& msg) { msg = // build a string }

Got it? It all kind of makes sense. Whenever you have trouble figuring out C++ syntax, the thing to do is go back to basics.

Q I recently generated a CLR console app with Visual Studio® 2005. I noticed that Visual Studio created a main function that looks like this:

int main(array<System::String ^> ^args) { ... return 0; }

This seems like a change from the old argc/argv I'm familiar with from C/C++. When I tried to access args[0], thinking it would be the filename (as in C/C++), I discovered that args[0] is not the filename, but the first command-line parameter. What happened to the filename? And can you explain the reason for this change?

Q I recently generated a CLR console app with Visual Studio® 2005. I noticed that Visual Studio created a main function that looks like this:

int main(array<System::String ^> ^args) { ... return 0; }

This seems like a change from the old argc/argv I'm familiar with from C/C++. When I tried to access args[0], thinking it would be the filename (as in C/C++), I discovered that args[0] is not the filename, but the first command-line parameter. What happened to the filename? And can you explain the reason for this change?

Jason Landrew

A Oh, the modern world in which we frolic. Nothing is the same any more, is it? We've lived with argc/argv since C was developed in 1972 and now the Redmondtonians have gone and changed it. Perhaps they wanted to make sure everyone knows how to use the new array syntax.

A Oh, the modern world in which we frolic. Nothing is the same any more, is it? We've lived with argc/argv since C was developed in 1972 and now the Redmondtonians have gone and changed it. Perhaps they wanted to make sure everyone knows how to use the new array syntax.

One reason for the new syntax is to generate a program that compiles with /clr:safe, which is the ultimate in code security. It's kind of like locking your program inside a clean room—you know, one of those sterile environments full of chrome equipment where you have to enter through a negative-pressure airlock and don a bonnet and booties. When you throw the /clr:safe switch, the compiler imposes all sorts of onerous restrictions. You can't use native types. You can't use globals. You can't call unmanaged functions. In short, you can't do anything fun. And why would you subject yourself to such misery? To be really, really, really sure your code is secure. In technical terms, /clr:safe generates "verifiable" code, which means the common language runtime (CLR) can verify that your code doesn't violate security settings, like trying to access a file when the user's security settings don't allow it. One of the many items on the /clr:safe taboo list is native pointers—which is to say, pointers. ("Native" is redundant since you can't have a pointer to a managed type.) Since pointers are not allowed with /clr:safe, the old argv declaration simply won't do. So the Redmondtonians changed the declaration to use a String^ array instead. And since arrays know their length, there's no need for argc. The runtime library provides the appropriate startup code to create and initialize the arg array before calling your main function. If you don't plan to use /clr:safe, you can always write a main that uses the old argc/argv signature.

That explains the reason for using a managed array, but what about args[0], why is it the first command parameter and not the file name? I can't speak for the Redmondtonians, but perhaps they figured it makes more sense to omit the file name, maybe they figured you don't need it. In any case, it doesn't much matter because getting the file name is easy. I'm sure there are many ways to do it, but the most obvious and CLR-centric (compatible with /clr:safe) way I can think of is with the System::Environment class. It exposes the following method:

static array<String^>^ GetCommandLineArgs ()

Unlike the args parameter supplied to the main method, the array returned from Environment::GetCommandLineArgs does in fact start with the executable file name.

Finally, why would you want to know the name of your own EXE file, anyway? After all, you're writing the program, you presumably know what it's called. One reason is to generate a help message that contains the program name, without hardcoding it. The help message might look something like this:

FooFile -- Turns every word in your file to "foo" usage: FooFile [/n:<number>] filespec filespec = the files you want to change <number> = change only the first <number> occurrences

Here, FooFile is the name of the program. I could simply write "FooFile" in my help text, but I learned long ago that I often rename programs or copy code from one program to another. By getting the program name from the program itself (through argv or Environment::GetCommandLineArgs), my help messages always display the correct name, even if I rename the file or copy my code to another program.

Happy programming!

Send your questions and comments for Paul to cppqa@microsoft.com.

Paul DiLascia is a freelance software consultant and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). In his spare time, Paul develops PixieLib, an MFC class library available from his Web site, www.dilascia.com.