AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

[::script::GetFullVersionInfo] string version info for execs
Goto page Previous  1, 2
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
toralf



Joined: 31 Jan 2005
Posts: 3841
Location: Bremen, Germany

PostPosted: Tue Mar 14, 2006 2:52 pm    Post subject: Reply with quote

PhiLho wrote:
I found an interesting article by Microsoft itself! They give a DLL (by actually an ActiveX) to access Office document properties. Alas, you need Com to use it. Perhaps one can write VBScript or JScript code to use it.
From the MS website:
Quote:
The Dsofile.dll sample file is an in-process ActiveX component for programmers that use Microsoft Visual Basic .NET or the Microsoft .NET Framework. You can use this in your custom applications to read and to edit the OLE document properties that are associated with Microsoft Office files, such as the following:• Microsoft Excel workbooks
• Microsoft PowerPoint presentations
• Microsoft Word documents
• Microsoft Project projects
• Microsoft Visio drawings
• Other files without those Office products installed
The source code (C++) for the DLL is coming with the download, so I guess that it could be translated into AHK or included into AHK.

And it has all the abilities. :) With the code form wOxxOm (first post) the item of the Planned Features list
Quote:
New command FileGet/Set or FileGetProperties to retrieve and set things from a file's property sheet (title, author, permissions, etc.)
is getting closer. :)
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
skrommel



Joined: 30 Jul 2004
Posts: 181

PostPosted: Wed Mar 15, 2006 7:51 am    Post subject: Great! Reply with quote

Smile Great script, wOxxOm!

Man I'm glad I gave up WinAPI! Can you understand why it's got to be so complicated? You've got to love AutoHotkey!

I just added it to my CloseMany tool at www.donationcoders.com/skrommel, and it's much faster than ShowVer, and it doesn't crash!

You should clean up the code by removing some of the |s in the FileType line, and adding a description next to the |s, as in |FileVersion:. This combined with output to standard out and you've got a tool many want.

Skrommel
Back to top
View user's profile Send private message Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3841
Location: Bremen, Germany

PostPosted: Fri Mar 24, 2006 4:22 pm    Post subject: Reply with quote

toralf wrote:
PhiLho wrote:
I found an interesting article by Microsoft itself! They give a DLL (by actually an ActiveX) to access Office document properties. Alas, you need Com to use it. Perhaps one can write VBScript or JScript code to use it.
From the MS website:
Quote:
The Dsofile.dll sample file is an in-process ActiveX component for programmers that use Microsoft Visual Basic .NET or the Microsoft .NET Framework. You can use this in your custom applications to read and to edit the OLE document properties that are associated with Microsoft Office files, such as the following:• Microsoft Excel workbooks
• Microsoft PowerPoint presentations
• Microsoft Word documents
• Microsoft Project projects
• Microsoft Visio drawings
• Other files without those Office products installed
The source code (C++) for the DLL is coming with the download, so I guess that it could be translated into AHK or included into AHK.
Could someone please help me to use this DLL from within AHK with DllCall()?
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
PhiLho



Joined: 27 Dec 2005
Posts: 6721
Location: France (near Paris)

PostPosted: Fri Mar 24, 2006 5:27 pm    Post subject: Reply with quote

toralf wrote:
PhiLho wrote:
They give a DLL (by actually an ActiveX) to access Office document properties. Alas, you need Com to use it. Perhaps one can write VBScript or JScript code to use it.
Could someone please help me to use this DLL from within AHK with DllCall()?

AFAIK, you cannot use an ActiveX with DllCall.
_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")
Back to top
View user's profile Send private message Visit poster's website
T800



Joined: 15 Oct 2006
Posts: 33
Location: Croatia

PostPosted: Tue Oct 17, 2006 11:01 pm    Post subject: Reply with quote

Quoting,
http://www.autohotkey.com/forum/viewtopic.php?t=8618
http://www.autohotkey.com/forum/topic13348.html
(I hope this cross-linking isn't too complicated)
It looks like we are in need of functions/ahk features with which one can edit file's metadata/tags.
I also noticed that it seems like Office documents are most easily manipulated, but every other material I searched for (codes, sources, general info) is doomed to c++ or bust.
If this "♣DocumentSummaryInformation" data stream (it smells like unicode to me) could be edited, we would have basics for some real hard-core file manipulations.
Back to top
View user's profile Send private message
bobbo



Joined: 19 Mar 2007
Posts: 14

PostPosted: Fri May 04, 2007 1:00 pm    Post subject: Reply with quote

T800 wrote:
It looks like we are in need of functions/ahk features with which one can edit file's metadata/tags.
I also noticed that it seems like Office documents are most easily manipulated, but every other material I searched for (codes, sources, general info) is doomed to c++ or bust.
If this "♣DocumentSummaryInformation" data stream (it smells like unicode to me) could be edited, we would have basics for some real hard-core file manipulations.


I (somewhat successfully) created the following code to operate on this info through brute force (binary stream parsing). Take note of the hyperlinks referenced for data structure and functions used. Using this I can successfully read/write the "category" shown in the Windows Explorer file properties summary tab.

But after getting this far, I've decided to abandon it. Reasons: (1) it only works on NTFS (only tested on XP), (2) doesn't work for "recognized" file types, such as MSOffice documents, (3) my understanding is that Vista is fundamentally different in handling streams, (4) parsing a binary stream in AHK (using text strings) was really difficult compared to the C/C++ programming I'm used to.

Anyway, here it is. Maybe someone else can take this and run with it.

Code:
; read in NTFS alternate stream data
file = % "y:\ahk\test.txt:" Chr(05) "DocumentSummaryInformation" ; includes category
file2 = % "y:\ahk\test.txt:" Chr(05) "SummaryInformation" ; includes title, author, keywords, comments, etc.
res := BinRead(file,data,0,0)
text := Hex2Txt(data)

; parsing SummaryInformation
; credit: http://sedna-soft.de/summary-information-stream/

; get section length
start := 48*2+1
StringMid,out,data,start,8
sectionlen := DWord2Dec(out)

; get date/time stamp length
start := 96*2+1
StringMid,out,data,start,8
datelen := DWord2Dec(out)

; get category text length
start := 96*2+datelen*2-13
StringMid,out,data,start,8
categorylen := DWord2Dec(out)

; extract category text, prompt user to change
StringMid,out,data,96*2+datelen*2-5,categorylen*2+2
category := Hex2Txt(out)
InputBox, newcat, "Category", , , , , , , , ,%category%

; assemble new binary stream
StringLeft, dataout1, data, 48*2
StringMid, dataout2, data, 48*2+9, datelen*2+74

cattext := Txt2Hex(newcat)
catlen := StrLen(newcat)+1
catlentext := Dec2DWord(catlen)
sectionlen := 67+catlen
sectionlentext := Dec2DWord(sectionlen)
dataout = %dataout1%%sectionlentext%%dataout2%%catlentext%%cattext%00000000

; write new binary stream
MsgBox %data%`n`n%dataout%
res := BinWrite(file,dataout,0,0)
;MsgBox ErrorLevel = %ErrorLevel%`nBytes Written = %res%`n`n%dataout%

ExitApp


; BinWrite/BinRead code credit: http://www.autohotkey.com/forum/viewtopic.php?t=4546

/* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BinWrite ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|  - Open binary file
|  - (Over)Write n bytes (n = 0: all)
|  - From offset (offset < 0: counted from end)
|  - Close file
|  data -> file[offset + 0..n-1], rest of file unchanged
|  Return #bytes actually written
*/ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

BinWrite(file, data, n=0, offset=0)
{
   ; Open file for WRITE (0x40..), OPEN_ALWAYS (4): creates only if it does not exists
   h := DllCall("CreateFile","str",file,"Uint",0x40000000,"Uint",0,"UInt",0,"UInt",4,"Uint",0,"UInt",0)
   IfEqual h,-1, SetEnv, ErrorLevel, -1
   IfNotEqual ErrorLevel,0,Return,0 ; couldn't create the file

   m = 0                            ; seek to offset
   IfLess offset,0, SetEnv,m,2
   r := DllCall("SetFilePointerEx","Uint",h,"Int64",offset,"UInt *",p,"Int",m)
   IfEqual r,0, SetEnv, ErrorLevel, -3
   IfNotEqual ErrorLevel,0, {
      t = %ErrorLevel%              ; save ErrorLevel to be returned
      DllCall("CloseHandle", "Uint", h)
      ErrorLevel = %t%              ; return seek error
      Return 0
   }

   TotalWritten = 0
   m := Ceil(StrLen(data)/2)
   If (n <= 0 or n > m)
       n := m
   Loop %n%
   {
      StringLeft c, data, 2         ; extract next byte
      StringTrimLeft data, data, 2  ; remove  used byte
      c = 0x%c%                     ; make it number
      result := DllCall("WriteFile","UInt",h,"UChar *",c,"UInt",1,"UInt *",Written,"UInt",0)
      TotalWritten += Written       ; count written
      if (!result or Written < 1 or ErrorLevel)
         break
   }

   IfNotEqual ErrorLevel,0, SetEnv,t,%ErrorLevel%

   h := DllCall("CloseHandle", "Uint", h)
   IfEqual h,-1, SetEnv, ErrorLevel, -2
   IfNotEqual t,,SetEnv, ErrorLevel, %t%

   Return TotalWritten
}

/* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BinRead ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|  - Open binary file
|  - Read n bytes (n = 0: all)
|  - From offset (offset < 0: counted from end)
|  - Close file
|  data (replaced) <- file[offset + 0..n-1]
|  Return #bytes actually read
*/ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

BinRead(file, ByRef data, n=0, offset=0)
{
   h := DllCall("CreateFile","Str",file,"Uint",0x80000000,"Uint",3,"UInt",0,"UInt",3,"Uint",0,"UInt",0)
   IfEqual h,-1, SetEnv, ErrorLevel, -1
   IfNotEqual ErrorLevel,0,Return,0 ; couldn't open the file

   m = 0                            ; seek to offset
   IfLess offset,0, SetEnv,m,2
   r := DllCall("SetFilePointerEx","Uint",h,"Int64",offset,"UInt *",p,"Int",m)
   IfEqual r,0, SetEnv, ErrorLevel, -3
   IfNotEqual ErrorLevel,0, {
      t = %ErrorLevel%              ; save ErrorLevel to be returned
      DllCall("CloseHandle", "Uint", h)
      ErrorLevel = %t%              ; return seek error
      Return 0
   }

   TotalRead = 0
   data =
   IfEqual n,0, SetEnv n,0xffffffff ; almost infinite

   format = %A_FormatInteger%       ; save original integer format
   SetFormat Integer, Hex           ; for converting bytes to hex

   Loop %n%
   {
      result := DllCall("ReadFile","UInt",h,"UChar *",c,"UInt",1,"UInt *",Read,"UInt",0)
      if (!result or Read < 1 or ErrorLevel)
         break
      TotalRead += Read             ; count read
      c += 0                        ; convert to hex
      StringTrimLeft c, c, 2        ; remove 0x
      c = 0%c%                      ; pad left with 0
      StringRight c, c, 2           ; always 2 digits
      data = %data%%c%              ; append 2 hex digits
   }

   IfNotEqual ErrorLevel,0, SetEnv,t,%ErrorLevel%

   h := DllCall("CloseHandle", "Uint", h)
   IfEqual h,-1, SetEnv, ErrorLevel, -2
   IfNotEqual t,,SetEnv, ErrorLevel, %t%

   SetFormat Integer, %format%      ; restore original format
   Totalread += 0                   ; convert to original format
   Return TotalRead
}


; Txt2Hex/Hex2Txt code credit: http://www.autohotkey.com/forum/viewtopic.php?t=17928

Txt2Hex(Txt) {
format := A_FormatInteger
StringSplit, TxtArray, Txt
loop, %TxtArray0%
   {
   CurrentTxt := TxtArray%A_Index%
   blah := Asc(CurrentTxt)
   SetFormat, Integer, Hex
   Hex := Asc(CurrentTxt)
   SetFormat, Integer, Decimal
   StringReplace, Hex, Hex, 0x,,All
   If StrLen(Hex) = 1
      {
      Hex := Hex . "0"
      }
   Hex%A_Index% := Hex
   }
Hex=
Loop, %TxtArray0%
   {
   Hex := Hex . Hex%A_Index%
   }
SetFormat, Integer, %Format%
return %Hex%
}

Hex2Txt(Hex) {
format := A_FormatInteger
go=1
Txt=
HexLen := StrLen(Hex)
Loop, %HexLen%
   {
   If go=1
   {
   go=0
   HexSet := "0x" . SubStr(Hex, A_Index, 2)
   SetFormat, Integer, Decimal
   HexSet += 0
   Txt := Txt . Chr(HexSet)
   }
   else
   {
   go=1
   }
   }
SetFormat, Integer, %Format%
return %Txt%
}

DWord2Dec(Txt) {

;convert to big endian
byte0 := SubStr(Txt,1,2)
byte1 := SubStr(Txt,3,2)
byte2 := SubStr(Txt,5,2)
byte3 := SubStr(Txt,7,2)
hex := byte3 . byte2 . byte1 . byte0

;convert to decimal
result := "0x" . hex
result += 0

; return resulting decimal value
return result
}

Dec2DWord(Val) {

;convert decimal to hex
SetFormat, integer, hex
Val += 0
SetFormat, integer, d

;extract text
Txt := SubStr(Val,3)
len := StrLen(Txt)

; convert to byte-wise little endian format
if (len=1)
   DWord = 0%Txt%000000
else if (len=2)
   DWord = %Txt%000000
else if (len=3)
   DWord := SubStr(Txt,2,2) . "0" . SubStr(Txt,3,1) . "0000"
else if (len=4)
   DWord := SubStr(Txt,3,2) . SubStr(Txt,1,2) . "0000"
else if (len=5)
   DWord := SubStr(Txt,4,2) . SubStr(Txt,2,2) . "0" . SubStr(Txt,1,1) . "00"
else if (len=6)
   DWord := SubStr(Txt,5,2) . SubStr(Txt,3,2) . SubStr(Txt,1,2) . "00"
else if (len=7)
   DWord := SubStr(Txt,6,2) . SubStr(Txt,4,2) . SubStr(Txt,2,2) . "0" . SubStr(Txt,1,1)
else if (len=8)
   DWord := SubStr(Txt,7,2) . SubStr(Txt,5,2) . SubStr(Txt,3,2) . SubStr(Txt,1,2)
else
   DWord = 00000000

; return resulting string
return %DWord%
}
Back to top
View user's profile Send private message
bobbo



Joined: 19 Mar 2007
Posts: 14

PostPosted: Tue May 08, 2007 3:44 pm    Post subject: Reply with quote

Here's DLL that let's you read/write Office summary info:
http://support.microsoft.com/?id=224351
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3652
Location: Belgrade

PostPosted: Tue May 08, 2007 5:56 pm    Post subject: Reply with quote

This streams are good to hide the data.

If you create file and write data into your own stream, it can not be seen very easy. Its size will be 0, if you give it stupid name like "msinst.tmp" and save it in %WINDIR% it will be very hard to find (if not enough, you can mascarade with some real ms temp sht), plus regular file copy will not see the alternate stream, (so file can be fully copied just with right tools). Combined with some ecription, using "accidential private keys" based on the frequency pattern of some unknown song, you can save anytnig you like and only genious will be able to find it, and God to read it.

What is more interesting, AHK script can be created to do all things I mention above (except measuring the average frequency of the song) Very Happy
_________________
Back to top
View user's profile Send private message MSN Messenger
PhiLho



Joined: 27 Dec 2005
Posts: 6721
Location: France (near Paris)

PostPosted: Wed May 09, 2007 5:25 am    Post subject: Reply with quote

bobbo wrote:
Here's DLL that let's you read/write Office summary info:
http://support.microsoft.com/?id=224351
Thanks for the link. It is the file toralf mentioned.
It is still an ActiveX DLL, but the interesting new info is that now AutoHotkey supports (indirectly) Com, so Sean, majkinetor or somebody else can use it.
I have only read the first chapter of the Com in C book (very interesting, thanks majkinetor for pointing at it), so I am still unable to help.
_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")
Back to top
View user's profile Send private message Visit poster's website
bobbo



Joined: 19 Mar 2007
Posts: 14

PostPosted: Wed May 09, 2007 12:16 pm    Post subject: Reply with quote

I didn't figure out how to directly call the DsoFile.dll, so I did the next best thing: created a Visual Basic Script to handle the transactions. Now you can make a command line call to "docprop.vbs" to read and/or set the properties. Examples:

Read: cscript docprop.vbs test.txt
Write: cscript docprop.vbs text.txt Title="My Document's Title"

(You'll need to download DsoFile.dll from the link above to use this script. Also, I've found that cscript is slow to exit; using wscript //b is much faster for batch processing a large number of files using AHK.)

Code:
'docprop.vbs
'Expects input filename, displays properties, sets any properties specified.
'Uses Dsofile.dll, available at http://support.microsoft.com/?id=224351

'Version History:
'v1.0 09May2007 Release

Set objArgs = WScript.Arguments
If objArgs.Count=0 Then
  'no arguments, so echo usage instructions
  Wscript.Echo "Syntax: docpop test.txt title=""doc title"" subject=""doc subject"" author=""author name"" category=""category name"" keywords=""keyword list"" comments=""your comments"""
  Wscript.Quit 0
Elseif InStr(objArgs(0),"?") Then
  'help requested, so echo usage instructions
  Wscript.Echo "Syntax: docpop test.txt title=""doc title"" subject=""doc subject"" author=""author name"" category=""category name"" keywords=""keyword list"" comments=""your comments"""
  Wscript.Quit 0
End If

InputFile = objArgs(0)
Set objFile = CreateObject("DSOFile.OleDocumentProperties")
objFile.Open(InputFile)

If objArgs.Count = 1 Then
  'just the file name, so only report the current properties
Elseif objArgs.Count > 1 Then
  'properties specified, so set them
  For n = 1 To (objArgs.Count - 1)
    'WScript.Echo n & " = " &  objArgs(n)
    pos = InStr(objArgs(n),"=")
    If InStr(objArgs(n),"Title") Then
      objFile.SummaryProperties.Title = Right(objArgs(n),Len(objArgs(n))-pos)
    Elseif InStr(objArgs(n),"Subject") Then
      objFile.SummaryProperties.Subject = Right(objArgs(n),Len(objArgs(n))-pos)
    Elseif InStr(objArgs(n),"Author") Then
      objFile.SummaryProperties.Author = Right(objArgs(n),Len(objArgs(n))-pos)
    Elseif InStr(objArgs(n),"Category") Then
      objFile.SummaryProperties.Category = Right(objArgs(n),Len(objArgs(n))-pos)
    Elseif InStr(objArgs(n),"Keywords") Then
      objFile.SummaryProperties.Keywords = Right(objArgs(n),Len(objArgs(n))-pos)
    Elseif InStr(objArgs(n),"Comments") Then
      objFile.SummaryProperties.Comments = Right(objArgs(n),Len(objArgs(n))-pos)
    Elseif InStr(objArgs(n),"Title") Then
      objFile.SummaryProperties.Title = Right(objArgs(n),Len(objArgs(n))-pos)
    End If
  Next
  objFile.Save
End If

'report properties of InputFile
Wscript.Echo "File = " & InputFile &_
  vbNewLine & "Title = " & objFile.SummaryProperties.Title &_
  vbNewLine & "Subject = " & objFile.SummaryProperties.Subject &_
  vbNewLine & "Author = " & objFile.SummaryProperties.Author &_
  vbNewLine & "Category = " & objFile.SummaryProperties.Category &_
  vbNewLine & "Keywords = " & objFile.SummaryProperties.Keywords &_
  vbNewLine & "Comments = " & objFile.SummaryProperties.Comments
Wscript.Quit 0
Back to top
View user's profile Send private message
quatermass



Joined: 14 Dec 2005
Posts: 135

PostPosted: Thu Oct 09, 2008 2:58 pm    Post subject: Reply with quote

bobbo wrote:

Code:

      objFile.SummaryProperties.Title = Right(objArgs(n),Len(objArgs(n))-pos)


This only gets the Summary Properties of a Word file.

You'd also need the Custom Properties as a lot of people put their own fields in there

My knowledge of COM and VBS is pretty awful.

But this bit works.

Bits taken from the MS sample FilePRop.frm

Code:

For Each oCustProp In objFile.CustomProperties
      sTmp = oCustProp.Name & ": " & CStr(oCustProp.Value)
      sTmp = sTmp & "   [" & CustTypeName(oCustProp.Type) & "]"
   Next
   
   
'report properties of InputFile
Wscript.Echo "File = " & InputFile &_
  vbNewLine & "Title = " & objFile.SummaryProperties.Title &_
  vbNewLine & "Subject = " & objFile.SummaryProperties.Subject &_
  vbNewLine & "Author = " & objFile.SummaryProperties.Author &_
  vbNewLine & "Category = " & objFile.SummaryProperties.Category &_
  vbNewLine & "Keywords = " & objFile.SummaryProperties.Keywords &_

  vbNewLine & "Custom = " & sTmp &_

  vbNewLine & "Comments = " & objFile.SummaryProperties.Comments
Wscript.Quit 0


Function CustTypeName(lType)

 ' This function simply maps string names to the
 ' VARIANT type of a custom property.
   Select Case lType
      Case 1
         CustTypeName = "String"
      Case 2
         CustTypeName = "Long"
      Case 3
         CustTypeName = "Double"
      Case 4
         CustTypeName = "Boolean"
      Case 5
         CustTypeName = "Date"
      Case Else
         CustTypeName = "Unknown"
   End Select
End Function



_________________
Stuart Halliday
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2
Page 2 of 2

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group