Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions comtypes/test/test_moniker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import ctypes
import os
import tempfile
import time
import unittest
from _ctypes import COMError
from ctypes import POINTER, WinDLL, byref
Expand All @@ -27,6 +28,7 @@
_CreateItemMoniker,
_GetRunningObjectTable,
)
from comtypes.test.time_structs_helper import CompareFileTime

with contextlib.redirect_stdout(None): # supress warnings
GetModule("msvidctl.dll")
Expand Down Expand Up @@ -176,6 +178,24 @@ def test_item(self):
self.assertEqual(mon.IsRunning(bctx, None, None), hresult.S_FALSE)


class Test_GetTimeOfLastChange(unittest.TestCase):
def test_file(self):
bctx = _create_bctx()
with tempfile.NamedTemporaryFile() as f:
tmpfile = Path(f.name)
f.write(b"test data")
# Create a File Moniker for the temporary file
file_mon = _create_file_moniker(str(tmpfile))
# Get initial time of last change for the file
initial_ft = file_mon.GetTimeOfLastChange(bctx, None)
# Modify the file to change its last write time
time.sleep(0.01) # Ensure a different timestamp
os.write(f.fileno(), b"more data")
after_change_ft = file_mon.GetTimeOfLastChange(bctx, None)
# Verify the time has changed (after_change_ft > initial_ft)
self.assertEqual(CompareFileTime(after_change_ft, initial_ft), 1)


class Test_CommonPrefixWith(unittest.TestCase):
def test_file(self):
bctx = _create_bctx()
Expand Down
18 changes: 18 additions & 0 deletions comtypes/test/test_rot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
_CreateItemMoniker,
_GetRunningObjectTable,
)
from comtypes.test.time_structs_helper import (
SYSTEMTIME,
CompareFileTime,
SystemTimeToFileTime,
)

with contextlib.redirect_stdout(None): # supress warnings
GetModule("msvidctl.dll")
Expand Down Expand Up @@ -74,3 +79,16 @@ def test_returns_enum_moniker(self):
rot = _create_rot()
enum_moniker = rot.EnumRunning()
self.assertIsInstance(enum_moniker, IEnumMoniker)


class Test_NoteChangeTime_GetTimeOfLastChange(unittest.TestCase):
def test_modified_time(self):
vidctl = CreateObject(msvidctl.MSVidCtl, interface=msvidctl.IMSVidCtl)
item_id = str(GUID.create_new())
mon = _create_item_moniker("!", item_id)
rot = _create_rot()
dw_reg = rot.Register(ROTFLAGS_ALLOWANYCLIENT, vidctl, mon)
ft = SystemTimeToFileTime(SYSTEMTIME(wYear=2000, wMonth=1, wDay=1))
rot.NoteChangeTime(dw_reg, ft)
self.assertEqual(CompareFileTime(rot.GetTimeOfLastChange(mon), ft), 0)
rot.Revoke(dw_reg)
63 changes: 15 additions & 48 deletions comtypes/test/test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,23 @@
import tempfile
import unittest
from _ctypes import COMError
from ctypes import HRESULT, POINTER, OleDLL, Structure, WinDLL, byref, c_ubyte
from ctypes.wintypes import BOOL, DWORD, FILETIME, LONG, PWCHAR, WORD
from ctypes import HRESULT, POINTER, OleDLL, byref, c_ubyte
from ctypes.wintypes import DWORD, FILETIME, PWCHAR
from pathlib import Path
from typing import Optional

import comtypes
import comtypes.client
from comtypes.malloc import CoGetMalloc
from comtypes.test.time_structs_helper import (
SYSTEMTIME,
CompareFileTime,
SystemTimeToFileTime,
)

comtypes.client.GetModule("portabledeviceapi.dll")
from comtypes.gen.PortableDeviceApiLib import WSTRING, IStorage, tagSTATSTG


class SYSTEMTIME(Structure):
_fields_ = [
("wYear", WORD),
("wMonth", WORD),
("wDayOfWeek", WORD),
("wDay", WORD),
("wHour", WORD),
("wMinute", WORD),
("wSecond", WORD),
("wMilliseconds", WORD),
]


_kernel32 = WinDLL("kernel32")

# https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime
_SystemTimeToFileTime = _kernel32.SystemTimeToFileTime
_SystemTimeToFileTime.argtypes = [POINTER(SYSTEMTIME), POINTER(FILETIME)]
_SystemTimeToFileTime.restype = BOOL

# https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-comparefiletime
_CompareFileTime = _kernel32.CompareFileTime
_CompareFileTime.argtypes = [POINTER(FILETIME), POINTER(FILETIME)]
_CompareFileTime.restype = LONG

STGTY_STORAGE = 1

STATFLAG_DEFAULT = 0
Expand All @@ -65,16 +44,6 @@ class SYSTEMTIME(Structure):
_StgCreateDocfile.restype = HRESULT


def _systemtime_to_filetime(st: SYSTEMTIME) -> FILETIME:
ft = FILETIME()
_SystemTimeToFileTime(byref(st), byref(ft))
return ft


def _compare_filetime(ft1: FILETIME, ft2: FILETIME) -> int:
return _CompareFileTime(byref(ft1), byref(ft2))


def _get_pwcsname(stat: tagSTATSTG) -> WSTRING:
return WSTRING.from_address(ctypes.addressof(stat) + tagSTATSTG.pwcsName.offset)

Expand All @@ -91,9 +60,7 @@ def _create_docfile(self, mode: int, name: Optional[str] = None) -> IStorage:
_StgCreateDocfile(name, mode, 0, byref(stg))
return stg # type: ignore

FIXED_TEST_FILETIME = _systemtime_to_filetime(
SYSTEMTIME(wYear=2000, wMonth=1, wDay=1)
)
FIXED_TEST_FILETIME = SystemTimeToFileTime(SYSTEMTIME(wYear=2000, wMonth=1, wDay=1))

def test_CreateStream(self):
storage = self._create_docfile(mode=self.CREATE_TEMP_TESTDOC)
Expand Down Expand Up @@ -218,11 +185,11 @@ def test_SetElementTimes(self):
modified_stat = storage.OpenStorage(
sub_name, None, self.RW_EXCLUSIVE_TX, None, 0
).Stat(STATFLAG_DEFAULT)
self.assertEqual(_compare_filetime(orig_stat.ctime, modified_stat.ctime), 0)
self.assertEqual(_compare_filetime(orig_stat.atime, modified_stat.atime), 0)
self.assertNotEqual(_compare_filetime(orig_stat.mtime, modified_stat.mtime), 0)
self.assertEqual(CompareFileTime(orig_stat.ctime, modified_stat.ctime), 0)
self.assertEqual(CompareFileTime(orig_stat.atime, modified_stat.atime), 0)
self.assertNotEqual(CompareFileTime(orig_stat.mtime, modified_stat.mtime), 0)
self.assertEqual(
_compare_filetime(self.FIXED_TEST_FILETIME, modified_stat.mtime), 0
CompareFileTime(self.FIXED_TEST_FILETIME, modified_stat.mtime), 0
)
with self.assertRaises(COMError) as cm:
storage.SetElementTimes("NonExistent", None, None, self.FIXED_TEST_FILETIME)
Expand Down Expand Up @@ -270,9 +237,9 @@ def test_Stat(self):
# Therefore, we only verify that each timestamp is a valid `FILETIME`
# (non-zero is sufficient for a newly created file).
zero_ft = FILETIME()
self.assertNotEqual(_compare_filetime(stat.ctime, zero_ft), 0)
self.assertNotEqual(_compare_filetime(stat.atime, zero_ft), 0)
self.assertNotEqual(_compare_filetime(stat.mtime, zero_ft), 0)
self.assertNotEqual(CompareFileTime(stat.ctime, zero_ft), 0)
self.assertNotEqual(CompareFileTime(stat.atime, zero_ft), 0)
self.assertNotEqual(CompareFileTime(stat.mtime, zero_ft), 0)
# Due to header overhead and file system allocation, the size may be
# greater than 0 bytes.
self.assertGreaterEqual(stat.cbSize, 0)
Expand Down
39 changes: 39 additions & 0 deletions comtypes/test/time_structs_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from ctypes import POINTER, Structure, WinDLL, byref
from ctypes.wintypes import BOOL, FILETIME, LONG, WORD
from typing import Literal


class SYSTEMTIME(Structure):
_fields_ = [
("wYear", WORD),
("wMonth", WORD),
("wDayOfWeek", WORD),
("wDay", WORD),
("wHour", WORD),
("wMinute", WORD),
("wSecond", WORD),
("wMilliseconds", WORD),
]


_kernel32 = WinDLL("kernel32")

# https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime
_SystemTimeToFileTime = _kernel32.SystemTimeToFileTime
_SystemTimeToFileTime.argtypes = [POINTER(SYSTEMTIME), POINTER(FILETIME)]
_SystemTimeToFileTime.restype = BOOL

# https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-comparefiletime
_CompareFileTime = _kernel32.CompareFileTime
_CompareFileTime.argtypes = [POINTER(FILETIME), POINTER(FILETIME)]
_CompareFileTime.restype = LONG


def SystemTimeToFileTime(st: SYSTEMTIME, /) -> FILETIME:
ft = FILETIME()
assert _SystemTimeToFileTime(byref(st), byref(ft))
return ft


def CompareFileTime(ft1: FILETIME, ft2: FILETIME, /) -> Literal[-1, 0, 1]:
return _CompareFileTime(byref(ft1), byref(ft2))