[winapi] dllexport를 사용하여 DLL에서 함수 내보내기

C ++ Windows DLL에서 함수를 내보내는 간단한 예제를 원합니다.

헤더, .cpp파일 및 .def파일 (절대적으로 필요한 경우) 을보고 싶습니다 .

내 보낸 이름을 꾸미지 않고 싶습니다 . 가장 표준적인 호출 규칙 ( __stdcall?) 을 사용하고 싶습니다 . 나는 사용을 원 __declspec(dllexport)하고 사용할 필요가 없습니다.def 파일을.

예를 들면 :

  //header
  extern "C"
  {
   __declspec(dllexport) int __stdcall foo(long bar);
  }

  //cpp
  int __stdcall foo(long bar)
  {
    return 0;
  }

링커가 이름에 밑줄 및 / 또는 숫자 (바이트 수?)를 추가하지 않도록하려고합니다.

나는 지원하지 않는으로 OK예요 dllimportdllexport같은 헤더를 사용. C ++ 클래스 메서드를 내보내는 데 대한 정보가 아니라 C 스타일 전역 함수 만 필요합니다.

최신 정보

호출 규칙을 포함하지 않고를 사용 extern "C"하면 원하는대로 내보내기 이름이 제공되지만 그게 무슨 뜻입니까? 어떤 기본 호출 규칙이 내가 어떤 pinvoke (.NET), 선언 (vb6)을 얻고 있으며 GetProcAddress예상합니까? ( GetProcAddress호출자가 만든 함수 포인터에 따라 달라질 것입니다).

이 DLL이 헤더 파일없이 사용되기를 원하기 때문에 #defines호출자가 헤더를 사용할 수 있도록 만드는 데는 많은 공상이 필요하지 않습니다 .

*.def파일 을 사용해야한다는 대답은 괜찮습니다 .



답변

일반 C 내보내기를 원하면 C ++가 아닌 C 프로젝트를 사용하십시오. C ++ DLL은 모든 C ++ ism (네임 스페이스 등)에 대해 이름 변경에 의존합니다. C / C ++-> Advanced 아래의 프로젝트 설정으로 이동하여 코드를 C로 컴파일 할 수 있습니다. 컴파일러 스위치 / TP 및 / TC에 해당하는 “Compile As”옵션이 있습니다.

여전히 C ++를 사용하여 lib의 내부를 작성하고 싶지만 일부 함수를 C ++ 외부에서 사용하기 위해 얽 히지 않은 상태로 내보내려면 아래 두 번째 섹션을 참조하십시오.

VC ++에서 DLL 라이브러리 내보내기 / 가져 오기

정말로 원하는 것은 DLL 프로젝트의 모든 소스 파일에 포함될 헤더에 조건부 매크로를 정의하는 것입니다.

#ifdef LIBRARY_EXPORTS
#    define LIBRARY_API __declspec(dllexport)
#else
#    define LIBRARY_API __declspec(dllimport)
#endif

그런 다음 내보내려는 함수에서 다음을 사용하십시오 LIBRARY_API.

LIBRARY_API int GetCoolInteger();

라이브러리 빌드 프로젝트에서 정의를 생성하면 LIBRARY_EXPORTSDLL 빌드를 위해 함수를 내보낼 수 있습니다.

이후 LIBRARY_EXPORTS 해당 프로젝트가 기능을 대신 가져옵니다 모두의 라이브러리의 헤더 파일을 포함하는 DLL을 소모 프로젝트에 정의되지 않습니다.

라이브러리가 교차 플랫폼 인 경우 Windows가 아닌 경우 LIBRARY_API를 아무것도 정의하지 않을 수 있습니다.

#ifdef _WIN32
#    ifdef LIBRARY_EXPORTS
#        define LIBRARY_API __declspec(dllexport)
#    else
#        define LIBRARY_API __declspec(dllimport)
#    endif
#elif
#    define LIBRARY_API
#endif

dllexport / dllimport를 사용할 때 DEF 파일을 사용할 필요가 없습니다. DEF 파일을 사용하는 경우 dllexport / dllimport를 사용할 필요가 없습니다. 두 가지 방법은 동일한 작업을 다른 방식으로 수행하므로 dllexport / dllimport가 두 가지 중에서 권장되는 방법이라고 생각합니다.

LoadLibrary / PInvoke 용 C ++ DLL에서 얽 히지 않은 함수 내보내기

LoadLibrary 및 GetProcAddress를 사용하거나 다른 언어 (예 : .NET의 PInvoke 또는 Python / R의 FFI 등)에서 가져 오기 위해 필요한 경우 다음을 사용할 수 있습니다. extern "C" dllexport와 함께 인라인을 하여 C ++ 컴파일러에 이름을 조작하지 않도록 지시 할 수 있습니다. 그리고 dllimport 대신 GetProcAddress를 사용하고 있으므로 위에서 ifdef 댄스를 수행 할 필요가 없습니다. 간단한 dllexport 만 있으면됩니다.

코드:

#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)

EXTERN_DLL_EXPORT int getEngineVersion() {
  return 1;
}

EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
  K.getGraphicsServer().addGraphicsDriver(
    auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
  );
}

다음은 Dumpbin / exports를 사용한 내보내기의 모습입니다.

  Dump of file opengl_plugin.dll

  File Type: DLL

  Section contains the following exports for opengl_plugin.dll

    00000000 characteristics
    49866068 time date stamp Sun Feb 01 19:54:32 2009
        0.00 version
           1 ordinal base
           2 number of functions
           2 number of names

    ordinal hint RVA      name

          1    0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
          2    1 00011028 registerPlugin = @ILT+35(_registerPlugin)

따라서이 코드는 잘 작동합니다.

m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");

m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
  ::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
  ::GetProcAddress(m_hDLL, "registerPlugin")
);


답변

C ++의 경우 :

나는 방금 같은 문제에 직면했고 하나가 __stdcall(또는 WINAPI) extern "C" 다음을 모두 사용할 때 문제가 발생한다고 언급하는 것이 가치가 있다고 생각합니다 .

아시다시피 extern "C"장식을 제거하여 대신 다음을 수행하십시오.

__declspec(dllexport) int Test(void)                        --> dumpbin : ?Test@@YaHXZ

장식되지 않은 기호 이름을 얻습니다.

extern "C" __declspec(dllexport) int Test(void)             --> dumpbin : Test

그러나 _stdcall(= 매크로 WINAPI, 호출 규칙을 변경) 또한 이름을 장식하므로 둘 다 사용하면 다음을 얻을 수 있습니다.

   extern "C" __declspec(dllexport) int WINAPI Test(void)   --> dumpbin : _Test@0

extern "C"기호가 장식되어 있기 때문에 의 이점 이 손실됩니다 (_ @bytes 포함).

참고이는 것을 단지 때문에 x86 아키텍처 발생 __stdcall대회가 64 무시됩니다 ( MSDN : 가능하면 규칙에 의해 64 아키텍처에 인수 레지스터에 전달하고, 이후의 인수는 스택에 전달됩니다 .).

x86 및 x64 플랫폼을 모두 대상으로하는 경우 특히 까다 롭습니다.


두 가지 솔루션

  1. 정의 파일을 사용하십시오. 그러나 이렇게하면 def 파일의 상태를 유지해야합니다.

  2. 가장 간단한 방법 : 매크로 정의 ( msdn 참조 ) :

#define EXPORT 주석 (링커, “/ EXPORT :”__FUNCTION__ “=”__FUNCDNAME__)

그런 다음 함수 본문에 다음 pragma를 포함합니다.

#pragma EXPORT

전체 예 :

 int WINAPI Test(void)
{
    #pragma EXPORT
    return 1;
}

이렇게하면 x86에 대한 __stdcall규칙을 유지하면서 x86 및 x64 대상 모두에 대해 데코 레이팅되지 않은 함수를 내 보냅니다 . 가 __declspec(dllexport) 되지 않는 이 경우이 필요합니다.


답변

나는 정확히 같은 문제가 있었는데, 내 해결책은 __declspec(dllexport)내보내기를 정의하는 대신 모듈 정의 파일 (.def)을 사용하는 것이 었습니다 ( http://msdn.microsoft.com/en-us/library/d91k01sh.aspx ). 왜 이것이 작동하는지 모르겠지만 작동합니다.


답변

_naked가 원하는 것을 얻을 수 있다고 생각하지만 컴파일러가 함수에 대한 스택 관리 코드를 생성하지 못하게합니다. extern “C”는 C 스타일 이름 장식을 발생시킵니다. 그것을 제거하면 _가 제거됩니다. 링커는 밑줄을 추가하지 않으며 컴파일러는 추가합니다. stdcall은 인수 스택 크기가 추가되도록합니다.

자세한 내용은 다음을 참조하십시오.
http://en.wikipedia.org/wiki/X86_calling_conventions
http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx

더 큰 질문은 왜 그렇게 하시겠습니까? 이름이 엉망이 된 게 뭐가 문제 야?


답변