Monday, July 07, 2008

Pardon me while I scratch my head and try to decipher this paradox:

 

This is one seriously confused program.

Monday, July 07, 2008 9:53:14 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, June 12, 2008

While looking on Reggie's blog for news on MySQL support for the Entity Framework, I enjoyed reading this article.  I continue to be amazed at the performance of the CLR.  Some people are absolutely baffled that managed code could be faster than native code, and the low-level reasons why have been blogged by people far smarter than I in the past.  Since someone just asked, A couple of the better-known reasons:

1) The managed heap rules:  If I say "object* foo = new object();" in C++, the OS looks around to find some memory it can give me.  CLR programs use a managed heap, however.  Despite the fact that calling "new()" is always expensive, the managed heap gives a huge advantage.  Instead of searching for free memory, the next chunk on the managed heap is returned immediately.  C developers writing games have used similar tricks for years, using malloc to get a big chunk of memory and managing bits of it themselves as needed.

2) JIT rules: C# is always JITified before execution.  JIT compiling turns your C# into machine code.  So what, you say, my C/C++ code is pre-compiled for the platform and doesn't pay the JIT penalty on application startup.  The advantage of the JIT is that it can be compiled each time the program is ran, and potentially take advantage of different situations and things specific not just to a processor architecture, but on each individual machine.  (I need to find a link to cite this appropriately) 

Anyway, read the article on Reggie's site, and thank him for the work he's doing.  Hey Reggie, when is that preview of Connector 5.3 come out?

.NET | C++ | O/R Mapping
Thursday, June 12, 2008 12:20:58 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, May 13, 2008

In addition to concurrency, the 64bit realm is another obvious place where the PC/Server world is going.  It’s faster than 32bit, we can address more memory, and we can have more accurate hardware floating point.  Ideally, it should be easy for everyone except low-level framework authors, device driver authors, and the like to migrate their applications to 64bit.  I mean this for Native applications too.  If you had to support both “interface X” and “interface Y” in your application, you’d create a level of indirection to do so, perhaps a Strategy pattern. 

If you are a .NET developer, all of your programs should magically become real 64bit applications when running on a 64bit .net framework on a 64bit machine.  Handy!

I have not written a non-trivial C program in quite some time, so I preface this statement with the disclaimer that it may be total crap: If you are a native developer you should also be able to effortlessly port your app to work with 64bit systems.  If you are NOT hard-coding any address sizes, struct sizes and the like, a simple recompile should work.  Using sizeof(), size_t, etc. the compiler becomes your level of indirection in a way.

Today I sat down to start testing TestDrivenàxUnitàNCover; I pretty much need TestDriven and NCover to do serious work. xUnit.Net ships with an easy installer that tells TestDriven it exists.  It reports success.  It doesn’t work.  I happen to have the xUnit code on hand so I run it through the debugger.  TestDriven looks at a registry key (HKEY_LOCAL_MACHINE\SOFTWARE\MutantDesign\TestDriven.NET\TestRunners) for a list of assemblies containing a class that implements its test runner interface and their locations.  The following code returns “true”:

        public static bool IsRunnerInstalled

        {

            get

            {

                using (RegistryKey runnerKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\MutantDesign\TestDriven.NET\TestRunners\xunit", true))

                    return (runnerKey != null);

            }

        }

The problem is, inspection using Regedit shows that the key is clearly not there.  What’s going on?  Just on a hunch, I thought I’d google for some 64bit issues.  Since there’s \Program Files\ and \Program Files(x86)\ on my 64bit machine, I wondered if there was a second 64bit/32bit registry somewhere.  Almost…

Microsoft, in their wisdom, decided that the registry needs a layer of indirection for 32bit applications.  What purpose this serves I cannot fathom, but you can read all about it here:

http://support.microsoft.com/kb/305097

So, something you THINK is in one place may really be below a “Wow6432Node” registry key.  As I’ve already stated, though, .NET programs should magically be 64bit on a 64bit framework.  There is a way to break this though, and sure enough, the xunit.installer assembly is marked with a Platform Target of x86 in its build properties.  Since the default is the magical “Any CPU” I assume there was a good reason to change this.  My missing registry key, sure enough, is being created in HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\MutantDesign\TestDriven.NET\TestRunners.   TestDriven is looking in the Correct location and not finding what it needs.  I copied the Wow6432 data up into the 64bit area of the registry, disabled and re-loaded TestDriven in the VS2008 add-in manager, and now I’m in business.  Xunit does not appear in the “Test With” menu but Run Tests and Test with debugger now run my XUnit tests.

Test withàCoverage using NCover works too.  I can now ditch NUnit.  Looking at the xUnit à TestDriven code, I’m sure I can get concurrent xUnit tests running from Visual Studio using TestDriven whenever I’m ready. 

Native Windows developers may know what issue this registry redirection practice solves, but it’s lost on me.  Maybe it’s a thunking limitation, I don’t know.  At any rate, it’s something to be aware of when combining 32 and 64 bit code.

 

Tuesday, May 13, 2008 10:11:56 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, April 08, 2008

Someone in my fan club has too much time/money on their hands.  I took this screenshot just now:

 

Tuesday, April 08, 2008 10:41:53 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Monday, March 31, 2008

Before I go ahead and publish my next article, I have two minor gripes about Extension Method Syntax.  Suppose I have a simple extension method:

public static string FriendlyClassName(this object o)

{

string className = o.ToString();

return className.Substring(className.LastIndexOf(".") + 1);

}

I don't care much for explicity use of "this", sice this is basic OO:

//I don't like Extension Methods making me explicitly use "this"

Console.WriteLine(this.FriendlyClassName() + " complete");

I also don't care much for the following being invalid syntax:

Console.WriteLine(base.FriendlyClassName() + " complete");

That one informs me that type "Object" does not contain the FriendlyClassName() method, it would appear that the "this" keyword triggers the syntactical sugar of the extension method paths of the compiler? 

Take a look at the next one, which Dan thinks is wonderful.  Using this extension method:

        public static bool IsNullOrEmpty(this System.Collections.ICollection collection)

        {

            if (null == collection || 0 == collection.Count)

            { return true; }

            return false;           

        }

The following code does not throw any exceptions:

            List<string> nullList = null;

            if (nullList.IsNullOrEmpty())

            {

                //Whiskey Tango Foxtrot?

            }

Hmm... you can read about the NullObject pattern here http://www.cs.oberlin.edu/~jwalker/nullObjPattern/

In a disgusting way, we could use a set of Extension Methods to impliment the NullObject pattern.  IF it makes sense in your program, IF doing the usual coding for nulls isn't appropriate, IF the classical Null Object pattern isn't appropirate, IF Nullable<T> isn't appropriate for your scenario.  That's a lot of ifs.  Still, I found this to be useful in certain cases.

Monday, March 31, 2008 12:58:11 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, March 30, 2008

A small part in my decision to make the leap to Vista 64 was Scott Hanselman's findings upon building his home office and a new workstation therein: moving to 64bit was a non issue.  I wish I could report the same.

My first issue was with installation.  With certain hardware configurations Vista x64 will crash with 4gb of RAM installed until a hotfix is applied.  My understanding of the state of the 64bit world is this: the current 64bit processors are not entirely 64bit.  They have 64 bit registers and some 64 bit instructions.  To the average PC hotrodder or developer the preceived benefit is the ability to use 4gb of RAM instead of 2gb.  I installed the hotfix, got my 4gb of RAM working, and set about playing the Sweeny Todd soundtrack whilst I decided what order to install things in...

The drivers for my X-Fi sound card installed, I started Windows Media Player.  Windows Media Player crashes the first time I start it 100% of the time.  Vista Sp1 and a horde of updates has not fixed this.  Since the X-Fi is probably the most popular aftermarket sound card in the known universe at this time this ought to to work.  It may have nothing to do with sound drivers at all.  The 2nd or 3rd time I launch WMP it will work happily forever.

My graphics card is a PNY GeForce 8800GT series.  It clearly lables itself as having both 32 and 64 bit drivers on the driver disc.  I experienced an new Vista phenomenon with this product: installing the drivers using the OEM program declared that no changes were made to the system.  I pointed Vista at the .inf file on the disc for x64 and Vista basically said its own driver ("Standard VGA Port", yeah right) was better and refused to take the OEM drivers.  Only the Nvidia unified driver install would work.  Odd...

My next stage is to get my various messenger programs up and running so I can chat whilst I wait on things to intall.  MSN messenger is objectively the best messenging experience by far so that went worst, no issues there.  I have used Pidigin for AIM (most CarSpot folks and some friends are on AIM) since having crashtastic experiences with Trillian and an absolutely astounding clusterfuck with the actual AIM product ruining a Vista install; it would seem AOL is the reigning champ for the national heavyweight Invasive Install Championships.  Pidgin has similar behavior as WMP, it will crash and then run perfectly for as long as the machine is up.

My Blu-Ray drive was working fine as a Serial ATA DVD drive.  Folks, installations of things like Office 2007 are actually quite painless with something better than an IDE CD drive.  This alone is worth the extra expense.  Still, it came with software that can play Blu-Ray and there are nights (like tonight) where I'd like to watch a movie on my 1080p monitor while some long-running tasks scroll by on my ancient 19" CRT.  My first attempts at getting Blu-Ray playback working met with failure.  The install process (damn you Pioneer) demanded ridiculously old and specific versions of DirectX that were clearly not going to happen on Vista 64.  Some combination of Windows Update and SP1 magically fixed this, so I watched Blood Diamond on Blu-Ray while logs scrolled by tonight. 

My last two 64bit issues dealt with actual software development.  Visual Studio and the like installed and ran fine except for one issue so infuriating I have a dent in my forehead: copying an ASP.net solution from one machine to another suddenly started claiming that "{MyCustomThinger} RoleProvider has already been added", and commenting this part of Web.config out certainly allows the site to run.  As expected, putting this web.config with the role provider commented out on any other machine (including my development laptop and the two servers where the site actually lies) crashes because of course the configuration is incorrect.  I still can't explain this one.  The next issue was the most time consuming.

MySQL comes in 32 and 64 bit flavors for Windows.  I needed MySQL for one of my development efforts.  MySQL x64 does not like Vista x64.  In fact, the slightly out of date platform notes on their site claims Vista x64 is not supported, but there are enough success stories out there suggesting this is just a CYA that I gave it a whirl anyway.  MySQL 64bit would not run, claimin a "side by side configuration" was incorrect.  OK, knowing very litting about anything this sounds like some sort of thunking layer issue, so I'll try the 32bit version.  Same error.  What does Google say?  Google says that the Application Manifest in the server configuration process is broken for 64bit windows.  The MySQL forums claim that only a program called Reshack can fix this.  The problem with Reshack is that it runs on 32bit platforms only.  For those who don't know: the various Icons, embedded resources, and execution manifests for a Windows .exe get compiled into a special section of a windows PE and programs like Reshack can read/write this area without doing anything to the code itself.  I eventually found a Delphi program posted by a company in the UK that would work for me.  Having had some bad experience with libraries without a strong name that I am missing the code to recompile, I was glad MySQL doesn't attempt to use authenticode signatures with their releases.  I changed the Vista application manifest XML to the appropriate "requireAdministrator" and I was finally off and running.

The last issue I experienced this weekend was with some code I "inherited".  It uses Microsoft Jet ODBC to treat a file as a "data source", access the file rows using DataReader constructs, and sort the contents in a DataGrid.  This code bombed when I ran it.  Google tells me that Jet does not exist for x64.  I had planned on rewriting this code anyway but wasn't up to the task tonight, so I kept looking.  .NET programs are usually targetted to "any CPU" by default, but Google told me changing the target specifically to x86 would allow some extra Thunking Magic to happen, and as soon as I did this I was back on my way with Jet magically found now.

After these experiments, I'm definately going to test all of my production code to make sure I haven't done anything that won't work with 64bit editions of Windows or the .NET framework.  I also need to take it upon myself to do some research: what does 64bit do for me and for Joe Consumer besides 4gb of RAM on Vista?

.NET | ASP.NET | Rant
Sunday, March 30, 2008 1:17:26 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Saturday, March 15, 2008

Dan has posted an excellent article on implementing a Tree structure in C#.  He has beaten me to the punch, after talking about my "Tree of dependent tasks" idea over beers.  I should have something to add to the discussion in the coming weeks.

Saturday, March 15, 2008 3:45:31 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, March 03, 2008

I have never been more ready for spring than I am this year.

It was a whopping 40degrees here yesterday and I took advantage of it.  I spent hours chopping ice out of my driveway, I can't type so well today due to the blisters on my fingertips.  The huge piles of snow on either side of my driveway make surprisingly good reach-level beer holding areas, and I pulled out the bbq pit and made some brats.

I also wrote a Mapping tool this weekend.  When you're first getting started it's interesting to note the similarities in flat file, XML, object-to-object, and SQL to Object mapping: at least in terms of the initial abstractions that present themselves.  I specifically wanted to solve a flat-file to complex object problem and write a tool that previews the mapping in real time and realized I couldd do quite a bit more if I weren't lazy.  The main place I could see using this in my own efforts are class-to-class mapping.  For example: if you are consuming a web service the proxy generated will return to you class instances using whatever naming conventions and object structure the designers of said service wanted to give you.  Even if another team in your organization is the maintainer of this service, Directly using these proxy types within your code is probably a mistake; mapping ProxyObject.PrimaryKey to MyDomainObject.Id quickly becomes tedious.  A mapping tool that does the annoying work for you and saves the mapping as an easy XML file or better yet generates transformation code might be helpful. 

Monday, March 03, 2008 9:50:15 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, February 27, 2008

Got the Zune today.  So far I am underwhelmed.  My very fast and very up to date Vista Laptop failed to install the Zune software the first time.  The error code explains this could be one of a number of thigns: .net 2, encoders and a lot of other developer-sounding nonsense.  Since the Zune installer checks for updates etc. this is unacceptable.  I am directed to a page with a svelte 173mb download to get all of the correct versions at once. 

Downloading...

.NET | Rant
Wednesday, February 27, 2008 2:37:04 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Tuesday, February 26, 2008

Invoking a .NET program on Device Insert

Given the popularity of the .NET Platform it seems odd that Microsoft does not provide easier means of doing certain incredibly useful and easy to imagine tasks from the .NET framework.  It took what seems like a ridiculous amount of research to accomplish a simple task:

Lots of programs come up as ‘launch’ options when I insert a storage card/camera/video camera/PDA/Mp3Player/younameit device into a USB port.  I’d like my .NET program to be in that list and to run if the user selects it.

File Types & WIA

The most useful thing you get in Visual Studio towards this goal is file types.  You can build an MSI for your desktop application and make your “.damon” files open with this program and have right clickàOpen appear in the shell context menu for “.damon” files.  I’ve written before about how to do multiple-select and some useless and goofy things with this scenario, but this doesn’t get us anywhere near where we want to be. 

WIA is very useful for media-specific actions like this, and handles the “something was plugged in” scenario in specific cases.  There is a WIA Automation Layer and managed wrappers for this.  Obviously if you care about something besides JPGs and AVIs WIA may not work for you.  Also, WIA requires that the device you’re speaking to has a WIA driver.  WIA drivers are certainly very common for the appropriate device types, but when you are distributing desktop software that depends on an OEM’s drivers you’re stepping into a world of pain (Not a world of Payne, which is actually a wonderful place).    Can the OEM give you merge-modules for the install?  Are their drivers buggy?  Be sure to treat your Support Group very well if you go down this path.

The Problem Solution: Shell Extensions and Managed Code

I’m going to focus on a simple subset of the overall problem.  Despite the perceived simplicity of the final solution, I couldn’t find a single working example of this online so if you got here via Goolge you’ve hit the jackpot.   Suppose you have an SD card, an SD card reader, and some family photos on said SD card.  You would like a .NET program to automatically create thumbnails of these when the SD card is inserted into your card reader.

Step 1

Step 1 is to read some obscure documentation found here: http://msdn.microsoft.com/msdnmag/issues/01/11/autoplay/

The first part of this is easy to follow. The Windows Autoplay functionality mostly resides in the sub keys of HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\AutoplayHandlers\EventHandlers\

Oddly enough, the names of these sub-keys are almost meaningful and you can do some Googling Windows Live Searching to get an idea of what exactly will cause this or that handler to be invoked.  In our case, the key we want for XP and Vista is ShowPicturesOnArrival.  Windows scans the SD card after it’s inserted and finds JPGs or whatever and checks this sub key to determine what choices the Shell should offer the user.  What can be gleaned from the MSDN article in Step 1 is that we can make up a Handler name, so I’ll call mine TestPhotoHandler.  This “TestPhotoHandler” must correspond to a sub key of HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\AutoplayHandlers\Handlers (../../Handlers from ShowPicturesOnArrival), which will in turn contain some values that describe what TestPhotoHandler is to the Windows Shell.

Step 2

Step two is to populate our Handler.

·         Action=what will appear to the user they are doing with this handler. 

·         DefaultIcon=Pretty self explanatory.  You can also use the Foo.exe,-1 syntax to specify an embedded resource. 

·         Provider=Name of your program

·         InvokeProgID, InvokeVerb=the interesting part, keep reading.

o   Note that it would be FAR too easy to just let you include the path to your .exe or .dll here…

With just the above work done, you can see the following behavior in Windows:

Step 3

Telling Windows where to go from here is far more difficult than it needs to be, and I could not find a single working example of getting this stuff to work with managed code.  In addition to telling Windows how to find our .NET program, Windows must be able to communicate with our program.  There are three interfaces that are interesting for these purposes:

1.       IHWEventHandler

2.       IHWEventHandler2

3.       IDropTarget

These are COM interfaces.  Because I keep meaning to get back into C++ development I always install C++ accessories in Visual Studio.  This means I have the various COM header and IDL files installed, and I can dig up some useful information.  Recall that .NET classes can implement COM interfaces as long as you know the GUID of the COM interface.  Here, then, is my IDropTarget interface definition for .NET objects.

    [ComImport,

     InterfaceType(ComInterfaceType.InterfaceIsIUnknown),

     Guid("00000122-0000-0000-C000-000000000046")]

    public interface IDropTarget

    {

        [PreserveSig()]

        int DragEnter(IntPtr pDataObj,               //  IDataObject

                      ulong grfKeyState,             //  DWORD

                      POINTL pt,

                      ref ulong pdwEffect);          //  DWORD*

 

        [PreserveSig()]

        int DragOver(ulong grfKeyState,              //  DWORD

                     POINTL pt,

                     ref ulong pdwEffect);           //  DWORD*

 

        [PreserveSig()]

        int DragLeave();

 

        [PreserveSig()]

        int Drop(IntPtr pDataObj,                    //  IDataObject

                 ulong grfKeyState,                  //  DWORD

                 POINTL pt,

                 ref ulong pdwEffect);               //  DWORD*

 

    }

 

    [StructLayout(LayoutKind.Sequential)]

    public struct POINTL

    {

        public POINTL(long xx, long yy) { x = xx; y = yy; }

        public long x;

        public long y;

        public override string ToString()

        {

            String s = String.Format("({0},{1})", x, y);

            return s;

        }

    }

 

PointL was copied from Dino Esposito’s excellent article on Shell Extensions on The Server Side.  At any rate, these three attributes on IDropTarget basically tells COM that we can speak the COM lingo and have the behaviors specified by the Interface identified by this GUID.

Step 4

Next we’ll implement the IDropTarget Interface.  The implementation can supposedly reside either in an .exe or .dll, but for our purposes it only works and is only safe when the implementation is on the Entry Point of an .exe project.  So,

    [Guid("C1D0D2F6-5F45-4462-9835-C463AFE367E3")]

    [ComVisible(true)]

    public class Program : IDropTarget

    {

        /// <summary>

        /// The main entry point for the application.

        /// </summary>

        static void Main()

        {

There are some things to take note of here.  Since we wish to enter the world of COM (and COM is love…) we put a ComVisible attribute on the class.  The default Program created for you in VS2008 has Program as a static class, which cannot have attributes so we’ve removed that.  We also have used the Tools in Visual Studio to create a GUID for this class.  We are using an API known as “HackingtheRegistrySinceNoRealRegistrationAPIExists” and guids are the coin of the realm.  I happen to hate this API but options are scarce.

Step 5

The next step is to register our code in such a way that it is findable via the appropriate magic.  For .NET programs RegAsm.exe is our registrar of choice.  Note that RegAsm will pout if your Assembly is not strongly named.  If we now “Regasm.exe /codebase LaunchTestProg.exe” we get some interesting registry settings created for us.  Under HKEY_CLASSES_ROOT\CLSID we find our Program’s GUID, along with the following:

A sub key named “ProgId” containing the fully qualified class name of the .NET program we’d like to run is nestled beneath our class id guid.  This is part of the explanation for the values in Step 2.  Continuing to search for our GUID in the registry we find that HKEY_CLASSES_ROOT\LaunchTestProg.Program has been created for us. 

Step 6

Rather than be clever I’ll just show the final piece we need to create.  The Shell depends on various lookups and naming conventions to determine what it needs to do. 

Below HKEY_CLASSES_ROOT\LaunchTestProg.Program, we need to create some registry keys and values ourselves.  We create sub keys shell, open, DropTarget, and put the GUID of our .NET program as a clsid attribute of the DropTarget key.  The entire process can now ask the user, find, and Invoke our .NET program when a USB device is inserted and it meets the original Handler criteria.

Further Investigation

Upon testing this, I found some interesting behaviors.  It appears that while the .NET project must be an .exe, what Windows is really doing is instantiating the Program object and invoking the members of IDropTarget on it.  I have not tried too hard to get Main to run.  In normal cases, IDropTarget.DragEnter and then IDropTarget.Drop will be called.  For my particular needs, the arguments to either method are throw-away, but using these should be normal P/Invoke & Marshalling exercises.  If you just want to start some other .NET code you can now do so from the your Drop()  implementation.

I will also warn that you want to read this and understand what it means: http://blogs.msdn.com/junfeng/archive/2005/11/18/494572.aspx

I will also warn that the class library Assembly that contains my IDropTarget interface seems to be file-locked once I actually invoke my program.  Restarting Explorer.exe fixes this, which may be a consideration if you are going to be auto-updating the program in some way.  It seems odd that the .exe containing the actual program is not locked.  I plan on investigating the registry areas of Windows\CurrentVersion\ShellExt\Cached to see if clearing that and firing the appropriate Registry Changed event will unlock the assembly. 

Other Avenues...

You will notice DeviceGroups and DeviceHandlers keys as siblings of the “EventHandlers” subkey.  I also mentioned IHWEventHandler and IHWEventHandler2.  The latter is merely the Vista version of the former that adds an extra method, presumably for UAC.  It seems that allowing a .NET program to reconfigure itself if a new input device is plugged in is certainly not out of the range of possibilities.

Would you like some thrown-together code that does all of this for you?  I’ve been meaning to get better about posting more code, so here we are:

    public class DropTargetInstaller

    {

 

        public bool Install(Assembly exe, string entryPointTypeName, string desiredHandlerName, string iconPath, string actionText, string usingText)

        {           

            Type t = exe.GetType(entryPointTypeName);

            object[] attributes = t.GetCustomAttributes(false);

            GuidAttribute guid = GetGUID(attributes);

 

            //Throws ArgumentExceptions to caller if the Assembly/class does not meet requirements

            VerifySuitability(entryPointTypeName, t, attributes, guid);

            string guidVal = guid.Value;

 

            Regasm(exe);

            WriteRegistryValues(desiredHandlerName, actionText, iconPath, entryPointTypeName, usingText,guidVal);

            

            return true;

        }

 

        /// <summary>

        /// Suitable classes must Implement the COM IDropTarget interface, be marked COMVisible, and contain a valid GUID attribute

        /// </summary>

        /// <param name="entryPointTypeName"></param>

        /// <param name="t"></param>

        /// <param name="attributes"></param>

        /// <param name="guid"></param>

        private void VerifySuitability(string entryPointTypeName, Type t, object[] attributes, GuidAttribute guid)

        {

            if (!VerifyDropTarget(t))

            {

                string msg = string.Format("Type {0} does not implement IDropTarget", entryPointTypeName);

                throw new ArgumentException(msg);

            }

            //

            if (!VerifyCOMVisible(attributes))

            {

                string msg = string.Format("Type {0} is missing COMVisble attribute", entryPointTypeName);

                throw new ArgumentException(msg);

            }

            //                        

            if (null == guid)

            {

                string msg = string.Format("Type {0} is missing Guid attribute", entryPointTypeName);

                throw new ArgumentException(msg);

            }

        }

 

        /// <summary>

        ///