Adventures in Porting Part 1

Well, I was going to talk about how I designed a wizard using the Open Tools API to create Control Panel applets, but I haven't succeeded in that, so it's a talk  planned for the near future when I managed to gain more of an understanding with the Open Tools API, or become “one with the Open Tools API” in Delphi 8.

Then, I figured, what the heck? I might as well talk about how I managed to migrate that Delphi 7 Date Control Panel applet, which runs on the Win32 platform, to Delphi 8, which runs on the .NET platform.

Right. Now, on with the talk. The difficulty in migrating the control panel applet is listed in the items shown below.

  1. Translating the control panel applet structures properly.
  2. Then, marshalling the structures properly, from Win32 to .NET, then .NET back to Win32.
  3. Getting the library's main initialization code to run.

The control panel applet structure looks like this.

  tagNEWCPLINFOA = packed record
    dwSize:        DWORD;   // similar to the commdlg
    dwFlags:       DWORD;
    dwHelpContext: DWORD;   // help context to use
    lData:         Longint; // user defined data
    hIcon:         HICON;   // icon to use, this is owned by CONTROL.EXE (may be deleted)
    szName:        array[0..31] of AnsiChar;    // or WideChar
    szInfo:        array[0..63] of AnsiChar;    
    szHelpFile:    array[0..127] of AnsiChar;
  end;

In order to translate this Win32 structure to .NET, there is a need to apply some attributes to the record. The structure, translated to .NET, looks like

  [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
  tagNEWCPLINFO = packed record
    dwSize:        DWORD;   // similar to the commdlg
    dwFlags:       DWORD;
    dwHelpContext: DWORD;   // help context to use
    lData:         Longint; // user defined data
    hIcon:         HICON;   // icon to use, this is owned by CONTROL.EXE (may be deleted)
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
    szName:        string;  // short name
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
    szInfo:        string;  // long name (status line)
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
    szHelpFile:    string;  // path to help file to use
  end;

The CLR controls the physical layout of data fields in a class or structure. With the StructLayout attribute, the class/structure can be arranged in a certain way so that it can be passed  to unmanaged code that expects a specific layout.

As seen in the translation above, most of the fields in the record can be translated without any changes, except for the 3 string fields, which are translated to string, and adorned with the MarshalAs attribute. The ByValTStr tells the CLR that when it is passed to unmanaged code, it should be unwrapped into an array of Char, or WideChar, depending on whether the platform is Win98/ME (in which case it's unwrapped into array of Char of 32, 64, or 128 in size), or WinNT/2000 (in which case, it's unwrapped into an array of WideChar of 32, 64, or 128 in size). The CharSet attribute, as set to Auto, informs the CLR what to do. So, if the CharSet is set to Ansi,  the strings are unwrapped to array[0..Size-1] of Char, wherelse if CharSet is set to Unicode, the strings are unwrapped to array of [0..Size-1] of WideChar, and when the CharSet is set to Auto, the strings are unwrapped to array[0..Size-1] of WideChar or array[0..Size-1] of Char, depending on whether the platform is Unicode (WinNT/2000/XP/2003) or Ansi (Win98/ME).

To be continued...

Comments

# re: Adventures in Porting Part 1

Sunday, May 23, 2004 11:00 AM by chuacw

Thanks for the pointers, Joe. The information provided would be useful when I'm writing services.

Based on your comments, I'm thinking whether you're mistaking a control panel applet for a service applet?

Leave a Comment

(required) 
(required) 
(optional)
(required) 
Enter the following code to ensure that your comment reaches the intended party:
Enter the numbers you see in the image: