.NET
.NET corner
It is nice to be back at Matrix. The story begins like this -
Couple of days back we were facing some issues regarding GACing and UnGACing our primary assembly. They(install team) were using some in-house tool called ourProductReg.exe for unGACing the primary assembly , let's call it ourProductAssembly.dll. The commandline tool accepts full qualified filepath of the assembly to be uninstalled(installtion - GACing , seemed doesn't have any issue). Strange. Like below
ourProductReg.exe /u c:\....\ourProductAssembly.dll
But the intention was to uninstall all the assemblies given just the filename not the path name. Like
ourProductReg.exe /u ourProductAssembly
So this would uninstall all the assemblies named ourProductAssembly irrespective of cultures, version etc. Sounds pretty gacutil-like. So I was looking into it. Suddenly, after one hour, my assignment was revisited. This time the intention was to match more partial names including cultures or versions which were left earlier.Like
ourProductReg.exe /u ourProductAssembly, Version=1.0.0.1
This should uninstall(unGAC) all the assemblies with name ourProductAssembly and version 1.0.0.1 irrespective of the cultures. Come home to the world of gacutil. I understood that client shouldn't have entire fatty .NET SDK installed for only this management tools like gacutil.exe. But I was not aware of the reason of not distributing gacutil.exe along with our product. Later I came to know that the SDK license does not allow to distribute the tools outside of the SDK, so client will have to download the entire package. Definitely not an option.
Find the part 2 here.
Guys I was busy in some HLD for past two weeks or so. I'm back now into my favourite blogspace - DNJ.
Yesterday when our 64 bit porting team is working for the immediate 64 bit release of our product, they came to me for suggestion on a topic which I think worth discussing. While they are happily ported unmanaged C++ code, they were facing some difficulties with porting managed code(which should be easier! right?). In unmanaged C++ world they handled pointer disparity between the platforms with smart #ifdefs and #define macros. But in managed C# layer we do have some unsafe pointer code that interoperate to the unmanaged C++ code. Situation become tricky. As there is no #define macros in C# world, they were unable to write ubiquitous code that would work on either platform.
In one particular situation we have a long native 64 bit pointer allocated in unmanaged layer and C# unsafe code would like to interoperate with it in following way.
___ ___ ___ ___ ___ ___ ______
x ----------> |___|___|___|___|___|___|___|___|
In 32 bit platform we wanted to access first 4 bytes(32 bit pointer size) of x
... = *((int*)x)
In 64 bit platform ,analogously, we wanted to access first 8 bytes(64 bit pointer size) of x
... = *((__int64*)x)
And in both cases we also used to use integer pointer arithmatic by adding integer offset values to (int*)x or (__int64*)x (which hasn't been shown). No problem as such here. The problem relates to merging these two set of lines to one. I immediately scream "use UIntPtr...". But just how? Well we need to construct a UIntPtr out of the long value - x. In looking through Reflector the specified ctor appears like below
public unsafe UIntPtr(UInt64 value)
{
this.m_value = (void*) ((uint) value);
}
But mine was a 32 bit CLR. What I guess that in 64 bit this ctor will not truncate the long value. It should be something like below
public unsafe UIntPtr(UInt64 value)
{
this.m_value = (void*) value;
}
A look into Rotor confirmed my guess. One can check IntPtr.cs file here
public unsafe UIntPtr(UInt64 value)
00051 {
00052 #if WIN32
00053 m_value = (void *)checked((uint)value);
00054 #else
00055 m_value = (void *)value;
00056 #endif
00057 }
The consequence is significant. This means m_value holds platform-specific integer pointer and so does the return value of ToPointer() which just exposes the m_value.
public unsafe void* ToPointer()
{
return this.m_value;
}
So the code should be something like below
UIntPtr uiptr = new UIntPtr(x); // Invoke the specified ctor
... = *(uiptr.ToPointer());
But the explicit construction of the UIntPtr doesn't look elegant. Then I looked at the following casting operator of UIntPtr which takes a long(64 bit) value and convert it to an UIntPtr -
public static explicit operator UIntPtr(UInt64 value)
{
return new UIntPtr(value);
}
This conversion operator internally invokes the same ctor we are interested with. So now the revised code looks like
... = *(((UIntPtr)x).ToPointer());
64 bit porting of managed application sometimes can create little surprises. When managed code heavily deals with P/Invoke, COM Interop and unsafe(c#) code porting becomes non-trivial. If interested you can look at this official MSDN 32-to-64 bit migration guide for managed code http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/64migrate.asp.
By the way I'm reading two wonderful books - both are theoretically non-windows books - "Art of Unix Programming" and "Innovation Happens Elsewhere". Do lots of programming4fun.
Amongst all the P2P file sharing softwares I have came accross so far BearShare is the most efficient one. Unlike some other P2P sharing softwares like Morpheus(hosts of viruses) or LimeWare(terribly slow because of its Java Runtime dependency), BearShare is completely written in C++ and hence very lightweight and efficient. P2P softwares often has a "reputation" of being a trojan horse of various viruses, worms and spywares. This threat is very low for BearShare and if appropriate measure has been taken it could do hardly any harm to your system(for one such measure look at this earlier post of mine regarding BearShare). In this post I will discuss some of the tips and tricks that I've learnt on the way of using BearShare.
Tip - 1
Get the pro version of 5.0.2.3. The serach-space of this version is greater and this also take care of broken downloads. How to get the pro. version. Well you have to pay for it. http://www.bearshare.com/. Another tricky way is to use the BearShare Lite(Free) version itself to search for downloading pro. version. It's analogous to crack SoftIce with SoftIce.
Tip - 2
Be a little selfish. Disable uploading to increase the overall download speed and to secure your system. Do this by going Option > Setup > Upload

Tip - 3
Always make sure a file exactly is what it claims to be. Use Bitzi ticket for that. But before going on using that there exists an easier thumb rule for validation. For example if you are searching for "Half-Life 2: Episode 1" game and got a file of size 202,477 bytes you are definitely dealing with a virus or other misleading files unless it points to a valid .torrent file(which are often pretty small) which could be downloaded by any torrent client like BitTorrent. BearShare falls in a Gnutella category and doesn't support downloading through .torrent files.
Tip - 4
Anytime you suspect about a file, use Bitzi ticket. Bitzi is basically a lookup service which compares a unique SHA1 hashid(that the file presents) with its online database to show details of it. As part of the details you could found the feedback and rating of other users about it. To see the SHA1 hashid of a file unhide the metadata column from "Results" button in the search window(top-left)

In one of the Bitzi lookup I found the following feedback/rating

Clearly One souldn't continue with this download.
Tip - 5
Instead of validating each search result(which could be vary tiresome) with Bitzi lookup, Use Bitzi search and find out the most recommended version of the file. Once you have found "most recommended" or "best version" file, go to the section in details page of the file which reads "APPLICATION IDENTIFIERS".

There you should have two category of URLs - Magnet and E2DK. We are interested with the first one.
Now if you click on the link, ideally BearShare should capture that and start downloading. But world is far from being ideal. Instead it will show you following messagebox.

To fix this remove all attributes from the magnet URL except sha1(first) and dn(usually the last). So the modified magnet URL looks like
magnet:?xt=urn:sha1:ROOFD7DSBLGFZVYMNDMFACGUP7PBGZ74&dn=(PC GAME ITA) The Sims 2 CD1.iso
Put that URL to browser's addressbar and BearShare's URL manager pops up

And you know what to do next. By the way my BrainBrush(BB) series on DataStructure will continue. Just I was busy for office-work for a while. Argentina won. Ole-Ole-Oleeeeeeeeeeeeee
For a long tome I kept myself away from this wonderful but ugly looking tool as I was the die-hard fan of a Compuware(Numega then) masterpiece - SoftIce. Hey I still have it installed inside of a Win98 Virtual Box in my XP host. Though SoftIce has got its new incarnation - DriverStudio, not the WinDbg. If you haven't downloaded it, get it Debugging tools for Windows. But I'm not going to give basics of WinDbg usage here. Nop. But quality articles are galore. One thing I must mention that if your technology stack contains anything .NET, there is a nice WinDbg extension DLL(always keep an eye on software extensibility model) exists at the .NET framework installation folder. It's sos.dll(Son Of Strike). This guy will help to inspect virually anything in .NET world from method table to managed stack
This encounter with Windbg is actually initiated from requirements at my office. I was analyzing some crash dump generated by our driver library. Boy they are fatty. But still they are smaller than the earlier days. Actually these beasts are tagged as Mini Dump which is much light-weight than the full fledged dumps often called Core Dump. Mini Dumps are very different than their older counterparts in the following way
- Instead of saving the entire process space, only certain sections are saved. There is no point in saving copies of modules such as Kernel32.dll; if the version number is included, it is easy to get a copy from a Windows CD. The actual memory heap of the application is by default not saved in a minidump; it is not required to debug a surprisingly high percentage of crashes. You can save the heap if you need to, however.
- The minidump save code works to get accurate and full information for modules, including their names, paths, version information, and internal timestamps.
- The minidump save code also gets the list of threads, their contexts (that is, register sets), and the memory behind their stacks.
- The whole file is compressed, further reducing its size. A minidump for Notepad is around 6K on Windows XP, almost 300 times smaller than the previous crash dump of the same process.
Now when do we generates dump? Certainly not always. Instead one of these scary moments when your software "sees" end of days at customer premises. Most of the softwares now-a-days employs structure exception handling(SEH) and displays custom user-friendly UI if a fault occurs. But sometimes it is necessary for the developers to analyze what's exactly happened when the fault occurred. Handling the exception is certainly not the answer. Instead with some configuration changes(may be application configuration or infamous Windows Registry backdoor) the "on site" guy enables the dump generation facility and reproduces the fault once more. Following is a vanilla piece of code found somewhere in this context.
[C#]
try
{
//where fault occurs...
}
catch(Exception e)
{
if (TraceLevel == TraceLevel.GenerateDump)
{
DumpGenerator.WriteCrachDump();
}
}
Couple of pointers here. First the check inside the exception handler is very important..I repeat..very very important. Here dump is generated as an elevated level of application(or driver) tracing configuration(again done by our beloved "on site" guy). Not always your application(or driver) should inflate the customer disks with fatty DMP files. Another subtle point - I didn't pass the exception object as a parameter to the dump generator function. As this is managed code the exception object will only contain the exception information pertaining to the managed stack. But we need an unmanaged Exception_Pointers struct to pass around. That could be generated within the function with the help of GetLastError() API. Still there is a catch - if you P/Invoke the GetLastError call from C#, always remember that there is chance that CLR will invalidate the error while doing its own stuff when control returns from unmanaged world(where error occurred) to managed world by Interop Marshallar. Look at this entry of Adam Nathan's blog for more detail.
The most important parameter to the MiniDumpWrite function is the first and second.
typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
DWORD ThreadId;
PEXCEPTION_POINTERS ExceptionPointers;
BOOL ClientPointers;
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
Naturally the exception pointers parameter is what I was talking about a liitle bit ago and its retrieval by GetLastError or Marshal.GetLastWin32Error. The first parameter identifies the thread which throws the exception.
Now from here the fun begins. I successfully able to analyze all but one DMP file by inspecting managed and unmanaged stack. But whenever I tried to open the offending DMP file, my WinDbg crashed. Oh God! Who will analyze the dump that WinDbg will generate? Purplexing !
Just before crash I saw a strange output at the WinDbg's console. Something related to invalid thraed id.
ERROR: Unable to find system thread 2
ERROR: The thread being debugged has either exited or cannot be accessed
ERROR: Many commands will not work properly
Now I was performing a suite of test cases while dump was generated. It was utmost important to me to know which process actually generated the dump so that I could narrow down the troubled section. Fortunately there is a tool in the same folder where WinDbg usually resides - dumpchk.exe. With
>> dumpchk -e "offending_dump_file.dmp"
I was instantly presented with enough information to determine the offending test case. But that doesn't solve the problem of WinDbg's unusal crash. Later I found out(through debugging - without dumpchk that even would have been impossible) that when ExceptionPointers parameter is not null(means exception actually happened at unmanaged layer of our code) we are pushing the "passed" thread id to this structure before calling the write dump function.Although if you pass null or 0 to this ExceptionPointers parameter dump will be generated but its unmanaged stack will be ignored. So to track back how we were passing the thread id to this function, I discovered following code
#ifdef DOTNET20
threadId = System.Threading.Thread.ManagedThreadId();
#else
threadId = AppDomain.GetCurrentThreadId();
#endif
Surely the Appdomain.GetCurrentThreadId API has been deprecated but It does something the newer API doesn't and neither supposed to that.
Managed threads has nothing to do with unmanaged threads. The mapping between managed thread to unmanage thread is tricky. The newer API is just a hash value used to determine a managed threads uniquely. It doesn't corresponds to the unmanaged thread. The mapping between these thraeds of two worlds could be controlled by a CLR host or could be changed by future versions of .NET Fx. Due to this unstability Microsoft recommends that you are not supposed to assume 1:1 correspondence between manage and unmanaged thread while using AppDomain.GetCurrentThreadId() in your code. Although in 2.0 it happens to return the unmanaged id but who knows. Sonner or later this code will definitely break. Check at this blogs about manage and unmanaged threads - 1, 2
With "not-so-offending" dump files your "!analyze -v" session will be peaceful. It's 3 AM here at India. I'm tired..See you guys.