The Microsoft Platform Builder IDE provides developers with a suite of tools for developing and debugging Windows CE based embedded systems. One of these tools is the 'Target Control Window' that, when enabled, allows the user to interact with the system via a command prompt styled interface. The default target control window shell implementation provides a number of commands which are all described in MSDN or in the platform builder shell help (accessed with the '?' command). This article describes the process by which a developer can add custom features with a simple plug-in DLL to extend the target control debugging command set.

The shell can be extended to do pretty much anything the developer wants. Some useful ideas for extensions include:

* Adding specialized commands to manipulate platform specific hardware
* Dumping platform specific information in readable format
* Add real time system info and status to help output

In order to extend the shell the developer must create a new utility in the platform for the plug-in DLL. After the utility is created and a minimal plug-in successfully compiles, the DLL must then be added to the system registry as a shell extension. The shell extension DLL can be left in the release directory for inclusion via RELFSD or included in the image itself.

Code included is intended to provide a general idea of what needs to be done for a shell extension.

Basic Shell Extension With Help

The shell extension utility code should compile into a DLL with one entry point called ParseCommand. The target control window shell will call into this function to handle all user commands that it does not recognize. Additionally, the shell will forward on requests for help (user enters '?' command) to give each shell extension a chance to append information regarding the commands that they support. A minimal shell extension should at least support the help command so that the developer can easily tell if the extension is being loaded properly.


		 //
		 // Copyright (c) Microsoft Corporation.  All rights reserved.
		 //
		 //
		 // Use of this sample source code is subject to the terms of the Microsoft
		 // license agreement under which you licensed this sample source code. If
		 // you did not accept the terms of the license agreement, you are not
		 // authorized to use this sample source code. For the terms of the license,
		 // please see the license agreement between you and Microsoft or, if applicable,
		 // see the LICENSE.RTF on your install media or the root of your tools installation.
		 // THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
		 //
	

		 typedef VOID (*PFN_FmtPuts)(WCHAR *s, ...);
		 typedef WCHAR* (*PFN_Gets)(WCHAR *s, int cch);
	

		 BOOL  Help(ULONG argc, LPWSTR args[], PFN_FmtPuts [pfnFmtPuts] );
	

		 // Supported commands are stored in a table
		 static struct {
		     LPCWSTR cmd;
		     BOOL (*pfnCommand)(ULONG argc, LPWSTR args[], PFN_FmtPuts [pfnFmtPuts);]
		 } s_cmdTable[] = {
		     { L"?", Help }
		 };
	

		 BOOL [ParseCommand(LPWSTR] cmd, LPWSTR cmdLine,PFN_FmtPuts [pfnFmtPuts,] PFN_Gets pfnGets)
		 {
		     BOOL rc = FALSE;
	

		     // Call command (Just support help for now)
		     if (wcscmp(cmd, s_cmdTable[0].cmd) == 0)
		     {
		         s_cmdTable[ix].pfnCommand(0, NULL, [pfnFmtPuts);]
		     }
	

		     pfnFmtPuts(L"\r\n");
	

		     rc = TRUE;
	

		 cleanUp:
		     return rc;
		 }
	

		 BOOL  Help(ULONG argc, LPWSTR args[], PFN_FmtPuts [pfnFmtPuts] )
		 {
		     pfnFmtPuts(L"  in16   address [size] -> read 16-bit registers\r\n");
		     pfnFmtPuts(L"\r\n");
		     pfnFmtPuts(L"    Address, size and value are hex numbers.\r\n");
		     return TRUE;
		 }
	



Activating Custom Shell Extensions

Each shell extension DLL must be specified in the system registry.

		 ;-------------------------------------------------------------------------------
		 ; HIVE BOOT SECTION
		 ; @CESYSGEN IF CE_MODULES_SHELL
		 ; Register platform specific shell extension
		   [HKEY_LOCAL_MACHINE\Software\Microsoft\TxtShell\Extensions]
		     "Platform Extension"="shell_extension.dll"
		 ; @CESYSGEN ENDIF CE_MODULES_SHELL
		 ; END HIVE BOOT SECTION
		 ;-------------------------------------------------------------------------------
	

The "Platform Extension" string is a tag invented by the developer and the DLL name must match the shell extension DLL. Multiple DLLs can be specified so multiple shell extensions can be used concurrently. In cases where multiple shell extension dlls are used, they should be included according to this example:

		 ;-------------------------------------------------------------------------------
		 ; HIVE BOOT SECTION
		 ; @CESYSGEN IF CE_MODULES_SHELL
		 ; Register platform specific shell extension
		 [HKEY_LOCAL_MACHINE\Software\Microsoft\TxtShell\Extensions]
		     "Platform Extension"="shell_extension.dll"
		     "Platform Extension 2"="shell_extension_2.dll"
		     "A Third Platform Extension"="shell_ext3.dll"
		 ; @CESYSGEN ENDIF CE_MODULES_SHELL
		 ; END HIVE BOOT SECTION
		 ;-------------------------------------------------------------------------------
	



Here there are two additional shell extensions compared to the initial example.

Generalizing the Shell Extension Code

The above example demonstrated how to get an extension building and loading into the shell. Next is to generalize the code to support arbitrary commands. Each supported command will be stored in the command data structure along with pointers to the functions which handle each command. With this framework adding or removing commands becomes trivial.

The following code changes add an addition command along with generalizing the command parsing routine:

		 //
		 // Copyright (c) Microsoft Corporation.  All rights reserved.
		 //
		 //
		 // Use of this sample source code is subject to the terms of the Microsoft
		 // license agreement under which you licensed this sample source code. If
		 // you did not accept the terms of the license agreement, you are not
		 // authorized to use this sample source code. For the terms of the license,
		 // please see the license agreement between you and Microsoft or, if applicable,
		 // see the LICENSE.RTF on your install media or the root of your tools installation.
		 // THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
		 //
	

		 void [CommandLineToArgs(TCHAR] szCommandLine[], int *argc, LPTSTR argv[]) ;
	

		 BOOL  [InReg16] (ULONG argc, LPWSTR args[], PFN_FmtPuts [pfnFmtPuts] );
	

		 // Supported commands are stored in a table
		 static struct {
		     LPCWSTR cmd;
		     BOOL (*pfnCommand)(ULONG argc, LPWSTR args[], PFN_FmtPuts [pfnFmtPuts);]
		 } s_cmdTable[] = {
		     { L"?", Help }
		     { L"in16", [InReg16] },
		 };
	

		 BOOL [ParseCommand(LPWSTR] cmd, LPWSTR cmdLine,PFN_FmtPuts [pfnFmtPuts,] PFN_Gets pfnGets)
		 {
		     BOOL rc = FALSE;
		     LPWSTR argv[64];
		     int argc = 64;
		     ULONG ix;
	

		     // Look for command
		     for (ix = 0; ix < dimof(s_cmdTable); ix++)
		     {
		         if (wcscmp(cmd, s_cmdTable[ix].cmd) == 0) break;
		     }
		     if (ix >= dimof(s_cmdTable)) goto cleanUp;
	

		     // Divide command line to token
		     if (cmdLine != NULL)
		     {
		         [CommandLineToArgs(cmdLine,] &argc, argv);
		     }
		     else
		     {
		         argc = 0;
		     }
	

		     // Call command
		     pfnFmtPuts(L"\r\n");
		     if (!s_cmdTable[ix].pfnCommand(argc, argv, [pfnFmtPuts))]
		     {
		         // Display help if command returned error
		         pfnFmtPuts(L"\r\n");
		         Help(0, NULL, [pfnFmtPuts);]
		     }
		     pfnFmtPuts(L"\r\n");
	

		     rc = TRUE;
	

		 cleanUp:
		     return rc;
		 }
	

		 BOOL  [InReg16(ULONG] argc, LPWSTR args[], PFN_FmtPuts [pfnFmtPuts)]
		 {
		     BOOL rc = FALSE;
		     UINT32 address, count;
	

		     // Interpret input arguments
		     if (argc < 1)
		     {
		         pfnFmtPuts(L"Missing address!\r\n");
		         goto cleanUp;
		     }
	

		     address = GetHex(args[0]);
		     count = (argc < 2) ? 1 : GetHex(args[1]);
		     if (count == 0) count = 1;
	

		     // TODO: Add functionality
	

		     rc = TRUE;
	

		 cleanUp:    
		     return rc;
		 }
	

		 void [CommandLineToArgs(TCHAR] szCommandLine[], int *argc, LPTSTR argv[]) 
		 {
		     int i, nArgc = 0;
		     BOOL [bInQuotes,] [bEndFound] = FALSE;
		     TCHAR *p = [szCommandLine;]
	

		     if [(szCommandLine] == NULL || argc == NULL || argv == NULL) return;
	

		     for (i = 0; i < *argc; i++) 
		     {
		         // Clear our quote flag
		         [bInQuotes] = FALSE;
	

		         // Move past and zero out any leading whitespace
		         while (*p && _istspace(*p)) *(p++) = _T('\0');
	

		         // If the next character is a quote, move over it and remember it
		         if (*p == _T('\"')) 
		         {
		             *(p++) = _T('\0');
		             [bInQuotes] = TRUE;
		         }
	

		         // Point the current argument to our current location
		         argv[i] = p;
	

		         // If this argument contains some text, then update our argument count
		         if (*p != _T('\0')) nArgc = i + 1;
	

		         // Move over valid text of this argument.
		         while (*p != _T('\0')) 
		         {
		             if (_istspace(*p) || [(bInQuotes] && (*p == _T('\"')))) 
		             {
		                 *(p++) = _T('\0');
		                 break;
		             }
		             p++;
		         }
	

		         // If reach end of string break;
		         if (*p == _T('\0')) break;
		     }
		     *argc = nArgc;
		 }
	





Go up to BSP Advanced Features
Go up to Big Book of BSP

Thank you for contributing to this BSP Wiki. To ensure your comments and concerns receive proper exposure, include bspwiki""@""microsoft"".""com when providing feedback or topical suggestions.




Microsoft Communities