Bill Agee
2010-Dec-14 20:13 UTC
[Win32utils-devel] How to initialize and pass a VARIANT to IAccessible::get_accName?
Hi all,
First off, thanks for Win32Utils! The libraries have been really beneficial
for me.
I have a question (hope this is an appropriate place for it):
I''m trying to write some demo scripts that use the IAccessible
interface:
http://msdn.microsoft.com/en-us/library/dd318466%28v=vs.85%29.aspx
I''ve had some success so far (for example, I can successfully call the
"IAccessible::get_accChildCount" method).
But I''m stuck on IAccessible::get_accName.
In fact, any method like get_accName that takes a VARIANT (note: not a
VARIANT*) seems to be failing for me.
Does anyone have experience using win32-api to define and call a function
that takes a VARIANT?
In the case of get_accName, I can''t get it to return successfully...
Either ruby.exe crashes (when I pass a 16-byte packed string as the VARIANT)
or I get the HRESULT ''The parameter is incorrect.'' (when I use
a >=28-byte
packed string as the VARIANT).
I''m wondering if the problem is one of:
- the Win32::API function prototype I''m using for the VARIANT param is
wrong, or:
- the way I''m initializing the VARIANT before passing it is incorrect
I''d be really grateful if anyone could weigh in on how to achieve this.
:)
Right now I''m treating the VARIANT as a pointer in the get_accName
prototype
(note the full example program this came from is at the end of the email):
Get_accName = Win32::API::Function.new(table[10], ''PPP'',
''L'')
The C prototype for the function (from OleAcc.h) is:
/* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accName )(
__RPC__in IAccessible * This,
/* [optional][in] */ VARIANT varChild,
/* [retval][out] */ __RPC__deref_out_opt BSTR *pszName);
I combed through the windows-pr code looking for examples of functions that
take a VARIANT param, but so far I''ve only found functions that take a
pointer-to-VARIANT.
Also, I suspect this code, which initializes my VARIANT, might be causing my
problem (it was cobbled together from snippets on the win32utils-devel list
and the pr-win32ole source):
# Initialize VARIANT that holds CHILDID_SELF
self_var = 0.chr * 16
VariantInit(self_var)
self_var[0, 2] = [VT_I4].pack(''S'')
self_var[2, 2] = [0].pack(''S'')
self_var[4, 2] = [0].pack(''S'')
self_var[6, 2] = [0].pack(''S'')
self_var[8, 4] = [CHILDID_SELF].pack(''L'')
self_var[12, 4] = [0].pack(''L'')
Here''s a complete script that shows the get_accName problem - please
let me
know if there are any questions I could answer that might help resolve the
issue. If it would help I also have a working C demo program where
get_accName is used.
# print_desktop_name.rb
require ''rubygems''
require ''win32/api''
require ''windows/com''
require ''windows/com/variant''
require ''windows/error''
require ''windows/msvcrt/buffer''
require ''windows/unicode''
require ''windows/window''
require ''windows/window/menu''
include Windows::COM
include Windows::COM::Variant
include Windows::Error
include Windows::MSVCRT::Buffer
include Windows::Unicode
include Windows::Window
include Windows::Window::Menu
IID_IAccessible = [0x618736e0, 0x3c3d, 0x11cf, 0x81, 0x0c, 0x00, 0xaa, 0x00,
0x38, 0x9b, 0x71].pack(''LSSC8'')
CHILDID_SELF = 0
CoInitialize(nil) # Initialize COM
desktop_hwnd = GetDesktopWindow.call() # Get the desktop''s HWND
# Use the HWND to get the desktop''s IAccessible object
AccessibleObjectFromWindow =
Win32::API.new(''AccessibleObjectFromWindow'',
''LLPP'',
''L'', ''oleacc'')
desktop_iacc = 0.chr * 4
hr = AccessibleObjectFromWindow.call(desktop_hwnd,
OBJID_WINDOW,
IID_IAccessible,
desktop_iacc)
raise "Failed to instantiate IAcc object!" if (hr != S_OK)
# Get the memory address of the IAcc pointer - need to use it later as a
# ''this'' pointer to pass to get_accName
desktop_iacc_ptr = desktop_iacc.unpack(''L'').first
# Get the IAccessibleVtbl C interface. It contains 28 functions (see
OleAcc.h).
lpVtbl = 0.chr * 4
table = 0.chr * (4 * 28)
# Unpack the IAccessibleVtbl:
memcpy(lpVtbl, desktop_iacc_ptr, 4)
memcpy(table, lpVtbl.unpack(''L'').first, 4 * 28)
table = table.unpack(''L*'')
# Define get_accName
Get_accName = Win32::API::Function.new(table[10], ''PPP'',
''L'')
# Create buffer for the BSTR that will receive the IAcc object name
name_bstr = 0.chr * 4
# Initialize CHILDID_SELF variant
self_var = 0.chr * 16
VariantInit(self_var)
self_var[0, 2] = [VT_I4].pack(''S'')
self_var[2, 2] = [0].pack(''S'')
self_var[4, 2] = [0].pack(''S'')
self_var[6, 2] = [0].pack(''S'')
self_var[8, 4] = [CHILDID_SELF].pack(''L'')
self_var[12, 4] = [0].pack(''L'')
# This segfaults on XP and Win7:
# (using ruby 1.8.7 i386-mingw32, 2010-08-16 patchlevel 302)
hr = Get_accName.call(desktop_iacc_ptr, self_var, name_bstr)
puts "HRESULT from get_accName is: " + hr.to_s
puts "HRESULT message is: ''#{get_last_error(hr)}''"
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://rubyforge.org/pipermail/win32utils-devel/attachments/20101214/3005e52c/attachment-0001.html>
Heesob Park
2010-Dec-15 02:36 UTC
[Win32utils-devel] How to initialize and pass a VARIANT to IAccessible::get_accName?
Hi, 2010/12/15 Bill Agee <billagee at gmail.com>:> Hi all, > > First off, thanks for Win32Utils!? The libraries have been really beneficial > for me. > > I have a question (hope this is an appropriate place for it): > > I''m trying to write some demo scripts that use the IAccessible interface: > > http://msdn.microsoft.com/en-us/library/dd318466%28v=vs.85%29.aspx > > I''ve had some success so far (for example, I can successfully call the > "IAccessible::get_accChildCount" method). > > But I''m stuck on IAccessible::get_accName. > > In fact, any method like get_accName that takes a VARIANT (note: not a > VARIANT*) seems to be failing for me. > > Does anyone have experience using win32-api to define and call a function > that takes a VARIANT? > > In the case of get_accName, I can''t get it to return successfully... > > Either ruby.exe crashes (when I pass a 16-byte packed string as the VARIANT) > or I get the HRESULT ''The parameter is incorrect.'' (when I use a >=28-byte > packed string as the VARIANT). > > > I''m wondering if the problem is one of: > > - the Win32::API function prototype I''m using for the VARIANT param is > wrong, or: > > - the way I''m initializing the VARIANT before passing it is incorrect > > I''d be really grateful if anyone could weigh in on how to achieve this. :) > > Right now I''m treating the VARIANT as a pointer in the get_accName prototype > (note the full example program this came from is at the end of the email): > > ? Get_accName = Win32::API::Function.new(table[10], ''PPP'', ''L'') > > The C prototype for the function (from OleAcc.h) is: > > ? /* [id][propget][hidden] */ HRESULT ( STDMETHODCALLTYPE *get_accName )( > ????? __RPC__in IAccessible * This, > ????? /* [optional][in] */ VARIANT varChild, > ????? /* [retval][out] */ __RPC__deref_out_opt BSTR *pszName); > > > I combed through the windows-pr code looking for examples of functions that > take a VARIANT param, but so far I''ve only found functions that take a > pointer-to-VARIANT. > > Also, I suspect this code, which initializes my VARIANT, might be causing my > problem (it was cobbled together from snippets on the win32utils-devel list > and the pr-win32ole source): > > ? # Initialize VARIANT that holds CHILDID_SELF > ? self_var = 0.chr * 16 > ? VariantInit(self_var) > ? self_var[0, 2] = [VT_I4].pack(''S'') > ? self_var[2, 2] = [0].pack(''S'') > ? self_var[4, 2] = [0].pack(''S'') > ? self_var[6, 2] = [0].pack(''S'') > ? self_var[8, 4] = [CHILDID_SELF].pack(''L'') > ? self_var[12, 4] = [0].pack(''L'') > > > Here''s a complete script that shows the get_accName problem - please let me > know if there are any questions I could answer that might help resolve the > issue.? If it would help I also have a working C demo program where > get_accName is used. > > # print_desktop_name.rb > > require ''rubygems'' > require ''win32/api'' > require ''windows/com'' > require ''windows/com/variant'' > require ''windows/error'' > require ''windows/msvcrt/buffer'' > require ''windows/unicode'' > require ''windows/window'' > require ''windows/window/menu'' > > include Windows::COM > include Windows::COM::Variant > include Windows::Error > include Windows::MSVCRT::Buffer > include Windows::Unicode > include Windows::Window > include Windows::Window::Menu > > IID_IAccessible = [0x618736e0, 0x3c3d, 0x11cf, 0x81, 0x0c, 0x00, 0xaa, 0x00, > 0x38, 0x9b, 0x71].pack(''LSSC8'') > CHILDID_SELF = 0 > > CoInitialize(nil) # Initialize COM > > desktop_hwnd = GetDesktopWindow.call() # Get the desktop''s HWND > > # Use the HWND to get the desktop''s IAccessible object > AccessibleObjectFromWindow = Win32::API.new(''AccessibleObjectFromWindow'', > ??????????????????????????????????????????? ''LLPP'', ''L'', ''oleacc'') > desktop_iacc = 0.chr * 4 > hr = AccessibleObjectFromWindow.call(desktop_hwnd, > ???????????????????????????????????? OBJID_WINDOW, > ???????????????????????????????????? IID_IAccessible, > ???????????????????????????????????? desktop_iacc) > raise "Failed to instantiate IAcc object!" if (hr != S_OK) > > # Get the memory address of the IAcc pointer - need to use it later as a > # ''this'' pointer to pass to get_accName > desktop_iacc_ptr = desktop_iacc.unpack(''L'').first > > # Get the IAccessibleVtbl C interface. It contains 28 functions (see > OleAcc.h). > lpVtbl = 0.chr * 4 > table = 0.chr * (4 * 28) > > # Unpack the IAccessibleVtbl: > memcpy(lpVtbl, desktop_iacc_ptr, 4) > memcpy(table, lpVtbl.unpack(''L'').first, 4 * 28) > table = table.unpack(''L*'') > > # Define get_accName > Get_accName = Win32::API::Function.new(table[10], ''PPP'', ''L'') > > # Create buffer for the BSTR that will receive the IAcc object name > name_bstr = 0.chr * 4 > > # Initialize CHILDID_SELF variant > self_var = 0.chr * 16 > VariantInit(self_var) > self_var[0, 2] = [VT_I4].pack(''S'') > self_var[2, 2] = [0].pack(''S'') > self_var[4, 2] = [0].pack(''S'') > self_var[6, 2] = [0].pack(''S'') > self_var[8, 4] = [CHILDID_SELF].pack(''L'') > self_var[12, 4] = [0].pack(''L'') > > # This segfaults on XP and Win7: > # (using ruby 1.8.7 i386-mingw32, 2010-08-16 patchlevel 302) > hr = Get_accName.call(desktop_iacc_ptr, self_var, name_bstr) > puts "HRESULT from get_accName is: " + hr.to_s > puts "HRESULT message is: ''#{get_last_error(hr)}''" >After some debugging, I found that passing VARIANT parameter is same to passing four DWORD parameters with each VARIANT content element. Get_accName = Win32::API::Function.new(table[10], ''PPP'', ''L'') should be Get_accName = Win32::API::Function.new(table[10], ''PLLLLP'', ''L'') hr = Get_accName.call(desktop_iacc_ptr, self_var, name_bstr) should be hr = Get_accName.call(desktop_iacc_ptr,self_var[0,4].unpack(''L'').first,self_var[4,4].unpack(''L'').first,self_var[8,4].unpack(''L'').first,self_var[12,4].unpack(''L'').first,name_bstr) Regards, Park Heesob
Bill Agee
2010-Dec-15 05:38 UTC
[Win32utils-devel] How to initialize and pass a VARIANT to IAccessible::get_accName?
On Tue, Dec 14, 2010 at 6:36 PM, Heesob Park <phasis at gmail.com> wrote:> After some debugging, I found that passing VARIANT parameter is same > to passing four DWORD parameters with each VARIANT content element. > > Get_accName = Win32::API::Function.new(table[10], ''PPP'', ''L'') > should be > Get_accName = Win32::API::Function.new(table[10], ''PLLLLP'', ''L'') > > hr = Get_accName.call(desktop_iacc_ptr, self_var, name_bstr) > should be > hr > Get_accName.call(desktop_iacc_ptr,self_var[0,4].unpack(''L'').first,self_var[4,4].unpack(''L'').first,self_var[8,4].unpack(''L'').first,self_var[12,4].unpack(''L'').first,name_bstr) >Outstanding! The code works great now. With your changes in place, get_accName return S_OK, and the BSTR contains the expected string (''Desktop'') when I print it with wprintf: Wprintf = Windows::API.new(''wprintf'', ''PP'', ''I'', ''msvcrt'') format_str = multi_to_wide("The object name is: ''%s''\n") format_str_ptr = [format_str].pack(''p'').unpack(''L'').first Wprintf.call(format_str_ptr, name_bstr.unpack(''L'').first) This is huge; being able to pass a VARIANT means that the IAccessible and IUIAutomation interfaces are now wide open. :) Thanks so much! Bill -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/win32utils-devel/attachments/20101214/713374a8/attachment.html>