23

I'm developing on Windows, and I've searched everywhere without finding anyone talking about this kind of thing.

I made a C++ app on my desktop that embedded Python 3.1 using MSVC. I linked python31.lib and included python31.dll in the app's run folder alongside the executable. It works great. My extension and embedding code definitely works and there are no crashes.

I sent the run folder to my friend who doesn't have Python installed, and the app crashes for him during the scripting setup phase.

A few hours ago, I tried the app on my laptop that has Python 2.6 installed. I got the same crash behavior as my friend, and through debugging found that it was the Py_Initialize() call that fails.

I installed Python 3.1 on my laptop without changing the app code. I ran it and it runs perfectly. I uninstalled Python 3.1 and the app crashes again. I put in code in my app to dynamically link from the local python31.dll, to ensure that it was using it, but I still get the crash.

I don't know if the interpreter needs more than the DLL to start up or what. I haven't been able to find any resources on this. The Python documentation and other guides do not seem to ever address how to distribute your C/C++ applications that use Python embedding without having the users install Python locally. I know it's more of an issue on Windows than on Unix, but I've seen a number of Windows C/C++ applications that embed Python locally and I'm not sure how they do it.

What else do I need other than the DLL? Why does it work when I install Python and then stop working when I uninstall it? It sounds like it should be so trivial; maybe that's why nobody really talks about it. Nevertheless, I can't really explain how to deal with this crash issue.

Thank you very much in advance.

pjs
  • 18,696
  • 4
  • 27
  • 56
apostrophest
  • 268
  • 1
  • 2
  • 6
  • 1
    Does your loadlibrary call succeed? Where are you putting the dll? It could be as simple as the application not being able to find the library. – patros Sep 07 '09 at 07:19
  • 1
    LoadLibrary succeeded, as did GetProcAddress. Since I got the same exception as I did without the dynamic link (rather than a NULL access violation) I believe Py_Initialize was called. The DLL is in the run directory. My run directory contains: .\ss_server.exe, .\python31.dll, .\admin.sqlite, .\settings.py, .\err.txt – apostrophest Sep 07 '09 at 08:03

4 Answers4

24

In addition to pythonxy.dll, you also need the entire Python library, i.e. the contents of the lib folder, plus the extension modules, i.e. the contents of the DLLs folder. Without the standard library, Python won't even start, since it tries to find os.py (in 3.x; string.py in 2.x). On startup, it imports a number of modules, in particular site.py.

There are various locations where it searches for the standard library; in your cases, it eventually finds it in the registry. Before, uses the executable name (as set through Py_SetProgramName) trying to find the landmark; it also checks for a file python31.zip which should be a zipped copy of the standard library. It also checks for a environment variable PYTHONHOME.

You are free to strip the library from stuff that you don't need; there are various tools that compute dependencies statically (modulefinder in particular).

If you want to minimize the number of files, you can

  1. link all extension modules statically into your pythonxy.dll, or even link pythonxy.dll statically into your application
  2. use the freeze tool; this will allow linking the byte code of the standard library into your pythonxy.dll.
  3. (alternatively to 2.) use pythonxy.zip for the standard library.
Martin v. Löwis
  • 124,830
  • 17
  • 198
  • 235
  • Thank you, Martin. Your explanation fills in some of the gaps that I couldn't fill while searching for an answer. I will investigate some of your proposed solutions. – apostrophest Sep 07 '09 at 07:27
  • 1
    I made a python31.zip consisting of C:\Python31\Lib\*.* and C:\Python31\DLLs\*.* I put this alongside my application EXE and the python31.dll. The program works! The zip is 13 megs, so, as you suggest, I should figure out just what I need and pare it down. I am marking this as a solution, thanks so much! – apostrophest Sep 07 '09 at 08:17
  • I could not make python26.zip work - tried almost everything. Are you sure that python was *not* installed when python31.zip worked? All other posts on stackoverflow (making pythonxx.zip work) are unanswered as well! – sambha Oct 06 '10 at 02:05
  • And what about the modules, do you put them in the python31.zip? – João Pereira Apr 15 '14 at 16:15
  • Which modules specifically? You can put any py and pyc modules into the zip file, but not any pyd files. – Martin v. Löwis Apr 16 '14 at 16:43
  • Modules made by me. Ok so i should put them in the zip file, thank you. But for example multiarray.pyd, it's inside numpy folder and inside Lib folder. It shouldn't go inside the zip? – João Pereira Apr 17 '14 at 13:10
13

Nice. And if you do not want to zip, copy Python26\DLLs & Python26\lib to your exe directory as:

.\myexe.exe       
.\python26.dll
.\Python26\DLLs
.\Python26\lib

And then set PYTHONHOME with Py_SetPythonHome() API. Apparently, this API is not in the list of "allowed" calls before Py_Initialize();

Below worked for me on Windows (Python not installed):

#include "stdafx.h"
#include <iostream>
#include "Python.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
   char pySearchPath[] = "Python26";
   Py_SetPythonHome(pySearchPath);
   Py_Initialize();
   PyRun_SimpleString("from time import time,ctime\n"
                     "print 'Today is',ctime(time())\n");
   //cerr << Py_GetPath() << endl;
   Py_Finalize();

    return 0;
}

Good that the search path is relative w.r.t the exe. Py_GetPath can show you where all it is looking for the modules.

sambha
  • 1,333
  • 3
  • 17
  • 30
  • the above solution worked fine for admin user. for non-admin user however, the application doesnt start. any ideas ? – balas bellobas Jun 26 '14 at 11:36
  • @balasbellobas Do you have pywin32 in your python? I think I had a similar issue (embedded Excel com calls worked only "as" administrator). There was some gen_py issue where python tried creating files in non-permitted folder (perhaps program files). You may want to search more on gen_py. I think I just had to delete some files. Hope it helps. – sambha Jul 07 '14 at 15:26
7

I wanted to add some additional info for others who might still be having troubles with this, as I was. I was eventually able to get my application working using the method proposed by user sambha, that is:

Program Files (x86)\
    MyApplicationFolder\
        MyApplication.exe
        python27.dll
        Python27\
            DLLs\ (contents of DLLs folder)
            Lib\ (contents of Lib folder)

...with one important addition: I also needed to install the MSVCR90.DLL. I'm using Python 2.7 and apparently python27.dll requires the MSVCR90.DLL (and maybe other MSVC*90.DLLs).

I solved this by downloading, installing, and running the 'vcredist_x86.exe' package from http://www.microsoft.com/en-us/download/details.aspx?id=29 . I think, though I am not certain, that you need to do it this way at least on Win7, as opposed to simply placing the MSVC*90.DLLs alongside your .exe as you may have done in the past. The Microsoft installer places the files and registers them in a special way under Win7.

I also tried the .zip file method but that did not work, even with the MSVCR90.DLL installed.

djangodude
  • 5,362
  • 3
  • 27
  • 39
6

A zip of the Python standard library worked for me with Python27.

I zipped the contents of Lib and dll, and made sure there was no additional python27-subfolder or Lib or dll subfolder. i.e. just a zip named python27.zip containing all the files.

I copied that zip and the python27.dll alongside the executable.

user1185601
  • 61
  • 1
  • 1