From bkline at rksystems.com Tue Jan 3 08:58:33 2023 From: bkline at rksystems.com (Bob Kline) Date: Tue, 3 Jan 2023 08:58:33 -0500 Subject: [python-win32] Object type puzzle Message-ID: I've made good progress on our project to port our XMetaL JScript macros to Python (see the thread from about a month ago referring to the project). All the macros (a couple of hundred of them) have been rewritten in Python, and they all appear to work correctly (though testing in earnest is still to come). One problem we ran into was that there are a number of XMetaL API calls which are documented to return an object of a specific type but which actually return an interface to the base type of the documented type. For example, the Add() method of a CommandBarControls object is documented to return a CommandBarButton object if the parameter specifying the control type is 1, but a CommandBarPopup if the parameter passed is 5. However, regardless of which value that parameter is given, what is actually returned is a CommandBarControl object, that is, an object of the base class. Clearly that documentation assumes that the interface navigation between base and derived classes will be transparent to the scripting code. Here's the mystery part. When we used the properties of the returned object in our JScript macros, the code worked as advertised. However, the equivalent Python code blows up, with an error claiming that we are trying to use a property which our object does not possess. And the error message says that our object is a CommandBarControl object, not a CommandBarButton or CommandBarPopup object. So the mystery is: why does the JScript code work, when Python code doing the same thing does not? The vendor came up with a workaround, which is for us to explicitly cast the interface which is returned to the type we need to use ourselves. For example, replacing button = toolbar.Controls.Add(1) with control = toolbar.Controls.Add(1) button = win32com.client.CastTo(control, "CommandBarButton") This works. We have over a dozen places in our code where we've had to apply this workaround. However, I would like to solve the mystery if that's possible, because I always prefer knowing how and why the tools we use do what they do. Cuts down on the number of unpleasant surprises. :-} I can think of two possible explanations. The first would be that the vendor is detecting when the scripting engine is JScript and is behaving differently in that case, performing the cast and actually returning the interface to the specific derived class instead of the interface to the base class. It's hard to imagine what the incentive would be for the vendor to do this (have the API behave differently depending on what the scripting engine is). Also, I asked them point blank if that's what's going on, and they said that it isn't. The other, more plausible explanation would be that JScript is navigating between the interfaces using the introspection capabilities provided by the COM architecture. When it sees a method call or property access on an interface for which the method or property isn't implemented, but for which derived classes exist which do implement it, it figures out which one supports the method or property, effectively doing the casting for us. The weak link in this explanation is the question: what would the scripting engine do if more than one derived class implemented the method invoked or the property accessed? Presumably that would trigger an exception. Normally I would clear up such a puzzle by reading the specification for the underlying framework. However, when I asked the vendor for the Windows Scripting Interface specification used to expose their scripting API, they told me that such a document doesn't exist. Assuming the second explanation above is correct, why is the Python scripting support not performing the interface navigation which (for example) JScript and VBScript are doing, and which the documentation for the XMetaL APIs appears to assume will happen? Or is there a third explanation for the mystery which has eluded me? Thanks, Bob Kline From skippy.hammond at gmail.com Tue Jan 3 21:08:36 2023 From: skippy.hammond at gmail.com (Mark Hammond) Date: Wed, 4 Jan 2023 13:08:36 +1100 Subject: [python-win32] Object type puzzle In-Reply-To: References: Message-ID: <61125ad4-2f61-9d54-bf83-bf3c0fef96a6@gmail.com> On 4/01/2023 12:58 am, Bob Kline wrote: > I've made good progress on our project to port our XMetaL JScript > macros to Python (see the thread from about a month ago referring to > the project). All the macros (a couple of hundred of them) have been > rewritten in Python, and they all appear to work correctly (though > testing in earnest is still to come). > > One problem we ran into was that there are a number of XMetaL API > calls which are documented to return an object of a specific type but > which actually return an interface to the base type of the documented > type. For example, the Add() method of a CommandBarControls object is > documented to return a CommandBarButton object if the parameter > specifying the control type is 1, but a CommandBarPopup if the > parameter passed is 5. However, regardless of which value that > parameter is given, what is actually returned is a CommandBarControl > object, that is, an object of the base class. Clearly that > documentation assumes that the interface navigation between base and > derived classes will be transparent to the scripting code. > > Here's the mystery part. When we used the properties of the returned > object in our JScript macros, the code worked as advertised. However, > the equivalent Python code blows up, with an error claiming that we > are trying to use a property which our object does not possess. And > the error message says that our object is a CommandBarControl object, > not a CommandBarButton or CommandBarPopup object. So the mystery is: > why does the JScript code work, when Python code doing the same thing > does not? > > The vendor came up with a workaround, which is for us to explicitly > cast the interface which is returned to the type we need to use > ourselves. For example, replacing > > button = toolbar.Controls.Add(1) > > with > > control = toolbar.Controls.Add(1) > button = win32com.client.CastTo(control, "CommandBarButton") > > This works. We have over a dozen places in our code where we've had to > apply this workaround. However, I would like to solve the mystery if > that's possible, because I always prefer knowing how and why the tools > we use do what they do. Cuts down on the number of unpleasant > surprises. :-} > > I can think of two possible explanations. > > The first would be that the vendor is detecting when the scripting > engine is JScript and is behaving differently in that case, performing > the cast and actually returning the interface to the specific derived > class instead of the interface to the base class. It's hard to imagine > what the incentive would be for the vendor to do this (have the API > behave differently depending on what the scripting engine is). Also, I > asked them point blank if that's what's going on, and they said that > it isn't. > > The other, more plausible explanation would be that JScript is > navigating between the interfaces using the introspection capabilities > provided by the COM architecture. When it sees a method call or > property access on an interface for which the method or property isn't > implemented, but for which derived classes exist which do implement > it, it figures out which one supports the method or property, > effectively doing the casting for us. The weak link in this > explanation is the question: what would the scripting engine do if > more than one derived class implemented the method invoked or the > property accessed? Presumably that would trigger an exception. > > Normally I would clear up such a puzzle by reading the specification > for the underlying framework. However, when I asked the vendor for the > Windows Scripting Interface specification used to expose their > scripting API, they told me that such a document doesn't exist. This is probably just defined by IDispatch and related interfaces. There is a good chance that JScript magically uses multiple interfaces at the same time for an object, whereas Python only tends to use one. I suspect the root of the problem is that Python ends up in https://github.com/mhammond/pywin32/blob/main/com/win32com/client/__init__.py#L38, and as you can see it only uses the first possible type info for the IDispatch. It would be interesting to know if the objects with this behaviour have multiple entries there with the one you care about being at somewhere other than 0. But even if that's true, I'm not quite sure what you can do to smooth this over - we can't just make that code use, say, `[-1]` - but it still would be useful to identify if that's the problem. Cheers, Mark > > Assuming the second explanation above is correct, why is the Python > scripting support not performing the interface navigation which (for > example) JScript and VBScript are doing, and which the documentation > for the XMetaL APIs appears to assume will happen? Or is there a third > explanation for the mystery which has eluded me? > > Thanks, > Bob Kline > _______________________________________________ > python-win32 mailing list > python-win32 at python.org > https://mail.python.org/mailman/listinfo/python-win32 From timr at probo.com Wed Jan 4 02:51:20 2023 From: timr at probo.com (Tim Roberts) Date: Tue, 3 Jan 2023 23:51:20 -0800 Subject: [python-win32] List all fonts filename In-Reply-To: <63b4e0ce.050a0220.b9f2a.0ee2@mx.google.com> References: <63b4e0ce.050a0220.b9f2a.0ee2@mx.google.com> Message-ID: <336c59f4-e595-1a0f-d65d-4333c0daa11d@probo.com> On 1/3/23 6:13 PM, moi15moismokerlolilol wrote: > I don't wanna list the family name. Then what do you want?? Do you understand how Windows arranges fonts?? "Arial" is a font family.? "Tahoma" is a font family.? You can enumerate all of the fonts using the WIn32 API, and everything in the Win32 API can be accessed through pywin32. -- Tim Roberts, timr at probo.com Providenza & Boekelheide, Inc. From bkline at rksystems.com Wed Jan 4 10:17:47 2023 From: bkline at rksystems.com (Bob Kline) Date: Wed, 4 Jan 2023 10:17:47 -0500 Subject: [python-win32] Object type puzzle In-Reply-To: <61125ad4-2f61-9d54-bf83-bf3c0fef96a6@gmail.com> References: <61125ad4-2f61-9d54-bf83-bf3c0fef96a6@gmail.com> Message-ID: On Tue, Jan 3, 2023 at 9:08 PM Mark Hammond wrote: > ... > This is probably just defined by IDispatch and related interfaces. There > is a good chance that JScript magically uses multiple interfaces at the > same time for an object, whereas Python only tends to use one. Right. Presumably JScript either examines the COM introspection information and figures out which interface can handle the request or (sloppier) it tries each in a try ... catch block until one doesn't fail. (Same for VBScript.) > I suspect the root of the problem is that Python ends up in > https://github.com/mhammond/pywin32/blob/main/com/win32com/client/__init__.py#L38, > and as you can see it only uses the first possible type info for the > IDispatch. It would be interesting to know if the objects with this > behaviour have multiple entries there with the one you care about being > at somewhere other than 0. >From what I can tell (at least for the paths I tested this morning), that helper function is never called without resultCLSID being set, so that block of the code is never entered (for our application). > But even if that's true, I'm not quite sure what you can do to smooth > this over - we can't just make that code use, say, `[-1]` - but it still > would be useful to identify if that's the problem. Well, at least we have a workaround that works. And our consolation prize is that the workaround is probably more efficient than the dynamic interface navigation the scripting engine would do. Again, thanks for all your patient help. The fact that this project has successfully reached the point of user acceptance testing owes a lot to your generous and capable assistance. ?? Gratefully, Bob From timr at probo.com Thu Jan 5 15:26:07 2023 From: timr at probo.com (Tim Roberts) Date: Thu, 5 Jan 2023 12:26:07 -0800 Subject: [python-win32] List all fonts filename In-Reply-To: References: <63b4e0ce.050a0220.b9f2a.0ee2@mx.google.com> <336c59f4-e595-1a0f-d65d-4333c0daa11d@probo.com> Message-ID: Moi15 Moi wrote: > My goal is to list all the filename of the font installed without > listing the file in those 2 folders: > - C:\Windows\Fonts > - %userprofile%\AppData\Local\Microsoft\Windows\Fonts > > I found a way to do it directwrite: > https://gist.github.com/JeremieBergeron/dc04bcb747c94a82a10020a990ba884a > But, from what I can see, pywin32 doesn't support directwrite api. It's just COM, which pywin32 does perfectly well.? The advantage of COM is that you don't need a custom interface.? Did you look at the pyglet code that does this? > But, I don't know how I get the font path > (ex:?C:\Users\Admin\AppData\Local\Microsoft\Windows\Fonts\Jester.ttf) > with GDI The easier way would be to read the registry. "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" is where Windows stores the registered fonts.? The key is the font name, the value is the file name.? If there is a file name without a path, it defaults to C:\Windows\Fonts. -- Tim Roberts, timr at probo.com Providenza & Boekelheide, Inc. -------------- next part -------------- A non-text attachment was scrubbed... Name: smime.p7s Type: application/pkcs7-signature Size: 3428 bytes Desc: S/MIME Cryptographic Signature URL: From joel.moberg at gmail.com Thu Jan 5 08:18:28 2023 From: joel.moberg at gmail.com (Joel Moberg) Date: Thu, 5 Jan 2023 14:18:28 +0100 Subject: [python-win32] taskscheduler: how to tick setting "Run task as soon as possible after scheduled start is missed" Message-ID: Maybe I can adjust the flag in the Trigger object but I have no idea what flags are available. [image: image.png] -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image.png Type: image/png Size: 13574 bytes Desc: not available URL: From bkline at rksystems.com Sat Jan 7 10:11:52 2023 From: bkline at rksystems.com (Bob Kline) Date: Sat, 7 Jan 2023 10:11:52 -0500 Subject: [python-win32] Object type puzzle In-Reply-To: References: <61125ad4-2f61-9d54-bf83-bf3c0fef96a6@gmail.com> Message-ID: As a very minor side note, just so you know, it looks like there's a bug in the mailing list software. I went back to the original of my previous post, and the ">" character which appears at the beginning of the paragraph beginning "From what I can tell ..." was not in the message that I sent, but was added by the mailing list, making it look as if that paragraph might have been part of the quoted message to which I was replying, instead of what it actually was (part of my own reply). Cheers, Bob From timr at probo.com Sat Jan 7 14:13:49 2023 From: timr at probo.com (Tim Roberts) Date: Sat, 7 Jan 2023 11:13:49 -0800 Subject: [python-win32] Object type puzzle In-Reply-To: References: <61125ad4-2f61-9d54-bf83-bf3c0fef96a6@gmail.com> Message-ID: <5c2ad448-1591-afea-d331-6dcf7645c3d0@probo.com> On 1/7/23 7:11 AM, Bob Kline wrote: > As a very minor side note, just so you know, it looks like there's a > bug in the mailing list software. I went back to the original of my > previous post, and the ">" character which appears at the beginning of > the paragraph beginning "From what I can tell ..." was not in the > message that I sent, but was added by the mailing list, making it look > as if that paragraph might have been part of the quoted message to > which I was replying, instead of what it actually was (part of my own > reply). Ah, you youngsters who don't remember the glory days.? That's not a bug, it's a feature. In the early days, mail messages were stored in a standard format called "mbox".? This was a simple, plain text format that just had the text of every message.? The separator between messages was a line that read "From Sat Jan 07 11:14:59 2023".? So, any line in a message that started with the word "From" needed to be escaped to prevent it from being read as a separator. The traditional escapement was to change it to "> From". -- Tim Roberts, timr at probo.com Providenza & Boekelheide, Inc. From bkline at rksystems.com Sat Jan 7 14:38:04 2023 From: bkline at rksystems.com (Bob Kline) Date: Sat, 7 Jan 2023 14:38:04 -0500 Subject: [python-win32] Object type puzzle In-Reply-To: <5c2ad448-1591-afea-d331-6dcf7645c3d0@probo.com> References: <61125ad4-2f61-9d54-bf83-bf3c0fef96a6@gmail.com> <5c2ad448-1591-afea-d331-6dcf7645c3d0@probo.com> Message-ID: On Sat, Jan 7, 2023 at 2:14 PM Tim Roberts wrote: > ... > Ah, you youngsters who don't remember the glory days. .... Well, I'll be 75 in a couple of weeks, so I don't know how well I still qualify as a "youngster." ? It's more likely that I knew about that "feature" at one time (I used Pine on Unix to read my email for quite a few years, and I wrote a bunch of email gateways several decades back), and in my scramble to keep up with the actual "youngsters" my brain pushes out to the bin the things it decides it doesn't need any more (your description does ring a bell). As time goes on, the ratio of what I once knew but have forgotten to what I still remember changes faster than I'd like. ? Your memory, on the other hand, seems to be doing just fine. ? Cheers, Bob From steven at manross.net Sat Jan 7 02:44:32 2023 From: steven at manross.net (Steven Manross) Date: Sat, 7 Jan 2023 07:44:32 +0000 Subject: [python-win32] taskscheduler: how to tick setting "Run task as soon as possible after scheduled start is missed" In-Reply-To: References: Message-ID: <56e7791b81e744fdb17c358f8c807979@manross.net> I haven?t seen a response for this yet, but? I have done work with the TaskScheduler in powershell, and in powershell you can do something like this: PS C:\Users\Administrator.DOMAIN> (Get-ScheduledTask -TaskName mytask).Settings AllowDemandStart : True AllowHardTerminate : True Compatibility : Win8 DeleteExpiredTaskAfter : DisallowStartIfOnBatteries : True Enabled : True ExecutionTimeLimit : PT72H Hidden : False IdleSettings : MSFT_TaskIdleSettings MultipleInstances : IgnoreNew NetworkSettings : MSFT_TaskNetworkSettings Priority : 7 RestartCount : 0 RestartInterval : RunOnlyIfIdle : False RunOnlyIfNetworkAvailable : False StartWhenAvailable : False StopIfGoingOnBatteries : True WakeToRun : False DisallowStartOnRemoteAppSession : False UseUnifiedSchedulingEngine : True MaintenanceSettings : volatile : False PSComputerName : ?StartWhenAvailable? is what you are looking for. You would set it to True I don?t know how to get at the TaskScheduler from python without directing you to importing C DLLs and using the ctypes module, but I hope this helps. As well, here is some documentation on TaskScheduler for Powershell https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/new-scheduledtasksettingsset?view=windowsserver2022-ps Sorry, it?s not docs for python but I hope it helps you get closer to what you are looking for. HTH Steven From: python-win32 On Behalf Of Joel Moberg Sent: Thursday, January 5, 2023 6:18 AM To: python-win32 at python.org Subject: [python-win32] taskscheduler: how to tick setting "Run task as soon as possible after scheduled start is missed" Maybe I can adjust the flag in the Trigger object but I have no idea what flags are available. [cid:image001.png at 01D92230.B0567150] -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image001.png Type: image/png Size: 13574 bytes Desc: image001.png URL: From eryksun at gmail.com Mon Jan 9 19:37:13 2023 From: eryksun at gmail.com (Eryk Sun) Date: Mon, 9 Jan 2023 18:37:13 -0600 Subject: [python-win32] taskscheduler: how to tick setting "Run task as soon as possible after scheduled start is missed" In-Reply-To: References: Message-ID: On 1/5/23, Joel Moberg wrote: > Maybe I can adjust the flag in the Trigger object but I have no idea what > flags are available. The following loosely adapts Microsoft's time trigger task example, with commented links to get further information. The property that directly addresses your question is taskDefinition.Settings.StartWhenAvailable. --- import win32com.client from pywintypes import com_error from datetime import datetime, timedelta # Connect a TaskService instance. # https://learn.microsoft.com/windows/win32/taskschd/taskservice service = win32com.client.Dispatch('Schedule.Service') service.Connect() # Create a TaskDefinition. # https://learn.microsoft.com/windows/win32/taskschd/taskdefinition taskDefinition = service.NewTask(0) # Set the task description and author name. # https://learn.microsoft.com/windows/win32/taskschd/registrationinfo taskDefinition.RegistrationInfo.Description = ( "Start notepad at a certain time") taskDefinition.RegistrationInfo.Author = "Author Name" # Enable the task to start after the scheduled time. # https://learn.microsoft.com/windows/win32/taskschd/tasksettings taskDefinition.Settings.Enabled = True taskDefinition.Settings.StartWhenAvailable = True # Add a TimeTrigger. # https://learn.microsoft.com/windows/win32/taskschd/triggercollection # https://learn.microsoft.com/windows/win32/taskschd/timetrigger TASK_TRIGGER_TIME = 1 # The time format is "YYYY-MM-DD" "T" "HH:MM:SS" [(+-)"HH:MM"]. For # example, "1980-01-01T00:00:00+00:00". Local time is used if the # optional UTC offset is omitted. TIME_TEMPLATE = "{:%Y-%m-%dT%H:%M:%S}" trigger = taskDefinition.Triggers.Create(TASK_TRIGGER_TIME) trigger.Id = "TimeTriggerId" trigger.Enabled = True trigger.StartBoundary = TIME_TEMPLATE.format( datetime.now() + timedelta(seconds=30)) trigger.EndBoundary = TIME_TEMPLATE.format( datetime.now() + timedelta(minutes=5)) trigger.ExecutionTimeLimit = "PT5M" # 5 minutes # Add an ExecAction. # https://learn.microsoft.com/windows/win32/taskschd/actioncollection # https://learn.microsoft.com/windows/win32/taskschd/execaction TASK_ACTION_EXEC = 0 action = taskDefinition.Actions.Create(TASK_ACTION_EXEC) action.Id = "ExecActionid" action.Path = r"C:\Windows\System32\notepad.exe" # Register the task to run only in the user's interactive session. # https://learn.microsoft.com/windows/win32/taskschd/taskfolder TASK_CREATE_OR_UPDATE = 6 TASK_LOGON_PASSWORD = 1 TASK_LOGON_S4U = 2 # requires admin access TASK_LOGON_INTERACTIVE_TOKEN = 3 TASK_LOGON_GROUP = 4 TASK_LOGON_SERVICE_ACCOUNT = 5 service.GetFolder("\\").RegisterTaskDefinition( "Time Trigger Test", taskDefinition, TASK_CREATE_OR_UPDATE, None, None, TASK_LOGON_INTERACTIVE_TOKEN)