Windows CE supports starting applications in the \Startup folder when Windows Explorer starts, but doesn’t automatically run applications on removable media when the media is inserted. It would be nice to have that ability for some devices, although tread lightly with this if security is an issue.
The following code example demonstrates how to use the Message Queue Point-to-Point functions and Storage Manager API to detect insertion of removable media and then launch exe files found in a Startup folder on the disk.
Header files used by this application
#include <Windows.h>
#include <msgqueue.h>
#include <pnp.h>
#include <storemgr.h>
 
A global variable for stopping the thread, although this code monitors for this, it does not set this or even have a way to do so.   I leave that as an exercise for you. There may also be better ways to do this, like using an event.
static BOOL StopMonitoringForNewDisks = FALSE;
 
The first task is to monitor for disk insertions. MonitorForNewDisks() does this and when new disks are detected will call HandleNewDisk() . MonitorForNewDisks() uses a message queue and notifications to receive notifications when a new Block driver is detected. The message in the queue will contain the device name of the block driver, example DSK1:, which will be passed to HandleNewDisk().
 
MonitorForNewDisks() is not interested in disk removal, but that could be added by including an else with the if(pDetail->fAttached).
 
void MonitorForNewDisks()
{
                HANDLE hNotify;
                HANDLE *qStore ;
                DWORD flags;
                DWORD size;
                BYTE DevDetail[sizeof(DEVDETAIL) + (MAX_PATH * sizeof( TCHAR ))];
DEVDETAIL * pDetail = (DEVDETAIL *)DevDetail;
MSGQUEUEOPTIONS msgopts;
 
                msgopts.dwSize = sizeof(MSGQUEUEOPTIONS);
                msgopts.dwFlags = 0;
                msgopts.dwMaxMessages = 0;
                msgopts.cbMaxMessage = sizeof(DevDetail);
                msgopts.bReadAccess = TRUE;
       
                qStore = CreateMsgQueue(NULL, &msgopts);
                hNotify = RequestDeviceNotifications(&BLOCK_DRIVER_GUID, qStore, TRUE);
 
                do
                {
                                if(WaitForSingleObject(qStore, 5000) == WAIT_OBJECT_0)
                                {
                                                while(ReadMsgQueue(qStore, pDetail, sizeof(DevDetail), &size, 1, &flags))
                                                {
                                                                if(pDetail->fAttached)
                                                                                HandleNewDisk(pDetail->szName);
                                                }
                                }
                } while( !StopMonitoringForNewDisks );
 
                StopDeviceNotifications(hNotify);
}
 
Now we have a been notified that a new disk has been inserted and HandleNewDisk() is called with the device name of the block driver. That is enough information to use OpenStore() to start finding the partition and then the Startup folder.
 
While working on this, I discovered that we can go from notification to calling OpenStore() quicker than the Store will be available. To handle that, this code loops until the Store can be opened. When the Store cannot be opened this will Sleep for 100 milliseconds to allow the Storage Manager to do its work.
 
When a Store is opened, this calls on RunStartupFolders() passing the handle to the Store.
 
void HandleNewDisk( TCHAR *DiskName )
{
                HANDLE hStore;
                BOOL success = FALSE;
                DWORD Count = 600;
 
                do
                {
                                hStore = OpenStore( DiskName );           
                                if( INVALID_HANDLE_VALUE != hStore )
                                {
                                                RunStartupFolders( hStore );
                                                CloseHandle( hStore );
                                                success = TRUE;
                                }
                                else
                                {
                                                // When the disk first shows up, it may not be ready
                                                // for us to OpenStore(), so wait a bit then retry
                                                Sleep( 100 );
                                }
                }while( !success && Count--);
}
 
RunStartupFolders() receives a handle to an open Store and then uses FindFirstPartition() and FindNextPartition() to find the partitions on the Store. When it finds a partition it calls StartExes() to run the applications in the Startup folder.
 
Again, we can get to this code faster than the partitions can be mounted, so this loops waiting for a valid, mounted partition. I have to be honest the first do/while loop could probably be simplified, but I found this is where I found that the Store might not have been opened (kind of wish that I had added more error handling earlier.)
 
void RunStartupFolders( HANDLE hStore )
{
HANDLE hFind = INVALID_HANDLE_VALUE;
                HANDLE hPartition = INVALID_HANDLE_VALUE;
PARTINFO partInfo = {0};
                DWORD Count = 120;
 
                do
                {
                                Sleep( 500 );
                                memset( &partInfo, 0, sizeof( partInfo ));
                                partInfo.cbSize = sizeof(PARTINFO);
                                hFind = FindFirstPartition(hStore, &partInfo);
                                if( INVALID_HANDLE_VALUE != hFind )
                                {
                                                if(PARTITION_ATTRIBUTE_MOUNTED & partInfo.dwAttributes)
                                                                break;
                                                else
                                                                FindClosePartition(hFind);
                                }
                } while( Count-- );
 
                if(INVALID_HANDLE_VALUE != hFind)
                {
                                do
                                {
                                                if(PARTITION_ATTRIBUTE_MOUNTED & partInfo.dwAttributes)
                                                {
                                                                StartExes( partInfo.szVolumeName );
                                                }
                                }while(FindNextPartition(hFind, &partInfo));
                                FindClosePartition(hFind);
                }
}
Now the it is time to find the exe files in the Startup folder on the partition. StartExes() receives the name of the partition and uses it to look for exes using FindFirstFile() and FIndNextFile(). Once an exe file has been found call CreateProcess() to start the exe.
 
void StartExes( TCHAR *PartitionName )
{
    WIN32_FIND_DATA a_file;
    TCHAR search[MAX_PATH];
    TCHAR path[MAX_PATH];
    PROCESS_INFORMATION pi;
                HANDLE FHandle;
 
                wsprintf( search, TEXT("%s\\Startup\\*.exe"), PartitionName );
 
                FHandle = FindFirstFile(search, &a_file);
 
                if (FHandle != INVALID_HANDLE_VALUE)
                {
                                do
                                {
                                                wsprintf( path, TEXT("%s\\Startup\\%s"), PartitionName, a_file.cFileName );
                                                if (CreateProcess( path, NULL, NULL, NULL, FALSE, 0,NULL, NULL, NULL, &pi))
                                                {
                                                                CloseHandle(pi.hProcess);
                                                                CloseHandle(pi.hThread);
                                                }
                                }while (FindNextFile(FHandle, &a_file));
 
                                FindClose (FHandle);
                }
}
 
And of course we need to kick the whole thing off, so here is a WinMain that calls SignalStarted, so that we can use this from HKEY_LOCAL_MACHINE\Init and then calls MonitorForNewDisks().
 
int WINAPI WinMain(     HINSTANCE hInstance,
                                                                                HINSTANCE hPrevInstance,
                                                                                LPTSTR    lpCmdLine,
                                                                                int       nCmdShow)
{
                SignalStarted( _wtol(lpCmdLine) );
                MonitorForNewDisks();
}
 
Now when disks are inserted into the device, applications will automatically run. This can be handy for servicing the device, but comes with some risk. You can mitigate that risk by using a less common folder name or some other method that you determine has more benefit than risk.
The sources file for building this with Platform Builder is fairly simple but included here to document the libraries that will need to be linked to the application.
TARGETNAME=StartupFolders
 
TARGETTYPE=PROGRAM
EXEENTRY=WinMain
RELEASETYPE=PLATFORM
 
TARGETLIBS= \
                $(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib \
    $(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\storeapi.lib
 
SOURCES=\
                StartupFolders.c

 

Copyright © 2008 – Bruce Eitman
All Rights Reserved