ComObjActive passing long int to set a variable

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
vlakmire
Posts: 3
Joined: 05 Jun 2020, 14:12

ComObjActive passing long int to set a variable

Post by vlakmire » 05 Jun 2020, 14:34

To all the great folks of AHK. Thanks for the forums and documentation.

Would someone please help me with this objective:

A) get current text insert position coordinates from Microsoft Word
B) draw a custom cursor overlay at that location (not part of this question b/c A isn't done yet)
C) update location of cursor overlay each time the cursor moves (should be easy once I know A)

Here is my initial approach:

1)

pLeft := 0
pTop := 0
pWidth := 0
pHeight := 0

wappdoc := ComObjActive("Word.Application").ActiveDocument
wappselrng := ComObjActive("Word.Application").Selection.Range
wappdoc.ActiveWindow.GetPoint(&pLeft, &pTop, &pWidth, &pHeight, wappselrng)
pLeft := wappdoc.ActiveWindow.Left
pWidth := wappdoc.ActiveWindow.Width
pTop := wappdoc.ActiveWindow.Top
pHeight := wappdoc.ActiveWindow.Height
MsgBox % "Left = " pLeft "\n Top = " pTop "\n Width = " pWidth "\n Height = " pHeight
wappdoc := ""
wappselrng :=

RESULTS: No errors, but Left = 0 Top =0 Width = 0 Height = 0 each time, so GetPoint isn't setting the variables pLeft, PTop...as expected. (see https://docs.microsoft.com/en-us/office/vba/api/word.window.getpoint)

Then I tried using the example from ComVar in the help file to see a working example:


1.1) "just a test of concept to see if I could get a ComObject to set an AHK variable that I passed to it"--didn't work

Preamble - ScriptControl requires a 32-bit version of AutoHotkey.
code =
(
Sub Example(Var)
MsgBox Var
Var = "out value!"
End Sub
)
sc := ComObjCreate("MSScriptControl.ScriptControl"), sc.Language := "VBScript", sc.AddCode(code)


; Example: Pass a VARIANT ByRef to a COM function.
var := ComVar()
var[] := "in value"
sc.Run("Example", var.ref)
MsgBox % var[]


; ComVar: Creates an object which can be used to pass a value ByRef.
; ComVar[] retrieves the value.
; ComVar[] := Val sets the value.
; ComVar.ref retrieves a ByRef object for passing to a COM function.
ComVar(Type=0xC)
{
static base := { __Get: "ComVarGet", __Set: "ComVarSet", __Delete: "ComVarDel" } ; For base, see Custom Objects.
; Create an array of 1 VARIANT. This method allows built-in code to take
; care of all conversions between VARIANT and AutoHotkey internal types.
arr := ComObjArray(Type, 1)
; Lock the array and retrieve a pointer to the VARIANT.
DllCall("oleaut32\SafeArrayAccessData", "ptr", ComObjValue(arr), "ptr*", arr_data)
; Store the array and an object which can be used to pass the VARIANT ByRef.
return { ref: ComObject(0x4000|Type, arr_data), _: arr, base: base }
}

ComVarGet(cv, p*) { ; Called when script accesses an unknown field.
if p.MaxIndex() = "" ; No name/parameters, i.e. cv[]
return cv._[0]
}

ComVarSet(cv, v, p*) { ; Called when script sets an unknown field.
if p.MaxIndex() = "" ; No name/parameters, i.e. cv[]:=v
return cv._[0] := v
}

ComVarDel(cv) { ; Called when the object is being freed.
; This must be done to allow the internal array to be freed.
DllCall("oleaut32\SafeArrayUnaccessData", "ptr", ComObjValue(cv._))
}
}

RESULTS: Error: not registered...after 1 hour of looking I gave up on how to get the example working on Win 7, so try something else.

2) Try to use ComVar to solve the issue inserting the below code into my original 1) solution:

;ComVar: Creates an object which can be used to pass a value ByRef.
ComVar[] retrieves the value.
ComVar[] := Val sets the value.
ComVar.ref retrieves a ByRef object for passing to a COM function.
ComVar(Type=0xC)
...
var := ComVar()
var[] := "in value"
...
wappdoc.ActiveWindow.GetPoint(var.ref, &pTop, &pWidth, &pHeight, wappselrng)

RESULTS: Error wrong type. Of course var is an array, however, I can't find an easy way to make var an integer because the example never worked?

QUESTION: How do I use ComVar for a long integer type? In my 1) solution I need 4 of these variables that Word.Application can write a number to. Please help.

Thanks in advance!

Jj

teadrinker
Posts: 4326
Joined: 29 Mar 2015, 09:41
Contact:

Re: ComObjActive passing long int to set a variable

Post by teadrinker » 05 Jun 2020, 15:06

Hi
This is the right option:

Code: Select all

Word := ComObjActive("Word.Application")
Range := Word.ActiveDocument.Range(0, 3)
MsgBox, % Range.text

type := (VT_BYREF := 0x4000) | (VT_I4 := 0x3)
VarSetCapacity(buff, 16, 0)
for k, v in ["x", "y", "w", "h"]
   %v% := ComObject(type, &buff + (k - 1)*4)
Word.ActiveWindow.GetPoint(x, y, w, h, Range)

for k, v in ["x", "y", "w", "h"]
   res .= (k = 1 ? "" : "`n") . v . ": " %v%[]
MsgBox, % res

vlakmire
Posts: 3
Joined: 05 Jun 2020, 14:12

Re: ComObjActive passing long int to set a variable

Post by vlakmire » 06 Jun 2020, 19:51

Teadrinker,

Wonderful it worked.

Do you have an easier way to extract 4 variables than this:

Code: Select all

for k, v in ["x", "y", "w", "h"]{
  res .= (k = 1 ? "" : "`n") . v . ": " %v%[]
  pLeft := x[]
  pWidth := y[]
  pTop := z[]
  pHeight := h[]
  }
I need 4 separate variables rather than one concatenated response.

Thanks so much for your expertise. I don't quite follow all the "type" "VarSetCapacity" and the other shortcut code that you have, but I am grateful for a working solution. Yes, I have read the documentation...but this is getting fairly low level. Thanks again!

Have a great weekend.
J

teadrinker
Posts: 4326
Joined: 29 Mar 2015, 09:41
Contact:

Re: ComObjActive passing long int to set a variable

Post by teadrinker » 07 Jun 2020, 03:34

vlakmire wrote: I need 4 separate variables rather than one concatenated response.

Code: Select all

Word := ComObjActive("Word.Application")
Range := Word.ActiveDocument.Range(0, 3)
MsgBox, % Range.text

type := (VT_BYREF := 0x4000) | (VT_I4 := 0x3)
VarSetCapacity(buff, 16, 0)
for k, v in ["x", "y", "w", "h"]
   %v% := ComObject(type, &buff + (k - 1)*4)
Word.ActiveWindow.GetPoint(x, y, w, h, Range)

for k, v in ["x", "y", "w", "h"]
   %v% := %v%[]
MsgBox, % x . "`n" . y . "`n" . w . "`n" . h ; now output in x, y, w, h
Another way:

Code: Select all

Word := ComObjActive("Word.Application")
Range := Word.ActiveDocument.Range(0, 3)
MsgBox, % Range.text

type := (VT_BYREF := 0x4000) | (VT_I4 := 0x3)
VarSetCapacity(buff, 16, 0)
params := []
Loop 4
   params.Push( ComObject(type, &buff + (A_Index - 1)*4) )
params.Push(Range)
Word.ActiveWindow.GetPoint(params*)

for k, v in ["left", "top", "width", "height"]
   %v% := NumGet(buff, 4*(k - 1), "UInt")

MsgBox, % left . "`n" . top . "`n" . width . "`n" . height ; now output in left, top, width, height

vlakmire
Posts: 3
Joined: 05 Jun 2020, 14:12

Re: ComObjActive passing long int to set a variable

Post by vlakmire » 12 Jun 2020, 21:47

Excellent assistance again.

I've got it all working almost perfectly now. I have the cursor box overlay following the cursor movement, however the height of the box isn't quite exact--which I'll have to research. Nonetheless, thanks so much! Great work.

Gif showing it working is attached. Thanks again!
Attachments
out.gif
out.gif (169.5 KiB) Viewed 1735 times

Spitzi
Posts: 313
Joined: 24 Feb 2022, 03:45

Re: ComObjActive passing long int to set a variable

Post by Spitzi » 13 Aug 2022, 02:47

Excellent, @teadrinker . Thanks for your code. Learned alot.

User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: ComObjActive passing long int to set a variable

Post by FanaticGuru » 15 Aug 2022, 12:30

Spitzi wrote:
13 Aug 2022, 02:47
Excellent, @teadrinker . Thanks for your code. Learned alot.

I agree. I missed this thread the first time around but now I think I actually know how to properly create a new object to use as a COM parameter variable. Before I just kind of took random stabs at it and sometimes got it to work. The Help now seems to make sense after seeing it in a practical example with a program that I am familiar with.


FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks


Spitzi
Posts: 313
Joined: 24 Feb 2022, 03:45

Re: ComObjActive passing long int to set a variable

Post by Spitzi » 29 Aug 2022, 03:09

Sorry to ask a little off topic, @teadrinker and @FanaticGuru but still very close, and still strugglich with Com objects

I have this:

Code: Select all

pic := oWord.Selection.Range.InlineShapes.AddPicture(somefilePath)																					
pic.ScaleWidth(1, True)
pic.ScaleHeight(1, True)
The second line of code should scale the the picture in Word to it's original size (I will use this later to crop the picture - since word crops relative to the original size).
It doesn't work (the picture in word is not resized, no error is shown), and I think it is because the arguments are not the correct type. Can you guys give me some advice?

Greets and Thanks!!

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: ComObjActive passing long int to set a variable

Post by lexikos » 29 Aug 2022, 03:37

Some methods require a proper boolean value, which would be ComObj(0xB, 0) or ComObj(0xB, -1). 0xB for boolean (VT_BOOL is 11). Importantly, the value used for true is -1, not just any non-zero value.

I think AutoHotkey v2 may allow you to simply call .GetPoint(&x, &y, &w, &h, Range), where & in v2 is the reference operator and x would receive the actual numeric value after the call (&x in v1 is address-of, totally different). However, it would only work if the method accepts VT_BYREF|VT_VARIANT as opposed to VT_BYREF|VT_I4. I don't have Word on hand to test.

Spitzi
Posts: 313
Joined: 24 Feb 2022, 03:45

Re: ComObjActive passing long int to set a variable

Post by Spitzi » 29 Aug 2022, 05:58

Hi @lexicos, thanks for your reply. I tried a few things.. to no avail. The best I could come up with is:

Code: Select all

				WordTrue := ComObject(0xB, -1) 	; 0xB = 11 = VT_Bool || -1 = true 
				WordFalse := ComObject(0xB, 0) 	; 0xB = 11 = VT_Bool || 0 = false
				One := ComObject(0x4, 1) 		; 0x4 = 4 = VT_R4 = 32bit floating point number
				
				pic := oWord.Selection.Range.InlineShapes.AddPicture(somefilePath)		
				pic.ScaleWidth(One, WordTrue)
				pic.ScaleHeight(One, WordTrue)
which gives a 0x80020011 Error - list not supported

In the VBA-Documentation, it says, the first parameter should be single, the second boolean...

Any ideas?

Spitzi
Posts: 313
Joined: 24 Feb 2022, 03:45

Re: ComObjActive passing long int to set a variable

Post by Spitzi » 29 Aug 2022, 06:44

Hi.

I ended up using

Code: Select all

pic.ScaleHeight := 100									
pic.ScaleWidth := 100
from

viewtopic.php?f=76&t=27536

which works with no fuss... if only microsoft's vba-Documentation was as good as the AHK-Documentation...

So for now, the problem is solved, but just for the heck of it: how would I have to code it if i wanted to use the function calls above?

Thanks for your support anyway. Always gives a new perspective on things...

User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: ComObjActive passing long int to set a variable

Post by FanaticGuru » 29 Aug 2022, 14:54

Spitzi wrote:
29 Aug 2022, 06:44
Hi.

I ended up using

Code: Select all

pic.ScaleHeight := 100									
pic.ScaleWidth := 100
from

viewtopic.php?f=76&t=27536

which works with no fuss... if only microsoft's vba-Documentation was as good as the AHK-Documentation...

So for now, the problem is solved, but just for the heck of it: how would I have to code it if i wanted to use the function calls above?

The problem is confusing Shape with InlineShape. They are different objects with different methods and properties. InlineShape does not have a method called ScaleHeight, it only has a property called ScaleHeight. Shape is the reverse, a method but not a property.

https://docs.microsoft.com/en-us/office/vba/api/word.inlineshapes
https://docs.microsoft.com/en-us/office/vba/api/word.shape

So, the MSDN documents are correct if maybe confusing if you do not realize that a Shape and a InlineShape are different types of objects.

Methods are like functions with parameters that then typically do something. Properties are like a variable where you assign or get a value.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks

Spitzi
Posts: 313
Joined: 24 Feb 2022, 03:45

Re: ComObjActive passing long int to set a variable

Post by Spitzi » 29 Aug 2022, 16:01

Thanks, @FanaticGuru.

Stupid me. It is as you say, thank you for pointing it out to me.

As I said: this forum always gives a new perspective on things...

Post Reply

Return to “Ask for Help (v1)”