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>