Ask Dr. GUI #38

(Remember, he's not a real doctor.)

In his most recent column, "Ask Dr. GUI #37," Dr. GUI announced that he was going to split his personality—or at least his time.

The new personality gives prescriptions about programming technologies and techniques, helping you make it all make sense. This new weekly column is easy to get to on the Web: http://www.microsoft.com/msdn/. The good doctor's already covered optimizing ActiveX controls for Internet Explorer 4.0, XML, J/Direct, and more!

Meanwhile, the old personality continues on, with a page full of questions and answers in each MSDN News issue.

So, without further ado, here are your questions—and the good doctor's answers:

Can you follow those links?

Dear Dr. GUI,

I'm experiencing this strange link problem while building my MFC-based ActiveX control. I created the project in MS Developer Studio (Visual C++ 5.0) using the ActiveX control wizard and added properties, methods, etc. using the class wizard. All of a sudden after a making an innocuous change in a .cpp file (believe me, this is all I did), the linker fails with the "DLLMain multiply defined" error. I've tried everything I could to get around this problem without success. Both of the libraries that the linker complains about are from MFC. And most surprisingly, what causes the linker to complain all of a sudden (after several successful builds)? I'm stumped. Can you please help?

Ashok K. Srinivasan, Intergraph Corporation

Dr. GUI replies:

What's happening here is that there's more than one function called DLLMain in the combination of libraries and object files you're linking. In the C Runtime library, the symbols for new, delete, and DLLMain are weak externals (see Knowledge Base article Q72651 at http://premium.microsoft.com/support/kb/articles/Q72/6/51.asp) for more on these. The good doctor's guess is that somehow the C Runtime Library has gotten moved to first in the list of libraries to link. It has to be after the MFC library, as described in article Q148652 (http://premium.microsoft.com/support/kb/articles/q148/6/52.asp).

Why is this happening now? The good doctor's stumped. But Dr. GUI suspects it may be that you were linking using incremental link before and the change you made forced a full link. Or maybe the moon was full.

Finding your dependencies

Dear Dr. GUI,

I tried to use Dependency Walker for Win32 (MSSDK "depends.exe" by Steve P. Miller). It worked fine. It, however, does not show the currently running program's (dll, exe) status.

Is there any program to show the list of all of the currently running programs and its dlls, as well as the list of dlls that are accessed by currently running programs? If we could have this information in similar format to "depends.exe", it would be really nice. (Pview.exe is not handy and does not show a list of running dlls! Also pwalker.exe shows only the selected process!)

Thanks for your response,
Hoo Kwon, Merrill Lynch

Dr. GUI replies:

Dr. GUI's got two ideas here. First, PVIEW.EXE does show you the list of DLLs loaded into a running process. To see this list just select the process and press the Memory Detail button.

Second, you can use the "tlist.exe" program from the Win32® SDK or the Microsoft Windows NT® Resource Kit. First run tlist.exe to obtain the process ID of the process that you are interested in and then run tlist.exe passing the process ID as the command line parameter.

Combatting COM stale ROT

Dear Dr. GUI,

We have a problem with COM under Windows® 95. Working about two weeks on this problem hasn't led to any result. We have no further ideas about how to resolve this problem and hope that you have some hints for us.

We want to extend two of our existing applications to COM Automation applications. One application is working as a local server and another as an Automation client. It is important for us not only to create an instance of the local server, but also to connect to an existing server. Our development environment is Visual Studio Professional 5.0.

Under Windows NT (Version 4.0, Build 1381) both are working fine. Creating an instance via CoCreateInstance (or derived function such as _com_ptr_t::CreateInstance) and connecting (GetActiveObject or _com_ptr_t::GetActiveObject) under Windows 95 CoCreateInstance works, but GetActiveObject fails with HRESULT set to ME_E_UNAVAILABLE. The following happens:

  1. Before calling GetActiveObject, the associated Item Moniker is registered in the Running Object Table (due to RegisterActiveObject) monitored with irotview.exe.

  2. When calling GetActiveObject (or IRunningObjectTable::GetObject, when enumerating the ROT with own code) the Kernel throws two exceptions with code 0x000006ba and the Item Moniker is removed from the ROT.

  3. GetActiveObject returns with MK_E_UNAVAILABLE.

The programmatic logic of our COM server is the same as in the COM-Hello example, but the application object is much more complex. The application is using a dual interface. Changing the registration (for example, RegisterActiveObject with REGISTEROBJECT_STRONG or not using CoLockObjectexternal for a visible server) doesn't resolve the problem.

Do you have some hints or additional information (we haven't found additional info in Online Books, MSDN, Inside OLE 2, or Knowledge Base) about:

  1. What exception code 0x000006ba means?

  2. Why is our running object removed from the ROT?

With best regards,
Frank Wiedenroth, Ingenieurbuero Schoop

Dr. GUI replies:

This problem is most likely due to the fact that the ROT entry has become stale. (Isn't the concept of a stale ROT scary?) In other words, the object associated with the ROT entry is no longer in existence. Perhaps the server process is no longer running. Perhaps the object deleted itself.

When you call GetActiveObject(), COM will try to find the object associated with the item moniker and if it cannot find it for any reason it will remove the stale moniker from the ROT.

The exception code 0x000006ba means RPC_S_SERVER_UNAVAILABLE. This error most likely means that the server process has gone away.

Dr. GUI hopes this is helpful and that you'll be able to debug this.

Getting an active remote object

Dear Dr. GUI,

I have a question about remote OLE automation. I can get an IDispatch interface about a running COM class on local machine. The code lists as below:

        ICalcDlgPtr m_calc;
        HRESULT hr = m_calc.GetActiveObject(__uuidof(CCCalcDlg));
        if (FAILED(hr))
        {
         AfxMessageBox("The CCCalcDlg COM class can't be Get!");
            if (FAILED(hr)) 
                _com_issue_error(hr);
        }

But I want to get an IDispatch interface about a running COM class on a remote machine. I have configured the Mfccalc.calculator COM class on local and remote machines. At the same time, I have run an Automation manage program on a remote machine. I used the same method to get the IDispatch interface, but it failed. I don't know what's wrong! Please tell me!

(Note: I can createinstance a COM class on remote machine, but I wish to get, not create!)

Best wishes,
Su.j.c

Dr. GUI replies:

Unfortunately, Dr. GUI has noticed that GetActiveObject() does not work remotely. Currently the only mechanism to access the ROT (Running Object Table) of a remote machine is via file monikers. A sample that shows how this can be done is available in the Knowledge Base article Q171974, Q171974 "FILE: Bind to an Object on a Remote Machine Using ROT" (http://premium.microsoft.com/support/kb/articles/q171/9/74.asp).

Bridge over Visual Basic waters

Dear Dr. GUI,

Do you know if there is something in Visual Basic 5.0 that will give me the same functionality as C++'s CoCreateInstanceEx()?

I'm not a C++ programmer and I would love to get a VB-based solution. Thanks in advance for any help.

Bernard Berger

Dr. GUI replies:

There isn't any way to do this directly, but there's almost always a solution to these things—usually by using a little of the most powerful programming language, C++.

So a Visual Basic–COM "bridge" can be used to accomplish this. This bridge is a DLL written in C++. It calls CoCreateInstanceEx on behalf of the Visual Basic program. One such bridge is available at http://www.wam.umd.edu/~mikenel/dcom/dcomtool.htm.

COM again? Should he be Dr. GUID?

Dear Dr. GUI,

I read the article on GUIDs in Inside OLE 2 on the MSDN Library and found a reference to "DEC/HP Network Computing Architecture RPC Run TimeExt. Specs. Ver OSF TX1.0.11" by Steven Miller (1992, July 23), said to be part of OSF DCE documentation. I am interested in the UUID allocation algorithm in Chapter 10. Can you help me to reach this reference? Is it available anywhere online so that I could download it? Else, perhaps you may help me find out how to access PC network communication cards (of different types . . .) and read the card ID, which is also rather globally unique.

Hoping to get your help and thanking you in advance,
Dr. Ben Zion Barta, Bar-code Computers Ltd., Haifa, Israel

Dr. GUI replies:

Well, of course you can do what Dr. GUI (should Dr. GUI be called Dr. GUID in honor of COM?) does: just use GUIDGEN. But if you want to create your own program to create UUIDs, read the IETF Internet draft that fully documents the algorithm for generating UUIDs (a.k.a. GUIDs). It's available at ftp://ftp.isi.edu/internet-drafts/draft-leach-uuids-guids-00.txt. The draft even includes C source code to a fully compliant generator. Note that Internet drafts are "works in progress" and should not be used for final reference purposes.

By hook or by crook . . .

Dear Dr. GUI,

I'm looking for a trick to redirect all keyboard and mouse events to a one window (that is, a dialog box). A function like the SetCapture works only for windows of the same process, while I need a trick to capture events from all desktop windows.

Thanks,
Michele Mazzurco, C.T. Cad Tech srl, Italy

Dr. GUI replies:

Somehow, Dr. GUI thinks your question has a hidden hook. In fact, the way to do this is to install Windows hooks that capture all keyboard and mouse messages at a system level. This can be done with the Win32 API function SetWindowsHookEx. The following code snippet will set up the hooks, do some other piece of code, and reset the hooks.

   HHOOK hKeyboardHook;
   HHOOK hMouseHook;

   'Hook ALL keyboard and mouse messages.
   hKeyboardHook = ::SetWindowsHookEx(WH_KEYBOARD, 
      pfnMyKeyboardMonitor, NULL, 0);
   hMouseHook = ::SetWindowsHookEx(WH_MOUSE, 
      pfnMyMouseMonitor, NULL, 0);

   'Invoke other code.
   DoSomethingElse();

   ::UnhookWindowsHookEx(hKeyboardHook);
   ::UnhookWindowsHookEx(hMouseHook);

Doing this will accomplish what you want but with some degradation to the system performance. (That's why hooks aren't supported under Windows CE.) If performance isn't an issue, then this function works just fine. Be warned though, debugging this type of hook is extremely tricky, if not downright ugly! This is definitely one of those times you want to be able to do remote debugging so that you don't crash the debugger too often.

A button for your macros

Dear Dr. GUI,

I have a bunch of Developer Studio macros that I'd like to get the people on my team to use, but to do it I need to make it as easy as possible for them. So is there a way I can put the macros on the tool bar with icons? That way they don't have to bring up the macro window and search for the macro they want. They just click on the icon.

Thanks,
Jake Watkins, Toadhall Software Studio

Dr. GUI replies:

One-click shopping, huh? A good idea. Fortunately, creating a toolbar with buttons that fire macros is very easy to do. First, create a new toolbar:

  1. Click Customize on the Tools menu.

  2. Click the Toolbars tab, and then click the New button.

Now you can place your macros on the toolbar. Bring up the Toolbars tab again and click on the Commands tab. Once there, scroll down to the macros selection and you will see a list of your defined macros. Simply drag the ones you want to the newly created toolbar and select an appropriate button appearance. Repeat this for each macro and voila! a toolbar with buttons that fire off macros.

ACE, ACL, SID, E-I-E-I-O

Dear Dr. GUI,

I am developing an administration utility for Windows NT that allows you to administer user rights on NTFS volumes. I want to display the Access Control List just like File Manager/Explorer does when you lookup the security permissions for a file or directory. I am able to go through the ACL for a file/dir. and retrieve individual ACEs, their SIDs, etc. The problem is that some ACLs have multiple ACEs for a given user/group and yet File Manager displays just one entry for each user/group. Can you tell me how File Manager clubs together multiple ACEs belonging to a user/group to come up with a single entry for each user/group in the security permissions list box?

Thanks,
Vijay Lele

Dr. GUI replies:

You have at least two options here:

  1. Use the Win32 API GetEffectiveRightsFromAcl(). This API takes a trustee and returns a mask that defines the effective rights for that user on the object.

  2. Do the grunt work yourself. Keep a list of the trustees as you process the ACEs (pay attention to whether the ACE is access denied or access allowed!) and perform a bitwise OR using the duplicate trustees' access mask.

Checking passwords

Dear Dr. GUI,

I want to boldly go where I haven't been before: programming for Windows NT. I want to use the security in NT for validating my users. I have found the GetUserName function, and have no problem using it.

How do I check the password that the user supplies?

Kind regards,
Michael K. Hansen, Naerum, Denmark

Dr. GUI replies:

So you want to be able to get the user's password? Uh, no. That would really mess up Windows NT security. Even an API to let you check it would make for a mess.

But it's OK—one of the best things about Windows NT is that you don't have to check passwords! You see, the system has already validated the user for you. Windows NT has already collected all the relevant credentials of the user and generated a handy-dandy security token that contains all the information you will likely need about that user.

All this usually comes into play in a client/server environment. If some client connects to your server you can use Windows NT security to impersonate that user and find out just who they are. Windows NT determines who you are by the information in your security token, which includes a list of Security Identifiers (SIDs) for yourself and all the groups to which you belong. Let's talk APIs. If your transport method is named pipes, you can call ImpersonateNamedPipeClient. For a sockets connection, you will want to look in the Platform SDK for SSPI documentation and the sample in the SDK called SOCKAUTH. Distributed COM (DCOM) offers a nice high-level interface through CoImpersonateUser().

Once you are impersonating, you can get the token from the current thread (this is where impersonation tokens are found). Once you have the security token of the client, you have two options:

  1. Search the token for a particular SID in the groups listed, or

  2. Get a Security Descriptor for an object that you want to check the user's access to and call AccessCheck().

This is just a quick summation of a system that is very complex. If you are serious about doing security right, take the time to look at the security section in the SDK documentation. If reading the docs from beginning to end is just too exciting for you, try searching for some of the APIs mentioned above. There is a large amount of security sample code in the SDK samples and Knowledge Base articles—your solution may already be coded!

What do you think?

Should Dr. GUI reinvent himself as Dr. GUID in honor of COM and ActiveX? Should the good doctor replace the "U" with a "V" to get that stylish Roman look (as in "Dr. GVID")? Do you have any questions of burning interest? You know the schtick: send mail to drgui@microsoft.com. The good doctor's surgery schedule doesn't allow him to answer or even acknowledge every question, but if we use your question in a column, we'll send you a cool t-shirt!

And a big THANKS . . .

. . . to Dr. GUI's specialists this issue: Saji Abraham, David Mowers, and Robert Coleridge.