Scott Hanselman

When .NET 2.0 Applications Attack - Debugging weirdness after installing the .NET Runtime

November 10, 2005 Comment on this post [10] Posted in Learning .NET | Bugs
Sponsored By

WeirdLogitechRemotingBugI'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.

Io2Software

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.

LplsHost

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.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Windows Clippings - A Cropper Contender

November 10, 2005 Comment on this post [2] Posted in Reviews | ASP.NET
Sponsored By

I dig Cropper in a big way. It's a great, elegant and lightweight way to do screenshot. However, lately I've been using Kenny Kerr's Windows Clippings 1.0 for posting to my blog.

It goes in the tray and when you double click on it the screen dims. You single click on the window you want to get a screenshot of and just that window brightens. A double-click then will send the screenshot to the clipboard, OneNote, or a Folder. It's even smart enough to pick a good filename based on the Window's Title.

Not only that, but it will create transparency in all the appropriate places, which is very useful when you consider that even Windows Explorer uses irregular regions and curved edges.

Kudos to Kenny for another killer util!

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.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Blogging directly from a Logitech IO2 Pen

November 10, 2005 Comment on this post [9] Posted in Coding4Fun
Sponsored By

UPDATE: In case this seemed like a non sequitur, this is in support of an upcoming article for Coding4Fun on the Logitech io2 pen.

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.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

What is the difference between Type 1 and Type 2 Diabetes?

November 07, 2005 Comment on this post [11] Posted in Diabetes
Sponsored By

I'm asked all the time what the difference is between Type 1 and Type 2 Diabetes. I'm a Type 1 and I wear an Insulin Pump that pushes insulin into my system 24 hours a day. Sometimes folks will say, "Oh, my uncle has diabetes, he got it when he was fifty" and assume it's just like mine.

Type 1 diabetics don't produce enough insulin and need augmentation. Type 2 diabetics product too much because they are resistant to their own insulin. Both of us have high blood sugar because we're not using insulin effectively. Here's a very clear difference:

Dr. Michael Murray clearly identifies the difference between type 1 and type 2 diabetes when he states, "Healthy individuals secrete approximately 31 units of insulin daily; the obese type 2 individual secretes an average of 114 units daily.  Individuals with type 1 diabetes secrete only 4 units of insulin daily."

This is totally true, because I use about 30 units of insulin each day delivered from my pump.

UPDATE: You might also enjoy my "Diabetes: The Airplane Analogy" post from a while back.

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.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Caching in ASP.NET - VaryByParam may need VaryByHeader

November 05, 2005 Comment on this post [5] Posted in ASP.NET | DasBlog | HttpModule
Sponsored By

I was tidying up the blog this evening and adding a Reviews category when I visited one of my own pages and noticed that chunks of content were in German. I literally did a double take.

Then I realized that one of my performance optimizations since the site's been getting slammed was to add this to Permalink.aspx (the page that ultimately services DasBlog's single-post pages, no matter what you see in the URL.)

<%@ OutputCache Duration="1800" VaryByParam="*"%>

What could be wrong with that you ask? The idea was to handle different combinations of input and cache page output as appropriate. However, what input would cause my site to serve me, an anglophone, German?

Well, if a German fellow browsing with his browser's Accept-Language header set to anything like "de-DE" and he happened to be the FIRST guy to visit a specific page before it was cached, given the directive above, the page would be cached as it was shown to him. DasBlog will generate most non-content text in the requested language.

Ah! So I needed to:

<%@ OutputCache Duration="1800" VaryByParam="*" VaryByHeader="Accept-Language" %>

Problem solved.

NOTE: I do use HttpCompression and some folks add Accept-Encoding to the VaryByHeader, but the HttpModule I use adds that programatically so everyone gets what they expect. All this caching DOES add up to memory. If someone visted every page in my site at least once with every possible language value, along with HttpCompression on in their browers, then again with HttpCompression off, my Web Server would have to hold a least numOfPages*2*numOfLanguages cached versions of pages.

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.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.