Lenard Lindstrom | 2 Oct 2005 01:33

Re: comtypes design question

On 30 Sep 2005 at 18:07, Thomas Heller wrote:

> Maybe I can get some opinions on this...
>

[snip wrapper module explanation]

> The question I have is: How would one access these wrapper modules?
> Typically, the wrappers expose interfaces, coclasses, and constants
> defined in the typelibrary, that you probably need.
> 

[snip]

> So, one idea would be that the CreateObject() functions would not return
> a simple com interface pointer, but instead return an object that allows
> to access the wrapper module, and also maybe the coclass (*) itself. An
> object that has the wrapper module and the coclass as attributes, and
> forwards other attribute accesses to the com interface pointer?  In this
> case, the above code would have to be written in this way:
> 

Why a special type for this? Make the module another attribute of the 
POINTER(<interface>) instance.

> ie = CreateObject("InternetExplorer.Application")
> # print a constant
> print ie._module.READSTATE_COMPLETE
> # another interface
> print ie._module.IWebBrowser
(Continue reading)

Bruce Dodson | 2 Oct 2005 08:41
Picon
Favicon

Re: comtypes design question

Cool!  This sounds good to me.

Assuming the concept of the alias module survives, a default alias name 
could be taken from the library name within the typelib, could it not?  By 
convention, this will usually be the same as the progid prefix.

CreateModule is a misleading name.  It looks like comtypes will use a 
previously generated module if it is available, so it isn't always creating 
modules.  So, GetModule might be a better name.  (If you like CreateModule 
and want to keep that name, don't worry I'll get over it, ha ha.)

In CreateObject, I don't think there's any need to get at the coclass as a 
property of the returned instance.  Also, it's kind of inconsistent: it 
would mean instances created by CreateObject are a different type than the 
instances returned from, e.g., a method call.

Just messing around in the interpreter, I see that I can get the interface's 
module (which doesn't have to be the same as the coclass module, but often 
is the same) by digging around a bit.  So I can say 
myObj.__com_interface__.__module__.    I don't know if I can rely on that to 
stay the same for the forseeable future.  It might be convenient for this to 
be encapsulated.  Would it be too gimmicky if GetModule returned the module 
when passed a COM interface pointer?

-

It would also be useful to have a way to look up a module based on the 
descriptive name.  These names often contain spaces and other symbols that 
wouldn't be valid in a module name.  However, programmers in other languages 
are used to seeing the descriptive names since these are the names that 
(Continue reading)

Thomas Heller | 4 Oct 2005 19:37
Favicon

Re: comtypes design question

"Lenard Lindstrom" <len-l <at> telus.net> writes:

> On 30 Sep 2005 at 18:07, Thomas Heller wrote:
>
>> Maybe I can get some opinions on this...
>>
>
> [snip wrapper module explanation]
>
>> The question I have is: How would one access these wrapper modules?
>> Typically, the wrappers expose interfaces, coclasses, and constants
>> defined in the typelibrary, that you probably need.
>> 
>
> [snip]
>
>> So, one idea would be that the CreateObject() functions would not return
>> a simple com interface pointer, but instead return an object that allows
>> to access the wrapper module, and also maybe the coclass (*) itself. An
>> object that has the wrapper module and the coclass as attributes, and
>> forwards other attribute accesses to the com interface pointer?  In this
>> case, the above code would have to be written in this way:
>> 
>
> Why a special type for this? Make the module another attribute of the 
> POINTER(<interface>) instance.

Yes.

>> ie = CreateObject("InternetExplorer.Application")
(Continue reading)

Thomas Heller | 4 Oct 2005 19:35
Favicon

Re: Re: comtypes design question

"Bruce Dodson" <bruce_dodson <at> hotmail.com> writes:

> Cool!  This sounds good to me.
>
> Assuming the concept of the alias module survives, a default alias name 
> could be taken from the library name within the typelib, could it not?  By 
> convention, this will usually be the same as the progid prefix.

Do you mean something like 'ESRI_Geometry_Object_Library', from the
example you give below?

> CreateModule is a misleading name.  It looks like comtypes will use a 
> previously generated module if it is available, so it isn't always creating 
> modules.  So, GetModule might be a better name.  (If you like CreateModule 
> and want to keep that name, don't worry I'll get over it, ha ha.)

Ok, I'll rename it to GetModule.

> In CreateObject, I don't think there's any need to get at the coclass as a 
> property of the returned instance.  Also, it's kind of inconsistent: it 
> would mean instances created by CreateObject are a different type than the 
> instances returned from, e.g., a method call.

Access to the coclass is needed to get events, when the com object does
not implement IProvideClassInfo2: the coclass has _outgoing_interfaces_.

> Just messing around in the interpreter, I see that I can get the
> interface's module (which doesn't have to be the same as the coclass
> module, but often is the same) by digging around a bit.  So I can say
> myObj.__com_interface__.__module__.
(Continue reading)

Lenard Lindstrom | 4 Oct 2005 19:59

Re: comtypes design question

On 4 Oct 2005 at 19:37, Thomas Heller wrote:

> "Lenard Lindstrom" <len-l <at> telus.net> writes:
> 
> >> Funny, even writing these things down in question form makes nearly
> >> convinces me that this is a better approach, I'm only wandering if the
> >> ._module attribute name is a good choice because there's also the
> >> standard '__module__' attribute. ;)
> >> 
> >
> > Having only a leading underscore bothers me since by convension it is
> > reserved for private attributes. So maybe _library_ ( _lib_ ) for
> > short.
> 
> Or _module_ or _mod_ ?
> 
Sure, except as mentioned _module_ is too much like __module__ and _mod_ may 
get confused with __mod__. What one is after is the type library definitions, which in 
ctypes are objects in a module for utility.

Lenard Lindstrom
<len-l <at> telus.net>

-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
Marcus Goldfish | 5 Oct 2005 15:40
Picon

Re: import urllib kills callback, but urllib2 doesn't!?

Thomas,
 
Thanks for the quick reply.  It sounds like the general answer is "good luck debugging callbacks."  However, I'm not quite sure I explained the problem clearly enough: is it really a callback problem, or a python/ctypes problem (it's obviously jointly a programmer problem :))?  Here is what I know;
 
   (1) the DLL was built with MFC and is production-ready (I can't access source)
   (2) my ctypes interface works perfectly, all day long, in a threaded python class *except*
       when I import urllib -- regardless of whether I actually use any urllib functionality!?
       That is the funny thing-- just placing the "import urllib" statement in my code kills it!?
   (3) I assume that since my ctypes wrapper runs for hours without problem that I am
       calling the function correctly, with the correct number of arguments and returns, and
       that CFUNCTYPE is the correct choice (vs. WINFUNCTYPE).
 
Does this change your assessment?  Also, would you mind expanding on your two additional suggestions-- regarding WINFUNCTYPE and thread-safety?  Specifically, I'm not clear about the following:
 
   (1) How does one know whether to use WINFUNCTYPE vs CFUNCTYPE without access to
        source code?  Can I tell by examining the DLL?
   (2) It seems that if CFUNCTYPE works, then it is the correct choice-- true?
   (3) How can I tell, in general, if an externally provided dll is "thread safe"?
   (4) Does thread safety refer to Python-side, C-DLL side, or the interface between the two?  Long
        ago I remember reading posts about C-DLLs calling back to C# code, but problems arising
        because threads in C weren't matched to C# (or something like that). 
 
Thanks again for all the help!
Marcus

 
 
On 9/29/05, Thomas Heller <theller <at> python.net> wrote:
Marcus Goldfish <magoldfish <at> gmail.com> writes:

> I think I've stumbled across a strange bug with ctypes, my C-DLL, and
> urllib/urllib2 running Python 2.4 on win32. Not sure where the problem lies,
> but I know a workaround. The issue seems to be that my C-Dll/ctypes code
> returns funny string results when I import urllib in my module, but not when
> I import urllib2!?

Callbacks are difficult to debug, so you should double check that you
are doing things correctly.  Since you sem to run windows, make sure
that CFUNCTYPE is correct instead of WINFUNCTYPE.  Is your dll really
threadsafe?  Are you passing the correct nunber of arguments?  Are you
handling error return values that your dlls functions return?  And so
on...

If you can build a debug version of ctypes (and the other modules that
you need), you should try that.

Thomas
Ray Schumacher | 5 Oct 2005 20:24
Favicon

set audio volume, auxSetVolume with winmm.dll?

I'd like to set the audio volume for the (only) sound card. Pymedia can get it, only...
After much Googling, I did:

>>> from ctypes import *
>>> from struct import *
>>> winmm= windll.winmm
>>> winmm.waveOutGetNumDevs
<ctypes._StdcallFuncPtr object at 0x00880A50>
>>> winmm.waveOutGetNumDevs()
1
>>> calcsize('iil32cli')
52
>>> wvcps= '\x00'*52
>>> winmm.waveOutGetDevCapsA(0,wvcps,len(wvcps))
0
>>> wvcps
'=\x00\x02\x00!\x8a\x0c\x00Winnov Videum Wave Playback\x00\x01\x00\x00\x00\xff\x
0f\x00\x00\x02\x00!\x00\x04\x00\x00\x00'

Ok so far... but

>>> winmm.auxSetVolume(0L, unpack('L','\x10\x00\x10\x00')[0])
2
>>> winmm.auxSetVolume(0L, 0L)
2

Which I think is not good; and no volume change...
Is it evident to anyone else what is wrong? Is the first parameter might need to be a handle, but I'm not sure
how to set that up, and, I saw a post which said that NULL is OK for it, but no difference.

The MSDN docs:
MMRESULT auxSetVolume(
  UINT uDeviceID, 
  DWORD dwVolume  
);
uDeviceIDIdentifier of the auxiliary output device to be queried. Device identifiers are determined
implicitly from the number of devices present in the system. Device identifier values range from zero to
one less than the number of devices present. Use the
<http://msdn.microsoft.com/library/en-us/multimed/htm/_win32_auxgetnumdevs.asp>auxGetNumDevs
function to determine the number of auxiliary devices in the system.
dwVolume
Specifies the new volume setting. The low-order word specifies the left-channel volume setting, and the
high-order word specifies the right-channel setting. A value of 0xFFFF represents full volume, and a
value of 0x0000 is silence.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_auxsetvolume.asp

From an example I saw:
Type WAVECAPS
        wMid As Integer
        wPid As Integer
        vDriverVersion As Long
        szPname As String * 32
        dwFormats As Long
        wChannels As Integer 
// The volume range is 0-0xffff.
 UINT MaxDevs = waveOutGetNumDevs();
 WAVEOUTCAPS woc;
 for (UINT DID = 0; DID < MaxDevs - 1; DID++)
 {
  MMRESULT res = waveOutGetDevCaps(DID, &woc, sizeof(AUXCAPS));
  if ((res == MMSYSERR_NOERROR) && (woc.dwSupport & WAVECAPS_LRVOLUME)) break;
 }
 if (DID == MaxDevs - 1) DID = AUX_MAPPER;
// MASTER VOLUME
#define MLEFTVOL  0x1000
#define MRIGHTVOL 0x1000
 auxSetVolume(DID, MAKELONG(MLEFTVOL, MRIGHTVOL)); 

Ray 

-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
Lenard Lindstrom | 6 Oct 2005 05:34

Re: set audio volume, auxSetVolume with winmm.dll?

On 5 Oct 2005 at 11:24, Ray Schumacher wrote:

> I'd like to set the audio volume for the (only) sound card. Pymedia can get it, only...
> After much Googling, I did:
> 
> >>> from ctypes import *
> >>> from struct import *
> >>> winmm= windll.winmm
> >>> winmm.waveOutGetNumDevs
> <ctypes._StdcallFuncPtr object at 0x00880A50>
> >>> winmm.waveOutGetNumDevs()
> 1
> >>> calcsize('iil32cli')
> 52
> >>> wvcps= '\x00'*52
> >>> winmm.waveOutGetDevCapsA(0,wvcps,len(wvcps))
> 0
> >>> wvcps
> '=\x00\x02\x00!\x8a\x0c\x00Winnov Videum Wave Playback\x00\x01\x00\x00\x00\xff\x
> 0f\x00\x00\x02\x00!\x00\x04\x00\x00\x00'
> 
> Ok so far... but
> 
> >>> winmm.auxSetVolume(0L, unpack('L','\x10\x00\x10\x00')[0])
> 2
> >>> winmm.auxSetVolume(0L, 0L)
> 2
> 

A return value of 2 means the device id is out of range. Try auxGetNumDevs. It will 
likely return 0 (no auxilary devices).

Try using waveOutSetVolume instead. Id 0 changes the Wave Out volume on my 
sound card.

Lenard Lindstrom
<len-l <at> telus.net>

-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
Michael E Urashka | 6 Oct 2005 20:58
Favicon

Pointer pointers function call question.

I've successfully used ctypes to call some simple dll
functions before but have a more complicated function
I'm trying to access from a C dll.

I'm hoping someone might be able to suggest the proper 
calling convention from ctypes or what conversions/mappings
I likely need to do in order to call these successfully.

I've tried various combinations that look similar to what is below
but have been getting the error listed each time.

>>> cadll.group_addchannel("MyGroup","b_current",106,1,byref(a),byref(b))

Traceback (most recent call last):
  File "<input>", line 1, in ?
ValueError: Procedure called with not enough arguments (24 bytes
missing) or wrong calling convention

There are quite a few functions in the DLL 
but there's only a handful of interest to me which from the 
header file look like:

zEntry int zCall
group_addchannel(
    char *group_name,		/* group name */
    char *channel_name,		/* channel name */
    int ztype,		        /* data type, e.g. 100 */
    int nmax,			/* number of data elements = 1 unless array */
    void **ppgroup,		/* returns pointer to group */
    void **ppchan );		/* returns pointer to channel *

zEntry int zCall
group_getchannelbyname(
    char *group_name,		/* group name */
    char *channel_name,		/* channel name */
    int scatype,		/* data type, e.g. 100 */
    int nelm,			/* number of data elements */
    void *pdest,		/* returns value(s) */
    int *pstatus );		/* returns status */

zEntry int zCall
group_getchannel(
    void *pchan,		/* pointer to channel */
    int nelm,			/* number of data elements */
    void *pdest,		/* returns value(s) */
    int *pstatus );		/* returns status *

Any suggestions would be most helpful.

========
Michael
========

-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
Bruce Dodson | 7 Oct 2005 00:10
Picon
Favicon

Re: Re: comtypes design question

Hi Thomas,

My notes are intermixed with yours.

"Thomas Heller" <theller <at> python.net> wrote in message
news:zmppdw2s.fsf <at> python.net...
> "Bruce Dodson" <bruce_dodson <at> hotmail.com> writes:
>
>> Cool!  This sounds good to me.
>>
>> Assuming the concept of the alias module survives, a default alias name
>> could be taken from the library name within the typelib, could it not?
>> By
>> convention, this will usually be the same as the progid prefix.
>
> Do you mean something like 'ESRI_Geometry_Object_Library', from the
> example you give below?

No, that's what I am calling the library description.  In makepy and in 
Visual Basic's References dialog, I see the library description listed as 
"ESRI Geometry Object Library", but in my code the library name is 
esriGeometry.  By library name, I meant from the ODL/IDL: "library 
library_name { .... }".

Hmm, let me forget ArcObjects for a second and use an example you might have 
seen before.  Very likely, scrrun.dll is present and registered on your 
computer.  In Visual Basic 6, I can bring in the type information from this 
DLL by going to Tools / References, and selecting "Microsoft Scripting 
Runtime".  But once it's loaded, the name you use in code is just 
"Scripting".  So in VB I can then write:

  Dim fso As Scripting.FileSystemObject
  Set fso = New Scripting.FileSystemObject

Also, in VB or even VBScript I can bring in the objects by progid using
CreateObject, even when it has not been referenced.

  Dim fso
  Set fso = CreateObject("Scripting.FileSystemObject")

In this case of course VB knows nothing about the type information, so it 
provides no code completion, and uses IDispatch to resolve calls at run 
time. So this is roughly equivalent to win32com.client.dynamic.Dispatch in 
the pywin32 approach to COM.

Note that the the prefix, "Scripting" is the same in both places the 
late-bound and early-bound versions.  This is just by convention; there is 
no rule that says "the progid prefix must be the same as the name recorded 
in the typelib" but programmers have come to expect it.

I don't know if I'm using exactly the right terminology, but I hope that
clears up the terms that I used: library description, library name, and
progid prefix.

>> In CreateObject, I don't think there's any need to get at the coclass as
>> a
>> property of the returned instance.  Also, it's kind of inconsistent: it
>> would mean instances created by CreateObject are a different type than
>> the
>> instances returned from, e.g., a method call.
>
> Access to the coclass is needed to get events, when the com object does
> not implement IProvideClassInfo2: the coclass has _outgoing_interfaces_.

Oh, I see.  But I don't think you have to do it that way.  In fact, I don't 
think it will "suffice" to do it that way, because there will be cases where 
you need to handle events for an object that you didn't instantiate through 
comtypes.client.CreateObject.  For example, it might be an instance that was 
returned from a function call on some other object.

As far as I can tell, VB implements events by just querying the object for
IConnectionPointContainer, and using that to find the appropriate connection
point.  As with most interface based programming, incompatibilities are
caught at run time.

(Aside: the VB6 "language" doesn't have a way to express which outbound 
interface your code wants handle.  So the programmer uses the coclass name 
in the declaration: they say "I would like handle events for this coclass's 
default outbound interface.  This does not imply any relationship between 
that coclass and the object that they eventually assign to that set of event 
handlers.)

If need be, I can give you some elaborate examples to illustrate bizarre 
misuses for VB's method of attaching event handlers to events.

Anyway, by the same token as VB, declare the events using a specific type, 
but use QueryInterface and FindConnectionPoint to test the compatibility of 
a given COM object with that interface.  What do you think about something 
like this as the high-level framework.  It roughly matches what VB does, but 
in a more Pythonic way.

First let's assume we have done the following first.

>> myCoolObjectFactory = CreateObject("myCoolLib.CoolObjectFactory")
>> myCoolLib = GetModule(myCoolObjectFactory)
>> myCoolObject = myCoolObjectFactory.giveMeSomethingCool()

Assume that there is a non-createable coclass called MyCoolClass whose 
default interface is ICool and whose default outbound interface is 
ICoolEvents.  The object returned by makeSomethingCool supports ICool and 
ICoolEvents, so for all intents and purposes it is an instance of 
MyCoolClass.

Let's assume that event handlers are defined like this:

>> class 
>> MyCoolEventHandler(comtypes.EventHandlerBase(myCoolLib.MyCoolClass)):
>>   def OnCoolEvent(self):
>>     print "dude!"

Here, the first line looks a little funny.  EventHandlerBase is a function 
that returns a generated class (either generated just in time, or perhaps 
ahead of time - whatever works best) that has the necessary plumbing (e.g. 
GetEvents and ReleaseEvents) to handle a specific outbound interface.  You 
can pass the function a generated coclass class object (in which case it 
uses that coclass's default outbound interface) or a generated interface 
class object (in which case it assumes that interface is an outbound 
interface on some coclass, somewhere!).  The base class provides default, 
empty (pass) implementations for all of the event on that interface.

Once we have declared the event handler, we hook it up to a specific COM 
object just by constructing an instance of the event handler.

>> myCoolEventHandler = MyCoolEventHandler(myCoolObject)

The constructor does several things:

1) queries myCoolObject for IConnectionPointContainer
2) uses FindConnectionPoint to get the appropriate connection point for the 
outbound interface we have declared
3) hooks up our event handler instance to the connection point
4) holds a reference to the connection point and to the cookie from advise
5) of course, throws a meaningful exception if any of this fails

And the destructor tears it all down.  The event handler won't outlast the 
source object, because it holds a reference to the connection point which in 
turn references the object.

How does that sound to you?  If you have something better, that's cool too.

>> Another thought.  When you were talking about how [out] parameters are
>> handled, it reminds me of a special issue that affects ArcObjects.  In
>> several places in the ArcObjects interfaces, an array is expected, but
>> the typelib declares it as a single byref parameter, rather than a
>> SAFEARRAY.
>>
>> This is advantageous for C++ programmers in that it allows them to use
>> regular C / C++ arrays.  For VB6, the syntax is a little
>> idiosyncratic, but since VB6's native type system is based on COM, it
>> still works out okay.  However, for other languages that have to
>> marshal to / from their own type systems using auto-generated
>> wrappers, it can be a problem.
>>
>> In Python, one way to handle it is to trust the caller.  If I pass an
>> array where the typelib says a single byref item is expected, the
>> wrapper should assume I know what I'm doing, and should handle this by
>> passing the underlying COM method a pointer to the first element.  I
>> wouldn't expect the wrapper to automatically pack and unpack arbitrary
>> sequences, but at a minimum it needs to accept ctypes arrays and
>> safearrays.
>
> For [in] parameters, it already works this way.  For [out] parameters,
> it is of course more complicated.  But I fear there is not enough
> information in the type libraries to handle this automatically.  There
> are at least some obscure IDL flags like [size_is()], or [string] which
> are, AFAIK, not present in the tlb files.
> It may be required to manually fix the generated code, or provide custom
> marshalling code.

Ah yes, I recall that the .NET bindings for ArcObjects are not just 
auto-generated by the framework.  Instead, ESRI provides their own .NET 
assemblies, mostly auto-generated, but also having some minor tweaks.  This 
is probably one of the reasons for that.

Perhaps I will have to provide some "annotations" to comtypes at 
wrapper-generation time, to help it sort this out.  I don't want to maintain 
a copy of the ArcObjects type information by hand (it's massive) but perhaps 
a set of annotations that I can develop incrementally as I encounter cases 
where there was not enough information in the typelib, and comtypes made a 
reasonable guess that happened to be incorrect.

Since this is Python, no special notation is needed for the annotations. 
They would just be in a dictionary or helper classes.  It would contain 
helpful information like, "hey, for this interface, that parameter declared 
as a [out] long * is actually a pointer to an array, the array maximum size 
is indicated in this parameter, and you can query the object for the 
required size by running this code".

>> This would mean, however, that I might need to pass a buffer / array
>> for an [out] param, so it conflicts a bit with your recent
>> improvements.  Not sure how to reconcile that...
>
> Currently it is not possible at all to pass the [out] value to a method
> call.

Hmm.  I think that's an important capability, because for the sake of 
performance, there are cases where I won't want to have comtypes allocate a 
new buffer for each call.  To get what I need, I might have to pretend the 
parameter is [in, out] even though it's really [out].

> Any ideas how VB handles this stuff would be greatly appreciated.

I'll try to provide examples as we go along.  I wish I could provide the 
ArcObjects themselves, but they are licensed components.  I have them on my 
laptop and will contribute some testing for the project, and also try to 
present a list of user stories to work against.  As I get more familiar with 
ctypes / comtypes, I will hopefully be able to make some contribution to the 
code as well.

>> That's all I have for now.  Hope it helps.
>
> I think so - thanks.
>
> Thomas
>

/Bruce 

-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl

Gmane