In the first two sessions, Part 1 and Part 2, I talked about the record structure used by the control panel applet, and about the Marshal class.

In this session, let's see how and where the record structure and the Marshal class is used.

Every control panel applet exports a function. The name of this function is CPlApplet, and the entire signature for this function reads: function CPlApplet(hwndCPl: THandle; uMsg: DWORD; lParam1, lParam2: Longint): Longint;. For further details on Control Panel applets, refer to Microsoft's documentation on Control Panel Applications at MSDN.

The two messages that pass record structural information is CPL_INQUIRE and CPL_NEWINQUIRE. When CPL_INQUIRE is received, lParam2 is a pointer to a CPLINFO structure. However, according to the signature for the function, lParam2 is declared as a longint. In order to extract the record pointed to by lParam2, first, the lParam2 longint needs to be transformed into an IntPtr, and then using the member method PtrToStructure of the Marshal class, the structure can be extracted. This was how it was done:

var
  ACPLInfo: TCPLInfo
;

...
ACPLInfo := TCPLInfo(Marshal.PtrToStructure(IntPtr(lParam2), typeof(ACPLInfo)));
DoInquire(ACPLInfo);
...

The DoInquire method might alter some information stored inside ACPLInfo, so, there is a need to transport the changes back to Win32.

This is done by calling Marshal.StructureToPtr(ACPLInfo, IntPtr(lParam2), False); which tells the CLR to take the record structure in ACPLInfo, and lay it out in memory according to the attributes defined on the type TCPLInfo into the memory pointed to by lParam2. It further instructs the CLR not to release the memory pointed to by lParam2 prior to laying it out, which is what the False meant.

The same of CPL_NEWINQUIRE, except that it reads:

ANewCPLInfo := TNewCPLInfo(Marshal.PtrToStructure(IntPtr(lParam2), typeof(ANewCPLInfo)));
DoNewInquire(ANewCPLInfo);
Marshal.StructureToPtr(ANewCPLInfo, IntPtr(lParam2), False);

There is one additional message that requires proper marshalling of data. That is, CPL_STARTWPARAMS. The code for marshalling the data reads DoStartWParms(Marshal.PtrToStringAuto(IntPtr(LParam2))); and what this does is left as an exercise for you to figure out.

That covers the most important portion of porting the Control Panel applet from Delphi 7 to Delphi 8.