pythonccl

How to use the cl command?


All, I found a piece of information on how to call c files in python, in these examples: there is a c file, which includes many other header files, the very beginning of this c files is #include Python.h, then I found that #include Python.h actually involves many many other header files, such as pystate.h, object.h, etc, so I include all the required header files. In an cpp IDE environment, it did not show errors. What I am trying to do is call this c code in python, so from ctypes import *, then it seems that a dll should be generated by code such as: cl -LD test.c -test.dll, but how to use the cl in this case? I used the cygwin: gcc, it worked fine. Could anyone help me with this i.e.: Call the C in python? Do I make myself clear? Thank you in advance!!

Well, Now I feel it important to tell me what I did: The ultimate goal I wanna achieve is: I am lazy, I do not want to re-write those c codes in python, (which is very complicated for me in some cases), so I just want to generate dll files that python could call. I followed an example given by googleing "python call c", there are two versions in this examples: linux and windows: The example test.c:

#include <windows.h>
BOOL APIENTRY  
DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)  {  
    return TRUE;  
}  
__declspec(dllexport) int 
multiply(int num1, int num2)  {  
    return num1 * num2;  
}

Two versions: 1, Complie under linux

gcc -c -fPIC test.c  
gcc -shared test.o -o test.so 

I did this in cygwin on my vista system, it works fine; :)

2, Compile under windows:

cl -LD test.c -test.dll

I used the cl in windows command line prompt, it won't work!

These are the python codes:

from ctypes import *   
import os  
libtest = cdll.LoadLibrary(os.getcwd() + '/test.so')  
print test.multiply(2, 2)  

Could anyone try this and tell me what you get? thank you!


Solution

  • You will find the command line options of Microsoft's C++ compiler here.

    Consider the following switches for cl:

    /nologo /GS /fp:precise /Zc:forScope /Gd
    

    ...and link your file using

    /NOLOGO /OUT:"your.dll" /DLL <your lib files> /SUBSYSTEM:WINDOWS /MACHINE:X86 /DYNAMICBASE
    

    Please have a look at what those options mean in detail, I just listed common ones. You should be aware of their effect nonetheless, so try to avoid copy&paste and make sure it's really what you need - the documentation linked above will help you. This is just a setup I use more or less often.

    Be advised that you can always open Visual Studio, configure build options, and copy the command line invokations from the project configuration dialog.

    Edit: Ok, here is some more advice, given the new information you've edited into your original question. I took the example code of your simple DLL and pasted it into a source file, and made two changes:

    #include <windows.h>
    BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
    {
        return TRUE;  
    } 
    
    extern "C" __declspec(dllexport) int __stdcall multiply(int num1, int num2)
    {
        return num1 * num2;  
    } 
    

    First of all, I usually expect functions exported from a DLL to use stdcall calling convention, just because it's a common thing in Windows and there are languages who inherently cannot cope with cdecl, seeing as they only know stdcall. So that's one change I made.

    Second, to make exports more friendly, I specified extern "C" to get rid of name mangling. I then proceeded to compile the code from the command line like this:

    cl /nologo /GS /Zc:forScope /Gd c.cpp /link /OUT:"foobar.dll" /DL kernel32.lib /SUBSYSTEM:WINDOWS /MACHINE:X86
    

    If you use the DUMPBIN tool from the Visual Studio toolset, you can check your DLL for exports:

    dumpbin /EXPORTS foobar.dll
    

    Seeing something like this...

    ordinal hint RVA      name
        1    0 00001010 ?multiply@@YGHHH@Z
    

    ...you can notice the exported name got mangled. You'll usually want clear names for exports, so either use a DEF file to specify exports in more details, or the shortcut from above.

    Afterwards, I end up with a DLL that I can load into Python like this:

    In [1]: import ctypes
    
    In [2]: dll = ctypes.windll.LoadLibrary("foobar.dll")
    
    In [3]: dll.multiply
    Out[3]: <_FuncPtr object at 0x0928BEF3>
    
    In [4]: dll.multiply(5, 5)
    Out[4]: 25
    

    Note that I'm using ctypes.windll here, which implies stdcall.