[Win32utils-devel] How to initialize and pass a VARIANT to IAccessible::get_accName?

Heesob Park phasis at gmail.com
Tue Dec 14 21:36:06 EST 2010


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


More information about the win32utils-devel mailing list