When .NET 2.0 Applications Attack - Debugging weirdness after installing the .NET Runtime
I've been developing using the Logitech io2 Pen SDK recently and also, coincidentally installed .NET 2.0 and Visual Studio 2005 around the same time.
I got this error a few days ago upon docking the pen: "Ticks must be between DateTime.MinValue and DateTime.MaxValue" and noticed that remoting was involved in the stack dump:
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at Logitech.Pen.Device.IPen.get_LastSynchronizationTime()
at Logitech.Pen.TrayIcon.PenBalloon.DoRefresh()
So, two assemblies were chatting each other up via remoting and something went wrong. Logitech has a number of EXEs that all work together to make io2 Pen Services available in multiple contexts. Don't worry, they don't all run at the same time; they come and go as needed.
I knew that Logitech used .NET 1.1 for their development and I knew that on systems that have both .NET 2.0 and .NET 1.1 that 1.1 EXEs would get the 1.1 CLR loaded by default. However, the only thing that changed was the installation of 2.0.
Which of these was running .NET 2.0? Using Sysinternal's glorious ProcExp that can highlight processes that contain a .NET runtime as well as show exactly what DLLs are loaded in a process's memory space. Looks like Pen.LplsHost.exe gets 2.0 loaded, while EVERY other .EXE gets 1.1.
That's weird, and it would make sense that if a .NET 1.1 application remoted into a .NET 2.0 application that something odd might happened (Honest question: Doesn't it?).
When you install .NET 2.0, does it changed any 1.1 DLLs? Yes and no. The system-wide CLR loader, mscoree.dll changes when you install a new version of the .NET Framework. It's always the last version. This is necessary because the loader ultimately makes the decision of which CLR to load into that process. So, mscoree was updated to make the 1.0, 1.1 or 2.0 decision.
Now, the real question is: What is it about Pen.LplsHost.exe that causes it to load .NET 2.0?
Here's why! It's NOT a .NET application! Reflector reminds me that it's an unmanaged C++ application. It does load the CLR though, so it's likely a CLR Host that does something like this to load the CLR:
LPWSTR pszFlavor = L"wks";
ICorRuntimeHost *pHost = NULL;
hr = CorBindToRuntimeEx(
//version
null,
// svr or wks
pszFlavor,
//domain-neutral"ness" and gc settings
STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST |
STARTUP_CONCURRENT_GC,
CLSID_CorRuntimeHost,
IID_ICorRuntimeHost,
(void **)&pHost);
However, if you don't pass in a value for version, MSDN says:
"If the caller specifies null for pwszVersion, the latest version of the common language runtime is loaded. Passing null gives the host no control over which version of the runtime is loaded. While this may be appropriate in some scenarios, it is strongly recommended that the host supply a specific version to load."
So instead, they should note http://msdn.com/library/en-us/cpgenref/html/grfuncorbindtoruntimeex.asp and try this instead:
LPWSTR pszVer = L"v1.1.4322"; // <!---- Oy!
LPWSTR pszFlavor = L"wks";
ICorRuntimeHost *pHost = NULL;
hr = CorBindToRuntimeEx(
//version
pszVer, // <!---- Oy!
// svr or wks
pszFlavor,
//domain-neutral"ness" and gc settings
STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST |
STARTUP_CONCURRENT_GC,
CLSID_CorRuntimeHost,
IID_ICorRuntimeHost,
(void **)&pHost);
Speculation: I'd bet $100 that the devs aren't asking for .NET 1.1 in Pen.LplsHost.exe and they should have if they were following best practices. As I said, you can override it with the .config file, but the RIGHT thing to do (in the short term) is to ask for the version you want.
I'm chatting with Logitech Support about this, so until I update this post everything I've said so far is just my opinion and speculation and in no way means to impugn they or there developers. It's a difficult thing to anticipate these things and to include version-specific information, especially as a CLR Host.
Short term fix: You can easily force Pen.LplsHost.exe (or any application for that matter) to load the .NET 1.1 framework by dropping the attached file into C:\Program Files\Logitech\io2Software. I have done this and haven't see any problems since and noted with ProcExp that 1.1 is now correctly loaded in this process's space.
<configuration>
<startup>
<requiredRuntime version="v1.1.4322" />
</startup>
</configuration>
File Attachment: Pen.LplsHost.exe.config (118 bytes)
UPDATE: Matt Davis (see the comments for this post) nailed it. I was mistaken. I spoke to the team at Logitech and it was, in fact, C++ calling a .NET Assembly via COM Interop. This could be controlled via the RunTimeVersion key for the InproServer32 key for this RCW within the Registry. However, everything in this post up to the CorBindToRuntimeEx conclusion holds, and the .exe.config solution is also workable one. Logitech is exploring ways to make this easier. My question is, what about Explorer.exe and hosting multiple shell extensions each written in different versions of .NET?
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
P.S.: I posted the same comment earlier and it never got posted. (Rejected as spam?)
P.P.S.:This comment page earlier (as of few days ago) did not have horizontal scroll. But now it does.
Out of curiosity, seriously, not picking on you, but why run 1024?
I'm pretty sure that what you describe with shell extensions (as an example) is exactly why MS did a complete about-face post-PDC2003, and reimplemented the Longhorn/Vista UI in *UN*managed code. Many of us were less than thrilled with the "system framework" notion that was required by such a setup. You completely lose the benefits of SxS when something as simple as displaying a messagebox loads whatever framework the system feels like into your process... :(
I even sent a letter, via snail mail, to Bob Wick, a VP at Logitech to make sure that the engineering team heard about this (I was unconvinced that the email support team forwards this information.)
Unfortunately for Logitech, we went to another solution for in-the-field data collection! You try to cut a few corners with tech support and look where it gets you--dissatisfied customers and lost sales.
After about 2 months, I got a SECOND answer from Logitech Tech Support. They have no clue. Here it is below:
-----
Dear Robert,
Thank you for your recent inquiry about your Digital Pen and Paper.
I apologize for not sending you the link. I also need to explain the link a little further. This happens because the performance information from some process is corrupted and we couldnt convert the information into DateTime. This may happen when a 3rd party program was installed. It may have changed an entry to the .Net reference our software is trying to make.
The error may also happen if you are running .Net Framework 2.0.
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=19247&SiteID=1
Unfortunately we do not have any specific list of programs that may change this entry our software is trying to call. You may find more information by doing a web search for the error "System.ArgumentOutOfRangeException: Ticks must be between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks." You will find alot of good information that may pertain to your specific message.
---
Logitech is now on my company's "Do Not Buy" list. This is just crazy. These companies try to save a few pennies by hiring cheapy kid "programmers" instead of experienced degreed professionals and we all suffer. To blame "other programs you installed" for their own incompetence is simply inexcusable.
Comments are closed.
You must've been heated while writing this, I counted about 5 grammar/spelling errors :)