Index: comtypes/test/test_shelllink.py
===================================================================
--- comtypes/test/test_shelllink.py	(revision 0)
+++ comtypes/test/test_shelllink.py	(revision 0)
@@ -0,0 +1,154 @@
+import unittest
+import ctypes
+from comtypes.client import CreateObject
+from comtypes.persist import IPersistFile
+from comtypes.shelllink import (ShellLink, IShellLinkDataList,
+    SLDF_HAS_NAME, SLDF_HAS_RELPATH, SLDF_HAS_WORKINGDIR, SLDF_HAS_ARGS,
+    NT_CONSOLE_PROPS, NT_CONSOLE_PROPS_SIG, NT_FE_CONSOLE_PROPS,
+    NT_FE_CONSOLE_PROPS_SIG)
+import os, sys, tempfile
+
+# GZ: is there some better cleanup method I'm missing?
+LocalFree = ctypes.windll.kernel32.LocalFree
+
+def _create_console_props():
+    """Create instance of NT_CONSOLE_PROPS with some typical properties"""
+    block = NT_CONSOLE_PROPS()
+    # 0x07 == FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
+    block.wFillAttribute = 0x07
+    # 0xF5 == FOREGROUND_BLUE | FOREGROUND_RED | BACKGROUND_BLUE
+    #     | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY
+    block.wPopupFillAttribute = 0xF5
+    block.dwScreenBufferSize = 80, 300
+    block.dwWindowSize = 80, 25
+    block.dwFontSize.Y = 16
+    block.FaceName = "Lucida Console"
+    block.uCursorSize = 25
+    block.bFullScreen = False
+    block.bQuickEdit = False
+    block.bInsertMode = True
+    block.bAutoPosition = True
+    block.uHistoryBufferSize = 50
+    block.uNumberOfHistoryBuffers = 4
+    block.bHistoryNoDup = False
+    block.ColorTable = (
+        0x000000, 0x800000, 0x008000, 0x808000,
+        0x000080, 0x800080, 0x008080, 0xC0C0C0,
+        0x808080, 0xFF0000, 0x00FF00, 0xFFFF00,
+        0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF)
+    return block
+
+
+class ShellLinkTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.tempdir = tempfile.mkdtemp()
+
+    def _get_shortcut_after_reload(self, shortcut):
+        savepath = os.path.join(self.tempdir, "tmp.lnk")
+        shortcut.QueryInterface(IPersistFile).Save(savepath, True)
+        self.assert_(os.path.isfile(savepath))
+        shortcut = CreateObject(ShellLink)
+        shortcut.QueryInterface(IPersistFile).Load(savepath, 0)
+        return shortcut
+
+    def test_basics(self):
+        shortcut = CreateObject(ShellLink)
+        shortcut.SetPath(sys.executable)
+        shortcut.SetArguments("-h")
+        shortcut.SetDescription("Python interpreter")
+        shortcut.SetIconLocation(sys.executable, 1)
+        wd = os.path.dirname(sys.executable)
+        shortcut.SetWorkingDirectory(wd)
+        shortcut = self._get_shortcut_after_reload(shortcut)
+        self.assertEqual(sys.executable, shortcut.GetPath())
+        self.assertEqual("-h", shortcut.GetArguments())
+        self.assertEqual("Python interpreter", shortcut.GetDescription())
+        self.assertEqual((sys.executable, 1), shortcut.GetIconLocation())
+        self.assertEqual(wd, shortcut.GetWorkingDirectory())
+
+    def _get_flags_after_reload(self, shortcut):
+        return self._get_shortcut_after_reload(shortcut
+            ).QueryInterface(IShellLinkDataList).GetFlags()
+
+    def test_get_flags_name(self):
+        shortcut = CreateObject(ShellLink)
+        shortcut.SetPath(sys.executable)
+        shortcut.SetDescription("Python interpreter")
+        flags = self._get_flags_after_reload(shortcut)
+        self.assertEqual(SLDF_HAS_NAME, flags & SLDF_HAS_NAME)
+        self.assertEqual(0, flags &
+            (SLDF_HAS_RELPATH | SLDF_HAS_WORKINGDIR | SLDF_HAS_ARGS))
+
+    def test_get_flags_relpath(self):
+        shortcut = CreateObject(ShellLink)
+        shortcut.SetPath(sys.executable)
+        shortcut.SetRelativePath(sys.executable, 0)
+        flags = self._get_flags_after_reload(shortcut)
+        self.assertEqual(SLDF_HAS_RELPATH, flags & SLDF_HAS_RELPATH)
+        self.assertEqual(0, flags &
+            (SLDF_HAS_NAME | SLDF_HAS_WORKINGDIR | SLDF_HAS_ARGS))
+
+    def test_get_flags_workingdir(self):
+        shortcut = CreateObject(ShellLink)
+        shortcut.SetPath(sys.executable)
+        shortcut.SetWorkingDirectory(self.tempdir)
+        flags = self._get_flags_after_reload(shortcut)
+        self.assertEqual(SLDF_HAS_WORKINGDIR, flags & SLDF_HAS_WORKINGDIR)
+        self.assertEqual(0, flags &
+            (SLDF_HAS_NAME | SLDF_HAS_RELPATH | SLDF_HAS_ARGS))
+
+    def test_get_flags_args(self):
+        shortcut = CreateObject(ShellLink)
+        shortcut.SetPath(sys.executable)
+        shortcut.SetArguments("-h")
+        flags = self._get_flags_after_reload(shortcut)
+        self.assertEqual(SLDF_HAS_ARGS, flags & SLDF_HAS_ARGS)
+        self.assertEqual(0, flags &
+            (SLDF_HAS_NAME | SLDF_HAS_RELPATH | SLDF_HAS_WORKINGDIR))
+
+    def test_console_data_blocks(self):
+        shortcut = CreateObject(ShellLink)
+        shortcut.SetPath("cmd")
+        datalist = shortcut.QueryInterface(IShellLinkDataList)
+        refblock = _create_console_props()
+        datalist.AddDataBlock(ctypes.byref(refblock))
+        datalist.AddDataBlock(ctypes.byref(NT_FE_CONSOLE_PROPS(437)))
+        shortcut = self._get_shortcut_after_reload(shortcut)
+        # Need to use basename as the path will be returned in absolute form
+        self.assertEqual("cmd.exe", os.path.basename(shortcut.GetPath()))
+        datalist = shortcut.QueryInterface(IShellLinkDataList)
+        vp = datalist.CopyDataBlock(NT_CONSOLE_PROPS_SIG)
+        try:
+            block = ctypes.cast(vp, ctypes.POINTER(NT_CONSOLE_PROPS)).contents
+            # Man this sucks, is there a better way of checking for equality?
+            # Could do byte-wise, but would prefer errors to make more sense.
+            for p, t in block._fields_:
+                a, b = getattr(block, p), getattr(refblock, p)
+                if isinstance(a, ctypes.Structure):
+                    for p, t in a._fields_:
+                        self.assertEqual(getattr(b, p), getattr(a, p), p)
+                elif isinstance(a, ctypes.Array):
+                    self.assertEqual(list(b), list(a), p)
+                else:
+                    self.assertEqual(b, a, p)
+        finally:
+            LocalFree(vp)
+        vp = datalist.CopyDataBlock(NT_FE_CONSOLE_PROPS_SIG)
+        try:
+            self.assertEqual(437, ctypes.cast(vp, ctypes.POINTER(
+                NT_FE_CONSOLE_PROPS)).contents.uCodePage)
+        finally:
+            LocalFree(vp)
+
+    def tearDown(self):
+        for root, dirs, files in os.walk(self.tempdir, False):
+            for f in files:
+                os.remove(os.path.join(root, f))
+            for d in dirs:
+                os.rmdir(os.path.join(root, d))
+        os.rmdir(root)
+
+
+if __name__ == "__main__":
+    unittest.main()

Property changes on: comtypes/test/test_shelllink.py
___________________________________________________________________
Added: svn:eol-style
   + native

Index: comtypes/shelllink.py
===================================================================
--- comtypes/shelllink.py	(revision 541)
+++ comtypes/shelllink.py	(working copy)
@@ -1,5 +1,6 @@
 from ctypes import *
-from ctypes.wintypes import DWORD, WIN32_FIND_DATAA, WIN32_FIND_DATAW, MAX_PATH
+from ctypes.wintypes import (BOOL, COLORREF, _COORD as COORD, DWORD, UINT, 
+    WCHAR, WORD, WIN32_FIND_DATAA, WIN32_FIND_DATAW, MAX_PATH)
 from comtypes import IUnknown, GUID, COMMETHOD, HRESULT, CoClass
 
 # for GetPath
@@ -191,6 +192,122 @@
     _com_interfaces_ = [IShellLinkW, IShellLinkA]
 
 
+# SHELL_LINK_DATA_FLAGS enum, from:
+# <http://msdn.microsoft.com/en-us/library/bb762540.aspx>
+SLDF_DEFAULT = 0x00000000
+SLDF_HAS_ID_LIST = 0x00000001
+SLDF_HAS_LINK_INFO = 0x00000002
+SLDF_HAS_NAME = 0x00000004
+SLDF_HAS_RELPATH = 0x00000008
+SLDF_HAS_WORKINGDIR = 0x00000010
+SLDF_HAS_ARGS = 0x00000020
+SLDF_HAS_ICONLOCATION = 0x00000040
+SLDF_UNICODE = 0x00000080
+SLDF_FORCE_NO_LINKINFO = 0x00000100
+SLDF_HAS_EXP_SZ = 0x00000200
+SLDF_RUN_IN_SEPARATE = 0x00000400
+SLDF_HAS_LOGO3ID = 0x00000800
+SLDF_HAS_DARWINID = 0x00001000
+SLDF_RUNAS_USER = 0x00002000
+SLDF_HAS_EXP_ICON_SZ = 0x00004000
+SLDF_NO_PIDL_ALIAS = 0x00008000
+SLDF_FORCE_UNCNAME = 0x00010000
+SLDF_RUN_WITH_SHIMLAYER = 0x00020000
+SLDF_FORCE_NO_LINKTRACK = 0x00040000 # > (6, 0)
+SLDF_ENABLE_TARGET_METADATA = 0x000800000 # > (6, 0)
+SLDF_DISABLE_LINK_PATH_TRACKING = 0x00100000 # > (6, 1)
+SLDF_DISABLE_KNOWNFOLDER_RELATIVE_TRACKING = 0x00200000 # > (6, 0)
+SLDF_NO_KF_ALIAS = 0x00400000 # > (6, 1)
+SLDF_ALLOW_LINK_TO_LINK = 0x00800000 # > (6, 1)
+SLDF_UNALIAS_ON_SAVE = 0x01000000 # > (6, 1)
+SLDF_PREFER_ENVIRONMENT_PATH = 0x02000000 # > (6, 1)
+SLDF_KEEP_LOCAL_IDLIST_FOR_UNC_TARGET = 0x04000000 # > (6, 1)
+SLDF_VALID = 0x07FFF7FF # > (6, 0)
+SLDF_RESERVED = 0x80000000
+
+
+class IShellLinkDataList(IUnknown):
+    """
+    Interface for manipulating flags and data blocks attached to a shortcut
+    """
+    _iid_ = GUID('{45E2B4AE-B1C3-11D0-B92F-00A0C90312E1}')
+    _methods_ = [
+        COMMETHOD([], HRESULT, 'AddDataBlock',
+                  ( ['in'], c_void_p, 'pDataBlock' )),
+        COMMETHOD([], HRESULT, 'CopyDataBlock',
+                  ( ['in'], DWORD, 'dwSig' ),
+                  ( ['out'], POINTER(c_void_p), 'ppDataBlock' )),
+        COMMETHOD([], HRESULT, 'RemoveDataBlock',
+                  ( ['in'], DWORD, 'dwSig' )),
+        COMMETHOD([], HRESULT, 'GetFlags',
+                  ( ['out'], POINTER(DWORD), 'pdwFlags' )),
+        COMMETHOD([], HRESULT, 'SetFlags',
+                  ( ['in'], DWORD, 'dwFlags' )),
+        ]
+
+
+class DATABLOCK_HEADER(Structure):
+    """
+    Data block pseudo-superclass that serves as a unique identifier
+    """
+    _fields_ = (("cbSize", DWORD),
+        ("dwSignature", DWORD))
+
+
+NT_CONSOLE_PROPS_SIG = 0xA0000002
+
+class NT_CONSOLE_PROPS(Structure):
+    """
+    Data block for extra properties of cmd windows
+
+    One thing to watch out for is that in addition to being stored in the
+    shortcut, when manually altered these properties are also written to the
+    registisy at HKEY_CURRENT_USER\\Console\\<name of shortcut>
+    """
+    _fields_ = DATABLOCK_HEADER._fields_ + (
+        ("wFillAttribute", WORD),
+        ("wPopupFillAttribute", WORD),
+        ("dwScreenBufferSize", COORD),
+        ("dwWindowSize", COORD),
+        ("dwWindowOrigin", COORD),
+        ("nFont", DWORD),
+        ("nInputBufferSize", DWORD),
+        ("dwFontSize", COORD),
+        ("uFontFamily", UINT),
+        ("uFontWeight", UINT),
+        ("FaceName", WCHAR * 32), # from wingdi.h: #define LF_FACESIZE 32
+        ("uCursorSize", UINT),
+        ("bFullScreen", BOOL),
+        ("bQuickEdit", BOOL),
+        ("bInsertMode", BOOL),
+        ("bAutoPosition", BOOL),
+        ("uHistoryBufferSize", UINT),
+        ("uNumberOfHistoryBuffers", UINT),
+        ("bHistoryNoDup", BOOL),
+        ("ColorTable", COLORREF * 16))
+    def __init__(self):
+        self.cbSize = sizeof(self)
+        self.dwSignature = NT_CONSOLE_PROPS_SIG
+
+
+NT_FE_CONSOLE_PROPS_SIG = 0xA0000004
+
+class NT_FE_CONSOLE_PROPS(Structure):
+    """
+    Data block for the codepage to be used for non-unicode cmd functions
+    
+    Seems a NT_CONSOLE_PROPS block must also be present to take effect
+    """
+    _fields_ = DATABLOCK_HEADER._fields_ + (
+        ("uCodePage", UINT),)
+    def __init__(self, codepage):
+        self.cbSize = sizeof(self)
+        self.dwSignature = NT_FE_CONSOLE_PROPS_SIG
+        self.uCodePage = codepage
+
+# other data blocks are given in shlobj.h and could also be added here
+
+
 if __name__ == "__main__":
 
     import sys
