2

My C# application calls a function in a C++ DLL at run-time and throws an exception. The error code generated is 262. Unfortunately, the Microsoft documentation is a bit lacking for this code.

Oddly, this exception is not thrown when running the DLL from the C++ test application that is part of the same Visual Studio solution as the DLL (separate project). (The C# application is in an entirely separate solution.) The error code is returned by a call to CoInitializeEx, which initializes the COM and is the first step my application uses to query the WMI.

The only other thing seems to be related is when I open the DLL with Dependency Walker I get these error and warnings:

Error: At least one required implicit or forwarded dependency was not found.
Warning: At least one delay-load dependency module was not found.
Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module.

The only thing that occurs to me is that the missing "required implicit or forwarded dependency" has what is needed ot make CoInitializeEx work. According to Dependency Walker, these modules could not be found:

  • MSVCR90D.DLL
  • IESHIMS.DLL
  • WER.DLL

Any thoughts or suggestions are appreciated. Thanks.

Jim Fell
  • 13,750
  • 36
  • 127
  • 202
  • You are lucky. Last time I had a linker problem (it's a total mess in Microsoft world...) my code crashed without any warning and it took hours to figure out the problem. You should take care of linking the program which calls the DLL *with the same runtime library* to which the DLL is linked. – Alexandre C. Dec 15 '10 at 17:58

5 Answers5

4

Your error handling isn't kosher, the real error you are probably getting is 0x80010106, the last word is 262. The error code is RPC_E_CHANGED_MODE, "Cannot change thread mode after it is set". Which is what CoInitialize/Ex returns when it was called before and you are trying to change from STA to MTA or the other way around.

This isn't possible, the apartment state for a thread is locked in on the first call to CoInitializeEx(). You need to find out where the first call happened. This could have been done by the CLR for a managed thread for example. The apartment state for a thread is determined by the [STAThread] or [MTAThread] on the Main() method for the startup thread. Or the Thread.SetApartmentState() for a managed thread that you create yourself. A threadpool thread is always MTA, that cannot be changed.

Altering the apartment state for a thread can have many side effects.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Hans, I think you may be on to something. There is a lot of code in this C# application over which I have no control. It's a fielded application that I'm updating to support some new hardware. Is there a way to deterministically check if `CoInitializeEx` should be called in my C++ DLL? Or is there a way I can programmatically check the thread's apartment state? Thanks. – Jim Fell Dec 15 '10 at 18:03
  • 3
    It is almost always wrong for a C++ DLL to call CoInitializeEx(). Unless it created the thread itself. – Hans Passant Dec 15 '10 at 18:07
  • Okay, but unfortunately, I can't count on that. What if I evaluate the return value of `CoGetApartmentType` for `CO_E_NOTINITIALIZED`, prior to a conditional call to `CoInitializeEx`? – Jim Fell Dec 15 '10 at 18:52
  • Of course you can count on that. Any COM call you make will fail with CO_E_NOTINITIALIZED, just return that to the caller. Who's programmer will very rapidly figure out "better initialize COM". Nothing else you can do makes sense, you cannot know what apartment to select. You can't pick STA because that requires a message loop that you don't provide. You can't pick MTA because that will make the client code fail sooner or later without the client programmer having any idea why his code failed when he used yours. The exact reason you started this question in the first place. – Hans Passant Dec 15 '10 at 19:05
  • After a little more research, it appears that `CoGetApartmentType` is supported only on Windows 7. (This needs to be compatible with XP). But, you say that any COM call will fail with `CO_E_NOTINITIALIZED`? That could work. I was also wondering, what if I fire off a new thread to run my COM calls? Would that help? It would probably be a "cleaner" way to do this. It's a finite process that queries WMI for the system configuration and cleans up when initialization is complete. – Jim Fell Dec 15 '10 at 19:12
  • 1
    Many a programmer, when confronted with a problem, thinks "I know, I'll use a thread". Now he's got two problems. – Hans Passant Dec 15 '10 at 19:22
  • @Hans: Is that a backhanded way of saying that it would work, but, generally speaking, COM should not be initialized twice, even on separate threads? – Jim Fell Dec 15 '10 at 22:47
  • 1
    @Jim, in your specific scenario, a separate thread is probably the best thing at your disposal. Creating threads from a DLL is a messy business because the DLL doesn't own the process' lifetime; the EXE does. HOWEVER... if you run it in a synchronous fashion (create thread, initialize, wait on the thread to complete, then continue), you will generally be safe. You could cache the thread, so you don't have to create and CoInitialize() it every time, but don't let it do work in the background and return to your caller: again, subtle lifetime issues arise. – Euro Micelli Dec 16 '10 at 04:29
  • @Euro Micelli: Thanks. If you want to submit that as an answer, I'll accept it. – Jim Fell Dec 16 '10 at 22:16
  • @Hans: I do appreciate your feedback. It has helped me to better understand the COM infrastructure, and you've pointed out good things to keep in mind the next time I write an application from the ground up. (+1) – Jim Fell Dec 17 '10 at 14:25
  • @Jim: Thank you. I just reposted as an answer. – Euro Micelli Jan 06 '11 at 23:05
  • WIN32=262 is HRESULT=-2147024634. – Elvedin Hamzagic May 12 '14 at 17:29
1

I assume you don't have Visual Studio 2008 C++ installed as then you'd have MSVCR90D.dll This dll is a debug only dll which means you are trying to load a DLL that has been compiled as a debug DLL.

As for the others ... see this thread: Dependency Walker reports IESHIMS.DLL and WER.DLL missing?

Community
  • 1
  • 1
Goz
  • 61,365
  • 24
  • 124
  • 204
1

In your specific scenario, a separate thread is probably the best thing at your disposal.

Creating threads from a DLL is a messy business because a DLL doesn't own the process' lifetime; the owner is the EXE.

However... if you run it in a synchronous fashion (create the thread; initialize; wait on the thread to complete; then continue), you will generally be safe. You could cache the thread, so you don't have to create and CoInitialize() it every time, but don't let it do work in the background and return to your caller: again, subtle lifetime issues arise.

Euro Micelli
  • 33,285
  • 8
  • 51
  • 70
0

Error WIN32=262 is HRESULT=-2147024634 (0x80070106). This is something else. CoInitializeEx should be called for every thread only once, but if it is called more than once, there should be one CoUninitialize for every CoInitializeEx.

Elvedin Hamzagic
  • 825
  • 1
  • 9
  • 22
0

Check that you have compiled everything in either release or debug mode.

Do you use any of the http based functionality from I.E? I'm curious why you're dependent on ieshims.dll.

JimR
  • 15,513
  • 2
  • 20
  • 26