Can't pass structs between an MFC COM object and .NET/C#

A subset of MFC is designed for COM.  Much of this support is in the form of MACROS (e.g., OLECREATE, DISP_FUNCTION, etc...).  After struggling trying to pass a structure back and forth between a C# client and an MFC COM server I gave up and decided to try with an ATL COM Server.

 

ATL was designed for creating "small, fast COM components".  As it turns out, passing structures (and pretty much everything else) is much easier with ATL than it is with MFC COM.  ATL takes a different approach to supporting COM (mainly via templates).

 

It seems to me that MFC COM is only designed to work with VARIANT compatible data types (perhaps because it was targeted towards interoperability with VB).  The MFC  "Add a method" wizard won't even allow you to specify a parameter or return type that isn't VARIANT compatible.  This restriction is further implemented in the limitation on the parameter and return types that can be specified via the DISP_FUNCTION (or DISP_FUNCTION_ID) macros.

 

The .NET Interop Marshaler has built-in support for some of these (e.g., dates) but does not support VARIANTs of type VT_RECORD (see this kb article).  To store a structure in a VARIANT requires a VARIANT of type VT_RECORD.  Ergo, you can't pass structures back and forth between C# and an MFC COM Server.

 

See this excellent article (CLR Inside Out Introduction to COM Interop) detailing how to pass a structure back and forth between C# and COM Server written with ATL.

 

The IDL uses the same parameter types as the C++ implementation; e.g., if you have a function that takes a struct MyUDT *ptr then that method's signature in the IDL will take a struct MyUDT *ptr.  So, from C++ to IDL to C# we have:

 

C++ .h

STDMETHOD(MyFunc)(struct MyUDT *ptr);

 

C++ .cpp

STDMETHODIMP MyCOMServer::MyFunc(struct MyUDT *ptr) { ... }

 

IDL struct definition

same as in C++, e.g., struct MyUDT { int x; int y; }

 

IDL interface method declaration

[id(1), helpstring("method MyFunc")] HRESULT MyFunc(struct MyUDT *ptr);

 

C#

MyUDT udt = new MyUDT();

udt.x = 1;

udt.y = 2;

MyCOMServerClass sc = new MyCOMServerClass();

sc.MyFunc(ref udt);

 

If the structure is entirely made up of blitabble types then you don't even have the overhead of copying; the Interop Marshaler will just pin it.  If not then you will need to apply the [in, out] attribute to the parameter in the IDL so that the Interop Marshaler copies the unmanaged representation back to the managed representation when the method call ends (so that you can see any changes).

 

Any changes made to the struct in the unmanaged code will be visible in the managed code.

 

Booting DOS from a USB flash drive

I wanted to boot into DOS from a USB flash drive (lexar jumpdrive) to run a memory tester (I believe it needs to run in real mode, windows runs in protected mode, but not sure on that). There are several pages out there describing a procedure that, unfortunately for me, requires the presence of a floppy drive. I don't have a floppy drive :( I also don't happen to have a copy of Windows 98 or MSDOS lying around. And I really didn't want to have to install FreeDOS (or any version of DOS).



So, from my Windows XP machine, these are the steps I took to create a bootable USB flash drive.


  1. Download Virtual Floppy Drive. There is no installer. Extract it to a directory then double-click on "vfdwin.exe" to open the user interface (called VFD Control Panel).
  2. Create a virtual a: drive by doing the following:

    1. From the Driver tab leave the Start Type at "Manual".
    2. click Install (if it's not grayed out). This installs the vfd driver.
    3. click Start. This starts the driver.
    4. Switch to the Drive0 tab.
    5. Click "Change" to choose a drive letter then select A from the drop-down (if it's available). This is the drive letter windows will think is a floppy drive. Make sure Persistent/Global is checked.
    6. Click "Open" then browse to a directory.
    7. Type floppy.img then click OK. This is where the floppy image will be stored when you create it.
    8. Choose Media Type (I leave it at 3.5" 1.44MB but you may want to emulate a different sized floppy).
    9. Click "Create".

  3. Create a startup disk on the virtual floppy drive.

    1. Windows now thinks you have a floppy drive at a: (or whatever you selected).
    2. Using Windows Explorer (or my computer), right click the a: drive and choose Format.
    3. Make sure "Create an MS-DOS Startup Disk" is checked.
    4. Click Start.

  4. Copy whatever DOS utilities/.exe files you want onto the virtual floppy drive.

    1. You can do this with Windows Explorer (drag them onto the a: drive) or command prompt.
    2. Remember - you will only have 1.44MB to work with, so choose carefully.

  5. Save the virtual floppy drive image.

    1. Close any windows explorer or my computer windows that currently have the a: drive open. Same goes for command prompts.
    2. In the VFD Control Panel switch to tab Drive0 if it's not still there.
    3. Click "Save".
    4. Check "Overwrite an existing file" then click "Save".

  6. Download the HP Drive Key Boot Utility. It works for many brands of flash drive, not just HP drives.
  7. Plug in the USB flash drive.
  8. Install the HP Drive Key Boot Utility.
  9. Start the HP Drive Key Boot Utility.
  10. Choose the letter currently assigned to your USB flash drive. Click Next.
  11. Leave "Create New or Replace Existing Configuration" selected. Click Next.
  12. Choose "Floppy Disk". Click Next.
  13. Choose "Image from file" then browse to the file you saved in step 1.7 (e.g., floppy.img). Click Next.

You should now have a USB flash drive that you can boot from.

Plug the key into whatever machine you want to test. Boot the machine, go into its BIOS settings screen (usually by pressing DEL or F2 or F10 during boot), disable all boot devices except for the USB key. You may have to try USB-FDD, USB-ZIP or USB-HDD to get it to work. Save the changes then reboot. You should be greeted with an A: prompt!

Setting network configuration from a command prompt

NetSh is an excellent command line tool that exposes pretty much every configuration option available from the UI in the form of a command-line tool.  It's also scriptable.

 

Its command structure is similar to that of the Cisco IOS shell on routers and switches.  You switch to the context of the device/port/setting you want to configure then execute a command to configure it.  E.g., to set an interface to a static IP, you would:

 

interface ip

set address "Local Area Connection" static 1.2.3.4 255.255.255.0

 

interface ip switches you into that context (configuring tcp/ip for interfaces).  set address is a command available in that context.

 

To view the existing configuration for all interfaces use the show config command from the interface ip context, e.g:

 

interface ip

show config

 

 

more on migrating from unix to windows

Although the Windows XP command shell and standard executables aren't as powerful as standard unix utilities, there are some features that provide similar functionality.  Some of the ones I've found helpful, along with their unix equivalents, are listed below.

 


  • fgrep -> find.  Basic substring search in multiple files.  The text string has to be quoted.  See find /? for usage.
  • grep -> findstr.  Supports basic regular expressions.  If the pattern contains spaces use /C, e.g., /C:"^pattern with spaces$" .  See findstr /? for usage.
  • find -> haven't found a command line equivalent with as much power but some of the functionality can be accomplished with dir, FOR and Windows Explorer's search (ok that's not a command line equivalent but it's a way to find files by pattern).

    • dir has several options, like /S for recurse, /A:d for directories, etc...  see dir /? for usage.  dir takes a filename glob.  For more powerful searching pipe the output to find or findstr ala dir /a:d | find "pp" to find all directories in the current directory that contain pp in their name.
    • FOR has several options that approximate find.  e.g., /R for recurse.  See FOR /? for usage.

  • loops (e.g., for in bash or foreach tcsh) -> FOR in the xp command shell with a few caveats.  Variables are denoted with %s instead of $.  The % is needed for the formal parameter.  So bash's for i in a b c ; do echo $i ; done becomes for %i in (a b c) DO echo %i in the xp command shell.

    • You can execute a multi-line command by placing it within parentheses.
    • By default, all variables except the FOR variable are evaluated once.  So if the body of your FOR loop changes the value of a variable the variable will only take the first value.  To change this, the command shell must be run with the /V:ON option.  Which changes the %s to !s vis a vis variable expansion.  See cmd /? for usage.

  • exit codes -> you can test for ERRORLEVEL in IF statements.  See IF /? for usage.  Not every executable sets a non-zero error code to indicate an error but the built-in commands seem to.
  • `cmd` parsing -> FOR /f.  To get the year and month of each line of the dir statement you would use for /f "usebackq tokens=1,2,* delims=/" %a in (`dir`) do echo a=%a b=%b

    • See for /? for usage.
    • If your FOR variable is j and you specify tokens then additional variables, starting at j, will be allocated (e.g., %j, %k, %l, etc....)

  • shift -> shift.  Identical!  Of course, you're still using %0, %1, etc... instead of $0, $1, ...  shift is a handy way to get around the 10 positional parameters limit.
  • variables -> set varname=value where value.  Values needn't be quoted.  To evaluate varname use %varname%.  It will only take the first value that is assigned to it unless you turn on delayed expansion via cmd /V:on in which case use !varname!
  • arithmetic -> bash's $((expr)) becomes set /a expr where expr includes the variable being assigned to and the operation being performed.  e.g.,

#!/bin/bash

x=$((1 + 1))

echo $x

becomes

set /a x=1+1

echo %x%