source: branches/branch-1-0/src/helpers/procstat.c@ 297

Last change on this file since 297 was 229, checked in by umoeller, 23 years ago

Sources as of 1.0.0.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 22.6 KB
Line 
1
2/*
3 *@@sourcefile procstat.c:
4 * functions for querying process information.
5 * This is an easy-to-use interface to the
6 * messy 16-bit DosQProcStatus function.
7 *
8 * Usage: All OS/2 programs.
9 *
10 * Function prefixes (new with V0.81):
11 * -- prc* Query Process helper functions
12 *
13 * Based on Kai Uwe Rommel's "dosqproc" package
14 * available at Hobbes:
15 * Kai Uwe Rommel - Wed 25-Mar-1992
16 * Sat 13-Aug-1994
17 *
18 * Note: If you link against procstat.obj, you
19 * need to import the following in your .DEF file:
20 *
21 + IMPORTS
22 + DOSQPROCSTATUS = DOSCALLS.154
23 + DosQuerySysState = DOSCALLS.368
24 *
25 * or linking will fail.
26 *
27 * Note: Version numbering in this file relates to XWorkplace version
28 * numbering.
29 *
30 *@@header "helpers\procstat.h"
31 */
32
33/*
34 * Copyright (C) 1992-1994 Kai Uwe Rommel.
35 * Copyright (C) 1998-2000 Ulrich M”ller.
36 * This file is part of the "XWorkplace helpers" source package.
37 * This is free software; you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published
39 * by the Free Software Foundation, in version 2 as it comes in the
40 * "COPYING" file of the XWorkplace main distribution.
41 * This program is distributed in the hope that it will be useful,
42 * but WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44 * GNU General Public License for more details.
45 */
46
47#define OS2EMX_PLAIN_CHAR
48 // this is needed for "os2emx.h"; if this is defined,
49 // emx will define PSZ as _signed_ char, otherwise
50 // as unsigned char
51
52#define INCL_DOSMODULEMGR
53#define INCL_DOSERRORS
54#include <os2.h>
55
56#include <stdlib.h> // already #include'd
57#include <string.h> // already #include'd
58#include <stdio.h>
59
60#include "setup.h" // code generation and debugging options
61
62#include "helpers\procstat.h"
63
64#pragma hdrstop
65
66/*
67 *@@category: Helpers\Control program helpers\Process status\16-bit DosQProcStat
68 */
69
70/********************************************************************
71 *
72 * DosQProcStat (16-bit) interface
73 *
74 ********************************************************************/
75
76/*
77 *@@ prc16GetInfo:
78 * nifty interface to DosQProcStat (16-bit).
79 * This returns the head of a newly
80 * allocated buffer which has plenty
81 * of pointers for subsequent browsing.
82 *
83 * Use prc16FreeInfo to free the buffer.
84 *
85 *@@added V0.9.3 (2000-05-05) [umoeller]
86 *@@changed V0.9.10 (2001-04-08) [umoeller]: this returned != NULL even though item was freed, fixed
87 *@@changed V0.9.10 (2001-04-08) [umoeller]: now using DosAllocMem, raised bufsize, changed prototype
88 */
89
90APIRET prc16GetInfo(PQPROCSTAT16 *ppps) // out: error, ptr can be NULL
91{
92 APIRET arc = NO_ERROR;
93 PQPROCSTAT16 pps = NULL;
94
95 if (!ppps)
96 return (ERROR_INVALID_PARAMETER);
97
98 // changed allocation V0.9.10 (2001-04-08) [umoeller]:
99 // malloc didn't guarantee that the object did not
100 // cross a 64K boundary, which could cause DosQProcStat
101 // to fail...
102 #define BUF_SIZE 0xFFFF // raised from 0x8000
103
104 if (!(arc = DosAllocMem((VOID**)&pps,
105 BUF_SIZE,
106 PAG_READ | PAG_WRITE | PAG_COMMIT
107 | OBJ_TILE // 16-bit compatible, ignored really
108 )))
109 {
110 if (arc = DosQProcStatus(pps, BUF_SIZE))
111 {
112 // error:
113 DosFreeMem(pps); // V0.9.10 (2001-04-08) [umoeller]
114
115 // and even worse, I forgot to set the return ptr
116 // to NULL, so this was freed twice... I guess
117 // this produced the crashes in WarpIN with the
118 // KILLPROCESS attribute... V0.9.10 (2001-04-08) [umoeller]
119 pps = NULL;
120 }
121 }
122
123 *ppps = pps;
124
125 return arc;
126}
127
128/*
129 *@@ prc16FreeInfo:
130 * frees memory allocated by prc16GetInfo.
131 *
132 *@@added V0.9.3 (2000-05-05) [umoeller]
133 *@@changed V0.9.10 (2001-04-08) [umoeller]: now using DosFreeMem
134 */
135
136APIRET prc16FreeInfo(PQPROCSTAT16 pInfo)
137{
138 if (!pInfo)
139 return ERROR_INVALID_PARAMETER;
140
141 return DosFreeMem(pInfo);
142}
143
144/*
145 *@@ prc16FindProcessFromName:
146 * searches the specified buffer for a process
147 * with the specified name and returns a pointer
148 * to its data within pInfo.
149 *
150 * Returns NULL if not found.
151 *
152 *@@added V0.9.3 (2000-05-05) [umoeller]
153 */
154
155PQPROCESS16 prc16FindProcessFromName(PQPROCSTAT16 pInfo, // in: from prc16GetInfo
156 const char *pcszName) // in: e.g. "pmshell.exe"
157{
158 PQPROCESS16 pProcess,
159 pReturn = NULL;
160 if (pInfo)
161 {
162 for ( pProcess = (PQPROCESS16)PTR(pInfo->ulProcesses, 0);
163 pProcess->ulType != 3;
164 pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList,
165 pProcess->usThreads * sizeof(QTHREAD16))
166 )
167 {
168 CHAR szModuleName[CCHMAXPATH];
169 if (DosQueryModuleName(pProcess->usHModule,
170 sizeof(szModuleName),
171 szModuleName)
172 == NO_ERROR)
173 {
174 // the module name is fully qualified, so find the
175 // file name (after the last backslash)
176 PSZ pLastBackslash = strrchr(szModuleName, '\\');
177 if (pLastBackslash)
178 // found:
179 if (stricmp(pLastBackslash + 1, pcszName) == 0)
180 {
181 // matches:
182 pReturn = pProcess;
183 break;
184 }
185 }
186 }
187 }
188
189 return (pReturn);
190}
191
192/*
193 *@@ prc16FindProcessFromPID:
194 * searches the specified buffer for a process
195 * with the specified PID and returns a pointer
196 * to its data within pInfo.
197 *
198 * Returns NULL if not found.
199 *
200 *V0.9.5 (2000-09-29) [umoeller]
201 */
202
203PQPROCESS16 prc16FindProcessFromPID(PQPROCSTAT16 pInfo, // in: from prc16GetInfo
204 ULONG ulPID) // in: PID
205{
206 PQPROCESS16 pProcess,
207 pReturn = NULL;
208 if (pInfo)
209 {
210 for ( pProcess = (PQPROCESS16)PTR(pInfo->ulProcesses, 0);
211 pProcess->ulType != 3;
212 pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList,
213 pProcess->usThreads * sizeof(QTHREAD16))
214 )
215 {
216 if (pProcess->usPID == ulPID)
217 {
218 pReturn = pProcess;
219 break;
220 }
221 }
222 }
223
224 return (pReturn);
225}
226
227/********************************************************************
228 *
229 * DosQProcStat (16-bit) helpers
230 *
231 ********************************************************************/
232
233/*
234 * prcReport16:
235 * fill PRCPROCESS structure
236 */
237
238VOID prcReport16(PQPROCESS16 pProcess, PPRCPROCESS pprcp)
239{
240 if (pProcess)
241 {
242 PQTHREAD16 pThread;
243 int i;
244
245 DosQueryModuleName(pProcess->usHModule,
246 sizeof(pprcp->szModuleName),
247 pprcp->szModuleName);
248 // DosGetPrty(PRTYS_PROCESS, &(pprcp->usPriority), pProcess->usPID);
249
250 // sum up CPU time for process
251 for (pprcp->ulCPU = 0,
252 i = 0,
253 pThread = (PQTHREAD16)PTR(pProcess->ulThreadList, 0);
254 i < pProcess->usThreads;
255 i++, pThread++ )
256 {
257 pprcp->ulCPU += (pThread->ulSysTime + pThread->ulUserTime);
258 }
259
260 pprcp->usPID = pProcess->usPID;
261 pprcp->usParentPID = pProcess->usParentPID;
262 pprcp->usThreads = pProcess->usThreads;
263 pprcp->ulSID = pProcess->ulSID;
264 pprcp->ulSessionType = pProcess->ulSessionType;
265 pprcp->ulStatus = pProcess->ulStatus;
266 }
267}
268
269/*
270 *@@ prc16QueryProcessInfo:
271 * this searches for a given process ID (usPID) and
272 * fills a given PRCPROCESS structure with lots of
273 * information about this process.
274 * Returns FALSE upon errors, e.g. if no process
275 * of that ID is found.
276 */
277
278BOOL prc16QueryProcessInfo(PQPROCSTAT16 pps, // in: from prc16GetInfo
279 USHORT usPID, // in: PID to query
280 PPRCPROCESS pprcp) // out: process info
281{
282 BOOL rc = FALSE;
283 if (pps)
284 {
285 PQPROCESS16 pProcess;
286
287 for ( pProcess = (PQPROCESS16)PTR(pps->ulProcesses, 0);
288 pProcess->ulType != 3;
289 pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList,
290 pProcess->usThreads * sizeof(QTHREAD16))
291 )
292 {
293 if (pProcess->usPID == usPID)
294 {
295 prcReport16(pProcess, pprcp);
296 rc = TRUE;
297 break;
298 }
299 }
300 }
301
302 return (rc);
303}
304
305/*
306 *@@ prc16ForEachProcess:
307 * this calls a given callback func for each running
308 * process. The callback must be a FNWP, which will be
309 * passed the following parameters for each call:
310 * -- HWND hwnd: like hwnd passed to this func
311 * -- ULONG msg: like msg passed to this func
312 * -- MPARAM mp1: like mp1 passed to this func
313 * -- PPRCPROCESS: mp2 pointer to a PRCPROCESS struct for each process
314 *
315 * This function returns the number of running processes on the
316 * system. If pfnwpCallback is NULL, only this number will be
317 * returned, so you can use this as a process counter too.
318 *
319 *@@changed V0.9.10 (2001-04-16) [pr]: now using DosAllocMem
320 */
321
322ULONG prc16ForEachProcess(PFNWP pfnwpCallback, HWND hwnd, ULONG ulMsg, MPARAM mp1)
323{
324 ULONG ulrc = 0;
325 PQPROCSTAT16 pps;
326 PQPROCESS16 pProcess;
327 PRCPROCESS prcp;
328
329 if (!DosAllocMem((PVOID*)&pps,
330 BUF_SIZE,
331 PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_TILE))
332 {
333 if (!DosQProcStatus(pps, BUF_SIZE))
334 for ( pProcess = (PQPROCESS16)PTR(pps->ulProcesses, 0);
335 pProcess->ulType != 3;
336 pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList,
337 pProcess->usThreads * sizeof(QTHREAD16))
338 )
339 {
340 if (pfnwpCallback)
341 {
342 prcReport16(pProcess, &prcp);
343 (*pfnwpCallback)(hwnd, ulMsg, mp1, &prcp);
344 }
345 ulrc++;
346 }
347
348 DosFreeMem(pps);
349 }
350
351 return (ulrc);
352}
353
354/*
355 *@@ prc16QueryThreadCount:
356 * returns the total number of running threads
357 * in the given process. If pid == 0, the
358 * total thread count for the system is returned.
359 *
360 *@@changed V0.9.9 (2001-03-07) [umoeller]: added pps param
361 */
362
363ULONG prc16QueryThreadCount(PQPROCSTAT16 pps, // in: from prc16GetInfo
364 USHORT usPID)
365{
366 ULONG ulrc = 0;
367
368 if (pps)
369 {
370 if (usPID)
371 {
372 // process query:
373 PQPROCESS16 pProcess;
374 for ( pProcess = (PQPROCESS16)PTR(pps->ulProcesses, 0);
375 pProcess->ulType != 3;
376 pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList,
377 pProcess->usThreads * sizeof(QTHREAD16))
378 )
379 {
380 if (pProcess->usPID == usPID)
381 {
382 ulrc = pProcess->usThreads;
383 break;
384 }
385 }
386 }
387 else
388 {
389 // global query:
390 PQGLOBAL16 pg;
391 pg = (PQGLOBAL16)PTR(pps->ulGlobal, 0);
392 ulrc = pg->ulThreads;
393 }
394 }
395
396 return (ulrc);
397}
398
399/*
400 *@@ prc16QueryThreadInfo:
401 * this searches for a given thread in a given process
402 * and fills a given PRCTHREAD structure with lots of
403 * information about that thread.
404 *
405 * Returns FALSE upon errors.
406 *
407 * Note: This function loops thru all processes which
408 * are currently running and is therefore not terribly
409 * fast. Use economically.
410 *
411 *@@changed V0.9.9 (2001-03-07) [umoeller]: added pps param
412 */
413
414BOOL prc16QueryThreadInfo(PQPROCSTAT16 pps, // in: from prc16GetInfo
415 USHORT usPID,
416 USHORT usTID,
417 PPRCTHREAD pprct)
418{
419 BOOL brc = FALSE;
420 if (pps)
421 {
422 PQPROCESS16 pProcess;
423
424 // find process:
425 for ( pProcess = (PQPROCESS16)PTR(pps->ulProcesses, 0);
426 pProcess->ulType != 3;
427 pProcess = (PQPROCESS16)PTR(pProcess->ulThreadList,
428 pProcess->usThreads * sizeof(QTHREAD16))
429 )
430 {
431 if (pProcess->usPID == usPID)
432 {
433 PQTHREAD16 pThread;
434 int i;
435 // process found: find thread
436 for ( i = 0, pThread = (PQTHREAD16)PTR(pProcess->ulThreadList, 0);
437 i < pProcess->usThreads;
438 i++, pThread++ )
439 {
440 if (pThread->usTID == usTID)
441 {
442 // thread found:
443 pprct->usTID = pThread->usTID;
444 pprct->usThreadSlotID = pThread->usThreadSlotID;
445 pprct->ulBlockID = pThread->ulBlockID;
446 pprct->ulPriority = pThread->ulPriority;
447 pprct->ulSysTime = pThread->ulSysTime;
448 pprct->ulUserTime = pThread->ulUserTime;
449 pprct->ucStatus = pThread->ucStatus;
450
451 brc = TRUE;
452
453 break; // thread-for loop
454 }
455 } // end for thread
456 break; // process-for loop
457 }
458 } // end for process
459 }
460
461 return brc;
462}
463
464/*
465 *@@ prcQueryPriority:
466 * shortcut to prc16QueryThreadInfo if you want the priority only.
467 *
468 * Returns -1 upon errors.
469 *
470 * Note: This function loops thru all processes which
471 * are currently running and is therefore not terribly
472 * fast. Use economically.
473 *
474 *@@changed V0.9.9 (2001-03-07) [umoeller]: added pps param
475 */
476
477ULONG prc16QueryThreadPriority(PQPROCSTAT16 pps, // in: from prc16GetInfo
478 USHORT usPID,
479 USHORT usTID)
480{
481 PRCTHREAD prct;
482 ULONG ulrc = -1;
483 if (prc16QueryThreadInfo(pps, usPID, usTID, &prct))
484 ulrc = prct.ulPriority;
485 return (ulrc);
486}
487
488/*
489 *@@category: Helpers\Control program helpers\Process status\32-bit DosQuerySysState
490 */
491
492/********************************************************************
493 *
494 * DosQuerySysState (32-bit) interface
495 *
496 ********************************************************************/
497
498/*
499 *@@ prc32GetInfo:
500 * nifty interface to DosQuerySysState,
501 * the 32-bit version of DosQProcStat.
502 * This returns the head of a newly
503 * allocated buffer which has plenty
504 * of pointers for subsequent browing.
505 *
506 * Use prc32FreeInfo to free the buffer.
507 *
508 *@@added V0.9.1 (2000-02-12) [umoeller]
509 *@@changed V0.9.3 (2000-05-01) [umoeller]: now using DosAllocMem
510 *@@changed V0.9.10 (2001-04-08) [umoeller]: fixed second QuerySysState param
511 */
512
513PQTOPLEVEL32 prc32GetInfo(APIRET *parc) // out: error, ptr can be NULL
514{
515 #define BUFSIZE (1024 * 1024) // 1 meg
516
517 PCHAR pBuf = NULL; // (PCHAR)malloc(BUFSIZE);
518
519 if (DosAllocMem((PVOID*)&pBuf,
520 BUFSIZE,
521 PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_TILE)
522 == NO_ERROR)
523 if (pBuf)
524 {
525 APIRET arc = DosQuerySysState(QS32_SUPPORTED,
526 QS32_SUPPORTED, // this was missing
527 // V0.9.10 (2001-04-08) [umoeller]
528 0, 0,
529 (PCHAR)pBuf,
530 BUFSIZE);
531 if (parc)
532 *parc = arc;
533
534 if (arc == NO_ERROR)
535 return ((PQTOPLEVEL32)pBuf);
536 else
537 DosFreeMem(pBuf);
538 }
539
540 return NULL;
541}
542
543/*
544 *@@ prc32FreeInfo:
545 * frees the memory allocated by prc32GetInfo.
546 *
547 *@@added V0.9.1 (2000-02-12) [umoeller]
548 *@@changed V0.9.3 (2000-05-01) [umoeller]: now using DosFreeMem
549 */
550
551VOID prc32FreeInfo(PQTOPLEVEL32 pInfo)
552{
553 DosFreeMem(pInfo);
554}
555
556/*
557 *@@ prc32FindProcessFromName:
558 *
559 *@@added V0.9.2 (2000-03-05) [umoeller]
560 *@@changed V1.0.0 (2002-08-16) [pr]: optimized
561 */
562
563PQPROCESS32 prc32FindProcessFromName(PQTOPLEVEL32 pInfo,
564 const char *pcszName) // in: e.g. "pmshell.exe"
565{
566 PQPROCESS32 pProcThis = pInfo->pProcessData;
567 while (pProcThis && pProcThis->ulRecType == 1)
568 {
569 PQTHREAD32 t = pProcThis->pThreads;
570 PQMODULE32 pModule;
571
572 if (pModule = prc32FindModule(pInfo,
573 pProcThis->usHModule))
574 {
575 // the module name is fully qualified, so find the
576 // file name (after the last backslash)
577 if (pModule->pcName)
578 {
579 PSZ pLastBackslash;
580 if (pLastBackslash = strrchr(pModule->pcName, '\\'))
581 // found:
582 if (stricmp(pLastBackslash + 1, pcszName) == 0)
583 // matches:
584 break;
585 }
586 }
587
588 // for next process, skip the threads info;
589 // the next process block comes after the
590 // threads
591 t += pProcThis->usThreadCount;
592 pProcThis = (PQPROCESS32)t;
593 }
594
595 if (pProcThis->ulRecType == 1)
596 return (pProcThis);
597 else
598 return NULL;
599}
600
601/*
602 *@@ prc32FindProcessFromPID:
603 *
604 *@@added V1.0.0 (2002-08-12) [umoeller]
605 *@@changed V1.0.0 (2002-08-16) [pr]: optimized
606 */
607
608PQPROCESS32 prc32FindProcessFromPID(PQTOPLEVEL32 pInfo,
609 ULONG pid)
610{
611 PQPROCESS32 pProcThis = pInfo->pProcessData;
612 while (pProcThis && pProcThis->ulRecType == 1)
613 {
614 PQTHREAD32 t = pProcThis->pThreads;
615
616 if (pProcThis->usPID == pid)
617 return pProcThis;
618
619 // for next process, skip the threads info;
620 // the next process block comes after the
621 // threads
622 t += pProcThis->usThreadCount;
623 pProcThis = (PQPROCESS32)t;
624 }
625
626 return NULL;
627}
628
629/*
630 *@@ prc32FindSem16:
631 * attempts to find the specified 16-bit semaphore
632 * in the specified info buffer.
633 *
634 * The return value points into the pInfo buffer.
635 * Returns NULL if not found.
636 *
637 *@@added V0.9.1 (2000-02-12) [umoeller]
638 */
639
640PQS32SEM16 prc32FindSem16(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo
641 USHORT usSemID) // in: as in QPROCESS32.pausSem16
642{
643 PQS32SEM16HEAD pSemHead = pInfo->pSem16Data;
644 PQS32SEM16 // pSemThis = &pSemData->sema;
645 pSemThis = &pSemHead->Sem16Rec;
646 ULONG i = 0;
647
648 while (pSemThis)
649 {
650 if (i == usSemID)
651 return (pSemThis);
652
653 i++;
654 pSemThis = pSemThis->pNext;
655 }
656
657 return NULL;
658}
659
660/*
661 *@@ prc32FindSem32:
662 * attempts to find the specified 32-bit semaphore
663 * in the specified info buffer. This might fail
664 * because the data isn't always complete.
665 *
666 * The return value points into the pInfo buffer.
667 * Returns NULL if not found.
668 *
669 *@@added V0.9.1 (2000-02-12) [umoeller]
670 */
671
672PQS32SEM32 prc32FindSem32(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo
673 USHORT usSemID) // in: as in QPROCESS32.pausSem16
674{
675 // PQSEM32STRUC32 pSemThis = pInfo->pSem32Data;
676
677 /* while (pSemThis)
678 {
679 if (pSemThis->usIndex == usSemID)
680 return (pSemThis);
681
682 pSemThis = pSemThis->pNext;
683 } */
684
685 return NULL;
686}
687
688/*
689 *@@ prc32FindShrMem:
690 * attempts to find the specified shared memory
691 * block description.
692 *
693 * The return value points into the pInfo buffer.
694 * Returns NULL if not found.
695 *
696 *@@added V0.9.1 (2000-02-12) [umoeller]
697 */
698
699PQSHRMEM32 prc32FindShrMem(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo
700 USHORT usShrMemID) // in: as in QPROCESS32.pausShrMems
701{
702 PQSHRMEM32 pShrMem = pInfo->pShrMemData;
703 while (pShrMem)
704 {
705 if (pShrMem->usHandle == usShrMemID)
706 return (pShrMem);
707 pShrMem = pShrMem->pNext;
708 }
709
710 return NULL;
711}
712
713/*
714 *@@ prc32FindModule:
715 * attempts to find the specified module description.
716 *
717 * The return value points into the pInfo buffer.
718 * Returns NULL if not found.
719 *
720 *@@added V0.9.1 (2000-02-12) [umoeller]
721 */
722
723PQMODULE32 prc32FindModule(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo
724 USHORT usHModule)
725{
726 PQMODULE32 pModule = pInfo->pModuleData;
727 while (pModule)
728 {
729 if (pModule->usHModule == usHModule)
730 return pModule;
731
732 pModule = pModule->pNext;
733 }
734
735 return NULL;
736}
737
738/*
739 *@@ prc32FindFileData:
740 *
741 *
742 *@@added V0.9.1 (2000-02-12) [umoeller]
743 */
744
745PQFILEDATA32 prc32FindFileData(PQTOPLEVEL32 pInfo, // in: as returned by prc32GetInfo
746 USHORT usFileID) // in: as in QPROCESS32.pausFds
747{
748 PQFILEDATA32 pFile = pInfo->pFileData;
749 while ( (pFile)
750 && (pFile->ulRecType == 8) // this is necessary, we'll crash otherwise!!
751 )
752 {
753 ULONG ul;
754 // for some reason, there is an array in the file struct,
755 // so search the array for the SFN
756 for (ul = 0;
757 ul < pFile->ulCFiles;
758 ul++)
759 {
760 if (pFile->paFiles[ul].usSFN == usFileID)
761 return (pFile);
762 }
763
764 pFile = pFile->pNext;
765 }
766
767 return NULL;
768}
769
770
Note: See TracBrowser for help on using the repository browser.