home *** CD-ROM | disk | FTP | other *** search
/ Power GUI Programming with VisualAge C++ / powergui.iso / trialva / ibmcppw / sdk / mapi / win16 / dev / common / mapiwin.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-11  |  33.7 KB  |  1,415 lines

  1. /*
  2.  *  MAPIWIN.C
  3.  *
  4.  *  Support for cross-platform development (WIN16 and WIN32) of MAPI
  5.  *  components and service providers.
  6.  *
  7.  *  Most functions have some limitations relative to their WIN32
  8.  *  counterparts. Such limitations are noted in the comments to the
  9.  *  function. Where possible, if the caller requests unsupported
  10.  *  functionality, the function fails.
  11.  *
  12.  *  Copyright 1993-1995 Microsoft Corporation. All Rights Reserved.
  13.  */
  14.  
  15. #pragma warning(disable:4001)   /* single line comments */
  16. #pragma warning(disable:4054)   /* cast function pointer to data pointer */
  17. #pragma warning(disable:4100)   /* unreferenced formal parameter */
  18. #pragma warning(disable:4127)   /* conditional expression is constant */
  19. #pragma warning(disable:4201)   /* nameless struct/union */
  20. #pragma warning(disable:4204)   /* non-constant aggregate initializer */
  21. #pragma warning(disable:4209)   /* benign typedef redefinition */
  22. #pragma warning(disable:4214)   /* bit field types other than int */
  23. #pragma warning(disable:4505)   /* unreferenced local function removed */
  24. #pragma warning(disable:4514)   /* unreferenced inline function removed */
  25. #pragma warning(disable:4702)   /* unreachable code */
  26. #pragma warning(disable:4704)   /* inline assembler turns off global optimizer */
  27. #pragma warning(disable:4705)   /* statement has no effect */
  28. #pragma warning(disable:4706)   /* assignment within conditional expression */
  29. #pragma warning(disable:4710)   /* function not expanded */
  30. #pragma warning(disable:4115)   /* named type def in parens */
  31.  
  32. #include <windows.h>
  33. #pragma warning(disable:4001)   /* single line comments */
  34. #include <windowsx.h>
  35. #include <memory.h>     /*  for memcpy, memset */
  36.  
  37. #include <mapiwin.h>
  38. #include <mapidbg.h>
  39. #include <compobj.h>
  40. #include <mapicode.h>
  41. #include <mapiutil.h>
  42. #include <_mapiu.h>
  43. #include <mapiperf.h>
  44.  
  45. #include <_mapiwin.h>
  46.  
  47. #include <stdlib.h>
  48.  
  49. #ifdef DBCS
  50. #ifdef DOS
  51. #include <gapidos.h>
  52. #else
  53. #include <mapigapi.h>
  54. #endif
  55. #endif
  56.  
  57. #pragma SEGMENT(MAPI_Util)
  58.  
  59. #pragma warning(disable: 4704)      /* asm disables global optimizations */
  60.  
  61. #ifdef  WIN16
  62.  
  63. /*
  64.  *  GetLastError
  65.  *
  66.  *  Limitations:
  67.  *      It seems likely that some of the DOS and CRT functions used
  68.  *      elsewhere in this module won't set the DOS extended error
  69.  *      info, so this function won't be as reliable as it is on NT.
  70.  *
  71.  *      If a function in this module fails because an unsupported
  72.  *      feature was requested, no error is set.
  73.  */
  74. DWORD __export WINAPI
  75. GetLastError()
  76. {
  77.     WORD    w;
  78.  
  79.     _asm {
  80.         push bp
  81.         push ds
  82.         push si
  83.         push di
  84.  
  85.         mov ah, 59h         ; Get Extended Error
  86.         call DOS3Call
  87.                             ; no failure report
  88.         mov w, ax
  89.  
  90.         pop di
  91.         pop si
  92.         pop ds
  93.         pop bp
  94.     }
  95.  
  96.     return (DWORD)w;
  97. }
  98.  
  99. /*
  100.  *  GetFileAttributes
  101.  *
  102.  *  Limitations:
  103.  *      Won't work on Netware without FILESCAN rights.
  104.  */
  105. DWORD __export WINAPI
  106. GetFileAttributes(LPCSTR lpFileName)
  107. {
  108.     WORD w;
  109.     WORD x;
  110.     char szFile[MAX_PATH];
  111.  
  112.     Assert(!IsBadStringPtr(lpFileName, MAX_PATH));
  113.     AnsiToOem(lpFileName, szFile);
  114.  
  115.     w = SELECTOROF(szFile);
  116.     x = OFFSETOF(szFile);
  117.  
  118.     _asm
  119.     {
  120.         push ds
  121.         mov dx, w
  122.         mov ds, dx
  123.         mov dx, x
  124.         mov ax, 4300h               ; Get File Attributes
  125.         call DOS3Call
  126.         pop ds                      ; doesn't affect flags
  127.         jc gfaErr
  128.  
  129.         mov w, cx                   ; here are attributes
  130.     }
  131.     return (DWORD)w;
  132.  
  133. gfaErr:
  134.     DebugTraceDos(GetFileAttributes);
  135.     return (DWORD)-1;
  136. }
  137.  
  138.  
  139. /*
  140.  *  GetFileSize
  141.  */
  142. DWORD __export WINAPI
  143. GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
  144. {
  145.     WORD    wLow;
  146.     WORD    wHigh;
  147.  
  148.     if (lpFileSizeHigh)
  149.     {
  150.         Assert(!IsBadWritePtr(lpFileSizeHigh, sizeof(DWORD)));
  151.         *lpFileSizeHigh = 0L;
  152.     }
  153.  
  154.     _asm {
  155.         mov bx, hFile
  156.         xor dx, dx                  ; seek offset 0L
  157.         xor cx, cx
  158.         mov ax, 4201h               ; current position
  159.                                     ; Move File Pointer
  160.         call DOS3Call
  161.         jc gfsErr
  162.  
  163.         push ax                     ; save current position
  164.         push dx
  165.  
  166.         xor dx, dx                  ; seek offset 0L
  167.         xor cx, cx
  168.         mov ax, 4202h               ; end of file
  169.                                     ; Move File Pointer
  170.         call DOS3Call
  171.         jc gfsErr1
  172.  
  173.         mov wLow, ax                ; save file size
  174.         mov wHigh, dx
  175.  
  176.         pop cx
  177.         pop dx                      ; restore original position
  178.         mov ax, 4200h               ; beginning of file
  179.                                     ; Move File Pointer
  180.         call DOS3Call
  181.         jc gfsErr
  182.         jmp gfsDone
  183.  
  184. gfsErr1:
  185.         pop ax                      ; clean stack
  186.         pop ax
  187. gfsErr:
  188.         xor ax, ax
  189.         dec ax
  190.         mov wLow, ax                ; return error
  191.         mov wHigh, ax
  192.  
  193. gfsDone:
  194.     }
  195.  
  196. #ifdef  DEBUG
  197.     if (wLow + wHigh == 0)
  198.         DebugTraceDos(GetFileSize);
  199. #endif
  200.     return wLow | ((DWORD)wHigh << 16);
  201. }
  202.  
  203. /*
  204.  *  CreateFile
  205.  *
  206.  *  Limitations
  207.  *      dwDesiredAccess is ignored.
  208.  *      lpSecurityAttributes is not supported, fails if not NULL.
  209.  *      dwCreationDisposition is partially supported:
  210.  *          handles CREATE_ALWAYS, OPEN_ALWAYS, TRUNCATE_EXISTING,
  211.  *              OPEN_EXISTING
  212.  *          does not handle CREATE_NEW
  213.  *      dwFlagsAndAttributes is ignored.
  214.  *      hTemplateFile is not supported, fails if nonzero.
  215.  */
  216. HANDLE __export WINAPI
  217. CreateFile(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
  218.     LPVOID lpSecurityAttributes, DWORD dwCreationDisposition,
  219.     DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
  220. {
  221.     int hf = -1;
  222.     BYTE b = 0;
  223.     WORD w;
  224.     WORD segPath;
  225.     WORD offPath;
  226.  
  227.     if (lpSecurityAttributes || hTemplateFile)
  228.     {
  229.         DebugTraceArg(CreateFile, "lpSecurityAttributes and hTemplateFile are unsupported");
  230.         return INVALID_HANDLE_VALUE;
  231.     }
  232.  
  233.     Assert(!IsBadStringPtr(lpFileName, MAX_PATH));
  234.  
  235.     segPath = SELECTOROF(lpFileName);
  236.     offPath = OFFSETOF(lpFileName);
  237.  
  238.     /*  Map the MAPIWIN.H definitions to the underlying */
  239.     /*  access modes */
  240.  
  241.     if ((dwDesiredAccess == 0) || (dwDesiredAccess == GENERIC_READ))
  242.         b = 0x00;                                           /* OPEN_ACCESS_READONLY */
  243.     else if (dwDesiredAccess == GENERIC_WRITE)
  244.         b = 0x01;                                           /* OPEN_ACCESS_WRITEONLY */
  245.     else if (dwDesiredAccess == GENERIC_READ|GENERIC_WRITE)
  246.         b = 0x02;                                           /* OPEN_ACCESS_READWRITE */
  247.  
  248.     if (dwShareMode == 0)
  249.         b |= 0x10;                                          /* OPEN_SHARE_DENYREADWRITE */
  250.     else if (dwShareMode == FILE_SHARE_READ)
  251.         b |= 0x20;                                          /* OPEN_SHARE_DENYWRITE */
  252.     else if (dwShareMode == FILE_SHARE_WRITE)
  253.         b |= 0x30;                                          /* OPEN_SHARE_DENYREAD */
  254.     else if (dwShareMode == FILE_SHARE_READ|FILE_SHARE_WRITE)
  255.     {
  256.         /*  We use OPEN_SHARE_DENYNONE because an */
  257.         /*  attempt to open the file in compatability mode fails if the file */
  258.         /*  had been opened in any other sharing mode.  Also if the file */
  259.         /*  has been opened in compatability mode attempting to open the  */
  260.         /*  file in any of the sharing modes will fail. */
  261.         
  262.         b |= 0x40;                                          /* OPEN_SHARE_DENYNONE */
  263.     }
  264.  
  265.     if (dwCreationDisposition == CREATE_ALWAYS)
  266.         goto doCreate;
  267.  
  268.     _asm
  269.     {
  270.         push ds
  271.  
  272.         mov dx, segPath             ; file name
  273.         mov ds, dx
  274.         mov dx, offPath
  275.         mov al, b                   ; access mode
  276.         mov ah, 3dh                 ; Open File
  277.         call DOS3Call
  278.         pop ds
  279.         jc cfErr10
  280.  
  281.         mov hf, ax                  ; file handle
  282.  
  283. cfErr10:
  284.     }
  285.  
  286.     if (dwCreationDisposition == CREATE_NEW)
  287.     {
  288.         if (hf == HFILE_ERROR)
  289.             goto doCreate;
  290.         else
  291.         {
  292.             CloseHandle(hf);
  293.             hf = HFILE_ERROR;
  294.             goto ret;
  295.         }
  296.     }
  297.     /*  If caller only wanted to open an existing file, give up. */
  298.     /*  OPEN_EXISTING doesn't need any explicit code */
  299.     else if (dwCreationDisposition == OPEN_ALWAYS)
  300.     {
  301.         if (hf == HFILE_ERROR)
  302.             goto doCreate;
  303.         else
  304.             goto ret;
  305.     }
  306.     else if (dwCreationDisposition == OPEN_EXISTING)
  307.     {
  308.             goto ret;
  309.     }
  310.  
  311.     /*  Set file size to 0 if requested. */
  312.     if (hf != HFILE_ERROR && dwCreationDisposition == TRUNCATE_EXISTING)
  313.     {
  314.         _asm
  315.         {
  316.             mov bx, hf
  317.             xor dx, dx              ; seek offset 0L
  318.             xor cx, cx
  319.             mov ax, 4200h           ; beginning of file
  320.                                     ; Move File Pointer
  321.             call DOS3Call
  322.             jc cfErr20
  323.  
  324.             push ds
  325.  
  326.             mov bx, hf
  327.             xor cx, cx              ; write 0 bytes
  328.             mov dx, segPath
  329.             mov ds, dx
  330.             mov dx, offPath
  331.             mov ah, 40h             ; Write File
  332.             call DOS3Call
  333.             pop ds
  334.             jnc cf30
  335.  
  336. cfErr20:
  337.             mov bx, hf
  338.             mov ah, 3eh             ; Close File
  339.             call DOS3Call
  340.             mov hf, -1
  341.  
  342. cf30:
  343.         }
  344.     }
  345.     goto ret;
  346.  
  347.     /*  Try to create a new file. */
  348.  
  349. doCreate:
  350.     /*  Get the file attributes */
  351.     if ((w = (WORD)dwFlagsAndAttributes) == FILE_ATTRIBUTE_NORMAL)
  352.         w = 0;
  353.     else if (w & FILE_ATTRIBUTE_NORMAL)
  354.         w &= ~FILE_ATTRIBUTE_NORMAL;
  355.  
  356.     _asm
  357.     {
  358.         push ds
  359.  
  360.         mov dx, segPath             ; path name
  361.         mov ds, dx
  362.         mov dx, offPath
  363.         mov cx, w                   ; file attributes
  364.         mov ah, 3ch                 ; Create File
  365.         call DOS3Call
  366.         pop ds
  367.         jc cfErr40
  368.  
  369.         mov hf, ax
  370.         jmp cf50
  371.  
  372. cfErr40:
  373.         mov hf, -1
  374. cf50:
  375.     }
  376.  
  377.     /*  When a file is created via 3Ch.  The file is */
  378.     /*  "opened" with read/write share compatibility. */
  379.     /*  If any other mode was requested, we have to */
  380.     /*  close the file and re-open it. */
  381.     
  382.     if ((hf != -1) &&
  383.         !(dwShareMode == (FILE_SHARE_WRITE | FILE_SHARE_READ)))
  384.     {
  385.         CloseHandle(hf);
  386.         hf = -1;
  387.         _asm
  388.         {
  389.             push ds
  390.             mov dx, segPath             ; file name
  391.             mov ds, dx
  392.             mov dx, offPath
  393.             mov al, b                   ; access mode
  394.             mov ah, 3dh                 ; Open File
  395.             call DOS3Call
  396.             pop ds
  397.             jc cfErr50
  398.             mov hf, ax                  ; file handle
  399. cfErr50:
  400.         }
  401.         if (hf == -1)
  402.             DeleteFile (lpFileName);
  403.     }
  404.  
  405. ret:
  406. #ifdef  DEBUG
  407.     if (hf == -1)
  408.         DebugTraceDos(CreateFile);
  409. #endif
  410.     return (HANDLE)hf;
  411. }
  412.  
  413. /* OpenFile() is the same in both Win16 and Win32. */
  414.  
  415. /*
  416.  *  ReadFile
  417.  *
  418.  *  Limitations
  419.  *      Count is limited to 64K.
  420.  *      lpOverlapped is not supported, fails if not NULL.
  421.  */
  422. BOOL __export WINAPI
  423. ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
  424.     LPDWORD lpNumberOfBytesRead, LPVOID lpOverlapped)
  425. {
  426.     WORD    w;
  427.     WORD    segBuf;
  428.     WORD    offBuf;
  429.     BOOL    f;
  430.     BYTE __huge *pb;
  431.     BYTE __far *pbT;
  432.  
  433.     if (lpOverlapped)
  434.     {
  435.         DebugTraceArg(ReadFile, "lpOverlapped is unsupported");
  436.         return FALSE;
  437.     }
  438.  
  439.     pb = (BYTE __huge *) lpBuffer;
  440.  
  441. #ifdef  DEBUG
  442.     Assert(!IsBadHugeWritePtr(pb, nNumberOfBytesToRead));
  443.     Assert(!IsBadWritePtr(lpNumberOfBytesRead, sizeof(DWORD)));
  444.  
  445.     if (nNumberOfBytesToRead > 0x10000)
  446.         DebugTrace("ReadFile: huge write 0x%lx\n", nNumberOfBytesToRead);
  447. #endif
  448.  
  449.     *lpNumberOfBytesRead = 0L;
  450.  
  451.     while (nNumberOfBytesToRead)
  452.     {
  453.         w = (WORD) min(nNumberOfBytesToRead, 0xffff);
  454.         pbT = (BYTE __far *) pb;
  455.         segBuf = SELECTOROF(pbT);
  456.         offBuf = OFFSETOF(pbT);
  457.         f = TRUE;
  458.  
  459.         _asm
  460.         {
  461.             push ds
  462.  
  463.             mov bx, hFile
  464.             mov cx, w                   ; read count
  465.             mov dx, segBuf              ; buffer address
  466.             mov ds, dx
  467.             mov dx, offBuf
  468.             mov ah, 3fh                 ; Read File
  469.             call DOS3Call
  470.             pop ds
  471.             jc rfErr10
  472.  
  473.             mov w, ax                   ; returned byte count
  474.             jmp rf20
  475.  
  476. rfErr10:
  477.             mov f, FALSE
  478.  
  479. rf20:
  480.         }
  481.  
  482.         if (f)
  483.         {
  484.             *lpNumberOfBytesRead += (DWORD)w;
  485.             if (w != (WORD) min(nNumberOfBytesToRead, 0xffff))
  486.             {
  487.                 /* short read, assume we're done */
  488.                 break;
  489.             }
  490.  
  491.             pb += w;
  492.             nNumberOfBytesToRead -= w;
  493.         }
  494.         else
  495.         {
  496. #ifdef  DEBUG
  497.             DebugTraceDos(ReadFile);
  498. #endif
  499.             break;
  500.         }
  501.     }
  502.  
  503.     return f;
  504. }
  505.  
  506. /*
  507.  *  WriteFile
  508.  *
  509.  *  Limitations
  510.  *      lpOverlapped is not supported, fails if not NULL.
  511.  */
  512. BOOL __export WINAPI
  513. WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
  514.     LPDWORD lpNumberOfBytesWritten, LPVOID lpOverlapped)
  515. {
  516.     WORD    w;
  517.     WORD    segBuf;
  518.     WORD    offBuf;
  519.     BOOL    f;
  520.     BYTE __huge *pb;
  521.     BYTE far *pbT;
  522.  
  523.     if (lpOverlapped)
  524.     {
  525.         DebugTraceArg(WriteFile, "lpOverlapped is unsupported");
  526.         return FALSE;
  527.     }
  528.  
  529.     pb = (BYTE __huge *) lpBuffer;
  530.  
  531. #ifdef  DEBUG
  532.     Assert(!IsBadHugeReadPtr(pb, nNumberOfBytesToWrite));
  533.     Assert(!IsBadWritePtr(lpNumberOfBytesWritten, sizeof(DWORD)));
  534.  
  535.     if (nNumberOfBytesToWrite > 0x10000)
  536.         DebugTrace("WriteFile: huge write 0x%lx\n", nNumberOfBytesToWrite);
  537. #endif
  538.  
  539.     *lpNumberOfBytesWritten = 0L;
  540.  
  541.     while (nNumberOfBytesToWrite)
  542.     {
  543.         w = (WORD) min(nNumberOfBytesToWrite, 0xffff);
  544.         pbT = (void far *) pb;
  545.         segBuf = SELECTOROF(pbT);
  546.         offBuf = OFFSETOF(pbT);
  547.         f = TRUE;
  548.  
  549.         _asm
  550.         {
  551.             push ds
  552.  
  553.             mov bx, hFile
  554.             mov cx, w                   ; byte count
  555.             mov dx, segBuf              ; buffer address
  556.             mov ds, dx
  557.             mov dx, offBuf
  558.             mov ah, 40h                 ; Write File
  559.             call DOS3Call
  560.             pop ds
  561.             jc wfErr10
  562.  
  563.             mov w, ax                   ; returned byte count
  564.             jmp wf20
  565.  
  566. wfErr10:
  567.             mov f, FALSE
  568.  
  569. wf20:
  570.         }
  571.  
  572.         if (f)
  573.         {
  574.             *lpNumberOfBytesWritten += (DWORD)w;
  575.             if (w != (WORD) min(nNumberOfBytesToWrite, 0xffff))
  576.             {
  577.                 /* We got a short write, bail. */
  578.                 break;
  579.             }
  580.             pb += w;
  581.             nNumberOfBytesToWrite -= w;
  582.         }
  583.         else
  584.         {
  585. #ifdef  DEBUG
  586.             DebugTraceDos(WriteFile);
  587. #endif
  588.             break;
  589.         }
  590.     }
  591.  
  592.     return f;
  593. }
  594.  
  595. /*
  596.  *  SetFilePointer
  597.  *
  598.  *  Limitations
  599.  *      Distance is limited to 2GB (signed 32-bits). Fails if
  600.  *      lpDistanceToMoveHigh is present and nonzero (unless it's the
  601.  *      sign extension of a negative distance).
  602.  */
  603. DWORD __export WINAPI
  604. SetFilePointer(HANDLE hFile, LONG lDistanceToMove, LONG FAR *lpDistanceToMoveHigh,
  605.     DWORD dwMoveMethod)
  606. {
  607.     WORD    w;
  608.     WORD    x;
  609.     BYTE    b;
  610.  
  611.     if (lpDistanceToMoveHigh && *lpDistanceToMoveHigh)
  612.     {
  613.         if ((*lpDistanceToMoveHigh != 0xFFFFFFFF) ||
  614.             !(lDistanceToMove & 0x80000000))
  615.         {
  616.             DebugTraceArg(SetFilePointer, "seeks > 4 gig are not supported");
  617.             return 0xFFFFFFFF;
  618.         }
  619.     }
  620.  
  621.     if (dwMoveMethod > 2)
  622.     {
  623.         DebugTraceArg(SetFilePointer, "dwMoveMethod unknown");
  624.         return 0xFFFFFFFF;
  625.     }
  626.  
  627.     w = LOWORD(lDistanceToMove);
  628.     x = HIWORD(lDistanceToMove);
  629.     b = (BYTE)dwMoveMethod;
  630.  
  631.     _asm
  632.     {
  633.         mov bx, hFile
  634.         mov dx, w                   ; seek offset
  635.         mov cx, x
  636.         mov al, b                   ; seek origin
  637.         mov ah, 42h                 ; Move File Pointer
  638.         call DOS3Call
  639.         jc sfpErr10
  640.  
  641.         mov w, ax
  642.         mov x, dx
  643.         jmp sfp20
  644.  
  645. sfpErr10:
  646.         xor ax, ax
  647.         dec ax
  648.         mov w, ax
  649.         mov x, ax
  650.  
  651. sfp20:
  652.     }
  653.  
  654. #ifdef  DEBUG
  655.     if ((w | ((DWORD)x << 16)) ==  0xFFFFFFFF)
  656.         DebugTraceDos(SetFilePointer);
  657. #endif
  658.     return w | ((DWORD)x << 16);
  659. }
  660.  
  661. /*
  662.  *  SetEndOfFile
  663.  *
  664.  *  Limitations
  665.  *      Certain SMB file servers, particularly UNIX-based ones, don't
  666.  *      recognize this DOS idiom as a rqeuest to set the end of
  667.  *      file mark.
  668.  */
  669. BOOL __export WINAPI
  670. SetEndOfFile(HANDLE hFile)
  671. {
  672.     BYTE bBuf;
  673.     WORD w = SELECTOROF(&bBuf);
  674.     WORD x = OFFSETOF(&bBuf);
  675.     BOOL f = TRUE;
  676.  
  677.     _asm
  678.     {
  679.         push ds                     ;
  680.  
  681.         mov bx, hFile               ;
  682.         mov dx, w                   ; buffer address
  683.         mov ds, dx
  684.         mov dx, x
  685.         xor cx, cx                  ; byte count = 0: set EOF
  686.         mov ah, 40h                 ; Write File
  687.         call DOS3Call
  688.         pop ds
  689.         jnc seof20
  690.  
  691.         mov f, FALSE                ; report error
  692.  
  693. seof20:
  694.     }
  695.  
  696. #ifdef  DEBUG
  697.     if (!f)
  698.         DebugTraceDos(SetEndOfFile);
  699. #endif
  700.     return f;
  701. }
  702.  
  703. /*
  704.  *  CloseHandle
  705.  *
  706.  *  Limitations
  707.  *      Don't expect it to close anything other than file handles.
  708.  */
  709. BOOL __export WINAPI
  710. CloseHandle(HANDLE hObject)
  711. {
  712.     BOOL    f = TRUE;
  713.  
  714.     _asm
  715.     {
  716.         mov bx, hObject             ; file handle
  717.         mov ah, 3eh                 ; Close File
  718.         call DOS3Call
  719.         jnc ch10
  720.  
  721.         mov f, FALSE                ; failed
  722. ch10:
  723.     }
  724.  
  725. #ifdef  DEBUG
  726.     if (!f)
  727.         DebugTraceDos(CloseHandle);
  728. #endif
  729.     return f;
  730. }
  731.  
  732. /* //$  REVIEW not sure whether GetDOSEnvironment returns OEM or ANSI */
  733. /* //$  charset. Assuming OEM. */
  734. STDAPI_(LPSTR)
  735. SzGetEnv(LPSTR szName)
  736. {
  737.     LPSTR   sz;
  738.     LPSTR   szDst;
  739.     CHAR    rgch[128];
  740.  
  741.     Assert(!IsBadStringPtr(szName, 128));
  742.  
  743.     if (!(sz = GetDOSEnvironment()))
  744.     {
  745.         DebugTrace("SzGetEnv: GetDOSEnvironment() failed\n");
  746.         return NULL;
  747.     }
  748.  
  749.     while (*sz)
  750.     {
  751.         szDst = rgch;
  752. #ifdef DBCS
  753.         while (*sz && (!FGLeadByte(*sz) && *sz != '='))
  754.         {
  755.             if(FGLeadByte(*sz))
  756.                 *szDst++ = *sz++;
  757.             *szDst++ = *sz++;
  758.         }
  759. #else
  760.         while (*sz && *sz != '=')
  761.             *szDst++ = *sz++;
  762. #endif
  763.         if (*sz == '=')
  764.             *szDst++ = 0;
  765.         OemToAnsi(rgch, rgch);      /* to match the argument */
  766.  
  767.         if (!lstrcmpi(szName, rgch))
  768.             return sz + 1;
  769.  
  770.         sz += lstrlen(sz) + 1;
  771.     }
  772.  
  773.     return NULL;
  774. }
  775.  
  776. /*
  777.  *  GetTempPath
  778.  *
  779.  *  Limitations
  780.  *      Uses the environment, not anything intrinsic to the system.
  781.  *      I mean, basically, we're guessing.
  782.  *      Buffer length limited to 64K (yawn).
  783.  */
  784. DWORD __export WINAPI
  785. GetTempPath(DWORD nBufferLength, LPSTR lpBuffer)
  786. {
  787.     char *sz;
  788.     
  789.     AssertSz( lpBuffer && !IsBadWritePtr( lpBuffer, (UINT)nBufferLength ),
  790.             "lpBuffer fails address check" );
  791.  
  792.     if (sz = SzGetEnv("TEMP"))
  793.         lstrcpy(lpBuffer, sz);
  794.     else if (sz = SzGetEnv("TMP"))
  795.         lstrcpy(lpBuffer, sz);
  796.     else
  797.         lstrcpy(lpBuffer, "C:\\");
  798.  
  799.     //  Guarantee that the path has a trailing backslash
  800.     sz = SzFindLastCh(lpBuffer, '\\');
  801.     if (!sz || *++sz != 0)              //  DBCS OK
  802.         lstrcat(lpBuffer, "\\");
  803.  
  804.     OemToAnsi(lpBuffer, lpBuffer);
  805.     return (DWORD)lstrlen(lpBuffer);
  806. }
  807.  
  808.  
  809. /*
  810.  *  GetTempFileName32
  811.  *
  812.  *  Limitations:
  813.  *      Not implemented.
  814.  */
  815. UINT __export WINAPI
  816. GetTempFileName32 (LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUnique,
  817.     LPSTR lpTempFileName)
  818. {
  819.     int ich;
  820.     UINT nExt;
  821.     UINT nStop = 0;
  822.     OFSTRUCT ofs;
  823.     char rgch[MAX_PATH];
  824.  
  825.     HANDLE hf = NULL;
  826.  
  827.     if (lpPathName == NULL)
  828.         return FALSE;
  829.         
  830.     AssertSz( !IsBadStringPtr( lpPathName, MAX_PATH ),
  831.             "lpPathName fails address check" );
  832.  
  833.     //  The prefix string is optionally NULL
  834.     //
  835.     AssertSz( !lpPrefixString || !IsBadStringPtr( lpPrefixString, MAX_PATH ),
  836.             "lpPrefixName fails address check" );
  837.         
  838.     AssertSz( !IsBadWritePtr( lpTempFileName, MAX_PATH ),
  839.             "lpTempFileName fails address check" );
  840.  
  841.     ich = lstrlen (lpPathName);
  842.  
  843.     if (!uUnique)
  844.     {
  845.         nExt = (UINT)((WORD)GetTickCount ());
  846.         nStop = nExt - 1;
  847.     }
  848.     else
  849.         nExt = uUnique;
  850.  
  851.     for (; nExt != nStop; nExt++)
  852.     {
  853. #ifdef DBCS
  854.         if ((*SzGPrev(lpPathName, lpPathName + ich) != '\\') &&
  855.             (*SzGPrev(lpPathName, lpPathName + ich) != ':' ))
  856. #else
  857.         if ((lpPathName[ich - 1] != '\\') && (lpPathName[ich - 1] != ':'))
  858. #endif
  859.             wsprintf (rgch, "%s\\%.3s%04X.TMP", lpPathName, lpPrefixString, nExt);
  860.         else
  861.             wsprintf (rgch, "%s%.3s%04X.TMP", lpPathName, lpPrefixString, nExt);
  862.  
  863.         hf = OpenFile (rgch, &ofs, OF_EXIST);
  864.         if ((hf == HFILE_ERROR) || uUnique)
  865.             break;
  866.     }
  867.  
  868.     if (((hf == HFILE_ERROR) && (ofs.nErrCode == 0x0002)) || uUnique)
  869.     {
  870.         lstrcpy (lpTempFileName, rgch);
  871.         return nExt;
  872.     }
  873.  
  874.     return 0;
  875. }
  876.  
  877.  
  878. /*
  879.  *  DeleteFile
  880.  */
  881. BOOL __export WINAPI
  882. DeleteFile(LPCSTR lpFileName)
  883. {
  884.     char    szPath[MAX_PATH];
  885.     WORD    segPath;
  886.     WORD    offPath;
  887.     BOOL    f = TRUE;
  888.  
  889.     Assert(!IsBadStringPtr(lpFileName, MAX_PATH));
  890.     AnsiToOem(lpFileName, szPath);
  891.  
  892.     segPath = SELECTOROF(szPath);
  893.     offPath = OFFSETOF(szPath);
  894.  
  895.     _asm
  896.     {
  897.         push ds
  898.  
  899.         mov dx, segPath             ; path name
  900.         mov ds, dx
  901.         mov dx, offPath
  902.         mov ah, 41h                 ; Delete File
  903.         call DOS3Call
  904.         pop ds
  905.         jnc df10
  906.  
  907.         mov f, FALSE                ; report error
  908. df10:
  909.     }
  910.  
  911. #ifdef  DEBUG
  912.     if (!f)
  913.         DebugTraceDos(DeleteFile);
  914. #endif
  915.     return f;
  916. }
  917.  
  918. /*
  919.  *  CreateDirectory
  920.  *
  921.  *  Limitations:
  922.  *      lpSecurityAttribtues not supported, fails if not NULL.
  923.  */
  924. BOOL __export WINAPI
  925. CreateDirectory(LPCSTR lpPathName, LPVOID lpSecurityAttributes)
  926. {
  927.     char    szPath[MAX_PATH];
  928.     WORD    segPath = SELECTOROF(lpPathName);
  929.     WORD    offPath = OFFSETOF(lpPathName);
  930.     BOOL    f = TRUE;
  931.  
  932.     if (lpSecurityAttributes)
  933.     {
  934.         DebugTraceArg(CreateDirectory, "lpSecurityAttributes is unsupported");
  935.         return FALSE;
  936.     }
  937.  
  938.     Assert(!IsBadStringPtr(lpPathName, MAX_PATH));
  939.     AnsiToOem(lpPathName, szPath);
  940.  
  941.     segPath = SELECTOROF(szPath);
  942.     offPath = OFFSETOF(szPath);
  943.  
  944.     _asm
  945.     {
  946.         push ds
  947.  
  948.         mov dx, segPath             ; path name
  949.         mov ds, dx
  950.         mov dx, offPath
  951.         mov ah, 39h                 ; Create Directory
  952.         call DOS3Call
  953.         pop ds
  954.         jnc cd10
  955.  
  956.         mov f, FALSE                ; report error
  957. cd10:
  958.     }
  959.  
  960. #ifdef  DEBUG
  961.     if (!f)
  962.         DebugTraceDos(CreateDirectory);
  963. #endif
  964.     return f;
  965. }
  966.  
  967. /*
  968.  *  RemoveDirectory
  969.  */
  970. BOOL __export WINAPI
  971. RemoveDirectory(LPCSTR lpPathName)
  972. {
  973.     char    szPath[MAX_PATH];
  974.     WORD    segPath;
  975.     WORD    offPath;
  976.     BOOL    f = TRUE;
  977.  
  978.     Assert(!IsBadStringPtr(lpPathName, MAX_PATH));
  979.     AnsiToOem(lpPathName, szPath);
  980.  
  981.     segPath = SELECTOROF(szPath);
  982.     offPath = OFFSETOF(szPath);
  983.  
  984.     _asm
  985.     {
  986.         push ds
  987.  
  988.         mov dx, segPath             ; path name
  989.         mov ds, dx
  990.         mov dx, offPath
  991.         mov ah, 3ah                 ; Remove Directory
  992.         call DOS3Call
  993.         pop ds
  994.         jnc rd10
  995.  
  996.         mov f, FALSE                ; report error
  997. rd10:
  998.     }
  999.  
  1000. #ifdef  DEBUG
  1001.     if (!f)
  1002.         DebugTraceDos(RemoveDirectory);
  1003. #endif
  1004.     return f;
  1005. }
  1006.  
  1007. /*
  1008.  *  GetFullPathName
  1009.  *
  1010.  *  input: lpFileName, a relative path
  1011.  *  output: lpBuffer contains the matching absolute path, including
  1012.  *  the drive; *lpFilePart points to the last node of the path in lpBuffer.
  1013.  *
  1014.  *  Returns the length of the string in lpBuffer. If the buffer is too small,
  1015.  *  returns the required size, and null string in lpBuffer.
  1016.  *
  1017.  *  Limitations
  1018.  *      Does not handle ".." path components in lpFileName.
  1019.  */
  1020. DWORD __export WINAPI
  1021. GetFullPathName(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer,
  1022.     LPSTR *lpFilePart)
  1023. {
  1024.     char        chDrive;
  1025.     char        szCwd[MAX_PATH];
  1026.     char        szSlash[2];
  1027.     char        szFile[MAX_PATH];
  1028.     WORD        w;
  1029.     LPSTR       sz = (LPSTR)szFile;
  1030.     LPSTR       szLast;
  1031.     WORD        segCwd;
  1032.     WORD        offCwd;
  1033.  
  1034.     if (IsBadWritePtr(lpBuffer, (UINT) nBufferLength))
  1035.     {
  1036.         DebugTraceArg(GetFullPathName, "lpBuffer fails address check");
  1037.         return 0L;
  1038.     }
  1039.     Assert(!IsBadStringPtr(lpFileName, MAX_PATH));
  1040.     AnsiToOem(lpFileName, szFile);
  1041.  
  1042.     *lpBuffer = 0;
  1043.  
  1044.     /* Get drive */
  1045.     if (lstrlen(sz) > 2 && sz[1] == ':')
  1046.     {
  1047.         chDrive = *sz;
  1048.         w = (WORD)(chDrive - 'A');
  1049.         sz += 2;
  1050.     }
  1051.     else
  1052.     {
  1053.         _asm
  1054.         {
  1055.             mov ah, 19h             ; Get Default Drive
  1056.             call DOS3Call
  1057.                                     ; no failure report
  1058.             mov w, ax
  1059.         }
  1060.         chDrive = (char)((w & 0xFF) + 'A');
  1061.     }
  1062.  
  1063.     /* Get directory */
  1064.     *szCwd = 0;
  1065.     szSlash[0] = szSlash[1] = 0;
  1066.     w = 0;                          /*  assume failure */
  1067.     if (*sz != '\\')
  1068.     {
  1069.         segCwd = SELECTOROF((LPSTR)&szCwd);
  1070.         offCwd = OFFSETOF((LPSTR)&szCwd);
  1071.         _asm
  1072.         {
  1073.             push ds
  1074.             push si
  1075.  
  1076.             mov si, segCwd          ; buffer for current directory
  1077.             mov ds, si
  1078.             mov si, offCwd
  1079.             mov dl, chDrive         ; drive ('A' - 'Z')
  1080.             sub dl, 41h             ; subtract off asciii 'A'
  1081.             inc dl                  ; zero means default drive, so add one
  1082.  
  1083.             mov ah, 47h             ; Get Current Directory
  1084.             call DOS3Call
  1085.             pop si
  1086.             pop ds
  1087.             jc gfpnErr10
  1088.  
  1089.             mov w, 1                ; signal success
  1090.  
  1091. gfpnErr10:
  1092.         }
  1093.  
  1094.         if (!w)
  1095.         {
  1096.             DebugTraceDos(GetFullPathName);
  1097.             goto ret;
  1098.         }
  1099.  
  1100.         if (lstrlen(szCwd) > 1)
  1101.             lstrcat(szCwd, "\\");
  1102.         if (*szCwd && *szCwd != '\\')
  1103.             szSlash[0] = '\\';
  1104.     }
  1105.  
  1106.     /* Build output path */
  1107.     w = (WORD)(3 + lstrlen(szCwd) + lstrlen(sz) + 1);
  1108.     if ((DWORD) w > nBufferLength)
  1109.         return w;
  1110.  
  1111.     wsprintf(lpBuffer, "%c:%s%s%s", chDrive, szSlash, szCwd, sz);
  1112.     OemToAnsi(lpBuffer, lpBuffer);
  1113.  
  1114.     /* point to filename component */
  1115.     szLast = lpBuffer + 2;
  1116. #ifdef DBCS
  1117.     for (sz = szLast; *sz; sz = SzGNext(sz))
  1118.         if (!FGLeadByte(*sz) && *sz == '\\')
  1119.             szLast = sz + 1;
  1120. #else
  1121.     for (sz = szLast; *sz; ++sz)
  1122.         if (*sz == '\\')
  1123.             szLast = sz + 1;
  1124. #endif
  1125.  
  1126.     *lpFilePart = szLast;
  1127.  
  1128. ret:
  1129.     return lstrlen(lpBuffer);
  1130. }
  1131.  
  1132. /*
  1133.  *  MoveFile
  1134.  *
  1135.  *  Limitations
  1136.  *      Won't move a directory. Will always work across drives
  1137.  *      //$ SPEED It always does a copy/delete.
  1138.  */
  1139. BOOL __export WINAPI
  1140. MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
  1141. {
  1142.     Assert(!IsBadStringPtr(lpExistingFileName, MAX_PATH));
  1143.     Assert(!IsBadStringPtr(lpNewFileName, MAX_PATH));
  1144.  
  1145.     /*  Note: no OEM conversion, because the subroutines do it. */
  1146.  
  1147.     if (!CopyFile(lpExistingFileName, lpNewFileName, TRUE))
  1148.     {
  1149.         DebugTraceDos(MoveFile);
  1150.         return FALSE;
  1151.     }
  1152.  
  1153.     if (!DeleteFile(lpExistingFileName))
  1154.     {
  1155.         DebugTraceDos(MoveFile);
  1156.         (void)DeleteFile(lpNewFileName);
  1157.         return FALSE;
  1158.     }
  1159.  
  1160.     return TRUE;
  1161. }
  1162.  
  1163.  
  1164. /*
  1165.  *  CopyFile
  1166.  *
  1167.  *  Limitations
  1168.  */
  1169. BOOL __export WINAPI
  1170. CopyFile(LPCSTR szSrc, LPCSTR szDst, BOOL fFailIfExists)
  1171. {
  1172.     HANDLE  hfSrc = INVALID_HANDLE_VALUE;
  1173.     HANDLE  hfDst = INVALID_HANDLE_VALUE;
  1174.     BYTE    rgb[512];
  1175.     DWORD   dwRead;
  1176.     DWORD   dwWritten;
  1177.     OFSTRUCT of;
  1178.     BOOL    f = TRUE;
  1179.  
  1180.     Assert(!IsBadStringPtr(szSrc, MAX_PATH));
  1181.     Assert(!IsBadStringPtr(szDst, MAX_PATH));
  1182.  
  1183.     /*  Note: no OEM conversion, Open/CreateFile do that */
  1184.  
  1185.     if ((hfSrc = OpenFile(szSrc, &of, OF_READ)) == INVALID_HANDLE_VALUE ||
  1186.         (hfDst = CreateFile(szDst, GENERIC_READ | GENERIC_WRITE,
  1187.             0, NULL, fFailIfExists ? CREATE_NEW : CREATE_ALWAYS, 0, 0))
  1188.                 == INVALID_HANDLE_VALUE)
  1189.         goto fail;
  1190.     for (;;)
  1191.     {
  1192.         if (!ReadFile(hfSrc, rgb, sizeof(rgb), &dwRead, NULL))
  1193.             goto fail;
  1194.         if (!dwRead)
  1195.             break;          /*  finished */
  1196.  
  1197.         if (!WriteFile(hfDst, rgb, dwRead, &dwWritten, NULL))
  1198.             goto fail;
  1199.     }
  1200.  
  1201. ret:
  1202.     if (hfSrc != INVALID_HANDLE_VALUE)
  1203.         CloseHandle(hfSrc);
  1204.     if (hfDst != INVALID_HANDLE_VALUE)
  1205.         CloseHandle(hfDst);
  1206.     return f;
  1207.  
  1208. fail:
  1209.     f = FALSE;
  1210.     DebugTraceDos(CopyFile);
  1211.     goto ret;
  1212. }
  1213.  
  1214. /*
  1215.  *  GetCurrentProcessId
  1216.  */
  1217. DWORD __export WINAPI
  1218. GetCurrentProcessId(void)
  1219. {
  1220.     return (DWORD)GetCurrentTask();
  1221. }
  1222.  
  1223. /*
  1224.  *  MulDiv32
  1225.  *
  1226.  *  Supports the MULDIV macro of mapiwin.h. Takes 32-bit arguments,
  1227.  *  unlike the native Win16 MulDiv.
  1228.  *
  1229.  *  Limitations
  1230.  *      Does not check for overflow on the multiply.
  1231.  */
  1232. long __export WINAPI
  1233. MulDiv32(long nMultiplicand, long nMultiplier, long nDivisor)
  1234. {
  1235.     FILETIME    ftProd;
  1236.     long        lQuot;
  1237.     long        lSign = 1;
  1238.  
  1239.  
  1240.     if (!nDivisor)
  1241.         return -1L;
  1242.  
  1243.     //  We want to use the math64.c stuff so make everything posistive
  1244.     //  and keep track of the sign of the result
  1245.     if (nDivisor < 0)
  1246.     {
  1247.         nDivisor = -nDivisor;
  1248.         lSign = -1;
  1249.     }
  1250.  
  1251.     if (!nMultiplier || !nMultiplicand)
  1252.         return 0L;
  1253.  
  1254.     //  Easy out for 16 bit case
  1255.     if (   (-((LONG) 0x8000) < nMultiplier && nMultiplier < (LONG) 0x7fff)
  1256.         && (-((LONG) 0x8000) < nMultiplicand && nMultiplicand < (LONG) 0x7fff))
  1257.     {
  1258.         return ((nMultiplier * nMultiplicand) / nDivisor);
  1259.     }
  1260.  
  1261.     //  We want to use the math64.c stuff so make everything posistive
  1262.     //  and keep track of the sign of the result
  1263.     if (nMultiplier < 0)
  1264.     {
  1265.         nMultiplier = -nMultiplier;
  1266.         lSign = -lSign;
  1267.     }
  1268.  
  1269.     if (nMultiplicand < 0)
  1270.     {
  1271.         nMultiplicand = -nMultiplicand;
  1272.         lSign = -lSign;
  1273.     }
  1274.  
  1275.     ftProd = FtMulDwDw( (DWORD) nMultiplicand, (DWORD) nMultiplier);
  1276.  
  1277.     lQuot = (long) DwDivFtDw( ftProd, (DWORD) nDivisor);
  1278.  
  1279.     if ((lQuot != -1) && (lSign == -1))
  1280.         lQuot = -lQuot;
  1281.  
  1282.  
  1283.     return lQuot;
  1284. }
  1285.  
  1286.  
  1287. /*
  1288.  *  FBadReadPtr
  1289.  *
  1290.  *  Functions like IsBadReadPtr, but returns FALSE if cbBytes is 0
  1291.  *  regardless of the value of lpvPtr.
  1292.  */
  1293. #undef  IsBadReadPtr
  1294. BOOL WINAPI
  1295. FBadReadPtr(const void FAR* lpvPtr, UINT cbBytes)
  1296. {
  1297.     return cbBytes > 0 && IsBadReadPtr(lpvPtr, cbBytes);
  1298. }
  1299. #define IsBadReadPtr    FBadReadPtr
  1300.  
  1301.  
  1302. /*
  1303.  *  Sleep
  1304.  *
  1305.  *  Limitations
  1306.  *      alt-TAB, alt-ESC for task switch not handled
  1307.  */
  1308.  
  1309. #if 1   /*  THIS IS THE WORKING CODE */
  1310.  
  1311. void __export WINAPI
  1312. Sleep(DWORD iMilisec)
  1313. {
  1314.     DWORD dwNow = GetCurrentTime();
  1315.     MSG msg;
  1316.  
  1317.     while ((GetCurrentTime() - dwNow) < iMilisec)
  1318.     {
  1319.         if (PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_NOREMOVE))
  1320.         {
  1321. /*          if (msg.message == WM_QUIT || msg.message == WM_CLOSE) */
  1322. /*              return; */
  1323.  
  1324.             if (msg.message == WM_PAINT)
  1325.             {
  1326.                 GetMessage(&msg, NULL, WM_PAINT, WM_PAINT);
  1327.                 TranslateMessage(&msg);
  1328.                 DispatchMessage(&msg);
  1329.             }
  1330.         }
  1331.     }
  1332.     return;
  1333. }
  1334.  
  1335. #else   /*  THIS IS THE OLD BROKEN CODE WITH "FOOTPRINTS" IN IT */
  1336.  
  1337. void __export WINAPI
  1338. Sleep(DWORD iMilisec)
  1339. {
  1340.     DWORD dwNow = GetCurrentTime();
  1341.     MSG msg;
  1342.  
  1343.     while ((GetCurrentTime() - dwNow) < iMilisec)
  1344.     {
  1345.         if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
  1346.         {
  1347.             if (msg.message == WM_QUIT || msg.message == WM_CLOSE)
  1348. {
  1349. DebugTrace("Sleep: caught 0x%x, exiting\n", msg.message);
  1350.                 return;
  1351. }
  1352.  
  1353.             GetMessage(&msg, NULL, 0, 0);
  1354.             TranslateMessage(&msg);
  1355.             if (msg.message == WM_PAINT)
  1356.                 DispatchMessage(&msg);
  1357. else
  1358. {
  1359. DebugTrace("Sleep: DISCARDING 0x%x !!!\n", msg.message);
  1360. }
  1361.         }
  1362.     }
  1363.     return;
  1364. }
  1365.  
  1366. #endif
  1367.  
  1368. #pragma warning(disable:4035)       /*  no return value */
  1369.  
  1370. /*
  1371.  *  DosGetConnection
  1372.  *  
  1373.  *  This is the non-WfW equivalent to WNetGetoOnnection.
  1374.  *  It asks the redirector to return the mapping for the drive
  1375.  *  indicated by szLocalPath.
  1376.  *  
  1377.  *  FOr internal use only - must change type to export it!
  1378.  */
  1379. UINT
  1380. DosGetConnection(LPSTR szLocal, LPSTR szRemote)
  1381. {
  1382.     WORD    segLocal = SELECTOROF(szLocal);
  1383.     WORD    offLocal = OFFSETOF(szLocal);
  1384.     WORD    segRemote = SELECTOROF(szRemote);
  1385.     WORD    offRemote = OFFSETOF(szRemote);
  1386.  
  1387.     _asm {
  1388.         push ds
  1389.         push es
  1390.         mov  ax, 5f02h      ; get assign-list entry
  1391.         mov  bl, 04h        ; disk device
  1392.         mov  ds, segLocal   ; local device name
  1393.         mov  si, offLocal
  1394.         mov  es, segRemote  ; remote name
  1395.         mov  di, offRemote
  1396.  
  1397.         call DOS3Call
  1398.  
  1399.         pop  es
  1400.         pop  ds
  1401.         jnc  dgc10          ; success
  1402.         mov  ax, WN_NOT_CONNECTED
  1403.         jmp  dgc20
  1404. dgc10:
  1405.         xor  ax,ax
  1406. dgc20:
  1407.     }
  1408. }
  1409.  
  1410. #pragma warning(default:4035)       /*  no return value */
  1411.  
  1412. #endif  /* WIN16 */
  1413.  
  1414. #pragma warning (default: 4704)
  1415.