# HG changeset patch
# User rbschk <vutil@ya.ru>
# Date 1498080291 -10800
#      Thu Jun 22 00:24:51 2017 +0300
# Node ID f68257d73710e66894f028f1d269ee31911afc66
# Parent  0db1b26904d55c6f4f21316621d28971696215c8
bug #752 - fix ERROR_BAD_LENGTH in GetFileInformationByHandleEx in x86 builds

When called with FileBasicInfo, GetFileInformationByHandleEx may throw
pywintypes.error(24, 'GetFileInformationByHandleEx',
'The program issued a command but the command length is incorrect.').

With some older SDKs an odd pragma pack(4) directive is issued
in <winsock2.h> before including of <windows.h>:

#if (!defined(_WIN64) && !defined(WIN32))
#include <pshpack4.h>
#endif
...no pragma pack(pop)
#include <windows.h>

If windows.h has not been seen earlier, it gets compiled with non default
alignment. This makes sizeof and layout of some structs incorrect,
including FILE_BASIC_INFO used in GetFileInformationByHandleEx.

Neither distutils nor setup.py defines 'WIN32' so we are affected.

Among SDKs with this quirk are 7.0a, the one that goes
with 'Visual C++ for Python 2.7' and apparently 7.1.
SDK known to be not affected are 8.0 and 8.1 from VS2015-update3,
but pythonwin's setup.py does not seem to support them now.

diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,6 +4,11 @@
    hg log -rb2xx: > log.out
 However contributors are encouraged to add their own entries for their work.
 
+Since build 221:
+----------------
+* win32file - fix ERROR_BAD_LENGTH in GetFileInformationByHandleEx 
+  in x86 builds(bug #752, rbschk)
+
 Since build 220:
 ----------------
 * Add PyIMapiSession::AdminServices method (Nick Czeczulin via patch #161)
diff --git a/win32/src/win32file.i b/win32/src/win32file.i
--- a/win32/src/win32file.i
+++ b/win32/src/win32file.i
@@ -27,6 +27,22 @@
 #ifndef _WIN32_WINNT
 #define _WIN32_WINNT 0x0600
 #endif
+
+// bug #752
+// Include windows.h before winsock2 because with older SDKs the latter may
+// include windows.h with wrong pragma pack directive in effect, making
+// incorrect sizeof and layout of some WINAPI structs.
+// One does not simple include windows.h before winsock2, because windows.h
+// pulls old winsock.h (MSDN: for historical reasons) which conflicts with
+// winsock2 causing compilation errors.
+// To avoid inclusion of winsock.h we define WIN32_LEAN_AND_MEAN macro to
+// drop some headers from compilation. We then have to explicitly include ole2
+// and winefs affected by the macro.
+#define WIN32_LEAN_AND_MEAN
+#include "windows.h"
+#include "ole2.h"
+#include "Winefs.h"
+
 #include "winsock2.h"
 #include "mswsock.h"
 #include "pywintypes.h"
diff --git a/win32/test/test_win32file.py b/win32/test/test_win32file.py
--- a/win32/test/test_win32file.py
+++ b/win32/test/test_win32file.py
@@ -209,6 +209,37 @@
             f.Close()
             os.unlink(filename)
 
+class TestGetFileInfoByHandleEx(unittest.TestCase):
+    __handle = __filename = None
+    def setUp(self):
+        fd, self.__filename = tempfile.mkstemp()
+        os.close(fd)
+
+    def tearDown(self):
+        if self.__handle is not None:
+            self.__handle.Close()
+        if self.__filename is not None:
+            try:
+                os.unlink(self.__filename)
+            except OSError:
+                pass
+        self.__handle = self.__filename = None
+
+    def testFileBasicInfo(self):
+        attr = win32file.GetFileAttributes(self.__filename)
+        f = win32file.CreateFile(self.__filename, win32file.GENERIC_READ, 0, None,
+                                 win32con.OPEN_EXISTING, 0, None)
+        self.__handle = f
+        ct, at, wt = win32file.GetFileTime(f)
+
+        # bug #752: this throws ERROR_BAD_LENGTH (24) in x86 binaries of build 221
+        basic_info = win32file.GetFileInformationByHandleEx(f, win32file.FileBasicInfo)
+
+        self.assertEqual(ct, basic_info['CreationTime'])
+        self.assertEqual(at, basic_info['LastAccessTime'])
+        self.assertEqual(wt, basic_info['LastWriteTime'])
+        self.assertEqual(attr, basic_info['FileAttributes'])
+
 class TestOverlapped(unittest.TestCase):
     def testSimpleOverlapped(self):
         # Create a file in the %TEMP% directory.
