6

I'm using Python's ctypes library to talk to a Windows DLL. When I run my code from IDLE, Ipython, or typed into the interactive python interpreter, it works fine. When I run the same code from the Windows command prompt, it crashes. Why does one way crash, and one way succeed?

Here's a simplified version of the code I'm running:

import ctypes, os, sys

print "Current directory:", os.getcwd()
print "sys.path:"
for i in sys.path:
    print i

PCO_api = ctypes.oledll.LoadLibrary("SC2_Cam")

camera_handle = ctypes.c_ulong()
print "Opening camera..."
PCO_api.PCO_OpenCamera(ctypes.byref(camera_handle), 0)
print " Camera handle:", camera_handle.value

wSensor = ctypes.c_uint16(0)
print "Setting sensor format..."
PCO_api.PCO_SetSensorFormat(camera_handle, wSensor)
PCO_api.PCO_GetSensorFormat(camera_handle, ctypes.byref(wSensor))
mode_names = {0: "standard", 1:"extended"}
print " Sensor format is", mode_names[wSensor.value]

When I run this code from IDLE or Ipython, I get the following result:

Current directory: C:\Users\Admin\Desktop\code
sys.path:
C:\Users\Admin\Desktop\code
C:\Python27\Lib\idlelib
C:\Windows\system32\python27.zip
C:\Python27\DLLs
C:\Python27\lib
C:\Python27\lib\plat-win
C:\Python27\lib\lib-tk
C:\Python27
C:\Python27\lib\site-packages
Opening camera...
 Camera handle: 39354336
Setting sensor format...
 Sensor format is standard
>>> 

When I run this code from the Windows command prompt, I get the following results:

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\Admin>cd Desktop\code

C:\Users\Admin\Desktop\code>C:\Python27\python.exe test.py
Current directory: C:\Users\Admin\Desktop\code
sys.path:
C:\Users\Admin\Desktop\code
C:\Windows\system32\python27.zip
C:\Python27\DLLs
C:\Python27\lib
C:\Python27\lib\plat-win
C:\Python27\lib\lib-tk
C:\Python27
C:\Python27\lib\site-packages
Opening camera...
 Camera handle: 43742176
Setting sensor format...
Traceback (most recent call last):
  File "test.py", line 18, in <module>
    PCO_api.PCO_GetSensorFormat(camera_handle, ctypes.byref(wSensor))
  File "_ctypes/callproc.c", line 936, in GetResult
WindowsError: [Error -1609945086] Windows Error 0xA00A3002

C:\Users\Admin\Desktop\code>

Notice that a few of the DLL calls work, it isn't until I get to setting the sensor format that we go off the rails.

By inspecting the documentation that came with the DLL I'm calling, I see the Windows Error decodes to "The wSize of a buffer is to small." (sic). I'm not sure that's relevant. Just in case it matters, here's the API documentation.

When I see "works in IDLE, fails at prompt", I assume there must be some environment variable set differently. What should I check?

EDIT:

I added sys.path and os.getcwd() to the test code.

EDIT:

Not sure if this matters, but the DLL I load (SC2_Cam.dll) is in the current working directory. Also in this directory is another DLL (sc2_cl_me4.dll), which I believe is loaded by SC2_Cam.dll. If I remove sc2_cl_me4.dll from this directory, none of the calls to SC2_Cam.dll work, including PCO_OpenCamera.

EDIT:

The code above also works if I type it into the 'vanilla' interactive python interpreter. I don't need IDLE or ipython to make it work. Only calling 'python.exe test.py' fails.

Andrew
  • 2,842
  • 5
  • 31
  • 49
  • Check `os.getcwd()` and `sys.path` for both in-IDLE and command-line executions. – Joel Cornett May 09 '12 at 23:39
  • Good suggestion, Joel Cornett. I've modified the question as you suggest. – Andrew May 10 '12 at 14:41
  • Does the Cpython on windows still make the `wpython.exe` vs `python.exe` distinction? If so, try `wpython.exe test.py` instead. – Francis Avila May 13 '12 at 02:17
  • @Francis: did you mean `pythonw.exe`? – utapyngo May 13 '12 at 07:05
  • @Francis I tried pythonw.exe instead of python.exe. It's harder to tell what happens, since nothing gets printed to the terminal. I believe it still crashes with pythonw.exe substituted for python.exe. I added an `import time` and a `time.sleep(10)` to the end of the script above, and watched the pythonw.exe process in task manager. The process does not last 10 seconds, unless I comment out the line that crashes python.exe. – Andrew May 14 '12 at 14:59

3 Answers3

5

Do you have more than one version of python installed on your system? Perhaps you are using a different version when you run interactively and when you run it from a file.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Good question. I suppose I can check this with `sys.executable`? – Andrew May 14 '12 at 14:59
  • Interesting. In IDLE, `sys.executable` is `C:\Python27\pythonw.exe`, and the test script works. In Ipython, `sys.executable` is `C:\Python27\python.exe` (note, python, not pythonw), and the test script works. From the Windows command prompt, `sys.executable` is `C:\Python27\python.exe`, and the test script fails. – Andrew May 14 '12 at 15:04
  • Which is to say, I don't think this is the problem. – Andrew May 15 '12 at 19:10
4

When you interface with a C program, you get all the difficulties of C. Any error you make can cause buffer overflows, stack overflows, segmentation violations, etc. If the program, because of an error, writes to a random memory location, its behavior will not be the same in all circumstances. On your machine, it seems to work in interactive mode, but crashes when run from the window command prompt. But on another operating system, or on another machine, or even in the same machine in another day, it could behave differently. Its behavior is not deterministic.

Given that, let's look at the following line:

PCO_api.PCO_OpenCamera(ctypes.byref(camera_handle), 0)

According to the API documentation, in the above call, the PCO_OpenCamera function does not just return a value in camera_handle; it also uses camera_handle as an input value. However, you leave camera_value uninitialized. I understand that you should set it to zero before the call. Another problem is that PCO_OpenCamera returns a value which should be checked. If there is a problem but the program continues as if there wasn't, it will continue to use a random value for the camera_handle. So one error in the program seems to be that the preceding line (save the print) should be

camera_handle = ctypes.c_ulong(0)

and another is that the return value of PCO_OpenCamera isn't checked. (I don't know if the rest is OK, I haven't examined carefully past that.)

Also, is c_ulong the proper type for the Windows HANDLE type? I don't know, and it may be ok. Even if c_ulong is larger than HANDLE, it's still probably OK. But probably is not enough; you must be certain that you know what you're doing.

Antonis Christofides
  • 6,990
  • 2
  • 39
  • 57
  • Good suggestions. I'll check the return value of the API call, and the initial value of camera_handle. As for the proper type for HANDLE, I believe I checked a header file to determine this, but that was a while ago, I'll check again. – Andrew May 15 '12 at 18:39
  • Ok, regarding the return value of the API call: [oledll handles it](http://docs.python.org/library/ctypes.html#loading-dynamic-link-libraries). The WindowsError messages I get match up to error codes described in the API. I open [this file](http://www.pco.de/fileadmin/user_upload/pco/test/SW_PCOSDKWIN_115.zip) as an archive using 7zip, pull out PCO_err.h and PCO_errt.h, and match the WindowsError code to the corresponding error message. That's how I got the message "The wSize of a buffer is to small" that I reference in the question above. – Andrew May 15 '12 at 19:01
  • Now, regarding the initial value of camera_handle: `camera_handle = ctypes.c_ulong(0)` and `camera_handle = ctypes.c_ulong()` both produce the same initial value of `camera_handle.value`, zero, whether they're run from IDLE or the command prompt. – Andrew May 15 '12 at 19:04
  • Regarding the proper type for HANDLE: In the file `sc2_SDKStructures.h`, which I got from opening [this file](http://www.pco.de/fileadmin/user_upload/pco/test/SW_PCOSDKWIN_115.zip) as an archive via 7zip, I read: "HANDLE will be 8byte on 64bit OS and 4byte on 32bit OS". I'm using 64-bit Windows 7. – Andrew May 15 '12 at 19:16
  • Interesting. `a = ctypes.c_ulong()` followed by `print ctypes.sizeof(a)` gives 4... – Andrew May 15 '12 at 19:19
  • HAHA! THAT DID IT! NICE WORK! – Andrew May 15 '12 at 19:20
  • I just change `camera_handle.ctypes.c_ulong()` to `camera_handle.ctypes.c_ulonglong()` and the problem seems to go away! If I was never using the right size object for my camera handle, I wonder how it was working in IDLE, ipython, and the interactive python interpreter? – Andrew May 15 '12 at 19:22
  • Ok, I'm going to poke around with the code a bit more, but if things continue to go well, there's a good chance you've given the correct answer. Thanks for the help. – Andrew May 15 '12 at 19:23
1

The error you're getting leads me to believe that the 16 bit integer you're using to store the wSensor variable is too small. I looked at their API, which just specifies it as type WORD, which historically by Microsoft standards should be 16 bits, but since there is a lot of ambiguity around how big a word is, try upping the value to 32 or 64 bits.

As to why this would cause different behavior in different environments, are you using a 64-bit OS? Do you have a different version of python installed?

falcojr
  • 1,299
  • 1
  • 10
  • 18
  • I am using 64-bit Windows 7. `sys.version` gives `'2.7.2 (default, Jun 12 2011, 14:24:46) [MSC v.1500 64 bit (AMD64)]'`. I'll try out changing the size of wSensor. Thanks for looking at the API! – Andrew May 14 '12 at 15:45
  • I tried changing the type of wSensor from `c_uint16` to `c_int32` and `c_uint64`. Still works in IDLE, ipython, or the interactive interpreter, still fails at the windows command prompt. – Andrew May 14 '12 at 15:49
  • In hindsight, I should have also checked camera_handle. This is a good suggestion. – Andrew Oct 26 '12 at 01:34