[python-win32] Calling an OLE Automation (IDispatch) server which uses "out params"
Mark Hammond
mhammond at skippinet.com.au
Tue Dec 18 02:25:24 CET 2012
Dave sent me the source via email.
The root problem is that the MFC project is not supporting the
GetTypeInfo() and associated calls, and TBH I'm not sure how hard it
would be to make it support that. The end result is that the makepy
support for the object isn't being used as pywin32 can't associate the
object with the typelib, and thus isn't specifying that additional
second param. This is also the reason you can't automagically generate
the typelib using, eg, win32com.client.gencache.EnsureDispatch() - you
are forced to run makepy manually and specify the exact typelib.
There is actually a deficiency in pywin32 here too - it *does* know that
MyApp.Application has a generated class, but it never tries that route -
instead it creates the object and tries to use the typeinfo stuff to
make the association. I think a bug on sourceforge for this would be good.
So after running makepy, a work-around is:
>>> klass=win32com.client.gencache.GetClassForProgID("MyApp.Application")
>>> x = klass()
>>> x.GetSettingValue("foo")
u'foo=testValue123'
>>>
If you can work out how to get the MFC app to respond to the
GetTypeInfo() and associated calls, it would all "just work". ie:
>>> x=win32com.client.Dispatch("MyApp.Application")._oleobj_
>>> x
<PyIDispatch at 0x00000000023590B0 with obj at 0x00000000003C0298>
>>> x.GetTypeInfo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
pywintypes.com_error: (-2147352565, 'Invalid index.', None, None)
>>>
should work - eg, a similar example using a WMI object:
>>> x = win32com.client.Dispatch("WbemScripting.SWbemLocator")._oleobj_
>>> x
<PyIDispatch at 0x0000000002359110 with obj at 0x0000000002C513D0>
>>> x.GetTypeInfo()
<PyITypeInfo at 0x00000000023590B0 with obj at 0x00000000003EB8B8>
>>>
So it kinda sucks :(
Mark
On 18/12/2012 10:46 AM, Dave Calkins wrote:
> On 12/16/2012 6:17 PM, Mark Hammond wrote:
>>
>>> I'm the vendor of the object :) As I mentioned, I just created this
>>> small test app which reproduces the issue having just those 3 simple
>>> methods. However, I'm not able to talk to it via Python.
>>
>> Is the source to this available?
>>
>
> Yes, its just a Visual C++ app-wizard generated project. In Visual C++
> 2010 I used new >> project, selected MFC application, chose a dialog app
> with default settings but I checked the "Automation" checkbox so it
> would set it up as an OLE Automation server. Once created, in the Class
> view I right-clicked the IMyApp interface and chose Add >> Method. I
> did this for each method shown earlier. When you add a method this way,
> it adds it to the IDL and then adds implementation methods in the
> DlgProxy class it creates as part of the project. Then you just fill in
> the implementation methods that it created for you.
>
> Here is the IDL.
>
> =====
> [ uuid(E85C16AC-7936-4792-8E2A-40537D9FA02B) ]
> dispinterface IMyApp
> {
> properties:
>
> methods:
> [id(1)] void ShowMessage(BSTR msg);
> [id(2)] void SetSettingValue(BSTR settingName, BSTR
> settingValue);
> [id(3)] void GetSettingValue(BSTR settingName, BSTR*
> settingValue);
> };
> =====
>
> Here's the implementation methods it created for the above. For the
> first two I'm just showing a message box with the passed in args. For
> the third, with the out param, I'm sending back a dummy value to the
> caller.
>
> =====
> void CMyAppDlgAutoProxy::ShowMessage(LPCTSTR msg)
> {
> AFX_MANAGE_STATE(AfxGetAppModuleState());
>
> // TODO: Add your dispatch handler code here
> CString s;
> s.Format(_T("ShowMessage\n\"%s\""), msg);
> AfxMessageBox(s);
> }
>
>
> void CMyAppDlgAutoProxy::SetSettingValue(LPCTSTR settingName, LPCTSTR
> settingValue)
> {
> AFX_MANAGE_STATE(AfxGetAppModuleState());
>
> // TODO: Add your dispatch handler code here
> CString s;
> s.Format(_T("SetSettingValue\nsettingName = \"%s\"\nsettingValue =
> \"%s\""), settingName, settingValue);
> AfxMessageBox(s);
> }
>
>
> void CMyAppDlgAutoProxy::GetSettingValue(LPCTSTR settingName, BSTR*
> settingValue)
> {
> AFX_MANAGE_STATE(AfxGetAppModuleState());
>
> // TODO: Add your dispatch handler code here
> CString s;
> s.Format(_T("%s=testValue123"), settingName);
> *settingValue = s.AllocSysString();
> }
> =====
>
> The first two methods work great, I can call those from python without
> any problems.
>
>>
>> Nope - it should all be good. For example, check out:
>>
>> http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/file/8b73631eeee0/com/TestSources/PyCOMTest/PyCOMTest.idl#l193
>>
>>
>> http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/file/8b73631eeee0/com/TestSources/PyCOMTest/PyCOMImpl.cpp#l133
>>
>>
>> and (slightly cryptic) Python code which calls it:
>>
>> http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/file/8b73631eeee0/com/win32com/test/testPyComTest.py#l225
>>
>>
>> Which shows some "out" param tests (and while there is an inout bstr
>> test, there doesn't seem to be a specific test for an out bstr, so
>> it's still possible you have hit a pywin32 bug)
>
> Thanks for the links. I'll take a look at them as soon as I can.
>
>>> You indicated that the out params should be converted to a return
>>> value. Why does the MakePy generated method signature not indicate
>>> this? i.e. why does it show 2 arguments?
>>
>> The makepy signature really just reflects the params passed to the
>> interface itself - hence those params all have default values.
>>
>>> You said above that pywin32 *will* be passing a second param - a byref
>>> bstr. That suggests to me that there's some other method I need to call
>>> which internally calls the real method from pywin32. Where is the method
>>> I need to call then? I don't see any method generated there with only a
>>> single arg?
>>
>> InvokeTypes is what does all the magic - it takes the VT_* values and
>> builds real params in C++, and also builds the tuple (if necessary)
>> which is returned. The params mainly exist for when the interface is
>> *implemented* by Python rather than when called by Python - the same
>> generated classes are used in both directions.
>>
>
> Got it, so I shouldn't expect the actual calling convention to be bound
> to whats in the MakePy code.
>
>> Hope this clarifies...
>
> Yes, thanks for the continued help on this. I'd love to see this work
> from Python and then be able to provide that as another way to automate
> this app.
>
More information about the python-win32
mailing list