Staging: add epl stack
[firefly-linux-kernel-4.4.55.git] / drivers / staging / epl / ShbIpc-Win32.c
1 /****************************************************************************
2
3   (c) SYSTEC electronic GmbH, D-07973 Greiz, August-Bebel-Str. 29
4       www.systec-electronic.com
5
6   Project:      Project independend shared buffer (linear + circular)
7
8   Description:  Implementation of platform specific part for the
9                 shared buffer
10                 (Implementation for Win32)
11
12   License:
13
14     Redistribution and use in source and binary forms, with or without
15     modification, are permitted provided that the following conditions
16     are met:
17
18     1. Redistributions of source code must retain the above copyright
19        notice, this list of conditions and the following disclaimer.
20
21     2. Redistributions in binary form must reproduce the above copyright
22        notice, this list of conditions and the following disclaimer in the
23        documentation and/or other materials provided with the distribution.
24
25     3. Neither the name of SYSTEC electronic GmbH nor the names of its
26        contributors may be used to endorse or promote products derived
27        from this software without prior written permission. For written
28        permission, please contact info@systec-electronic.com.
29
30     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
33     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
34     COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
35     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
36     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
40     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41     POSSIBILITY OF SUCH DAMAGE.
42
43     Severability Clause:
44
45         If a provision of this License is or becomes illegal, invalid or
46         unenforceable in any jurisdiction, that shall not affect:
47         1. the validity or enforceability in that jurisdiction of any other
48            provision of this License; or
49         2. the validity or enforceability in other jurisdictions of that or
50            any other provision of this License.
51
52   -------------------------------------------------------------------------
53
54   2006/06/27 -rs:   V 1.00 (initial version)
55
56 ****************************************************************************/
57
58 #define WINVER       0x0400             // #defines necessary for usage of
59 #define _WIN32_WINNT 0x0400             // function <SignalObjectAndWait>
60
61 #include <windows.h>
62 #include <stdio.h>
63 #include "global.h"
64 #include "sharedbuff.h"
65 #include "shbipc.h"
66
67
68
69
70
71 /***************************************************************************/
72 /*                                                                         */
73 /*                                                                         */
74 /*          G L O B A L   D E F I N I T I O N S                            */
75 /*                                                                         */
76 /*                                                                         */
77 /***************************************************************************/
78
79 #if (!defined(SHBIPC_INLINED)) || defined(SHBIPC_INLINE_ENABLED)
80
81 //---------------------------------------------------------------------------
82 //  Configuration
83 //---------------------------------------------------------------------------
84
85
86
87 //---------------------------------------------------------------------------
88 //  Constant definitions
89 //---------------------------------------------------------------------------
90
91 #define MAX_LEN_BUFFER_ID       MAX_PATH
92
93 #define IDX_EVENT_NEW_DATA      0
94 #define IDX_EVENT_TERM_REQU     1
95 #define IDX_EVENT_TERM_RESP     2
96
97 #define NAME_MUTEX_BUFF_ACCESS  "BuffAccess"
98 #define NAME_EVENT_NEW_DATA     "NewData"
99 #define NAME_EVENT_TERM_REQU    "TermRequ"
100 #define NAME_EVENT_TERM_RESP    "TermResp"
101 #define NAME_EVENT_JOB_READY    "JobReady"
102
103 #define TIMEOUT_ENTER_ATOMIC    1000        // for debgging: INFINITE
104 #define TIMEOUT_TERM_THREAD     2000
105
106 #define SBI_MAGIC_ID            0x5342492B  // magic ID ("SBI+")
107 #define SBH_MAGIC_ID            0x5342482A  // magic ID ("SBH*")
108
109
110
111 //---------------------------------------------------------------------------
112 //  Local types
113 //---------------------------------------------------------------------------
114
115 // This structure is the common header for the shared memory region used
116 // by all processes attached this shared memory. It includes common
117 // information to administrate/manage the shared buffer from a couple of
118 // separated processes (e.g. the refernce counter). This structure is
119 // located at the start of the shared memory region itself and exists
120 // consequently only one times per shared memory instance.
121 typedef struct
122 {
123     unsigned long       m_SbhMagicID;       // magic ID ("SBH*")
124     unsigned long       m_ulShMemSize;
125     unsigned long       m_ulRefCount;
126     char                m_szBufferID[MAX_LEN_BUFFER_ID];
127
128     #ifndef NDEBUG
129         unsigned long   m_ulOwnerProcID;
130     #endif
131
132 } tShbMemHeader;
133
134
135 // This structure is the "external entry point" from a separate process
136 // to get access to a shared buffer. This structure includes all platform
137 // resp. target specific information to administrate/manage the shared
138 // buffer from a separate process. Every process attached to the shared
139 // buffer has its own runtime instance of this structure with its individual
140 // runtime data (e.g. the scope of an event handle is limitted to the
141 // owner process only). The structure member <m_pShbMemHeader> points
142 // to the (process specific) start address of the shared memory region
143 // itself.
144 typedef struct
145 {
146     unsigned long       m_SbiMagicID;           // magic ID ("SBI+")
147     HANDLE              m_hSharedMem;
148     HANDLE              m_hMutexBuffAccess;
149     HANDLE              m_hThreadNewData;       // thraed to signal that new data are available
150     HANDLE              m_ahEventNewData[3];    // IDX_EVENT_NEW_DATA + IDX_EVENT_TERM_REQU + ID_EVENT_TERM_RESP
151     tSigHndlrNewData    m_pfnSigHndlrNewData;
152     HANDLE              m_hThreadJobReady;      // thread to signal that a job/operation is ready now (e.g. reset buffer)
153     HANDLE              m_hEventJobReady;
154     unsigned long       m_ulTimeOutJobReady;
155     tSigHndlrJobReady   m_pfnSigHndlrJobReady;
156     tShbMemHeader*      m_pShbMemHeader;
157
158     #ifndef NDEBUG
159         unsigned long   m_ulThreadIDNewData;
160         unsigned long   m_ulThreadIDJobReady;
161     #endif
162
163 } tShbMemInst;
164
165
166
167 //---------------------------------------------------------------------------
168 //  Global variables
169 //---------------------------------------------------------------------------
170
171
172
173 //---------------------------------------------------------------------------
174 //  Local variables
175 //---------------------------------------------------------------------------
176
177
178
179 //---------------------------------------------------------------------------
180 //  Prototypes of internal functions
181 //---------------------------------------------------------------------------
182
183 //---------------------------------------------------------------------------
184 //  Get pointer to process local information structure
185 //---------------------------------------------------------------------------
186
187 INLINE_FUNCTION tShbMemInst*  ShbIpcGetShbMemInst (
188     tShbInstance pShbInstance_p)
189 {
190
191 tShbMemInst*  pShbMemInst;
192
193
194     pShbMemInst = (tShbMemInst*)pShbInstance_p;
195     ASSERT (pShbMemInst->m_SbiMagicID == SBI_MAGIC_ID);
196
197     return (pShbMemInst);
198
199 }
200
201
202
203 //---------------------------------------------------------------------------
204 //  Get pointer to shared memory header
205 //---------------------------------------------------------------------------
206
207 INLINE_FUNCTION tShbMemHeader*  ShbIpcGetShbMemHeader (
208     tShbInstance pShbInstance_p)
209 {
210
211 tShbMemInst*    pShbMemInst;
212 tShbMemHeader*  pShbMemHeader;
213
214
215     pShbMemInst   = ShbIpcGetShbMemInst (pShbInstance_p);
216     pShbMemHeader = pShbMemInst->m_pShbMemHeader;
217     ASSERT(pShbMemHeader->m_SbhMagicID == SBH_MAGIC_ID);
218
219     return (pShbMemHeader);
220
221 }
222
223
224 // not inlined internal functions
225 DWORD  WINAPI   ShbIpcThreadSignalNewData  (LPVOID pvThreadParam_p);
226 DWORD  WINAPI   ShbIpcThreadSignalJobReady (LPVOID pvThreadParam_p);
227 const char*     ShbIpcGetUniformObjectName (const char* pszEventJobName_p, const char* pszBufferID_p, BOOL fGlobalObject_p);
228
229 #endif
230
231 #if !defined(SHBIPC_INLINE_ENABLED)
232 // true internal functions (not inlined)
233 static void*        ShbIpcAllocPrivateMem      (unsigned long ulMemSize_p);
234 static void         ShbIpcReleasePrivateMem    (void* pMem_p);
235 #endif
236
237 //=========================================================================//
238 //                                                                         //
239 //          P U B L I C   F U N C T I O N S                                //
240 //                                                                         //
241 //=========================================================================//
242
243 #if !defined(SHBIPC_INLINE_ENABLED)
244 // not inlined external functions
245
246 //---------------------------------------------------------------------------
247 //  Initialize IPC for Shared Buffer Module
248 //---------------------------------------------------------------------------
249
250 tShbError  ShbIpcInit (void)
251 {
252
253     return (kShbOk);
254
255 }
256
257
258
259 //---------------------------------------------------------------------------
260 //  Deinitialize IPC for Shared Buffer Module
261 //---------------------------------------------------------------------------
262
263 tShbError  ShbIpcExit (void)
264 {
265
266     return (kShbOk);
267
268 }
269
270
271
272 //---------------------------------------------------------------------------
273 //  Allocate Shared Buffer
274 //---------------------------------------------------------------------------
275
276 tShbError  ShbIpcAllocBuffer (
277     unsigned long ulBufferSize_p,
278     const char* pszBufferID_p,
279     tShbInstance* ppShbInstance_p,
280     unsigned int* pfShbNewCreated_p)
281 {
282
283 HANDLE          hSharedMem;
284 LPVOID          pSharedMem;
285 unsigned long   ulShMemSize;
286 tShbMemInst*    pShbMemInst;
287 tShbMemHeader*  pShbMemHeader;
288 tShbInstance    pShbInstance;
289 unsigned int    fShMemNewCreated;
290 const char*     pszObjectName;
291 HANDLE          hMutexBuffAccess;
292 HANDLE          hEventNewData;
293 HANDLE          hEventJobReady;
294 tShbError       ShbError;
295
296
297     ulShMemSize      = ulBufferSize_p + sizeof(tShbMemHeader);
298     pSharedMem       = NULL;
299     pShbInstance     = NULL;
300     fShMemNewCreated = FALSE;
301     ShbError         = kShbOk;
302
303
304     //---------------------------------------------------------------
305     // (1) open an existing or create a new shared memory
306     //---------------------------------------------------------------
307     // try to open an already existing shared memory
308     // (created by an another process)
309     hSharedMem = OpenFileMapping (FILE_MAP_ALL_ACCESS,      // DWORD dwDesiredAccess
310                                   FALSE,                    // BOOL bInheritHandle
311                                   pszBufferID_p);           // LPCTSTR lpName
312     if (hSharedMem != NULL)
313     {
314         // a shared memory already exists
315         fShMemNewCreated = FALSE;
316     }
317     else
318     {
319         // it seams that this process is the first who wants to use the
320         // shared memory, so it has to create a new shared memory
321         hSharedMem = CreateFileMapping(INVALID_HANDLE_VALUE,// HANDLE hFile
322                                        NULL,                // LPSECURITY_ATTRIBUTES lpAttributes
323                                        PAGE_READWRITE,      // DWORD flProtect
324                                        0,                   // DWORD dwMaximumSizeHigh
325                                        ulShMemSize,         // DWORD dwMaximumSizeLow
326                                        pszBufferID_p);      // LPCTSTR lpName
327
328         fShMemNewCreated = TRUE;
329     }
330
331     if (hSharedMem == NULL)
332     {
333         ShbError = kShbOutOfMem;
334         goto Exit;
335     }
336
337
338     //---------------------------------------------------------------
339     // (2) get the pointer to the shared memory
340     //---------------------------------------------------------------
341     pSharedMem = MapViewOfFile (hSharedMem,                 // HANDLE hFileMappingObject
342                                 FILE_MAP_ALL_ACCESS,        // DWORD dwDesiredAccess,
343                                 0,                          // DWORD dwFileOffsetHigh,
344                                 0,                          // DWORD dwFileOffsetLow,
345                                 ulShMemSize);               // SIZE_T dwNumberOfBytesToMap
346
347     if (pSharedMem == NULL)
348     {
349         ShbError = kShbOutOfMem;
350         goto Exit;
351     }
352
353
354     //---------------------------------------------------------------
355     // (3) setup or update header and management information
356     //---------------------------------------------------------------
357     pShbMemHeader = (tShbMemHeader*)pSharedMem;
358
359     // allocate a memory block from process specific mempool to save
360     // process local information to administrate/manage the shared buffer
361     pShbMemInst = (tShbMemInst*) ShbIpcAllocPrivateMem (sizeof(tShbMemInst));
362     if (pShbMemInst == NULL)
363     {
364         ShbError = kShbOutOfMem;
365         goto Exit;
366     }
367
368     // reset complete header to default values
369     pShbMemInst->m_SbiMagicID                          = SBI_MAGIC_ID;
370     pShbMemInst->m_hSharedMem                          = hSharedMem;
371     pShbMemInst->m_hMutexBuffAccess                    = INVALID_HANDLE_VALUE;
372     pShbMemInst->m_hThreadNewData                      = INVALID_HANDLE_VALUE;
373     pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA]  = INVALID_HANDLE_VALUE;
374     pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_REQU] = INVALID_HANDLE_VALUE;
375     pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP] = INVALID_HANDLE_VALUE;
376     pShbMemInst->m_pfnSigHndlrNewData                  = NULL;
377     pShbMemInst->m_hThreadJobReady                     = INVALID_HANDLE_VALUE;
378     pShbMemInst->m_hEventJobReady                      = INVALID_HANDLE_VALUE;
379     pShbMemInst->m_ulTimeOutJobReady                   = 0;
380     pShbMemInst->m_pfnSigHndlrJobReady                 = NULL;
381     pShbMemInst->m_pShbMemHeader                       = pShbMemHeader;
382
383     #ifndef NDEBUG
384     {
385         pShbMemInst->m_ulThreadIDNewData  = 0;
386         pShbMemInst->m_ulThreadIDJobReady = 0;
387     }
388     #endif
389
390     // create mutex for buffer access
391     pszObjectName = ShbIpcGetUniformObjectName (NAME_MUTEX_BUFF_ACCESS, pszBufferID_p, TRUE);
392     hMutexBuffAccess = CreateMutex (NULL,                   // LPSECURITY_ATTRIBUTES lpMutexAttributes
393                                     FALSE,                  // BOOL bInitialOwner
394                                     pszObjectName);         // LPCTSTR lpName
395     pShbMemInst->m_hMutexBuffAccess = hMutexBuffAccess;
396     ASSERT(pShbMemInst->m_hMutexBuffAccess != NULL);
397
398     // The EventNewData is used for signaling of new data after a write
399     // operation (SetEvent) as well as for waiting for new data on the
400     // reader side (WaitForMultipleObjects). Because it's not known if
401     // this process will be read or write data, the event will be
402     // always created here.
403     pszObjectName = ShbIpcGetUniformObjectName (NAME_EVENT_NEW_DATA, pszBufferID_p, TRUE);
404     hEventNewData = CreateEvent (NULL,                      // LPSECURITY_ATTRIBUTES lpEventAttributes
405                                  FALSE,                     // BOOL bManualReset
406                                  FALSE,                     // BOOL bInitialState
407                                  pszObjectName);            // LPCTSTR lpName
408     pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA] = hEventNewData;
409     ASSERT(pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA] != NULL);
410
411     // The EventJobReady is used for signaling that a job is done (SetEvent)
412     // as well as for waiting for finishing of a job (WaitForMultipleObjects).
413     // Because it's not known if this process will signal or wait, the event
414     // will be always created here.
415     pszObjectName = ShbIpcGetUniformObjectName (NAME_EVENT_JOB_READY, pszBufferID_p, TRUE);
416     hEventJobReady = CreateEvent (NULL,                     // LPSECURITY_ATTRIBUTES lpEventAttributes
417                                   FALSE,                    // BOOL bManualReset
418                                   FALSE,                    // BOOL bInitialState
419                                   pszObjectName);           // LPCTSTR lpName
420     pShbMemInst->m_hEventJobReady = hEventJobReady;
421     ASSERT(pShbMemInst->m_hEventJobReady != NULL);
422
423     if ( fShMemNewCreated )
424     {
425         // this process was the first who wanted to use the shared memory,
426         // so a new shared memory was created
427         // -> setup new header information inside the shared memory region
428         //    itself
429         pShbMemHeader->m_SbhMagicID  = SBH_MAGIC_ID;
430         pShbMemHeader->m_ulShMemSize = ulShMemSize;
431         pShbMemHeader->m_ulRefCount  = 1;
432         strncpy (pShbMemHeader->m_szBufferID, pszBufferID_p, sizeof(pShbMemHeader->m_szBufferID)-1);
433
434         #ifndef NDEBUG
435         {
436             pShbMemHeader->m_ulOwnerProcID = GetCurrentProcessId();
437         }
438         #endif
439     }
440     else
441     {
442         // any other process has created the shared memory and this
443         // process has only attached to it
444         // -> check and update existing header information inside the
445         //    shared memory region itself
446         if (pShbMemHeader->m_ulShMemSize != ulShMemSize)
447         {
448             ShbError = kShbOpenMismatch;
449             goto Exit;
450         }
451
452         #ifndef NDEBUG
453         {
454             if ( strncmp(pShbMemHeader->m_szBufferID, pszBufferID_p, sizeof(pShbMemHeader->m_szBufferID)-1) )
455             {
456                 ShbError = kShbOpenMismatch;
457                 goto Exit;
458             }
459         }
460         #endif
461
462         pShbMemHeader->m_ulRefCount++;
463     }
464
465
466     // set abstarct "handle" for returning to application
467     pShbInstance = (tShbInstance*)pShbMemInst;
468
469
470 Exit:
471
472     if (ShbError != kShbOk)
473     {
474         if (pShbMemInst != NULL)
475         {
476             ShbIpcReleasePrivateMem (pShbMemInst);
477         }
478         if (pSharedMem != NULL)
479         {
480             UnmapViewOfFile (pSharedMem);
481         }
482         if (hSharedMem != NULL)
483         {
484             CloseHandle (hSharedMem);
485         }
486     }
487
488     *pfShbNewCreated_p = fShMemNewCreated;
489     *ppShbInstance_p   = pShbInstance;
490
491     return (ShbError);
492
493 }
494
495
496
497 //---------------------------------------------------------------------------
498 //  Release Shared Buffer
499 //---------------------------------------------------------------------------
500
501 tShbError  ShbIpcReleaseBuffer (
502     tShbInstance pShbInstance_p)
503 {
504
505 tShbMemInst*    pShbMemInst;
506 tShbMemHeader*  pShbMemHeader;
507 HANDLE          hEventNewData;
508 HANDLE          hMutexBuffAccess;
509 tShbError       ShbError;
510 tShbError       ShbError2;
511
512
513     if (pShbInstance_p == NULL)
514     {
515         return (kShbOk);
516     }
517
518
519     pShbMemInst   = ShbIpcGetShbMemInst   (pShbInstance_p);
520     pShbMemHeader = ShbIpcGetShbMemHeader (pShbInstance_p);
521
522
523     if ( !--pShbMemHeader->m_ulRefCount )
524     {
525         ShbError = kShbOk;
526     }
527     else
528     {
529         ShbError = kShbMemUsedByOtherProcs;
530     }
531
532
533     ShbError2 = ShbIpcStopSignalingNewData (pShbInstance_p);
534     hEventNewData = pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA];
535     if (hEventNewData != INVALID_HANDLE_VALUE)
536     {
537         CloseHandle (hEventNewData);
538         pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA] = INVALID_HANDLE_VALUE;
539     }
540
541     hMutexBuffAccess = pShbMemInst->m_hMutexBuffAccess;
542     if (hMutexBuffAccess != INVALID_HANDLE_VALUE)
543     {
544         CloseHandle (hMutexBuffAccess);
545         pShbMemInst->m_hMutexBuffAccess = INVALID_HANDLE_VALUE;
546     }
547
548     UnmapViewOfFile (pShbMemHeader);
549     if (pShbMemInst->m_hSharedMem != INVALID_HANDLE_VALUE)
550     {
551         CloseHandle (pShbMemInst->m_hSharedMem);
552         pShbMemInst->m_hSharedMem = INVALID_HANDLE_VALUE;
553     }
554
555     ShbIpcReleasePrivateMem (pShbMemInst);
556
557
558     if (ShbError == kShbOk)
559     {
560         ShbError = ShbError2;
561     }
562
563     return (ShbError);
564
565 }
566
567 #endif  // !defined(SHBIPC_INLINE_ENABLED)
568
569 #if (!defined(SHBIPC_INLINED)) || defined(SHBIPC_INLINE_ENABLED)
570
571 //---------------------------------------------------------------------------
572 //  Enter atomic section for Shared Buffer access
573 //---------------------------------------------------------------------------
574
575 INLINE_FUNCTION tShbError  ShbIpcEnterAtomicSection (
576     tShbInstance pShbInstance_p)
577 {
578
579 tShbMemInst*  pShbMemInst;
580 HANDLE        hMutexBuffAccess;
581 DWORD         dwWaitResult;
582 tShbError     ShbError;
583
584
585     if (pShbInstance_p == NULL)
586     {
587         return (kShbInvalidArg);
588     }
589
590
591     pShbMemInst = ShbIpcGetShbMemInst (pShbInstance_p);
592     ShbError = kShbOk;
593
594     hMutexBuffAccess = pShbMemInst->m_hMutexBuffAccess;
595     if (hMutexBuffAccess != INVALID_HANDLE_VALUE)
596     {
597         dwWaitResult = WaitForSingleObject (hMutexBuffAccess, TIMEOUT_ENTER_ATOMIC);
598         switch (dwWaitResult)
599         {
600             case WAIT_OBJECT_0 + 0:
601             {
602                 break;
603             }
604
605             case WAIT_TIMEOUT:
606             {
607                 TRACE0("\nShbIpcEnterAtomicSection(): WAIT_TIMEOUT");
608                 ASSERT(0);
609                 ShbError = kShbBufferInvalid;
610                 break;
611             }
612
613             case WAIT_ABANDONED:
614             {
615                 TRACE0("\nShbIpcEnterAtomicSection(): WAIT_ABANDONED");
616                 ASSERT(0);
617                 ShbError = kShbBufferInvalid;
618                 break;
619             }
620
621             case WAIT_FAILED:
622             {
623                 TRACE1("\nShbIpcEnterAtomicSection(): WAIT_FAILED -> LastError=%ld", GetLastError());
624                 ASSERT(0);
625                 ShbError = kShbBufferInvalid;
626                 break;
627             }
628
629             default:
630             {
631                 TRACE1("\nShbIpcEnterAtomicSection(): unknown error -> LastError=%ld", GetLastError());
632                 ASSERT(0);
633                 ShbError = kShbBufferInvalid;
634                 break;
635             }
636         }
637     }
638     else
639     {
640         ShbError = kShbBufferInvalid;
641     }
642
643
644     return (ShbError);
645
646 }
647
648
649
650 //---------------------------------------------------------------------------
651 //  Leave atomic section for Shared Buffer access
652 //---------------------------------------------------------------------------
653
654 INLINE_FUNCTION tShbError  ShbIpcLeaveAtomicSection (
655     tShbInstance pShbInstance_p)
656 {
657
658 tShbMemInst*  pShbMemInst;
659 HANDLE        hMutexBuffAccess;
660 BOOL          fRes;
661 tShbError     ShbError;
662
663
664     if (pShbInstance_p == NULL)
665     {
666         return (kShbInvalidArg);
667     }
668
669
670     pShbMemInst = ShbIpcGetShbMemInst (pShbInstance_p);
671     ShbError = kShbOk;
672
673     hMutexBuffAccess = pShbMemInst->m_hMutexBuffAccess;
674     if (hMutexBuffAccess != INVALID_HANDLE_VALUE)
675     {
676         fRes = ReleaseMutex (hMutexBuffAccess);
677         ASSERT( fRes );
678     }
679     else
680     {
681         ShbError = kShbBufferInvalid;
682     }
683
684
685     return (ShbError);
686
687 }
688
689
690
691 //---------------------------------------------------------------------------
692 //  Start signaling of new data (called from reading process)
693 //---------------------------------------------------------------------------
694
695 INLINE_FUNCTION tShbError  ShbIpcStartSignalingNewData (
696     tShbInstance pShbInstance_p,
697     tSigHndlrNewData pfnSignalHandlerNewData_p,
698     tShbPriority ShbPriority_p)
699 {
700
701 tShbMemInst*    pShbMemInst;
702 tShbMemHeader*  pShbMemHeader;
703 const char*     pszObjectName;
704 HANDLE          hEventTermRequ;
705 HANDLE          hEventTermResp;
706 HANDLE          hThreadNewData;
707 unsigned long   ulThreadIDNewData;
708 tShbError       ShbError;
709 int             iPriority;
710
711     if ((pShbInstance_p == NULL) || (pfnSignalHandlerNewData_p == NULL))
712     {
713         return (kShbInvalidArg);
714     }
715
716
717     pShbMemInst   = ShbIpcGetShbMemInst   (pShbInstance_p);
718     pShbMemHeader = ShbIpcGetShbMemHeader (pShbInstance_p);
719     ShbError = kShbOk;
720
721     if ( (pShbMemInst->m_hThreadNewData                      != INVALID_HANDLE_VALUE) ||
722          (pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_REQU] != INVALID_HANDLE_VALUE) ||
723          (pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP] != INVALID_HANDLE_VALUE) ||
724          (pShbMemInst->m_pfnSigHndlrNewData                  != NULL) )
725     {
726         ShbError = kShbAlreadySignaling;
727         goto Exit;
728     }
729
730
731     pShbMemInst->m_pfnSigHndlrNewData = pfnSignalHandlerNewData_p;
732
733
734     // Because the event <pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA]>
735     // is used for signaling of new data after a write operation too (using
736     // SetEvent), it is always created here (see <ShbIpcAllocBuffer>).
737
738     pszObjectName = ShbIpcGetUniformObjectName (NAME_EVENT_TERM_REQU, pShbMemHeader->m_szBufferID, FALSE);
739     hEventTermRequ = CreateEvent (NULL,                         // LPSECURITY_ATTRIBUTES lpEventAttributes
740                                   FALSE,                        // BOOL bManualReset
741                                   FALSE,                        // BOOL bInitialState
742                                   pszObjectName);               // LPCTSTR lpName
743     pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_REQU] = hEventTermRequ;
744     ASSERT(pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_REQU] != NULL);
745
746     pszObjectName = ShbIpcGetUniformObjectName (NAME_EVENT_TERM_RESP, pShbMemHeader->m_szBufferID, FALSE);
747     hEventTermResp = CreateEvent (NULL,                         // LPSECURITY_ATTRIBUTES lpEventAttributes
748                                   FALSE,                        // BOOL bManualReset
749                                   FALSE,                        // BOOL bInitialState
750                                   pszObjectName);               // LPCTSTR lpName
751     pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP] = hEventTermResp;
752     ASSERT(pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP] != NULL);
753
754     hThreadNewData = CreateThread (NULL,                        // LPSECURITY_ATTRIBUTES lpThreadAttributes
755                                    0,                           // SIZE_T dwStackSize
756                                    ShbIpcThreadSignalNewData,   // LPTHREAD_START_ROUTINE lpStartAddress
757                                    pShbInstance_p,              // LPVOID lpParameter
758                                    0,                           // DWORD dwCreationFlags
759                                    &ulThreadIDNewData);         // LPDWORD lpThreadId
760
761     switch (ShbPriority_p)
762     {
763         case kShbPriorityLow:
764             iPriority = THREAD_PRIORITY_BELOW_NORMAL;
765             break;
766
767         case kShbPriorityNormal:
768             iPriority = THREAD_PRIORITY_NORMAL;
769             break;
770
771         case kshbPriorityHigh:
772             iPriority = THREAD_PRIORITY_ABOVE_NORMAL;
773             break;
774
775     }
776
777     ASSERT(pShbMemInst->m_hThreadNewData != NULL);
778
779     SetThreadPriority(hThreadNewData, iPriority);
780
781     pShbMemInst->m_hThreadNewData = hThreadNewData;
782
783     #ifndef NDEBUG
784     {
785         pShbMemInst->m_ulThreadIDNewData = ulThreadIDNewData;
786     }
787     #endif
788
789
790 Exit:
791
792     return (ShbError);
793
794 }
795
796
797
798 //---------------------------------------------------------------------------
799 //  Stop signaling of new data (called from reading process)
800 //---------------------------------------------------------------------------
801
802 INLINE_FUNCTION tShbError  ShbIpcStopSignalingNewData (
803     tShbInstance pShbInstance_p)
804 {
805
806 tShbMemInst*  pShbMemInst;
807 HANDLE        hEventTermRequ;
808 HANDLE        hEventTermResp;
809 DWORD         dwWaitResult;
810
811
812     if (pShbInstance_p == NULL)
813     {
814         return (kShbInvalidArg);
815     }
816
817     pShbMemInst = ShbIpcGetShbMemInst (pShbInstance_p);
818
819
820     // terminate new data signaling thread
821     // (set event <hEventTermRequ> to wakeup the thread and dispose it
822     // to exit, then wait for confirmation using event <hEventTermResp>)
823     hEventTermRequ = pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_REQU];
824     hEventTermResp = pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP];
825     if ( (hEventTermRequ != INVALID_HANDLE_VALUE) &&
826          (hEventTermResp != INVALID_HANDLE_VALUE)  )
827     {
828         TRACE0("\nShbIpcStopSignalingNewData(): enter wait state");
829         dwWaitResult = SignalObjectAndWait (hEventTermRequ,         // HANDLE hObjectToSignal
830                                             hEventTermResp,         // HANDLE hObjectToWaitOn
831                                             TIMEOUT_TERM_THREAD,    // DWORD dwMilliseconds
832                                             FALSE);                 // BOOL bAlertable
833         TRACE0("\nShbIpcStopSignalingNewData(): wait state leaved: ---> ");
834         switch (dwWaitResult)
835         {
836             case WAIT_OBJECT_0 + 0:     // event "new data signaling thread terminated"
837             {
838                 TRACE0("Event = WAIT_OBJECT_0+0");
839                 break;
840             }
841
842             default:
843             {
844                 TRACE0("Unhandled Event");
845                 ASSERT(0);
846                 break;
847             }
848         }
849     }
850
851
852     if (pShbMemInst->m_hThreadNewData != INVALID_HANDLE_VALUE)
853     {
854         CloseHandle (pShbMemInst->m_hThreadNewData);
855         pShbMemInst->m_hThreadNewData = INVALID_HANDLE_VALUE;
856     }
857
858     if (pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_REQU] != INVALID_HANDLE_VALUE)
859     {
860         CloseHandle (pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_REQU]);
861         pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_REQU] = INVALID_HANDLE_VALUE;
862     }
863
864     if (pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP] != INVALID_HANDLE_VALUE)
865     {
866         CloseHandle (pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP]);
867         pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP] = INVALID_HANDLE_VALUE;
868     }
869
870     pShbMemInst->m_pfnSigHndlrNewData = NULL;
871
872
873     return (kShbOk);
874
875 }
876
877
878
879 //---------------------------------------------------------------------------
880 //  Signal new data (called from writing process)
881 //---------------------------------------------------------------------------
882
883 INLINE_FUNCTION tShbError  ShbIpcSignalNewData (
884     tShbInstance pShbInstance_p)
885 {
886
887 tShbMemInst*  pShbMemInst;
888 HANDLE  hEventNewData;
889 BOOL    fRes;
890
891
892     // TRACE0("\nShbIpcSignalNewData(): enter\n");
893
894     if (pShbInstance_p == NULL)
895     {
896         return (kShbInvalidArg);
897     }
898
899
900     pShbMemInst = ShbIpcGetShbMemInst (pShbInstance_p);
901
902     ASSERT(pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA] != INVALID_HANDLE_VALUE);
903     hEventNewData = pShbMemInst->m_ahEventNewData[IDX_EVENT_NEW_DATA];
904     if (hEventNewData != INVALID_HANDLE_VALUE)
905     {
906         fRes = SetEvent (hEventNewData);
907         // TRACE1("\nShbIpcSignalNewData(): EventNewData set (Result=%d)\n", (int)fRes);
908         ASSERT( fRes );
909     }
910
911     // TRACE0("\nShbIpcSignalNewData(): leave\n");
912     return (kShbOk);
913
914 }
915
916
917
918 //---------------------------------------------------------------------------
919 //  Start signaling for job ready (called from waiting process)
920 //---------------------------------------------------------------------------
921
922 INLINE_FUNCTION tShbError  ShbIpcStartSignalingJobReady (
923     tShbInstance pShbInstance_p,
924     unsigned long ulTimeOut_p,
925     tSigHndlrJobReady pfnSignalHandlerJobReady_p)
926 {
927
928 tShbMemInst*    pShbMemInst;
929 tShbMemHeader*  pShbMemHeader;
930 HANDLE          hThreadJobReady;
931 unsigned long   ulThreadIDJobReady;
932 tShbError       ShbError;
933
934
935     if ((pShbInstance_p == NULL) || (pfnSignalHandlerJobReady_p == NULL))
936     {
937         return (kShbInvalidArg);
938     }
939
940
941     pShbMemInst   = ShbIpcGetShbMemInst   (pShbInstance_p);
942     pShbMemHeader = ShbIpcGetShbMemHeader (pShbInstance_p);
943     ShbError = kShbOk;
944
945     if ( (pShbMemInst->m_hThreadJobReady     != INVALID_HANDLE_VALUE) ||
946          (pShbMemInst->m_pfnSigHndlrJobReady != NULL) )
947     {
948         ShbError = kShbAlreadySignaling;
949         goto Exit;
950     }
951
952
953     pShbMemInst->m_ulTimeOutJobReady   = ulTimeOut_p;
954     pShbMemInst->m_pfnSigHndlrJobReady = pfnSignalHandlerJobReady_p;
955
956
957     // Because the event <pShbMemInst->m_ahEventJobReady> is used for
958     // signaling of a finished job too (using SetEvent), it is always
959     // created here (see <ShbIpcAllocBuffer>).
960
961     hThreadJobReady = CreateThread (NULL,                       // LPSECURITY_ATTRIBUTES lpThreadAttributes
962                                     0,                          // SIZE_T dwStackSize
963                                     ShbIpcThreadSignalJobReady, // LPTHREAD_START_ROUTINE lpStartAddress
964                                     pShbInstance_p,             // LPVOID lpParameter
965                                     0,                          // DWORD dwCreationFlags
966                                     &ulThreadIDJobReady);       // LPDWORD lpThreadId
967
968     pShbMemInst->m_hThreadJobReady = hThreadJobReady;
969     ASSERT(pShbMemInst->m_hThreadJobReady != NULL);
970
971     #ifndef NDEBUG
972     {
973         pShbMemInst->m_ulThreadIDJobReady = ulThreadIDJobReady;
974     }
975     #endif
976
977
978 Exit:
979
980     return (ShbError);
981
982 }
983
984
985
986 //---------------------------------------------------------------------------
987 //  Signal job ready (called from executing process)
988 //---------------------------------------------------------------------------
989
990 INLINE_FUNCTION tShbError  ShbIpcSignalJobReady (
991     tShbInstance pShbInstance_p)
992 {
993
994 tShbMemInst*  pShbMemInst;
995 HANDLE  hEventJobReady;
996 BOOL    fRes;
997
998
999     // TRACE0("\nShbIpcSignalJobReady(): enter\n");
1000
1001     if (pShbInstance_p == NULL)
1002     {
1003         return (kShbInvalidArg);
1004     }
1005
1006
1007     pShbMemInst = ShbIpcGetShbMemInst (pShbInstance_p);
1008
1009     ASSERT(pShbMemInst->m_hEventJobReady != INVALID_HANDLE_VALUE);
1010     hEventJobReady = pShbMemInst->m_hEventJobReady;
1011     if (hEventJobReady != INVALID_HANDLE_VALUE)
1012     {
1013         fRes = SetEvent (hEventJobReady);
1014         // TRACE1("\nShbIpcSignalJobReady(): EventJobReady set (Result=%d)\n", (int)fRes);
1015         ASSERT( fRes );
1016     }
1017
1018     // TRACE0("\nShbIpcSignalJobReady(): leave\n");
1019     return (kShbOk);
1020
1021 }
1022
1023
1024
1025 //---------------------------------------------------------------------------
1026 //  Get pointer to common used share memory area
1027 //---------------------------------------------------------------------------
1028
1029 INLINE_FUNCTION void*  ShbIpcGetShMemPtr (
1030     tShbInstance pShbInstance_p)
1031 {
1032
1033 tShbMemHeader*  pShbMemHeader;
1034 void*  pShbShMemPtr;
1035
1036
1037     pShbMemHeader = ShbIpcGetShbMemHeader (pShbInstance_p);
1038     if (pShbMemHeader != NULL)
1039     {
1040         pShbShMemPtr = (BYTE*)pShbMemHeader + sizeof(tShbMemHeader);
1041     }
1042     else
1043     {
1044         pShbShMemPtr = NULL;
1045     }
1046
1047     return (pShbShMemPtr);
1048
1049 }
1050
1051 #endif
1052
1053
1054
1055 //=========================================================================//
1056 //                                                                         //
1057 //          P R I V A T E   F U N C T I O N S                              //
1058 //                                                                         //
1059 //=========================================================================//
1060
1061 #if !defined(SHBIPC_INLINE_ENABLED)
1062
1063 //---------------------------------------------------------------------------
1064 //  Allocate a memory block from process specific mempool
1065 //---------------------------------------------------------------------------
1066
1067 static void*  ShbIpcAllocPrivateMem (
1068     unsigned long ulMemSize_p)
1069 {
1070
1071 HGLOBAL  hMem;
1072 void*    pMem;
1073
1074
1075     hMem = GlobalAlloc (GMEM_FIXED, ulMemSize_p+sizeof(HGLOBAL));
1076     pMem = GlobalLock  (hMem);
1077     if (pMem != NULL)
1078     {
1079         *(HGLOBAL*)pMem = hMem;
1080         (BYTE*)pMem += sizeof(HGLOBAL);
1081     }
1082
1083
1084     #ifndef NDEBUG
1085     {
1086         memset (pMem, 0xaa, ulMemSize_p);
1087     }
1088     #endif
1089
1090
1091     return (pMem);
1092
1093 }
1094
1095
1096
1097 //---------------------------------------------------------------------------
1098 //  Release a memory block from process specific mempool
1099 //---------------------------------------------------------------------------
1100
1101 static void  ShbIpcReleasePrivateMem (
1102     void* pMem_p)
1103 {
1104
1105 HGLOBAL  hMem;
1106
1107
1108     if (pMem_p == NULL)
1109     {
1110         return;
1111     }
1112
1113
1114     (BYTE*)pMem_p -= sizeof(HGLOBAL);
1115     hMem = *(HGLOBAL*)pMem_p;
1116
1117     GlobalUnlock (hMem);
1118     GlobalFree   (hMem);
1119
1120     return;
1121
1122 }
1123
1124
1125
1126 //---------------------------------------------------------------------------
1127 //  Create uniform object name (needed for inter-process communication)
1128 //---------------------------------------------------------------------------
1129
1130 const char*  ShbIpcGetUniformObjectName (
1131     const char* pszObjectJobName_p,
1132     const char* pszBufferID_p,
1133     BOOL fGlobalObject_p)
1134 {
1135
1136 static  char  szObjectName[MAX_PATH];
1137 char  szObjectPrefix[MAX_PATH];
1138
1139
1140     if ( fGlobalObject_p )
1141     {
1142         strncpy (szObjectPrefix, "Global\\", sizeof(szObjectPrefix));
1143     }
1144     else
1145     {
1146         _snprintf (szObjectPrefix, sizeof(szObjectPrefix), "PID%08lX_",
1147                    (unsigned long)GetCurrentProcessId());
1148     }
1149
1150
1151     _snprintf (szObjectName, sizeof(szObjectName), "%s%s#%s",
1152                szObjectPrefix, pszBufferID_p, pszObjectJobName_p);
1153
1154
1155     return (szObjectName);
1156
1157 }
1158
1159
1160
1161 //---------------------------------------------------------------------------
1162 //  Thread for new data signaling
1163 //---------------------------------------------------------------------------
1164
1165 DWORD  WINAPI  ShbIpcThreadSignalNewData (
1166     LPVOID pvThreadParam_p)
1167 {
1168
1169 tShbInstance  pShbInstance;
1170 tShbMemInst*  pShbMemInst;
1171 DWORD         dwWaitResult;
1172 BOOL          fTermRequ;
1173 int           fCallAgain;
1174
1175
1176     TRACE1("\nShbIpcThreadSignalNewData(): SignalThread started (pShbInstance=0x%08lX)\n", (DWORD)pvThreadParam_p);
1177
1178     pShbInstance = (tShbMemInst*)pvThreadParam_p;
1179     pShbMemInst  = ShbIpcGetShbMemInst (pShbInstance);
1180     fTermRequ    = FALSE;
1181
1182     do
1183     {
1184         ASSERT((pShbMemInst->m_ahEventNewData[0] != INVALID_HANDLE_VALUE) && (pShbMemInst->m_ahEventNewData[0] != NULL));
1185         ASSERT((pShbMemInst->m_ahEventNewData[1] != INVALID_HANDLE_VALUE) && (pShbMemInst->m_ahEventNewData[1] != NULL));
1186
1187         TRACE0("\nShbIpcThreadSignalNewData(): enter wait state");
1188         dwWaitResult = WaitForMultipleObjects (2,                               // DWORD nCount
1189                                                pShbMemInst->m_ahEventNewData,   // const HANDLE* lpHandles
1190                                                FALSE,                           // BOOL bWaitAll
1191                                                INFINITE);                       // DWORD dwMilliseconds
1192         TRACE0("\nShbIpcThreadSignalNewData(): wait state leaved: ---> ");
1193         switch (dwWaitResult)
1194         {
1195             case WAIT_OBJECT_0 + 0:     // event "new data"
1196             {
1197                 TRACE0("Event = WAIT_OBJECT_0+0");
1198                 if (pShbMemInst->m_pfnSigHndlrNewData != NULL)
1199                 {
1200                     TRACE0("\nShbIpcThreadSignalNewData(): calling SignalHandlerNewData");
1201                     do
1202                     {
1203                         fCallAgain = pShbMemInst->m_pfnSigHndlrNewData (pShbInstance);
1204                         // d.k.: try to run any shared buffer which has higher priority.
1205                         //           under Windows this is not really necessary because the Windows scheduler
1206                         //           already preempts tasks with lower priority.
1207                     } while (fCallAgain != FALSE);
1208                 }
1209                 break;
1210             }
1211
1212             case WAIT_OBJECT_0 + 1:     // event "terminate"
1213             {
1214                 TRACE0("Event = WAIT_OBJECT_0+1");
1215                 fTermRequ = TRUE;
1216                 break;
1217             }
1218
1219             default:
1220             {
1221                 TRACE0("Unhandled Event");
1222                 ASSERT(0);
1223                 fTermRequ = TRUE;
1224                 break;
1225             }
1226         }
1227     }
1228     while ( !fTermRequ );
1229
1230
1231     if (pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP] != INVALID_HANDLE_VALUE)
1232     {
1233         SetEvent (pShbMemInst->m_ahEventNewData[IDX_EVENT_TERM_RESP]);
1234     }
1235
1236     TRACE1("\nShbIpcThreadSignalNewData(): SignalThread terminated (pShbInstance=0x%08lX)\n", (DWORD)pShbInstance);
1237
1238     ExitThread (0);
1239
1240 }
1241
1242
1243
1244 //---------------------------------------------------------------------------
1245 //  Thread for new data signaling
1246 //---------------------------------------------------------------------------
1247
1248 DWORD  WINAPI  ShbIpcThreadSignalJobReady (
1249     LPVOID pvThreadParam_p)
1250 {
1251
1252 tShbInstance*  pShbInstance;
1253 tShbMemInst*   pShbMemInst;
1254 DWORD          ulTimeOut;
1255 DWORD          dwWaitResult;
1256 unsigned int   fTimeOut;
1257
1258
1259     TRACE1("\nShbIpcThreadSignalJobReady(): SignalThread started (pShbInstance=0x%08lX)\n", (DWORD)pvThreadParam_p);
1260
1261
1262     pShbInstance = (tShbInstance*)pvThreadParam_p;
1263     pShbMemInst  = ShbIpcGetShbMemInst (pShbInstance);
1264     fTimeOut     = FALSE;
1265
1266     if (pShbMemInst->m_ulTimeOutJobReady != 0)
1267     {
1268         ulTimeOut = pShbMemInst->m_ulTimeOutJobReady;
1269     }
1270     else
1271     {
1272         ulTimeOut = INFINITE;
1273     }
1274
1275     ASSERT((pShbMemInst->m_hEventJobReady != INVALID_HANDLE_VALUE) && (pShbMemInst->m_hEventJobReady != NULL));
1276
1277     TRACE0("\nShbIpcThreadSignalJobReady(): enter wait state");
1278     dwWaitResult = WaitForSingleObject (pShbMemInst->m_hEventJobReady,          // HANDLE hHandle
1279                                         ulTimeOut);                             // DWORD dwMilliseconds
1280     TRACE0("\nShbIpcThreadSignalJobReady(): wait state leaved: ---> ");
1281     switch (dwWaitResult)
1282     {
1283         case WAIT_OBJECT_0 + 0:     // event "new data"
1284         {
1285             TRACE0("Event = WAIT_OBJECT_0+0");
1286             fTimeOut = FALSE;
1287             break;
1288         }
1289
1290         case WAIT_TIMEOUT:
1291         {
1292             TRACE0("\nEvent = WAIT_TIMEOUT");
1293             fTimeOut = TRUE;
1294             // ASSERT(0);
1295             break;
1296         }
1297
1298         default:
1299         {
1300             TRACE0("Unhandled Event");
1301             fTimeOut = TRUE;
1302             ASSERT(0);
1303             break;
1304         }
1305     }
1306
1307
1308     if (pShbMemInst->m_pfnSigHndlrJobReady != NULL)
1309     {
1310         TRACE0("\nShbIpcThreadSignalJobReady(): calling SignalHandlerJobReady");
1311         pShbMemInst->m_pfnSigHndlrJobReady (pShbInstance, fTimeOut);
1312     }
1313
1314
1315     pShbMemInst->m_hThreadJobReady     = INVALID_HANDLE_VALUE;
1316     pShbMemInst->m_pfnSigHndlrJobReady = NULL;
1317
1318
1319     TRACE1("\nShbIpcThreadSignalJobReady(): SignalThread terminated (pShbInstance=0x%08lX)\n", (DWORD)pShbInstance);
1320
1321     ExitThread (0);
1322
1323 }
1324
1325 #endif
1326
1327 // EOF
1328