1+ import tempfile
12import unittest
23from _ctypes import COMError
34from ctypes import HRESULT , POINTER , OleDLL , byref , c_ubyte
45from ctypes .wintypes import DWORD , PWCHAR
56from pathlib import Path
7+ from typing import Optional
68
79import comtypes
810import comtypes .client
911
1012comtypes .client .GetModule ("portabledeviceapi.dll" )
11- from comtypes .gen .PortableDeviceApiLib import IStorage
13+ from comtypes .gen .PortableDeviceApiLib import IStorage , tagSTATSTG
14+
15+ STGTY_STORAGE = 1
1216
1317STATFLAG_DEFAULT = 0
1418STGC_DEFAULT = 0
2327STREAM_SEEK_SET = 0
2428
2529STG_E_PATHNOTFOUND = - 2147287038
26-
30+ STG_E_INVALIDFLAG = - 2147286785
2731
2832_ole32 = OleDLL ("ole32" )
2933
@@ -39,13 +43,16 @@ class Test_IStorage(unittest.TestCase):
3943 CREATE_TESTDOC = STGM_DIRECT | STGM_CREATE | RW_EXCLUSIVE
4044 CREATE_TEMP_TESTDOC = CREATE_TESTDOC | STGM_DELETEONRELEASE
4145
42- def _create_docfile (self , mode : int ) -> IStorage :
46+ def _create_docfile (self , mode : int , name : Optional [ str ] = None ) -> IStorage :
4347 stg = POINTER (IStorage )()
44- _StgCreateDocfile (None , mode , 0 , byref (stg ))
48+ _StgCreateDocfile (name , mode , 0 , byref (stg ))
4549 return stg # type: ignore
4650
4751 def test_CreateStream (self ):
4852 storage = self ._create_docfile (mode = self .CREATE_TEMP_TESTDOC )
53+ # When created with `StgCreateDocfile(NULL, ...)`, `pwcsName` is a
54+ # temporary filename. The file really exists on disk because Windows
55+ # creates an actual temporary file for the compound storage.
4956 filepath = Path (storage .Stat (STATFLAG_DEFAULT ).pwcsName )
5057 self .assertTrue (filepath .exists ())
5158 stream = storage .CreateStream ("example" , self .RW_EXCLUSIVE_CREATE , 0 , 0 )
@@ -151,3 +158,30 @@ def test_SetClass(self):
151158 # Re-set CLSID to CLSID_NULL and verify it is correctly set.
152159 storage .SetClass (comtypes .GUID ())
153160 self .assertEqual (storage .Stat (STATFLAG_DEFAULT ).clsid , comtypes .GUID ())
161+
162+ def test_Stat (self ):
163+ with tempfile .TemporaryDirectory () as t :
164+ tmpdir = Path (t )
165+ tmpfile = tmpdir / "test_docfile.cfs"
166+ self .assertFalse (tmpfile .exists ())
167+ # When created with `StgCreateDocfile(filepath_string, ...)`, the
168+ # compound file is created at that location.
169+ storage = self ._create_docfile (
170+ name = str (tmpfile ), mode = self .CREATE_TEMP_TESTDOC
171+ )
172+ self .assertTrue (tmpfile .exists ())
173+ with self .assertRaises (COMError ) as cm :
174+ storage .Stat (0xFFFFFFFF ) # Invalid flag
175+ self .assertEqual (cm .exception .hresult , STG_E_INVALIDFLAG )
176+ stat = storage .Stat (STATFLAG_DEFAULT )
177+ self .assertIsInstance (stat , tagSTATSTG )
178+ del storage # Release the storage to prevent 'cannot access the file ...'
179+ self .assertEqual (stat .type , STGTY_STORAGE )
180+ # Due to header overhead and file system allocation, the size may be
181+ # greater than 0 bytes.
182+ self .assertGreaterEqual (stat .cbSize , 0 )
183+ # `grfMode` should reflect the access mode flags from creation.
184+ self .assertEqual (stat .grfMode , self .RW_EXCLUSIVE | STGM_DIRECT )
185+ self .assertEqual (stat .grfLocksSupported , 0 )
186+ self .assertEqual (stat .clsid , comtypes .GUID ()) # CLSID_NULL for new creation.
187+ self .assertEqual (stat .grfStateBits , 0 )
0 commit comments