Word VBA "For...Each" to AHK COM script, help.

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Heezea
Posts: 59
Joined: 30 Sep 2013, 21:33

Word VBA "For...Each" to AHK COM script, help.

28 Jul 2014, 13:09

I'm trying to figure out how to translate the "For...Each" Word VBA functionality into AHK script, but I'm at a loss. Can anyone please help?

Code: Select all

For Each aStory In ActiveDocument.StoryRanges 
 If aStory.StoryType <> wdMainTextStory Then aStory.Font.Reset 
Next aStory
User avatar
Blackholyman
Posts: 1293
Joined: 29 Sep 2013, 22:57
Location: Denmark
Contact:

Re: Word VBA "For...Each" to AHK COM script, help.

28 Jul 2014, 14:19

Code: Select all

For Each, aStory In ActiveDocument.StoryRanges 
 If (aStory.StoryType <> wdMainTextStory)
     aStory.Font.Reset
Then you just need to set the constent value of "wdMainTextStory"
Also check out:
Courses on AutoHotkey

My Autohotkey Blog
:dance:
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Word VBA "For...Each" to AHK COM script, help.

28 Jul 2014, 18:56

I'm guessing Blackholyman didn't test the code. I'm not going to either, but I'm pretty sure the Each, part needs to be removed. (Each is a variable in this case; it looks "cool" to people that are used to VB, but can be quite confusing. I don't recommend it.)

Unlike AutoHotkey Objects, COM object enumerators put the value in the first variable, not the second.
User avatar
Blackholyman
Posts: 1293
Joined: 29 Sep 2013, 22:57
Location: Denmark
Contact:

Re: Word VBA "For...Each" to AHK COM script, help.

29 Jul 2014, 05:16

Your guess was right on lexikos...

Sorry I did not test the code before posting it, I do try to always test if possible. But thank you for pointing out my mistake ( and giving some info on how it works) now there is less chance of me doing it wrong again :)

Thank you
Also check out:
Courses on AutoHotkey

My Autohotkey Blog
:dance:
Heezea
Posts: 59
Joined: 30 Sep 2013, 21:33

Re: Word VBA "For...Each" to AHK COM script, help.

30 Jul 2014, 09:01

Thanks for the help so far guys. When I try to use the For loop, I'm getting an error (see image) with description: '_NewEnum' is not a method. I've tried with and without the "each" but I'm getting the same error in both cases.
2014-07-30_095515.jpg
I'm not sure what to do from here. I've posted the actual script I'm trying to run (see below), changing red text to black text throughout a word document with multiple sections. The original VBA code, which is functioning as desired, is based on guidance from this site: http://word.mvps.org/faqs/macrosvba/Fin ... ithVBA.htm

AHK Testing Script:

Code: Select all

#InstallKeybdHook 
#InstallMouseHook

Loop
{
	sleep 100
	#SingleInstance Force
} 

Numpad6::
	Reload
	
Numpad5::
	ExitApp
	
Numpad1::
	Current1()
return

Numpad2::
	Current2()
return

Current1()
{
	Testing1()
	return
}

Current2()
{
	Testing2()
	return
}

;****TTMsg****
TTMsg(Message, Timer) ;example TTMsg("Testing", 1000)
{
	MouseGetPos ttX, ttY
	ToolTip, %Message%, ttX + 50, ttY + 50
	SetTimer, RemoveToolTip, %Timer%
	return 
}

RemoveToolTip:
		SetTimer, RemoveToolTip, Off
		ToolTip
		return
;----TTMsg----

Testing1()
{
	oWord := ComObjActive("Word.Application")
		
	For myStoryRange, In oWord.ActiveDocument.StoryRanges
	{
		findObject := myStoryRange.Find
		findObject.Text := ""
		findObject.Font.Color := "255"
		findObject.Replacement.Font.Color := "0"
		findObject.Replacement.Text := ""
		findObject.Forward := 1
		findObject.Wrap := 1
		findObject.Execute(,,,,,,,,,,2)
				
		While !(myStoryRange.NextStoryRange = "")
		{
			myStoryRange := myStoryRange.NextStoryRange
			findObject := myStoryRange.Find
			findObject.Text := ""
			findObject.Font.Color := "255"
			findObject.Replacement.Font.Color := "0"
			findObject.Replacement.Text := ""
			findObject.Forward := 1
			findObject.Wrap := 1
			findObject.Execute(,,,,,,,,,,2)
		}
	}
	
	return
}

Testing2()
{
	oWord := ComObjActive("Word.Application")
		
	For Each, myStoryRange In oWord.ActiveDocument.StoryRanges
	{
		findObject := myStoryRange.Find
		findObject.Text := ""
		findObject.Font.Color := "255"
		findObject.Replacement.Font.Color := "0"
		findObject.Replacement.Text := ""
		findObject.Forward := 1
		findObject.Wrap := 1
		findObject.Execute(,,,,,,,,,,2)
				
		While !(myStoryRange.NextStoryRange = "Nothing")
		{
			myStoryRange := myStoryRange.NextStoryRange
			findObject := myStoryRange.Find
			findObject.Text := ""
			findObject.Font.Color := "255"
			findObject.Replacement.Font.Color := "0"
			findObject.Replacement.Text := ""
			findObject.Forward := 1
			findObject.Wrap := 1
			findObject.Execute(,,,,,,,,,,2)
		}
	}
	
	return
}
VBA Code that is functioning properly:

Code: Select all

Sub wReColor(wColorFind, wColorReplace)

Dim myStoryRange As Range

For Each myStoryRange In ActiveDocument.StoryRanges
    With myStoryRange.Find
        .Text = ""
        .Font.Color = wColorFind
        .Replacement.Text = ""
        .Replacement.Font.Color = wColorReplace
        .Wrap = wdFindContinue
        .Execute Replace:=wdReplaceAll
    End With
    Do While Not (myStoryRange.NextStoryRange Is Nothing)
        Set myStoryRange = myStoryRange.NextStoryRange
        With myStoryRange.Find
            .Text = ""
            .Font.Color = wColorFind
            .Replacement.Text = ""
            .Replacement.Font.Color = wColorReplace
            .Wrap = wdFindContinue
            .Execute Replace:=wdReplaceAll
        End With
    Loop
Next myStoryRange
End Sub
Again, I appreciate any help with this, thanks.
Wade Hatler
Posts: 60
Joined: 03 Oct 2013, 19:49
Location: Seattle Area
Contact:

Re: Word VBA "For...Each" to AHK COM script, help.

31 Jul 2014, 16:52

UNTESTED: I didn't test this in your example, but I've used it before for COM enumeration with some success. This is a script I found on the forum some time ago, but don't know who wrote it.

Code: Select all

ComObjEnum(obj) {
    if ComObjType(obj) & 0x2000 ; if *obj* is a SafeArray
        return, obj
    else if ComObjType(obj) != 9
        MsgBox, Object is not a valid Dispatch Object.
    else if ComObj_IsMemberOf(obj, "_NewEnum")
        return, obj
    else if Not ComObj_IsMemberOf(obj, "length")
        MsgBox, Object does not have a "Length" property
    else,
        return, {ComObj:obj, i:0, _NewEnum:Func("ComObj_NewEnum"), Next:Func("ComObj_Next")}
}

ComObj_NewEnum(this) {
    return, this
}

ComObj_Next(this, ByRef item) {
    if Not this.HasKey("stored")
        this.stored := item
    if this.i < this.ComObj.length
        if ComObj_IsMember(this.ComObj, "item")
            return, true, item:=this.ComObj.item(this.i++)
        else { ; if there isn't an "item" property/method
            retry_enum:
            DllCall("SetLastError", "uint", 0) ; Ensure A_LastError is 0
            ComError:=ComObjError(), ComObjError(false)
            item:=this.ComObj, ComObjError(ComError)
            if A_LastError ; if there was a Com Error
                if this.i = 1 ; zero-based failed - try one-based
                    GoTo, retry_enum
                else {
                    MsgBox, Object does not have an "Item" property, and the enumeration attempt failed.
                    return, false
                }
            return, true
        }
    item := this.stored ; Reset the first param in the for-loop
    return, false
}

ComObj_IsMemberOf(obj, name) {
   return, DllCall(NumGet(NumGet(1*p:=ComObjUnwrap(obj))+A_PtrSize*5), "Ptr",p, "Ptr",VarSetCapacity(iid,16,0)*0+&iid, "Ptr*",&name, "UInt",1, "UInt",1024, "Int*",dispID)=0 && dispID+1, ObjRelease(p)
}
If you want to give it a try, save the script as ComObjEnum.ahk, and then #Include it. Then you call it with something like:

Code: Select all

for key, value in ComObjEnum(yourobject)
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Word VBA "For...Each" to AHK COM script, help.

31 Jul 2014, 17:05

StoryRanges does not have a Length property, so I don't think that'll work.

You can probably use

Code: Select all

ranges := oWord.ActiveDocument.StoryRanges
Loop % ranges.Count
{
    myStoryRange := ranges.Item(A_Index).Text
    ;...
instead of

Code: Select all

For myStoryRange, In oWord.ActiveDocument.StoryRanges
However, I don't have Word at hand to test it.

Edit: Fixed as below.
Wade Hatler
Posts: 60
Joined: 03 Oct 2013, 19:49
Location: Seattle Area
Contact:

Re: Word VBA "For...Each" to AHK COM script, help.

31 Jul 2014, 17:48

Good catch. You're correct that it doesn't have a .Length parameter, but it also won't index from the Ranges object. You have to use Ranges.Item[n] and it's 1.nn instead of 0..n-1.

This works:

Code: Select all

App    := ComObjActive("Word.Application")
Ranges := App.ActiveDocument.StoryRanges
Loop % ranges.Count
{
	MsgBox % Ranges.Item[A_Index].Text
}
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Word VBA "For...Each" to AHK COM script, help.

31 Jul 2014, 20:42

Thanks. 1..n is unusual for COM/Microsoft. Item(Index) is actually a method, but AutoHotkey's COM support is designed to treat methods and property-get interchangeably most of the time.
Wade Hatler
Posts: 60
Joined: 03 Oct 2013, 19:49
Location: Seattle Area
Contact:

Re: Word VBA "For...Each" to AHK COM script, help.

31 Jul 2014, 21:35

Yes, you're right there. I find that most MS COM in general is 0..n-1 except for Office Interop, which is mostly 1..n because most of it started out as VBA. Don't count on it though, because I've seen 0..n-1 here and there.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Anput and 88 guests