// // TAPSERVER.C // // TAP // File Transfer Data Sharing // Server Code // Revision 1.10 // // 12/28/94 First Created // #define INCL_WIN #define INCL_DOS #include #include #include #include #include #include #include "tapserve.h" #include "tapres.h" // // PTAPSERVERINFO InitializeServer_TAP(char szVersion[]); // // Initializes the TAP Server // // Parameters: // szVersion // Server version string, max. 30 characters. // // Returns: Pointer to server instance data, or NULL // on failure. // PTAPSERVERINFO InitializeServer_TAP(char szVersion[]) { PTAPSERVERINFO pTapServerInfo = NULL; // Create pTapServerInfo pTapServerInfo = malloc(sizeof(TAPSERVERINFO)); // Did we get back a valid pointer? if (pTapServerInfo) { // Initialize pTapServerInfo pTapServerInfo -> cb = sizeof(TAPSERVERINFO); pTapServerInfo -> phWritePipe = NULL; pTapServerInfo -> lNumPipes = 0; pTapServerInfo -> lShutdown = FALSE; pTapServerInfo -> lFileOpen = FALSE; // Create unowned mutual exclusion semaphore for // controlling access to the TAP server DosCreateMutexSem(NULL, &(pTapServerInfo -> hServerMutex), 0L, FALSE); // Initialize version string strncpy(pTapServerInfo -> szVersion, TAP_SERVER_VERSION, sizeof (pTapServerInfo->szVersion) - 1); // NUL terminate (note that strncpy will not NUL terminate when max len is reached) pTapServerInfo -> szVersion[sizeof (pTapServerInfo->szVersion) - 1] = '\0'; // Initialize pTapServerInfo -> tiTapInfo (Pipe packet) pTapServerInfo -> tiTapInfo . szFileName [0] = 0; pTapServerInfo -> tiTapInfo . lCurrentFileSize = TAP_SIZE_UNKNOWN; pTapServerInfo -> tiTapInfo . lCompleteFileSize = TAP_SIZE_UNKNOWN; pTapServerInfo -> tiTapInfo . ulFlags = 0; // Create pipe update semaphore in set mode DosCreateEventSem(NULL, &(pTapServerInfo -> hevUpdate), 0L, FALSE); // Start server thread #ifdef __BORLANDC__ pTapServerInfo -> tidTAPServer = (TID) _beginthread(ServerThread_TAP, 8192, (void *) pTapServerInfo); #else pTapServerInfo -> tidTAPServer = (TID) _beginthread(ServerThread_TAP, NULL, 8192, (void *) pTapServerInfo); #endif } szVersion = szVersion; return pTapServerInfo; } // // int DeInitializeServer_TAP(PTAPSERVERINFO pTapServerInfo); // // DeInitializes the TAP Server // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // // Returns: TRUE on success. // int DeInitializeServer_TAP(PTAPSERVERINFO pTapServerInfo) { int iRet = TRUE; // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Tell server thread to shutdown, // it will take care of freeing memory pTapServerInfo -> lShutdown = TRUE; iRet = !DosPostEventSem(pTapServerInfo -> hevUpdate); // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); return iRet; } // // int StartApplication_TAP(PTAPSERVERINFO pTapServerInfo, // char *szAppPath, // char *szAppParams, // char *szAppTitle) // // Starts a TAP Application and sets up the TAP Server // to service the application. // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // szAppPath // Fully qualified application executable specification. // szAppParams // Command line parameters sent to the application. // szAppTitle // Default TAP Application title (for VIO titlebar). // // Returns: TRUE on success // int StartApplication_TAP(PTAPSERVERINFO pTapServerInfo, char *szAppPath, char *szAppParams, char *szAppTitle) { int iRet = TRUE; char *szParameters; PROGDETAILS Details; PTIB ptib; PPIB ppib; char szPipeName[CCHMAXPATH]; // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Allocate memory for a new pipe pTapServerInfo -> lNumPipes ++; if (pTapServerInfo -> phWritePipe) pTapServerInfo -> phWritePipe = (HFILE *) realloc(pTapServerInfo -> phWritePipe, (size_t)(pTapServerInfo -> lNumPipes * sizeof(HFILE))); else pTapServerInfo -> phWritePipe = (HFILE *) malloc((size_t)(pTapServerInfo -> lNumPipes * sizeof(HFILE))); // Query our environment // so we can create a unique // pipe name! DosGetInfoBlocks(&ptib, &ppib); // Create pipe name sprintf(szPipeName, "\\PIPE\\TapPipe%d_%d_%d", ppib -> pib_ulpid, ptib -> tib_ptib2 -> tib2_ultid, pTapServerInfo -> lNumPipes); // Create a new named pipe DosCreateNPipe(szPipeName, &(pTapServerInfo -> phWritePipe[ pTapServerInfo -> lNumPipes - 1]), NP_NOWRITEBEHIND | NP_INHERIT | NP_ACCESS_DUPLEX, NP_NOWAIT | NP_TYPE_BYTE | 1, 1024, 8192, 0); // Allow a client to connect DosConnectNPipe(pTapServerInfo -> phWritePipe [ pTapServerInfo -> lNumPipes - 1]); // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); // *** // Setup for application launch // *** // Query our environment // to pass on to the shell DosGetInfoBlocks(&ptib, &ppib); // Setup calling data structure memset (&Details, 0, sizeof(PROGDETAILS)); Details.Length = sizeof(PROGDETAILS); Details.progt.progc = PROG_DEFAULT; Details.progt.fbVisible = SHE_VISIBLE; Details.pszTitle = szAppTitle; Details.pszExecutable = szAppPath; Details.pszEnvironment = ppib->pib_pchenv; Details.swpInitial.fl = SWP_ACTIVATE; /* window positioning */ Details.swpInitial.hwndInsertBehind = HWND_TOP; // Add our parameter to the end of the parameter list szParameters = (char *) malloc(strlen(szAppParams) + 100); sprintf(szParameters, "%s /TAP=%s", szAppParams, szPipeName); // Start application if (!WinStartApp(NULLHANDLE, &Details, szParameters, NULL, SAF_STARTCHILDAPP)) iRet=FALSE; // Free up parameter list free((void *) szParameters); // Wait for client to connect { ULONG ulMaxRetries = 50; // Wait no longer than 5 seconds! for (;;) { AVAILDATA availData; ULONG ulDummy; ULONG ulState; DosPeekNPipe (pTapServerInfo -> phWritePipe [ pTapServerInfo -> lNumPipes - 1], &ulDummy, 0, &ulDummy, &availData, &ulState); if (ulState == NP_CONNECTED) break; if (ulMaxRetries-- == 0) return (-1); DosSleep (100); } } // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Write server version { PTAPPACKET pTapPacket; ULONG ulBytesWritten; USHORT usVersionLength = (USHORT)strlen (pTapServerInfo->szVersion); USHORT usPacketLength = (USHORT)(sizeof (pTapPacket->versionPacket) + usVersionLength); pTapPacket = malloc (usPacketLength); pTapPacket->versionPacket.cb = usPacketLength; pTapPacket->versionPacket.usFlags = TAP_VERSION; pTapPacket->versionPacket.usVersionLength = usVersionLength; strcpy (pTapPacket->versionPacket.szVersion, pTapServerInfo->szVersion); DosWrite (pTapServerInfo -> phWritePipe [ pTapServerInfo -> lNumPipes - 1], pTapPacket, pTapPacket->versionPacket.cb, &ulBytesWritten); free (pTapPacket); } // If a file is currently open we need to send the new pipe a BOF ! if (pTapServerInfo->lFileOpen) { PTAPPACKET pTapPacket; ULONG ulBytesWritten; USHORT usFileNameLength = (USHORT)strlen (pTapServerInfo->tiTapInfo.szFileName); USHORT usPacketLength = (USHORT)(sizeof (pTapPacket->beginFilePacket) + usFileNameLength); pTapPacket = malloc (usPacketLength); pTapPacket->beginFilePacket.cb = usPacketLength; pTapPacket->beginFilePacket.usFlags = TAP_BOF; pTapPacket->beginFilePacket.lCurrentFileSize = pTapServerInfo->tiTapInfo.lCurrentFileSize; pTapPacket->beginFilePacket.lCompleteFileSize = pTapServerInfo->tiTapInfo.lCompleteFileSize; pTapPacket->beginFilePacket.usFileNameLength = usFileNameLength; strcpy (pTapPacket->beginFilePacket.szFileName, pTapServerInfo->tiTapInfo.szFileName); DosWrite (pTapServerInfo -> phWritePipe [ pTapServerInfo -> lNumPipes - 1], pTapPacket, pTapPacket->beginFilePacket.cb, &ulBytesWritten); free (pTapPacket); } // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); return iRet; } // // int SelectApplication_TAP(PTAPSERVERINFO pTapServerInfo, // HWND hWndParent, // HWND hWndOwner, // HMODULE hmod); // // Allows the user to select from a list of installed // TAP applications and launches that TAP application. // // In order for this function to work you must add the // resource file TAPSERVE.RC to your resources. To locate // these resources you must pass the module handle or // NULLHANDLE if the resources should be loaded from the // application's EXE. // // Alternatively you can launch TAP applications directly // by calling StartApplication_TAP. // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // hWndParent // Handle to the parent window of the dialog. // hWndOwner // Handle to the owner window of the dialog. // hmod // Handle to the module that contains the application's // resources, or NULLHANDLE if resources come from the // application's EXE. // // Returns: TRUE on success. // int SelectApplication_TAP(PTAPSERVERINFO pTapServerInfo, HWND hWndParent, HWND hWndOwner, HMODULE hmod) { int iRet = TRUE; // Bring up the application selection dialog if (WinDlgBox(hWndParent, hWndOwner, SelectApplicationDlgProc, hmod, DLG_SELECT_TAP, pTapServerInfo ) == DID_ERROR ) iRet = FALSE; return iRet; } // // int GetApplications_TAP(char *szApplications, PULONG pulSize); // // Gets a list of registered TAP Applications and/or size // of the list of TAP Applications. The list of TAP applications // is returned as a buffer of null-terminated ASCII strings, // the final ASCII string double null-terminated. // // For Example: String1\0String2\0FinalString\0\0 // // Parameters: // szApplications // Pointer to a buffer where the application list will // be stored. If this is NULL, only the application list // size will be put in pulSize. (Output) // pulSize // Size of the szApplications buffer in bytes. (Input) // Size of the application list, in bytes. (Output) // // Returns: TRUE on success. // int GetApplications_TAP(char *szApplications, PULONG pulSize) { int iRet = TRUE; // If we have a pointer, get the data if (szApplications) if (!PrfQueryProfileString(HINI_SYSTEMPROFILE, TAP_INI_APPNAME, NULL, "\0\0", szApplications, *pulSize)) iRet = FALSE; // And always return how much data we query in pulSize if (!PrfQueryProfileSize(HINI_SYSTEMPROFILE, TAP_INI_APPNAME, NULL, pulSize)) iRet = FALSE; return iRet; } // // int GetApplicationInfo_TAP(char *szAppName, // PTAPAPPENTRY pTapAppInfo); // // Given a TAP Application name (retrieved with // GetApplications_TAP) returns a structure containing // information about that TAP Application. For more // information, the structure is located in TAP.H. // // Parameters: // szAppName // Name of the TAP Application. // pTapAppInfo // Pointer to a TapAppInfo structure. (Output) // // Returns: TRUE on success. // int GetApplicationInfo_TAP(char *szAppName, PTAPAPPENTRY pTapAppInfo) { int iRet = TRUE; ULONG DataLength; // Setup length of our data structure DataLength = sizeof(TAPAPPENTRY); // Query application information if (!PrfQueryProfileData(HINI_SYSTEMPROFILE, TAP_INI_APPNAME, szAppName, pTapAppInfo, &DataLength)) iRet = FALSE; return iRet; } // // int SetFileName_TAP(PTAPSERVERINFO pTapServerInfo, // char *szFileName); // // Sets the name of the current TAP file. // // This filename must include path information (either // relative or fully qualified). That is, "TAP\TAPSAMP.EXE" // is an acceptable path. This function will automatically // fully qualify a relative path. // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // szFileName // Relative or fully qualified file specification. // // Returns: TRUE on success. // int SetFileName_TAP(PTAPSERVERINFO pTapServerInfo, char *szFileName) { int iRet = TRUE; // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Create a fully qualified file name and put // it into the pipe packet DosQueryPathInfo(szFileName, FIL_QUERYFULLNAME, pTapServerInfo -> tiTapInfo . szFileName, CCHMAXPATH); // Set flag indicating we started a new file pTapServerInfo -> lFileOpen = TRUE; pTapServerInfo -> tiTapInfo.ulFlags |= TAP_BOF; // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); return iRet; } // // int SetCompleteSize_TAP(PTAPSERVERINFO pTapServerInfo, // long lCompleteFileSize); // // Sets the complete size of the file. If this size is // not known it may be set to -1 (TAP_SIZE_UNKNOWN). // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // lCompleteFileSize // Size of the file when transfer is complete. // // Returns: TRUE on success. // int SetCompleteSize_TAP(PTAPSERVERINFO pTapServerInfo, long lCompleteFileSize) { int iRet = TRUE; // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Set complete file size in pipe packet pTapServerInfo -> tiTapInfo . lCompleteFileSize = lCompleteFileSize; // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); return iRet; } // // int SetCurrentSize_TAP(PTAPSERVERINFO pTapServerInfo, // long lCurrentFileSize); // // Sets the current size of the file. If thie size is // not known it may be set to -1 (TAP_SIZE_UNKNOWN). // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // lCurrentFileSize // Size of the file at the current time. // // Returns: TRUE on success. // int SetCurrentSize_TAP(PTAPSERVERINFO pTapServerInfo, long lCurrentFileSize) { int iRet = TRUE; // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Set current file size in pipe packet pTapServerInfo -> tiTapInfo . lCurrentFileSize = lCurrentFileSize; // Set flag indicating the file size changed pTapServerInfo->tiTapInfo.ulFlags |= TAP_NEW_SIZE; // Send new file information down the pipe DosPostEventSem(pTapServerInfo -> hevUpdate); // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); return iRet; } // // int EmergencyClose_TAP(PTAPSERVERINFO pTapServerInfo) // // Calls for all TAP Applications to immediately // close the current file. // // This function might be called if the current file needs // to be erased, moved, copied, etc. Note that this file // close operation will not take affect as soon as this // function returns. A 1/4 second delay is recommended // following this call before attempting any other file // operations. Normally the operation will succeed, but // if it fails delay another 1/4 second and retry the // operation. // // NOTE: This does NOT close the current file on the // server side. You must do this yourself. // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // // Returns: TRUE on success. // int EmergencyClose_TAP(PTAPSERVERINFO pTapServerInfo) { int iRet = TRUE; // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Set flag asking for an emergency close file pTapServerInfo->tiTapInfo.ulFlags |= TAP_EMERGENCY_CLOSE; pTapServerInfo -> lFileOpen = FALSE; // Send new file information down the pipe DosPostEventSem(pTapServerInfo -> hevUpdate); // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); return iRet; } // // int Cancel_TAP(PTAPSERVERINFO pTapServerInfo); // // Alerts the TAP Applications that the current // transfer batch has been cancelled. // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // // Returns: TRUE on success. // int Cancel_TAP(PTAPSERVERINFO pTapServerInfo) { int iRet = TRUE; // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Set flag asking for a cancel transfer pTapServerInfo->tiTapInfo.ulFlags |= TAP_CANCEL; pTapServerInfo -> lFileOpen = FALSE; // Send new file information down the pipe DosPostEventSem(pTapServerInfo -> hevUpdate); // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); return iRet; } // // int EndOfFile_TAP(PTAPSERVERINFO pTapServerInfo); // // Alerts the TAP Applications that we have reached // the end of the current file. // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // // Returns: TRUE on success. // int EndOfFile_TAP(PTAPSERVERINFO pTapServerInfo) { int iRet = TRUE; // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Set flag saying we've reached the end of this file pTapServerInfo -> tiTapInfo.ulFlags |= TAP_EOF; pTapServerInfo -> lFileOpen = FALSE; // Send new file information down the pipe DosPostEventSem(pTapServerInfo -> hevUpdate); // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); return iRet; } // // int EndOfBatch_TAP(PTAPSERVERINFO pTapServerInfo); // // Alerts the TAP Applications that we have reached // the end of the current file transfer batch. // That is, that the application should expect no more // files. // // Parameters: // pTapServerInfo // Pointer to the TAP Server instance data. // // Returns: TRUE on success. // int EndOfBatch_TAP(PTAPSERVERINFO pTapServerInfo) { int iRet = TRUE; // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Set flag saying we've reached the end of the batch pTapServerInfo->tiTapInfo.ulFlags |= TAP_EOB; pTapServerInfo -> lFileOpen = FALSE; // Send new file information down the pipe DosPostEventSem(pTapServerInfo -> hevUpdate); // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); return iRet; } // // void ServerThread_TAP(void *ServerInfo) // // -= Internal only =- // -= Not to be called from outside this module =- // // Handles the dispatch of file information to // TAP Applications. // void ServerThread_TAP (void *ServerInfo) { PTAPSERVERINFO pTapServerInfo = (PTAPSERVERINFO) ServerInfo; ULONG PostCount; ULONG BytesWritten; long cnter; PTAPPACKET pTapPacket; USHORT usPacketLength; for (;;) { // Wait until someone asks us to send a new pipe packet DosWaitEventSem(pTapServerInfo -> hevUpdate, (ULONG)SEM_INDEFINITE_WAIT); // Request exclusive access to the server DosRequestMutexSem(pTapServerInfo -> hServerMutex, (ULONG)SEM_INDEFINITE_WAIT); // Note that TAP_VERSION is handled elsewhere; it is sent immediately when thread is created! if (pTapServerInfo->tiTapInfo.ulFlags & TAP_BOF) { USHORT usFileNameLength = (USHORT)strlen (pTapServerInfo->tiTapInfo.szFileName); usPacketLength = (USHORT)(sizeof (pTapPacket->beginFilePacket) + usFileNameLength); pTapPacket = malloc (usPacketLength); pTapPacket->beginFilePacket.lCurrentFileSize = pTapServerInfo->tiTapInfo.lCurrentFileSize; pTapPacket->beginFilePacket.lCompleteFileSize = pTapServerInfo->tiTapInfo.lCompleteFileSize; pTapPacket->beginFilePacket.usFileNameLength = usFileNameLength; strcpy (pTapPacket->beginFilePacket.szFileName, pTapServerInfo->tiTapInfo.szFileName); } else if (pTapServerInfo->tiTapInfo.ulFlags & TAP_NEW_SIZE) { usPacketLength = (USHORT)sizeof (pTapPacket->newSizePacket); pTapPacket = malloc (usPacketLength); pTapPacket->newSizePacket.lCurrentFileSize = pTapServerInfo->tiTapInfo.lCurrentFileSize; pTapPacket->newSizePacket.lCompleteFileSize = pTapServerInfo->tiTapInfo.lCompleteFileSize; } else { usPacketLength = sizeof (pTapPacket->flagPacket); pTapPacket = malloc (usPacketLength); } pTapPacket->flagPacket.cb = usPacketLength; pTapPacket->flagPacket.usFlags = (USHORT)pTapServerInfo->tiTapInfo.ulFlags; // Send the packet down all pipes for (cnter=0; cnter < pTapServerInfo->lNumPipes; cnter++) { DosWrite (pTapServerInfo->phWritePipe[cnter], pTapPacket, pTapPacket->flagPacket.cb, &BytesWritten); } free (pTapPacket); // Reset flags pTapServerInfo -> tiTapInfo . ulFlags = 0L; // Reset update event semaphore DosResetEventSem(pTapServerInfo -> hevUpdate, &PostCount); // Release exclusive access to the server DosReleaseMutexSem(pTapServerInfo -> hServerMutex); // If this is a shutdown request break out if (pTapServerInfo -> lShutdown) break; } // *** // Shut down code // *** // Close semaphores DosCloseEventSem(pTapServerInfo -> hevUpdate); DosCloseMutexSem(pTapServerInfo -> hServerMutex); // Close pipes for(cnter=0; cnter < pTapServerInfo->lNumPipes; cnter++) { DosClose(pTapServerInfo -> phWritePipe[cnter]); } // Free memory free((void *) pTapServerInfo -> phWritePipe); free((void *) pTapServerInfo); // End this thread _endthread(); } // // void LaunchApplicationThread_TAP(void *data); // // Thread to start the TAP application, // so we don't hog the message queue // void LaunchApplicationThread_TAP(void *data) { PLAUNCHINFO pLaunchInfo = (PLAUNCHINFO) data; HAB hab; HMQ hmq; // And to make things even more fun, // StartApplication_TAP requires the // presence of a message queue hab=WinInitialize(0); hmq=WinCreateMsgQueue(hab, 0); // Start TAP application StartApplication_TAP(pLaunchInfo -> pTapServerInfo, pLaunchInfo -> szProgram, pLaunchInfo -> szParams, pLaunchInfo -> szTitle); // We are responsible for freeing this structure free(pLaunchInfo); // Free up PM resources WinDestroyMsgQueue(hmq); WinTerminate(hab); // End this thread _endthread(); } // // MRESULT EXPENTRY SelectApplicationDlgProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2); // // -= Internal only =- // -= Not to be called from outside this module =- // MRESULT EXPENTRY SelectApplicationDlgProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { MRESULT mResult; switch (msg) { case WM_INITDLG : { char *szApps; ULONG ulSize; // *** // Store PTAPSERVERINFO in the window words // *** WinSetWindowPtr(hWnd, QWL_USER, (PVOID) mp2); // *** // Initialize listbox with TAP applications available // *** // Get size of application list if (GetApplications_TAP(NULL, &ulSize)) { // Allocate enough memory for list szApps = (char *) malloc(ulSize + 10); if (GetApplications_TAP(szApps, &ulSize)) { ULONG cnter; char szApp[256]; int iAppLen; // Traverse the array of strings for (cnter=0, iAppLen = 0; cnter cb = sizeof(LAUNCHINFO); pLaunchInfo -> pTapServerInfo = pTapServerInfo; strcpy(pLaunchInfo -> szProgram, TapAppEntry.szProgram); strcpy(pLaunchInfo -> szParams, TapAppEntry.szParams); strcpy(pLaunchInfo -> szTitle, "TAP Application"); #ifdef __BORLANDC__ _beginthread(LaunchApplicationThread_TAP, 8192L, pLaunchInfo); #else _beginthread(LaunchApplicationThread_TAP, NULL, 8192L, pLaunchInfo); #endif } } // Call default handler mResult = WinDefDlgProc(hWnd, msg, mp1, mp2); break; } default : // Call default handler mResult = WinDefDlgProc(hWnd, msg, mp1, mp2); break; } return mResult; }