May 2003 - Posts

It seems to me that regasm /codebase gets a bad rap. I guess that's to be expected -- it's a pretty self-deprecating little command line switch. This is what it has to say for itself, when run against an unsigned assembly:

RegAsm warning: Registering an unsigned assembly with /codebase can cause 
your assembly to interfere with other applications that may be installed on the 
same computer. The /codebase switch is intended to be used only with signed 
assemblies. Please give your assembly a strong name and re-register it. 

Adam Nathan also has some harsh things to say about regasm /codebase in his excellent COM Interop book... and lately some colleages of mine, both here and abroad, have been on my case about this, for some reason. (The guys who make our setups say that InstallShield just totally refuses to register an unsigned assembly for COM Interop.)

Maybe I just don't see what the problem is... I did the COM development thing for the better part of a decade, and none of my DLLs were ever signed with a strongname. In COM, we had GUIDs for strongnames. Well with COM Interop, we still have GUIDs! COM Interop is still COM.

Fundamentally, regasm /codebase is no different than regsvr32: they both register a path to a DLL under HKCR/CLSID/{some big number}/InprocServer32. The fact that regasm squeezes a virtual class factory in there -- forwarding to Assembly.LoadFrom from mscoree.dll!DllGetClassObject -- is an implementation detail.

The only good argument I can think of against regasm /codebase (one which I haven't seen mentioned anywhere) is perf. The codebase value in the registry is apparently the very last place the CLR looks, when loading a type for COM Interop (after the GAC, and after probing the apppath, etc -- note that these semantics differ from that of the <codebase> element in an app config file, which override probing).

But hey, no strongname means no signature to verify, thus saving a few cycles on that side of the equation. ;-) And CoCreateInstance is already such a big bloody monster, I doubt that the perf difference between GAC and codebase would be noticable to an end-user -- one certainly wouldn't want to call CoCreateInstance in a very tight loop, even in the best of circumstances!

So, what's the deal, folks?  Let regasm /codebase live!

 

One of the things I like most about .NET is that there's so little difference between Release- and Debug-builds.  Anyone who's ever attempted to debug a Release-mode only problem (or, for that matter, a Debug-only problem) in unmanaged code knows what I'm talking about.

But this past week saw not one, but two release-mode only bugs in a development effort I've been leading.  Perhaps the GC gods are angry with me, because they overheard me disparage my working set size...  or, more likely: my false sense of security has finally caught up with me!

Traditionally when I encounter a "Release-mode only bug", I look for three things:  subtle timing problems in my application code, differences in memory layout/initialization, or (lastly) true compiler/optimizer bugs. 

One bug I ran into this week was definitely of the first variety:  a foolish developer on my team depending on two tightly-spaced events to always fire in the same order that they do on my... err, I mean "his", dev box.  Bad, bad developer!

It's a little early to say with certainty, but the other bug is looking like it belongs in the latter category -- maybe not incorrect JIT code, per se, but definitely a noticable difference in run-time behavior.  (Bad JIT code is something I've yet to run into with .NET...  anyone?)

A developer on my team was attempting to use a Mutex (declared as a local variable in the app's Main method) to ensure that only one instance of the app would be run, in a given session.  Simple enough, and it worked like a charm in Debug mode -- but not in Release!  That is, two or more instance of a Release build were able to start, simultaneously, while only one instance of a Debug build was able to do so.  Even stranger:  wrapping the Mutex variable with a C# "using" block eradicated the bug.  (Making the Mutex variable a static member of the class also worked...  or a well-placed call to GC.KeepAlive...  yadda yadda.)

So naturally I began to suspect that this was a GC problem:  in Debug-mode, the lifetime of the Mutex was lasting for the entire duration of the Main function, but in Release-mode, the Mutex was being finalized earlier.  Wow.  Sure, this kind of thing is easy to work around, when you know what to look for (and when you notice it, at all!) but it can also be very frustrating, for those poor souls who don't.

I'm curious to learn what other people are experiencing, in this regard:  has anyone encountered a bug of the second category -- a Debug/Release bug due to the difference in memory layout, or initialization, in the CLR?

Share your horror stories in the comments!

 

Apparently I just won a Tablet PC!  Wow!  I never win anything.  And with just about 8 significant lines of code...  apparently I've been trying too hard, all my life.  ;-)

ps:  If anyone is listening...  Sorry my blog has been so gray, lately.  In my new pseudo-managerial role at LGI, I've been hunkered down, trying to get some overdue projects out the door.  My hat-rack is full: apparently I'm lead-dev, architect, PM, doc manager, and QA manager for 3 products now.  Life is fun, play hard!

 

The Problem:  NDoc emits hyperlinks to the standard BCL entities, like System.Object.  These hyperlinks are specific to VS.NET 7.0.  For machines with Everett-only, the links are broken.

The Solution:  Until a proper fix is available from the NDoc folks, or someone with more free cycles than me contributes a fix to their open-source effort, you should use a regex search-and-replace tool to patch the hyperlinks.

The Tool:  Wouldn't you know, I just happened to author a nice regex search-and-replace tool.  I've been dogfooding it for a while now, but it hasn't been thoroughly tested... use with care!

The Automation:  Here's how to run it, to transform v1.0 NDoc output into v1.1 NDoc output (assuming the NDoc-generated output lies in the cwd... and both rsar.exe and hhc.exe lie in the system path):

    rsar.exe *.html \bms-help://MS.NETFrameworkSDK\b
        /r=ms-help://MS.NETFrameworkSDKv1.1
   
    for %I in (*.hhp) do hhc.exe %I

Remember to double-up the percent symbols, when pasting that "for" command into a batch file.

HTH,
-S

 

I just tried to re-sign an assembly (think: sn.exe -R) with a different public/private keypair than it was originally signed with -- and I was surprised to find it didn't work.
 
That is, sn.exe doesn't complain, but the .NET loader sure does.
 
I tried setting [AssemblyDelaySign(true)] and recompiling...  still, I'm not able to re-sign with a different keypair than I used, during the original compilation.
 
So, is the assembly signature not just a block in the metadata table, somewhere?  I guess I don't understand what's going on, here...  but of course, the princples of public-key crytpo are anything but intuitive to me.
 
Just to be clear:  I know how to do the conventional delay-signing dance -- I'm trying to experiment with alternative approaches to the problem.  (Background: I don't like the idea of my dev team running around with partially signed assemblies, on machines riddled with verification-skipping entries...  our managers are prone to dropping private-builds onto customer machines, for demos, etc.  I was hoping we could keep our companywide keypair on the official build machine, and yet still allow everyone else to build fully verifiable assemblies, on their own boxes.)
 
Can anyone shed some light on this, for me? 
 
Am I stuck with sn -Vr *,<mypublickeyhash>?
More Posts