ctypes, function pointers and a lot of trouble

Discussion in 'Python' started by Matt, May 28, 2008.

  1. Matt

    Matt Guest

    Hi friends,

    Okay so well, I have quite a problem right now with a file stream. What
    I am doing is to use the Cannon SDK dlls to get control over my old
    Cannon A60 Camera for some surveillance useage.

    By using ctypes it all worked well until now. I am able to load the
    dlls, use a lot of functions, am able to connect to the camera, read out
    some params, send commands etc... but now I am stuck with reading the
    picture data.

    In C the code looks as follows (found in an supplemental *.h SDK file:

    ------------------------------CODE-----------------------------------------

    #define cdSTDCALL __stdcall

    typedef void cdSTDCALL cdSOpen(cdContext contextH, cdPermission,
    cdError* err);
    typedef void cdSTDCALL cdSClose(cdContext contextH, cdError* err);
    typedef void cdSTDCALL cdSRead(cdContext contextH, void* buf,
    cdUInt32* bufsize, cdError* err);
    typedef void cdSTDCALL cdSWrite(cdContext contextH, const void *buf,
    cdUInt32* bufsize, cdError *err);
    typedef void cdSTDCALL cdSSeek(cdContext contextH, cdWhence, cdInt32
    offset, cdError* err);
    typedef cdInt32 cdSTDCALL cdSTell(cdContext contextH, cdError* err);
    typedef void cdSTDCALL cdSProgress(cdContext contextH, cdUInt16
    percentDone, cdError* err);

    /* cdStream
    */
    typedef struct {
    cdContext contextH;
    /* stream I/O function pointers */
    cdSOpen* open;
    cdSClose* close;
    cdSRead* read;
    cdSWrite* write;
    cdSSeek* seek;
    cdSTell* tell;
    } cdStream;

    /* cdStgMedium
    */
    typedef struct {
    cdMemType Type; /* Type of the medium (u). */
    union {
    cdChar* lpszFileName;
    cdStream* pStream;
    #ifdef macintosh
    cdFSSpec* pFSSpec;
    #endif
    }u; /* Union of all transfer medium */
    } cdStgMedium;

    ------------------------------\CODE----------------------------------------

    and this is the function definition that should give me access to the
    data stream (available via DLL):

    ------------------------------CODE-----------------------------------------

    cdCAPI CDGetReleasedData(
    cdHSource hSource,
    cdProgressCallbackFunction * pCallbackFunc,
    cdContext Context,
    cdProgressOption ProgressOption,
    cdReleaseImageInfo* pInfo,
    cdStgMedium* pStgMedium
    );

    ------------------------------\CODE----------------------------------------

    So, since I'm no C-Professional, I can only guess what that code does.
    With some previous commands I tell the camera to make a picture. This
    picture is then automatically moved to the PCs RAM and with the function
    above (CDGetReleasedData) I should be able to access this stream. Now I
    havn't accessed any stream with ctypes yet so I have only a rough idea
    how it could work.

    The following are the relevant parts of my code that don't work:

    ------------------------------CODE-----------------------------------------

    # Definitions:

    class cdReleaseImageInfo(Structure):
    _fields_ = [("SequenceID", c_uint),
    ("DataType", c_uint),
    ("Format", c_ubyte),
    ("DataSize", c_uint),
    ("Filename", c_char * 2)]


    class cdStream(Structure):
    _fields_ = [("contextH", c_uint),
    ("open", c_uint),
    ("close", c_uint),
    ("read", c_uint),
    ("write", c_uint),
    ("seek", c_uint),
    ("tell", c_uint)]

    class memunion(Union):
    _fields_ = [("lpszFileName", c_char),
    ("pStream", cdStream)]


    class cdStgMedium(Structure):
    _fields_ = [("Type", c_uint),
    ("u", memunion)]




    # command:

    datainfo = cdReleaseImageInfo()
    data = cdStgMedium()
    errorcode = cdsdk.CDGetReleasedData(devicehandle, byref(cbfunct),
    c_uint(1), c_uint(1), byref(datainfo), byref(data))

    ------------------------------\CODE----------------------------------------


    The function "cdsdk.CDGetReleasedData" itself gets executed correctly,
    but returns an "Invalid Parameter" errorcode. there's also no useful
    data whereas datainfo gets written correctly. I know that my cdStream
    can't work, facing the C-code, but what'd be the right cdStream class?
    What can I do? Any ideas?

    Best regards and thanks,
    Matt
    Matt, May 28, 2008
    #1
    1. Advertising

  2. Matt

    Matt Guest

    Okay, thanks a lot for your reply Nick, I think you pushed me back on
    the right way.

    Now I started with trying to implement the callback functions and am
    stuck at the following point:

    I define my classes/structures/unions:

    ------------------------------CODE-----------------------------------------

    class cdStream(Structure):
    _fields_ = [("contextH", c_uint),
    ("open", c_void_p),
    ("close", c_void_p),
    ("read", c_void_p),
    ("write", c_void_p),
    ("seek", c_void_p),
    ("tell", c_void_p)]

    class memunion(Union):
    _fields_ = [("lpszFileName", c_char_p),
    ("pStream", cdStream)]


    class cdStgMedium(Structure):
    _fields_ = [("Type", c_uint),
    ("u", memunion)]

    ------------------------------\CODE----------------------------------------

    then i define my functions (right now I do just nothing) and at the same
    time I define the datatypes like they're listed in my C sample program:


    ------------------------------CODE-----------------------------------------

    def pystreamopen (contextH, mode, pErr):
    pass

    cstreamopen = CFUNCTYPE(c_uint, c_ushort, c_uint)

    def pystreamclose (contextH, pErr):
    pass

    cstreamclose = CFUNCTYPE(c_uint, c_uint)

    def pystreamread (contextH, pBuf, pBufsize, pErr):
    pass

    cstreamread = CFUNCTYPE(c_uint, c_void_p, c_uint, c_uint)

    def pystreamtell (contextH, pErr):
    pass

    cstreamtell = CFUNCTYPE(c_uint, c_uint)

    def pystreamwrite (contextH, origin, offset, pErr):
    print "writing..."
    pass

    cstreamwrite = CFUNCTYPE(c_uint, c_void_p, c_uint, c_uint)

    ------------------------------\CODE----------------------------------------

    and now the problem starts: i want the pointers in the cdStream
    Structure point at my functions and tried to do it the following way:


    ------------------------------CODE-----------------------------------------

    data = cdStgMedium()
    data.type = 0
    data.u.pStream.contextH = c_uint(3) #must be some kind of identifier.
    data.u.pStream.open = cstreamopen(pystreamopen)
    data.u.pStream.close = cstreamclose(pystreamclose)
    data.u.pStream.write = cstreamwrite(pystreamwrite)
    data.u.pStream.tell = cstreamtell(pystreamtell)
    data.u.pStream.read = cstreamread(pystreamread)

    ------------------------------\CODE----------------------------------------

    unfortunately that doesn't work because Python returns a TypeError:
    incompatible types, CFunctionType instance instead of c_void_p instance

    Any ideas/help (please)?

    Best regards,
    Matt
    Matt, May 29, 2008
    #2
    1. Advertising

  3. Matt

    Matt Guest

    Hi,

    okay, thanks now my DLL function seems to accept the functions given,
    but instead of running my program keeps crashing (with windows address
    violation errors). I did some further investigations on that and figured
    out that the contextH variable is not just an identifier as I thought,
    but a quite complicated datastreamhandler.

    Now to get it to work I additionally need to implement the following c
    Structures:

    ------------------------------CODE-----------------------------------------

    /* The internal data structure object of a stream */
    typedef struct tagMemStreamData
    {
    cdChar mode;
    cdInt32 lPos;
    cdUInt32 dwVisibleSize;
    cdUInt32 dwBufferSize;
    cdChar *cpBuffer;
    }MemStreamData;

    typedef struct tagFilStreamData
    {
    cdChar szFileName[MAX_PATH];
    HANDLE hFile;
    }FilStreamData;

    ------------------------------\CODE----------------------------------------

    This function just creates the pStream filestream (which is what I have
    to do too)

    ------------------------------CODE-----------------------------------------

    BOOL CreateMyFilStream( cdStream *pStream,
    cdChar *szFileName )
    {
    FilStreamData *pFilStrm;

    /* The domain for data is secured. */
    pFilStrm = new FilStreamData;
    if( pFilStrm == NULL )
    {
    return FALSE;
    }

    /* Data is changed the first stage. */
    pFilStrm->hFile = INVALID_HANDLE_VALUE;
    strcpy( pFilStrm->szFileName, szFileName );

    pStream->contextH = (cdContext)pFilStrm;
    pStream->close = _CloseMyFilStream;
    pStream->open = _OpenMyFilStream;
    pStream->read = _ReadMyFilStream;
    pStream->seek = _SeekMyFilStream;
    pStream->tell = _TellMyFilStream;
    pStream->write = _WriteMyFilStream;

    return TRUE;
    }


    ------------------------------\CODE----------------------------------------



    Now my solution is the following:


    ------------------------------CODE-----------------------------------------

    class MemStreamData(Structure):
    _fields_ = [("mode", c_byte),
    ("lPos", c_uint),
    ("dwVisibleSize", c_uint),
    ("dwBufferSize", c_uint),
    ("cpBuffer", POINTER(c_char))]

    class FilStreamData (Structure):
    _fields_ = [("szFileName", c_char * 30),
    ("hFile", c_uint)]


    ------------------------------\CODE----------------------------------------

    and the code to fill all that with the right data is the following:

    ------------------------------CODE-----------------------------------------

    datainfo = cdReleaseImageInfo()

    databuf = c_char * 100000
    cbuffer=MemStreamData()
    cbuffer.mode = c_byte(0)
    cbuffer.lPos = c_uint(0)
    cbuffer.dwVisibleSize = c_uint(100000)
    cbuffer.dwBufferSize = c_uint(100000)

    #this line does not work, wrong datatype!?
    cbuffer.cpBuffer = POINTER(databuf)

    cpointer = cast(cbuffer, POINTER(c_uint))
    stream = cdStream()
    stream.contextH=cpointer
    stream.open = cstreamopen(pystreamopen)
    stream.close = cstreamclose(pystreamclose)
    stream.write = cstreamwrite(pystreamwrite)
    stream.tell = cstreamtell(pystreamtell)
    stream.read = cstreamread(pystreamread)



    data = cdStgMedium()
    data.Type = c_uint(1)
    data.u.pStream = POINTER(cdStream)(stream)

    ------------------------------\CODE----------------------------------------

    Does anyone see where the errors are?

    Best regards and thanks a lot,
    Matt
    Matt, Jun 2, 2008
    #3
  4. Matt

    Matt Guest

    Hello!

    ouch, I should have seen that c_char... :S Well, I guess I just prove
    that it's useless to go to work and do some programming while having a
    headache like I had yesterday...

    okay well, back to topic:

    The DLL function seems to accept my parameters now, but unfortunately
    Python terminates after the DLL gets the result from the "open" callback
    function (at least its printouts are the last I get before it
    terminates). So without any kind of error message it's getting more
    difficult now.

    Well, since I didn't invest much time into my callback functions I
    suspect the error must be somewhere in there.

    This is my code:

    ------------------------------CODE-----------------------------------------

    class MemStreamData(Structure):
    _fields_ = [("mode", c_byte),
    ("lPos", c_uint),
    ("dwVisibleSize", c_uint),
    ("dwBufferSize", c_uint),
    ("cpBuffer", POINTER(c_char))]

    class FilStreamData (Structure):
    _fields_ = [("szFileName", c_char * 30),
    ("hFile", c_uint)]






    def pystreamopen (contextH, mode, pErr=0):
    print "opening..."
    print contextH
    print mode
    print pErr
    return 0

    cstreamopen = CFUNCTYPE(c_uint, c_ushort, c_uint)

    def pystreamclose (contextH, pErr):
    print "closing..."
    return 0

    cstreamclose = CFUNCTYPE(c_uint, c_uint)

    def pystreamread (contextH, pBuf, pBufsize, pErr):
    print "reading..."
    return 0

    cstreamread = CFUNCTYPE(c_uint, c_void_p, c_uint, c_uint)

    def pystreamtell (contextH, pErr):
    print "telling..."
    return 0

    cstreamtell = CFUNCTYPE(c_uint, c_uint)


    def pystreamseek (contextH, origin, offset, pErr):
    print "seeking..."
    return 0

    cstreamseek = CFUNCTYPE(c_uint, c_uint, c_uint, c_uint)


    def pystreamwrite (contextH, origin, offset, pErr):
    print "writing..."
    return 0

    cstreamwrite = CFUNCTYPE(c_uint, c_void_p, c_uint, c_uint)


    class cdStream(Structure):
    _fields_ = [("contextH", POINTER(MemStreamData)),
    ("open", cstreamopen),
    ("close", cstreamclose),
    ("read", cstreamread),
    ("write", cstreamwrite),
    ("seek", cstreamseek),
    ("tell", cstreamtell)]

    -----------------------------/CODE-----------------------------------------



    This is the way I create the vars:




    ------------------------------CODE-----------------------------------------


    databuf = create_string_buffer(100000)
    cbuffer=MemStreamData()
    cbuffer.mode = c_byte(0)
    cbuffer.lPos = c_uint(0)
    cbuffer.dwVisibleSize = 100000
    cbuffer.dwBufferSize = 100000
    cbuffer.cpBuffer = databuf

    stream = cdStream()
    stream.contextH = POINTER(MemStreamData)(cbuffer)
    stream.open = cstreamopen(pystreamopen)
    stream.close = cstreamclose(pystreamclose)
    stream.write = cstreamwrite(pystreamwrite)
    stream.tell = cstreamtell(pystreamtell)
    stream.read = cstreamread(pystreamread)


    data = cdStgMedium()
    data.Type = c_uint(1) # 0...FilStream 1...MemStream
    data.u.pStream = POINTER(cdStream)(stream)

    errorcode = cdsdk.CDGetReleasedData(devicehandle, byref(cbfunct),
    c_uint(0), c_uint(0), byref(datainfo), POINTER(cdStgMedium)(data))

    ------------------------------/CODE-----------------------------------------



    Now I have two problems:

    1st: since contextH is not a c_uint (and pErr is a pointer) as I thought
    earlier, I tried to change my definition of the open function to:

    cstreamopen = CFUNCTYPE(POINTER(MemStreamData), c_ushort, POINTER(c_uint))

    unfortunately that throws an error when I try to:

    stream.open = cstreamopen(pystreamopen)


    2nd: as may saw, I defined "def pystreamopen (contextH, mode, pErr=0)".
    The pErr variable should be a pointer to a c_uint where my function can
    tell the DLL that opening the stream went well (or give some errorcode).

    When I do not define pErr=0 and simply say pErr, I get the following error:

    Traceback (most recent call last):
    File "\loewis\25\python\Modules\_ctypes\callbacks.c", line 206, in
    'calling callback function'
    TypeError: pystreamopen() takes exactly 3 arguments (2 given)

    At first I thought okay, maybe there's no pErr and there's some error in
    the C-Code, but when I do "def pystreamopen (contextH, mode)" I get the
    same Error with:
    TypeError: pystreamopen() takes exactly 3 arguments (2 given)


    Any ideas?


    And another question: my callback functions are all defined as void...
    in C. That means that there shouldn't be anything returned. I tried this
    by using the pass statement, but got an error that returntype int was
    expected. Also "return" or "return None" don't work. Why?



    Puh, long mail again... hope you're so kind again and take the time to
    help me out.

    Best regards from Austria,
    Matt
    Matt, Jun 3, 2008
    #4
  5. Matt

    MRAB Guest

    On Jun 3, 11:22 am, Matt <> wrote:
    > Hello!
    >
    > ouch, I should have seen that c_char... :S Well, I guess I just prove
    > that it's useless to go to work and do some programming while having a
    > headache like I had yesterday...
    >
    > okay well, back to topic:
    >
    > The DLL function seems to accept my parameters now, but unfortunately
    > Python terminates after the DLL gets the result from the "open" callback
    > function (at least its printouts are the last I get before it
    > terminates). So without any kind of error message it's getting more
    > difficult now.
    >
    > Well, since I didn't invest much time into my callback functions I
    > suspect the error must be somewhere in there.
    >
    > This is my code:
    >
    > ------------------------------CODE-----------------------------------------
    >
    > class MemStreamData(Structure):
    > _fields_ = [("mode", c_byte),
    > ("lPos", c_uint),
    > ("dwVisibleSize", c_uint),
    > ("dwBufferSize", c_uint),
    > ("cpBuffer", POINTER(c_char))]
    >
    > class FilStreamData (Structure):
    > _fields_ = [("szFileName", c_char * 30),
    > ("hFile", c_uint)]
    >
    > def pystreamopen (contextH, mode, pErr=0):
    > print "opening..."
    > print contextH
    > print mode
    > print pErr
    > return 0
    >
    > cstreamopen = CFUNCTYPE(c_uint, c_ushort, c_uint)
    >
    > def pystreamclose (contextH, pErr):
    > print "closing..."
    > return 0
    >
    > cstreamclose = CFUNCTYPE(c_uint, c_uint)
    >
    > def pystreamread (contextH, pBuf, pBufsize, pErr):
    > print "reading..."
    > return 0
    >
    > cstreamread = CFUNCTYPE(c_uint, c_void_p, c_uint, c_uint)
    >
    > def pystreamtell (contextH, pErr):
    > print "telling..."
    > return 0
    >
    > cstreamtell = CFUNCTYPE(c_uint, c_uint)
    >
    > def pystreamseek (contextH, origin, offset, pErr):
    > print "seeking..."
    > return 0
    >
    > cstreamseek = CFUNCTYPE(c_uint, c_uint, c_uint, c_uint)
    >
    > def pystreamwrite (contextH, origin, offset, pErr):
    > print "writing..."
    > return 0
    >
    > cstreamwrite = CFUNCTYPE(c_uint, c_void_p, c_uint, c_uint)
    >
    > class cdStream(Structure):
    > _fields_ = [("contextH", POINTER(MemStreamData)),
    > ("open", cstreamopen),
    > ("close", cstreamclose),
    > ("read", cstreamread),
    > ("write", cstreamwrite),
    > ("seek", cstreamseek),
    > ("tell", cstreamtell)]
    >
    > -----------------------------/CODE-----------------------------------------
    >
    > This is the way I create the vars:
    >
    > ------------------------------CODE-----------------------------------------
    >
    > databuf = create_string_buffer(100000)
    > cbuffer=MemStreamData()
    > cbuffer.mode = c_byte(0)
    > cbuffer.lPos = c_uint(0)
    > cbuffer.dwVisibleSize = 100000
    > cbuffer.dwBufferSize = 100000
    > cbuffer.cpBuffer = databuf
    >
    > stream = cdStream()
    > stream.contextH = POINTER(MemStreamData)(cbuffer)
    > stream.open = cstreamopen(pystreamopen)
    > stream.close = cstreamclose(pystreamclose)
    > stream.write = cstreamwrite(pystreamwrite)
    > stream.tell = cstreamtell(pystreamtell)
    > stream.read = cstreamread(pystreamread)
    >
    > data = cdStgMedium()
    > data.Type = c_uint(1) # 0...FilStream 1...MemStream
    > data.u.pStream = POINTER(cdStream)(stream)
    >
    > errorcode = cdsdk.CDGetReleasedData(devicehandle, byref(cbfunct),
    > c_uint(0), c_uint(0), byref(datainfo), POINTER(cdStgMedium)(data))
    >
    > ------------------------------/CODE-----------------------------------------
    >
    > Now I have two problems:
    >
    > 1st: since contextH is not a c_uint (and pErr is a pointer) as I thought
    > earlier, I tried to change my definition of the open function to:
    >
    > cstreamopen = CFUNCTYPE(POINTER(MemStreamData), c_ushort, POINTER(c_uint))
    >
    > unfortunately that throws an error when I try to:
    >
    > stream.open = cstreamopen(pystreamopen)
    >
    > 2nd: as may saw, I defined "def pystreamopen (contextH, mode, pErr=0)".
    > The pErr variable should be a pointer to a c_uint where my function can
    > tell the DLL that opening the stream went well (or give some errorcode).
    >
    > When I do not define pErr=0 and simply say pErr, I get the following error:
    >
    > Traceback (most recent call last):
    > File "\loewis\25\python\Modules\_ctypes\callbacks.c", line 206, in
    > 'calling callback function'
    > TypeError: pystreamopen() takes exactly 3 arguments (2 given)
    >
    > At first I thought okay, maybe there's no pErr and there's some error in
    > the C-Code, but when I do "def pystreamopen (contextH, mode)" I get the
    > same Error with:
    > TypeError: pystreamopen() takes exactly 3 arguments (2 given)
    >
    > Any ideas?
    >
    > And another question: my callback functions are all defined as void...
    > in C. That means that there shouldn't be anything returned. I tried this
    > by using the pass statement, but got an error that returntype int was
    > expected. Also "return" or "return None" don't work. Why?
    >
    > Puh, long mail again... hope you're so kind again and take the time to
    > help me out.
    >

    The docs say CFUNCTYPE(restype, *argtypes), so:

    cstreamopen = CFUNCTYPE(c_uint, c_ushort, c_uint)

    is saying that the result type is c_uint, not void. I think you need:

    cstreamopen = CFUNCTYPE(None, c_uint, c_ushort, c_uint)

    instead.
    MRAB, Jun 3, 2008
    #5
  6. Matt

    Matt Guest


    > The docs say CFUNCTYPE(restype, *argtypes), so:
    >
    > cstreamopen = CFUNCTYPE(c_uint, c_ushort, c_uint)
    >
    > is saying that the result type is c_uint, not void. I think you need:
    >
    > cstreamopen = CFUNCTYPE(None, c_uint, c_ushort, c_uint)
    >
    > instead.


    Hm, thanks, now I can access my data in the functions and also write
    them but the program keeps terminating right at the point when the
    "open" function finishes. Unfortunately everything closes and I get no
    error messages.

    I did some additional work in the meantime and changed my code so it has
    the correct datatypes now:

    --------------------------CODE--------------------------------------------

    def pystreamopen (contextH, mode, pErr):
    print "opening..."
    print contextH.contents.dwBufferSize #just to check the structure
    print mode #tells about what the DLL wants to do with this stream
    contextH.contents.mode = c_byte(5) #5=Permission to read and write
    contextH.contents.lPos = c_uint(0) #start position

    print pErr.contents
    pErr.contents = c_uint(0)


    cstreamopen = CFUNCTYPE(None, POINTER(MemStreamData), c_ushort,
    POINTER(c_uint))

    def pystreamclose (contextH, pErr):
    print "closing..."
    pass

    cstreamclose = CFUNCTYPE(None, POINTER(MemStreamData), POINTER(c_uint))

    def pystreamread (contextH, pBuf, pBufsize, pErr):
    print "reading..."
    pass

    cstreamread = CFUNCTYPE(None, POINTER(MemStreamData), c_uint, c_uint,
    POINTER(c_uint))

    def pystreamtell (contextH, pErr):
    print "telling...

    ..
    ..
    ..etc...

    --------------------------/CODE--------------------------------------------

    and the function call:

    --------------------------CODE--------------------------------------------

    errorcode = cdsdk.CDGetReleasedData(devicehandle, byref(cbfunct),
    c_uint(0), c_uint(0), byref(datainfo), POINTER(cdStgMedium)(data))

    --------------------------/CODE--------------------------------------------


    while it's running my program prints the following and then
    exits/terminates:

    >>> opening...990001
    >>> 2
    >>> c_ulong(0L)
    >>> Script terminated. ##This is a message by my editor (SPE)


    I also tried different editors, but get the same result...



    Anyway, meanwhile decided to try a different approach. Maybe I have
    more luck by having the function write the data directly into a file on
    the HDD.
    Doe anyone know how to translate the following into Python/ctypes?

    I googled quite a lot before but all topic-related I found was my own
    posting here in this NG :S

    --------------------------CODE--------------------------------------------

    pFilStrm->hFile = CreateFile( pFilStrm->szFileName,
    dwDesiredAccess, dwShareMode, NULL,
    dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL );


    --------------------------/CODE--------------------------------------------

    This function is obviously a default C-function that generates a file
    and returns a c-handle to that file. In my case I'd need exactly such a
    handle to get things working...

    Furthermore there's a writefile function I'd need to translate too:

    --------------------------CODE--------------------------------------------

    WriteFile( pFilStrm->hFile, pBuf, dwNumberOfBytesToWrite, pBufsize, NULL );

    --------------------------/CODE--------------------------------------------


    Ideas?

    Thanks and best wishes,
    Matt
    Matt, Jun 3, 2008
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Evan
    Replies:
    1
    Views:
    1,308
    Howard Hinnant
    Jun 23, 2003
  2. Henk Punt
    Replies:
    0
    Views:
    390
    Henk Punt
    Jul 23, 2004
  3. Replies:
    5
    Views:
    346
  4. Thomas G. Marshall
    Replies:
    2
    Views:
    675
    Chris Torek
    Jul 3, 2004
  5. Replies:
    0
    Views:
    492
Loading...

Share This Page