Click with care

by Brian Ensink 20. October 2009 21:58

Yipes!  Don't ever accidentally click "Unshelve" a shelf set in TFS containing code you already checked in weeks ago.  I wanted to delete an old shelf set but absent mindedly unshelved it instead, I even cancelled it half way through but the damage was already done.  This completely hid the fact that I had already checked in those files.  TFS showed the unshelved files as "Added" or "Edited" making me doubt whether I ever checked them in weeks ago.  Only after confirming with another coworker whose TFS was not obscured with my shelf set did I trust that I had in fact checked in those files weeks ago and not made some other brain dead mistake.  As penance I got to spend the next half hour or so undoing checkouts, getting latest, merging conflicts, overwriting readonly files, again and again, file by file, and gently reassuring TFS that I was not intentionally trying to violate the space-time continuum.  So the moral of the story is don't accidently unshelve ancient code!  Click with care.

Tags:

General Computing

Platform Invoke Your Own Functions

by Brian Ensink 5. October 2009 23:17

In a previous post I pointed out some resources to help write PInvoke signatures to call platform functions from C# or VB.NET code.  This type of interop can also be used to introduce C#/VB.NET code to an application not using the .NET Framework.  For example perhaps a new GUI can be written in C# that reads and writes data using an existing C++ database layer.  This form of interop is useful if you:

  1. Need to call unmanaged code from C# or VB.NET
  2. Have control over the unmanaged C/C++ codebase
  3. Don’t have or don’t want to use a COM interface
  4. Can reduce your module’s interface to C functions (PInvoke provides limited object oriented support, essentially whatever you can do with structs and no classes)

Below is very well near the simplest possible example.

First create a Win32 DLL project in Visual Studio and add the code below, a single trivial function.  The “__declspec(dllexport)” exports the function from the DLL allowing outside modules to call it and the “extern “C”” prevents C++ name mangling.

   1: extern "C" __declspec(dllexport) int Add(int a, int b)
   2: {
   3:     return a + b;
   4: }

Next create a C# project (or VB.NET if you prefer).  If you are already experienced with platform-invoking Windows functions this code will be familiar.  Lines 8 and 9 import the function from the DLL created above.   See my previous post linked above for more resources on PInvoke signatures.

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         Console.WriteLine(Add(10, 20));
   6:     }
   7:  
   8:     [DllImport("My_CPP_Project.dll")]
   9:     static extern int Add(int a, int b);
  10: }

To run the code both the DLL from the first step and the EXE from the second step must be in the same folder.  However its possible to dynamically load the unmanaged DLL from a different folder using LoadLibrary from the C# EXE.  Simply ensure that you call LoadLibrary to correctly load the DLL before making any calls to the functions exported by the DLL.

Tags:

Software Development

Runtime Callable Wrappers and their COM objects

by Brian Ensink 25. September 2009 00:15

Today I was testing some fresh code that I had written over the past weeks when I found I needed a fix from another developer.  So I merged changes from the main branch into my local working copy and kicked off a deep rebuild.  The build churned for a while but failed with some unusual errors, not the ordinary syntax errors.  Aww nuts, the code was building fine before I got latest … but now I’m stuck until I fix this.

Ultimately the biggest problem was a circular dependency that I had introduced (it seems obvious now looking back) but that is not what this post is about.  Along the way I ran into this nice little error that stumped me.

> regasm /verbose /tlb:CAP.SIFCatalogHandler.tlb CAP.SifCatalogHandler.dll

Types registered successfully
RegAsm : error RA0000 : Type library exporter encountered an error while processing 'CAP.SifCatalogHandler.SifCatalogHandler, CAP.SifCatalogHandler'. Error: Referenced type is defined in managed component, which is imported from a type library that could not be loaded (type: 'CAP.ICAPCatalog2'; component: 'C:\WINDOWS\assembly\GAC_MSIL\CAP.CAPDOM\1.0.0.0__414f99c4b6a0bb9a\CAP.CAPDOM.dll').

This error came up while trying to register a .NET assembly containing a COM object written in C#.  Notice that this regasm.exe call also generates a TLB file.  The error claims that it cannot load one of the referenced assemblies called CAP.CAPDOM.dll.  This assembly is a runtime callable wrapper (RCW) for an older COM object written in C++.  I checked and rechecked that CAP.CAPDOM.dll existed.  I manually rebuilt it and checked again but I still kept getting the same error.  I even pulled out fuslogvw.exe which showed that CAP.CAPDOM.dll was loading just fine.

Sigh, for once the tools give a detailed four-line error message that seems to say exactly what the problem is but I’m still to slow witted to figure it out.

A few Google searches on this error and I found a number of posts that seemed to generally confirm some sort of dependency error but nothing gelled.  Besides, I already confirmed that the assembly its complaining about actually exists!

Finally I had an idea.  CAP.CAPDOM is a RCW for a COM object.  The RCW exists, but does the COM object?  No.  Does it need to?  I didn’t know the answer at the time but it does need to exist in this case.  This is when I realized I had a circular dependency.  I backed out my changes and built the actual COM object.

This time regasm.exe worked correctly.  Hooray!

I then unregistered the COM object to see what would happen and again the regasm.exe command gave the same error.  Notice that the error text above actually says “Types registered successfully”.  It seems to just fail while generating the TLB.  Perhaps the RCW’s real COM object is only needed when generating a TLB like this.  Perhaps its only needed when the assembly implements an interface originally declared in the missing COM object.  Apparently the RCW alone is enough to code against and compile the project, but not enough to register it with regasm.exe.  Turns out the beautiful four-line error message is spot on and like usual the problem exists between the keyboard and the chair.

The moral of the story is that an assembly load error for an RCW assembly might actually mean the RCW’s COM object DLL is missing. 

I will remember that.

Now I just need to orchestrate something to get around the circular dependency, restore my changes and I’m back in business.

Tags:

Software Development

About the author

I am currently a .NET developer and really enjoy the platform.  .NET seems to be able to take the developer whereever he/she wants to go.  To the desktop, to the web, to a database, etc.  At my day job I write desktop apps but I also like to toy with other tech as I have time.