2014-11-26 2 views
1

일부 코드 (PyCXX에서 곧바로 적용됨) 주위에 머리를 쓰려고합니다. 다중 플랫폼 C++ 파이썬 랩퍼입니다.Windows에서 지연로드

편집 : 원본 코드 here.

그것은 단지 윈도우에 존재하는 어떤 특별한 현상을 음식을 장만 할 나타납니다

#ifdef PY_WIN32_DELAYLOAD_PYTHON_DLL 
: 
#else 
: 
#endif 

내가 아래 목록 전체 파일을 줄 것이다, 꽤 깁니다.

이 PY_WIN32_DELAYLOAD_PYTHON_DLL 토큰은 CPython 내에 존재하지 않으며 PyCXX에도 정의되어 있지 않습니다. 따라서 PyCXX는 선택적 컴파일러 플래그로 제공 될 것이라고 생각할 수 있습니다.

내가 알고 싶은 것은 : 그 목적은 무엇인가? 어떤 문제가 해결됩니까? 이 메커니즘은 왜 존재합니까?

아마도 Windows 프로그래밍에 익숙한 사람이 코드에서 알아낼 수 있습니까?

코드가 15 세 이상이므로 해결해야 할 문제가 여전히 최신 Windows에 있는지 여부를 알고 싶습니다.

키 질문은 다음과 같습니다. 이를 제거 할 수 있습니까? 아니면 청소기로 교체 할 수 있습니까?

나는 그것을 아주 잘랐다. 하지만 여전히 현대 Windows 환경에서 유용한 목적을 수행합니까?

코드 :

는 Win32에
#include "Base.hxx" //"IndirectPythonInterface.hxx" 

namespace Py 
{ 

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 

#ifdef PY_WIN32_DELAYLOAD_PYTHON_DLL 

# ifndef MS_WINDOWS 
#  error "Can only delay load under Win32" 
# endif 
# include <windows.h> // !!! Is it possible we ever want Windows.h but no delay load? If so, this is wrong... 

static HMODULE python_dll; 

# define DECLARE_ERROR(THE_ERROR) \ 
     static PyObject* ptr__Exc_##THE_ERROR = nullptr; 
ALL_ERRORS(DECLARE_ERROR) 


static PyObject* ptr__PyNone  = nullptr; 
static PyObject* ptr__PyFalse  = nullptr; 
static PyObject* ptr__PyTrue  = nullptr; 

# define DECLARE_TYPE(T) \ 
     static PyTypeObject* ptr__##T##_Type = nullptr; 
ALL_TYPES(DECLARE_TYPE) 


static int* ptr_Py_DebugFlag  = nullptr; 
static int* ptr_Py_InteractiveFlag = nullptr; 
static int* ptr_Py_OptimizeFlag  = nullptr; 
static int* ptr_Py_NoSiteFlag  = nullptr; 
static int* ptr_Py_VerboseFlag  = nullptr; 

static char** ptr__Py_PackageContext = nullptr; 

# ifdef Py_REF_DEBUG 
int* ptr_Py_RefTotal; // !!! Why not static? 
# endif 


//-------------------------------------------------------------------------------- 
class GetAddressException 
{ 
public: 
    GetAddressException(const char* _name) 
     : name(_name) 
    { } 
    virtual ~GetAddressException() { } 
    const char* name; 
}; 


//-------------------------------------------------------------------------------- 

# define GET_PTR(FUNC, RETURN_TYPE) \ 
     static FUNC(const char *name) \ 
      { \ 
       FARPROC addr = GetProcAddress(python_dll, name); \ 
       if(addr == nullptr) \ 
        throw GetAddressException(name); \ 
       \ 
       return RETURN_TYPE addr; \ 
      } 

GET_PTR(PyObject *    GetPyObjectPointer_As_PyObjectPointer  , *(PyObject **)  ) 
GET_PTR(PyObject *     GetPyObject_As_PyObjectPointer  , (PyObject *)  ) 
GET_PTR(PyTypeObject *  GetPyTypeObjectPointer_As_PyTypeObjectPointer , *(PyTypeObject**) ) 
GET_PTR(PyTypeObject *   GetPyTypeObject_As_PyTypeObjectPointer , (PyTypeObject*)  ) 
GET_PTR(int *        GetInt_as_IntPointer   , (int*)    ) 
GET_PTR(char **     GetCharPointer_as_CharPointerPointer , (char**)   ) 


# ifdef _DEBUG 
static const char python_dll_name_format[] = "PYTHON%1.1d%1.1d_D.DLL"; 
# else 
static const char python_dll_name_format[] = "PYTHON%1.1d%1.1d.DLL"; 
# endif 

//-------------------------------------------------------------------------------- 
bool InitialisePythonIndirectInterface() 
{ 
    char python_dll_name[sizeof(python_dll_name_format)]; 

    _snprintf(python_dll_name, sizeof(python_dll_name_format)/sizeof(char) - 1, python_dll_name_format, PY_MAJOR_VERSION, PY_MINOR_VERSION); 

    python_dll = LoadLibraryA(python_dll_name); 
    if(python_dll == nullptr) 
     return false; 

    try 
    { 
# ifdef Py_REF_DEBUG 
     ptr_Py_RefTotal    = GetInt_as_IntPointer("_Py_RefTotal"); 
# endif 
     ptr_Py_DebugFlag   = GetInt_as_IntPointer("Py_DebugFlag"); 
     ptr_Py_InteractiveFlag  = GetInt_as_IntPointer("Py_InteractiveFlag"); 
     ptr_Py_OptimizeFlag   = GetInt_as_IntPointer("Py_OptimizeFlag"); 
     ptr_Py_NoSiteFlag   = GetInt_as_IntPointer("Py_NoSiteFlag"); 
     ptr_Py_VerboseFlag   = GetInt_as_IntPointer("Py_VerboseFlag"); 
     ptr__Py_PackageContext  = GetCharPointer_as_CharPointerPointer("_Py_PackageContext"); 

# define ASSIGN_PTR(E) \ 
     ptr__Exc_##E = GetPyObjectPointer_As_PyObjectPointer("PyExc_" #E); 
     ALL_ERRORS(ASSIGN_PTR) 

     ptr__PyNone     = GetPyObject_As_PyObjectPointer("_Py_NoneStruct"); 
     ptr__PyFalse    = GetPyObject_As_PyObjectPointer("_Py_ZeroStruct"); 
     ptr__PyTrue     = GetPyObject_As_PyObjectPointer("_Py_TrueStruct"); 

# define MAKE_PTR(TYPENAME) \ 
     ptr__##TYPENAME##_Type  = GetPyTypeObject_As_PyTypeObjectPointer("Py" #TYPENAME "_Type"); 
     ALL_TYPES(MAKE_PTR) 
    } 
    catch(GetAddressException &e) 
    { 
     OutputDebugStringA(python_dll_name); 
     OutputDebugStringA(" does not contain symbol "); 
     OutputDebugStringA(e.name); 
     OutputDebugStringA("\n"); 

     return false; 
    } 

    return true; 
} 


//#if 0 
//#define Py_INCREF(op) (      \ 
// _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA  \ 
// ((PyObject*)(op))->ob_refcnt++) 
// 
//#define Py_DECREF(op)       \ 
// if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \ 
//  --((PyObject*)(op))->ob_refcnt != 0) \ 
//  _Py_CHECK_REFCNT(op)     \ 
// else          \ 
//  _Py_Dealloc((PyObject *)(op)) 
//#endif 

void _XINCREF(PyObject* op) 
{ 
    // This function must match the contents of Py_XINCREF(op) 
    if(op == nullptr) 
     return; 

# ifdef Py_REF_DEBUG 
    (*ptr_Py_RefTotal)++; 
# endif 
    (op)->ob_refcnt++; 

} 

void _XDECREF(PyObject* op) 
{ 
    // This function must match the contents of Py_XDECREF(op); 
    if(op == nullptr) 
     return; 

# ifdef Py_REF_DEBUG 
    (*ptr_Py_RefTotal)--; 
# endif 

    if (--(op)->ob_refcnt == 0) 
     _Py_Dealloc((PyObject *)(op)); 
} 


#else // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


// 
// Needed to keep the abstactions for delayload interface 
// 
// !!! π Py_XDECREF has been deprecated in favour of Py_CLEAR 

void _XINCREF(PyObject* op) 
{ 
    Py_XINCREF(op); 
} 

void _XDECREF(PyObject* op) 
{ 
    Py_XDECREF(op); 
} 

#endif // PY_WIN32_DELAYLOAD_PYTHON_DLL 

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 


// 
// Wrap Python-types, type checks, errors, flags, etc as function calls 
// 

#ifdef PY_WIN32_DELAYLOAD_PYTHON_DLL 
# define IF_DELAYLOAD_ELSE(A, B) A 
#else 
# define IF_DELAYLOAD_ELSE(A, B) B 
#endif 


#define _Foo_Check(TYPENAME) \ 
    bool _##TYPENAME##_Check(PyObject *pyob) \ 
    { \ 
     return pyob->ob_type == _##TYPENAME##_Type(); \ 
    } 
ALL_TYPES(_Foo_Check) 

#define _Foo_Type(TYPENAME) \ 
    PyTypeObject* _##TYPENAME##_Type() \ 
    { \ 
     return IF_DELAYLOAD_ELSE(ptr__##TYPENAME##_Type, & Py##TYPENAME##_Type); \ 
    } 
ALL_TYPES(_Foo_Type) 



#define _Exc_Foo(E) \ 
    PyObject* _Exc_##E() \ 
    { \ 
     return IF_DELAYLOAD_ELSE(ptr__Exc_##E, ::PyExc_##E); \ 
    } 
ALL_ERRORS(_Exc_Foo) 


int& _Py_DebugFlag()     { return IF_DELAYLOAD_ELSE(*ptr_Py_DebugFlag  , Py_DebugFlag); } 
int& _Py_InteractiveFlag()    { return IF_DELAYLOAD_ELSE(*ptr_Py_InteractiveFlag , Py_InteractiveFlag); } 
int& _Py_OptimizeFlag()     { return IF_DELAYLOAD_ELSE(*ptr_Py_OptimizeFlag , Py_OptimizeFlag); } 
int& _Py_NoSiteFlag()     { return IF_DELAYLOAD_ELSE(*ptr_Py_NoSiteFlag  , Py_NoSiteFlag); } 
int& _Py_VerboseFlag()     { return IF_DELAYLOAD_ELSE(*ptr_Py_VerboseFlag  , Py_VerboseFlag); } 

char* __Py_PackageContext()    { return IF_DELAYLOAD_ELSE(*ptr__Py_PackageContext , _Py_PackageContext); } 


PyObject* _None()      { return IF_DELAYLOAD_ELSE(ptr__PyNone    , &::_Py_NoneStruct); } 
PyObject* _False()      { return IF_DELAYLOAD_ELSE(ptr__PyFalse   , Py_False); } 
PyObject* _True()      { return IF_DELAYLOAD_ELSE(ptr__PyTrue    , Py_True); } 

} // namespace Py 
+0

Windows 로더가 Linux ld.so와 매우 다르게 작동하기 때문에. @Waldo의 대답에 따라 현대 Windows에서이 모든 것을 삭제하고/delayload를 사용할 수 있습니다. 왜 해보지 그래? – bmargulies

답변

4

, 지연 로딩이 로더가 파일을, 또는에 출시되는 시점에이를 것으로 예상 어디에 아닌 다른 PE 파일을 참조 할 PE 파일을 허용하는 메커니즘입니다 그것이 전혀 없으면 우아하게 넘어지지. 그것은 마치 파이썬 자체를 포함하고있는 Windows 프로그램을 수용하는 것처럼 보이지만, 파이썬을 포함하는 DLL을 PATH에 저장하고 싶지는 않습니다.

일부 인터넷 검색은 이것이 파이썬과 파이썬에 의해로드되도록 만들어진 모듈 사이의 원형을 피하는 것과 관련이 있음을 제안합니다.

2

매크로는 사전 처리기 인수로 정의 할 수 있습니다. 그 이유는 어디에서나 볼 수 있습니다. Microsoft 컴파일러는/D 인수를 사용합니다.

/D는/D가 명령 줄에서 따옴표를 제거하고 #define이이를 유지한다는 점을 제외하면 소스 코드 파일의 시작 부분에서 #define 지시문과 동일한 효과가 있습니다.

https://www.daniweb.com/software-development/c/threads/348802/passing-string-as-d-compiler-option

지연 부하가 전에하고있는 OS 대신 사용되는 전용 라이브러리를로드 할 수있는 메커니즘이다 : GCC의

http://msdn.microsoft.com/en-us/library/hhzbb5c8.aspx

는 -D 체크하여이 기준 링크 인 응용 프로그램이 시작됩니다. dll이 전혀로드되지 않을 수도 있으므로 (코드 흐름에 따라) 소중한 메모리와로드 시간을 절약 할 수 있습니다.

이 코드는 자체가 지연된로드를 구현하려고 시도하고 있습니다. 매크로가 정의되어 있으면 다른 방법으로 정상적으로 진행됩니다. Microsoft 링커는 자동으로 작업을 수행 할 수 있습니다. 즉, 아무 것도 프로그래밍 할 필요가 없습니다. 이 기능은 플랫폼의 기능이 아니라 링커 기능입니다.

확인이 참조 : 이 http://en.wikipedia.org/wiki/Dynamic-link_library#Delayed_loading

당신은 멀리 코드를 할 당신이 원하는 경우에 당신을위한 코드를 추가하려면 Microsoft 링커를 지시 할 수 있습니다. 이 문서에 설명 된대로

당신은/DELAYLOAD 인수와 함께 할 수 있습니다

http://msdn.microsoft.com/en-us/library/yx9zd12s.aspx

그냥 DLL이 발견되지 않는 경우는 적절한 예외 고리에 제대로 걸 렸는지 확인합니다.

+0

이 코드는 자동으로 수행 할 수있는 작업을 수동으로 시도합니다. 왜 그것이 존재합니까? 15 년 전/DELAYLOAD 링커 플래그가없는 곳에서 언젠가 있었습니까? –

+0

어쩌면 그것이 그 능력을 가진 링커에 접근 할 수 없었던 원인 일 수도 있습니다. 정확히 구현 된시기는 기억이 나지 않지만 15 년 전의 가능성이 있습니다. –