I am looking for a way to easily copy the file details that appear in a Windows Explorer (details view) and paste it as tabular text.
Ideally, the procedure would be to select some files in an Explorer, make a choice in the context menu (or use a shortcut key), and the list would be copied to the clipboard. When pasting, the tabular format would be preserved so that Excel would recognize the columns or Word keep tabs (or create a table).
I would like to have a solution that transfers the available columns, and not just a predefined set a details such as name + size + date.
Do you think that there is an easy way to achieve this functionality ? I am ready to program in any language if necessary but I need a path to follow. I also need a procedure to integrate it in Windows (Vista and later) so that a few clicks suffice.
1) Create context menu shell extension. It must implement IShellExtInit, IContextMenu(2,3) and IObjectWithSite. Register your shell extension on HKCR\AllFilesystemObjects key.
2) Before Explorer calls IContextMenu.InvokeCommand it calls IObjectWithSite.SetSite. Save Site value.
3) Inside IContextMenu.InvokeCommand:
Site.QueryInterface(IServiceProvider, ServiceProvider)
ServiceProvider.QueryService(SID_SFolderView, IColumnManager, ColumnManager)
ColumnManager.GetColumnCount(CM_ENUM_VISIBLE, Count)
GetMem(Keys, SizeOf(PPropertyKey) * Count)
ColumnManager.GetColumns(CM_ENUM_VISIBLE, Keys, Count)
Now you have array of all visible columns.
4) Extract IShellFolder of current folder from IDataObject passed to your handler in IShellExtInit.Initialize.
5) Extract PItemIDList of every file in IDataObject.
6) For every PItemIDList:
6.1) Call ShellFolder.BindToObject(Child, nil, IPropertyStore, PropertyStore) to get PropertyStore of item.
6.2) For every PropertyKey in Keys array:
6.2.1) Call PropertyStore.GetValue(PropertyKey, Value);
6.2.2) Convert Value to string with PropVariantToStringAlloc function.
6.2.3) Store string representation of Value in you internal txt storage.
7) Copy your txt storage to clipboard.
8) Free all resources.
Update 1
Also you can try to use IShellFolder2.GetDetailsEx instead of using IPropertyStore.
Update 2
In case of using IPropertyStore you can additionally call IPropertySystem.FormatForDisplayAlloc to format the value. For example for PKEY_Size PropertyStore.GetValue return "100000" but PropertySystem.FormatForDisplayAlloc will format value to "100 KB".
Update 3
It was quite interesting task so I created my own shell extension which copies details to clipboard. It can be downloaded via link http://www.shellace.com/bin/CopyDetails.zip
Related
In Windows image files can be tagged. These tags can be viewed and edited by right clicking on a file, clicking over to the Details tab, then clicking on the Tags property value cell.
I want to be able to read and write these tags using Python 3.
This is not EXIF data so EXIF solutions won't work. I believe it's part of the Windows Property System, but I can't find a reference in Dev Center. I looked into win32com.propsys and couldn't see anything in there either.
I wrote a program that does this once before, but I've since lost it, so I know it's possible. Previously I did it without pywin32, but any solution would be great. I think I used windll, but I can't remember.
Here is some sample code that's using the IPropertyStore interface through propsys:
import pythoncom
from win32com.propsys import propsys
from win32com.shell import shellcon
# get PROPERTYKEY for "System.Keywords"
pk = propsys.PSGetPropertyKeyFromName("System.Keywords")
# get property store for a given shell item (here a file)
ps = propsys.SHGetPropertyStoreFromParsingName("c:\\path\\myfile.jpg", None, shellcon.GPS_READWRITE, propsys.IID_IPropertyStore)
# read & print existing (or not) property value, System.Keywords type is an array of string
keywords = ps.GetValue(pk).GetValue()
print(keywords)
# build an array of string type PROPVARIANT
newValue = propsys.PROPVARIANTType(["hello", "world"], pythoncom.VT_VECTOR | pythoncom.VT_BSTR)
# write property
ps.SetValue(pk, newValue)
ps.Commit()
This code is pretty generic for any Windows property.
I'm using System.Keywords because that's what corresponds to jpeg's "tags" property that you see in the property sheet.
And the code works for jpeg and other formats for reading (GetValue) properties, but not all Windows codecs support property writing (SetValue), to it doesn't work for writing extended properties back to a .png for example.
Is there a way to retrieve result of an Automator app script in an external Applescript app (not the Applescript lines in Automator)?
Something like:
tell application "My_Automator_App"
-- suppose My_Automator_App checks the Calendar to see if there some events today
-- "Show Result" in Automator will display a list
get the_Result -- list returned by Automator
end tell
I looked into this a little bit and didn't find a natural means by which AppleScript and Automator applets can communicate, although this doesn't mean one definitely doesn't exist.
In the meantime, you could implement one of a couple of workarounds/hacks that, although a little unseemly in their methods, do achieve the desired result without creating any side issues that would affect the functionality of an applet itself.
1. Use The Clipboard
Append a Copy to Clipboard action at the end of the applet's workflow, or following the action whose result you would wish to be reported.
Retrieving the clipboard from AppleScript is simple:
get the clipboard
This will probably suit return values that are simple text strings or a number. Passing an array of items from an Automator action to the clipboard isn't very reliable, sometimes only allowing access to the first item. However, this can be resolved with a small AppleScript within the workflow to process results arrays properly and convert them into an accessible format, e.g. a comma-delimited string.
However, the clipboard is also capable of storing image data, file references, and other data types, so it will be possible (if not always straightforward) to send those to be retrieved in an AppleScript.
Where possible, strings and numbers are the safest storage types.
2. Write Out To A Temporary File
To avoid using the clipboard as an intermediary, or if you wish the applet to report multiple variables without too much work, then writing the data to a temporary file is a fairly common practice, such as is done in shell scripts when persistant values are needed between multiple executions of the same script.
There's actually a special directory that gets periodically purged so that temporary data files don't accumulate: /tmp. It's hidden in Finder, but you can still create files and delete them as you would any other directory. Files that aren't access for 3 days get purged by the system.
There is a New Text File action that can write text to a file:
Specifying the /tmp directory is most easily done by creating a variable whose value is "/tmp" (without the quotes), and dragging that variable onto the appropriate field.
But my inclination would be to insert an AppleScript, or more suitably, a shell script into the workflow, with which file manipulation becomes easy and more capable.
Calendar Events Example
Using a similar example to the scenario you described, a simple applet that retrieves calendar events might have a workflow that looks like this:
where you can calibrate the first action to isolate the events you want, such as today's events. That action returns a type of object that isn't easily processed by AppleScript, but the second action extracts the relevant data in text format, summarising the list of events that the first action returned.
This is where a temporary file is useful to write out the data to a text file, which can then be retrieved in an AppleScript.
Given this Automator applet saved under the named "CalEvents", this AppleScript makes use of that applet and its result:
property tidEvents : [linefeed, linefeed, "EVENT", space] as text
property tidDetails : {tab, " to "}
property tid : a reference to my text item delimiters
run application id "com.apple.automator.CalEvents"
set tid's contents to tidEvents
set EventsSummary to read POSIX file "/tmp/EventsSummary.txt"
set EventsList to the EventsSummary's text items
set [[n], EventsList] to [it, rest] of EventsList
set n to n's last word as number
EventsList -- The final list of events from first to last
Upon its first run, the applet requires consent to access your calendar information, which only needs to be done once and will make the above script appear to fail. Once authorised, you can run the script as often as you like to get the most up-to-date content of the /tmp/EventsSummary.txt file.
Each item in the list variable EventsList is a block of text that looks like this (asterisks are my redactions for privacy, as are the address items in curly braces):
4 OF 8
Summary: GP Appointment
Status: none
Date: 07/12/2017 to 07/12/2017
Time: 14:45:00 to 15:45:00
Location: ******** Medical Centre
{Address Line 1}
{Address Line 2}
{County}
{Post Code}
United Kingdom
Notes: 01*** *****9
Each value is separated from the preceding colon by a tab character, which won't be obvious here. Also, as you can tell from the date format and address, these are British-formatted values, but yours will, of course, be whatever they are set as in Calendar.
But since each list item is much the same, extracting details for a particular event will be simple in AppleScript, first by splitting a particular event item into paragraphs, and then splitting a particular paragraph by either a tab or space character (or both) or some preposition that naturally delimits useful bits of text:
set |Event| to some item in the EventsList
set tid's contents to tidDetails
set EventDetails to {title:text item 2 of paragraph 2 ¬
, startTime:text item 2 of paragraph 5 ¬
, EndTime:text item 3 of paragraph 5} of the |Event|
which places the important event details, such as its name and start/end times, in an AppleScript record:
{title:"GP Appointment", startTime:"15:45:00", EndTime:"16:00:00"}
I have recorded a script to Click on an XML file(highlight, right-click and open) from a popup treeview, the popup contains a number of files(varying amounts/types and they can appear in any order), the one I will always want to select always begins with 'AB', the numerics of the filename will change per test however:
SwfWindow("APPMAIN").SwfWindow("2000HOME").SwfTreeView("MainTreeList").SelectCell "AB99872","Object Name"
SwfWindow("APP-MAIN").SwfToolbar("SwfToolbar").Select "Open"
After Recording, I run the script, but I get the following error:
SelectCell :SelectCell :Cannot identify the specified item = AB99872 of the TreeView.
So my question is 2 part:
Why can it not select the file AB99872 after the initial record using SelectCell?
Considering that the filename will change per test(ie... AB*), what is the best way to automate this to be robust enough to select any Filename beginning with 'AB'. I did try UI Automation/Object Recognition and I used a regular expression like ^AB.* but UFT(v12.54) continually crashed with this approach.
You can use the tree's GetContent method and then use regular expressions to find the name of the node to select (then use that value in SelectCell.
Is it possible to replace text without having to create a TAG?
I'm using a template to fill some data in a word document. One of the things I need to fill is the revision number, something like: "1º Revision" ([onshow.rev])
But the generated file can be re-submited several times, and I need to increment the revision for each time, but since the tag [onshow.rev] is gone from the first time, I can't do it anymore.
Is it possible the replace just the text "1º Revision" with something line "2º Revision"?
thks
With TinyButStrong, You can directly modify the contents using $TBS->Source.
But, with OpenTBS over TBS, you have to take care that only the current sub-file is actually accessible by $TBS->Source.
For example, if you are working on an Ms Excel sheet, then you have to do a $TBS->PlugIn(OPENTBS_SELECT_SHEET, $Sheet) in order to select the sheet to modify.
Here is how to replace your item in the template :
$TBS->Source = str_replace("1º Revision", "2º Revision", $TBS->Source);
I use:
retVal = Shell("program.EXE " & filename, vbNormalFocus)
To execute a program need for my excel spreadsheet.
Is it possible to embed the EXE file in the excel file itself?
And how would I execute it then?
Ideias:
1 - Some kind of a bin2str function to turn binary to string (so I can store it in the program as a variable and a str2bin (the oposite)
2 - I read something about OLE Control (that you can embed it there), but I really don't know where to start on this one
Here's an outline solution that avoids OLE:
Create a hidden worksheet.
Use a base 64 encoded to convert the exe to text.
Store that text in worksheet cells on the hidden worksheet. Since there is a limit on the number of characters in a cell (32,767) you will need to break the string into chunks.
Obviously you'll need to reverse this procedure when you want to save and execute the exe file.
You can do this by using: Insert > Object and then selecting 'Create from File'.
To add it to your sheet using VBA:
Dim o As OLEObject
Set o = ActiveSheet.OLEObjects.Add(Filename:="C:\program.exe")
Then this is the command to execute program.exe:
o.Verb Verb:=xlPrimary
Not sure how to pass arguments to it, however (e.g. your filename).
Note: Untrusted applications prompt a warning when you run them.