DLL-書き方

最初に、独自のDLLを開発する際に考慮すべき問題と要件について説明します。

DLLの種類

アプリケーションにDLLをロードする場合、2つのリンク方法を使用して、エクスポートされたDLL関数を呼び出すことができます。リンクの2つの方法は次のとおりです。

  • ロード時のダイナミックリンク、および
  • ランタイムダイナミックリンク。

ロード時のダイナミックリンク

ロード時のダイナミックリンクでは、アプリケーションはローカル関数のようなエクスポートされたDLL関数を明示的に呼び出します。ロード時のダイナミックリンクを使用するには、アプリケーションをコンパイルしてリンクするときに、ヘッダー(.h)ファイルとインポートライブラリ(.lib)ファイルを提供します。これを行うと、リンカはDLLをロードし、ロード時にエクスポートされたDLL関数の場所を解決するために必要な情報をシステムに提供します。

ランタイムダイナミックリンク

実行時のダイナミックリンクでは、アプリケーションはLoadLibrary関数またはLoadLibraryEx関数のいずれかを呼び出して、実行時にDLLをロードします。DLLが正常にロードされたら、GetProcAddress関数を使用して、呼び出したいエクスポートされたDLL関数のアドレスを取得します。ランタイムダイナミックリンクを使用する場合、インポートライブラリファイルは必要ありません。

次のリストは、ロード時の動的リンクと実行時の動的リンクのどちらかを選択するためのアプリケーション基準を示しています。

  • Startup performance −アプリケーションの初期起動パフォーマンスが重要な場合は、ランタイムダイナミックリンクを使用する必要があります。

  • Ease of use−ロード時のダイナミックリンクでは、エクスポートされたDLL関数はローカル関数のようなものです。これらの関数を簡単に呼び出すのに役立ちます。

  • Application logic−実行時の動的リンクでは、アプリケーションは必要に応じて分岐してさまざまなモジュールをロードできます。これは、多言語バージョンを開発するときに重要です。

DLLエントリポイント

DLLを作成するときに、オプションでエントリポイント関数を指定できます。エントリポイント関数は、プロセスまたはスレッドがDLLにアタッチするか、DLLからデタッチするときに呼び出されます。エントリポイント関数を使用して、DLLの必要に応じてデータ構造を初期化または破棄できます。

さらに、アプリケーションがマルチスレッドの場合、スレッドローカルストレージ(TLS)を使用して、エントリポイント関数の各スレッドにプライベートなメモリを割り当てることができます。次のコードは、DLLエントリポイント関数の例です。

BOOL APIENTRY DllMain(
   HANDLE hModule,	   // Handle to DLL module 
   DWORD ul_reason_for_call, 
   LPVOID lpReserved )     // Reserved
{
   switch ( ul_reason_for_call )
   {
      case DLL_PROCESS_ATTACHED:
      // A process is loading the DLL.
      break;
      
      case DLL_THREAD_ATTACHED:
      // A process is creating a new thread.
      break;
      
      case DLL_THREAD_DETACH:
      // A thread exits normally.
      break;
      
      case DLL_PROCESS_DETACH:
      // A process unloads the DLL.
      break;
   }
   return TRUE;
}

エントリポイント関数がFALSE値を返す場合、ロード時のダイナミックリンクを使用しているとアプリケーションは起動しません。ランタイムダイナミックリンクを使用している場合、個々のDLLのみがロードされません。

エントリポイント関数は、単純な初期化タスクのみを実行する必要があり、他のDLLロード関数または終了関数を呼び出さないでください。たとえば、エントリポイント関数では、直接または間接的にを呼び出すべきではありませんLoadLibrary 関数または LoadLibraryEx関数。さらに、あなたは呼び出すべきではありませんFreeLibrary プロセスが終了しているときに機能します。

WARNING−マルチスレッドアプリケーションでは、データの破損を防ぐために、DLLグローバルデータへのアクセスが同期されている(スレッドセーフ)ことを確認してください。これを行うには、TLSを使用して各スレッドに一意のデータを提供します。

DLL関数のエクスポート

DLL関数をエクスポートするには、エクスポートされたDLL関数にfunctionキーワードを追加するか、エクスポートされたDLL関数を一覧表示するモジュール定義(.def)ファイルを作成します。

関数キーワードを使用するには、エクスポートする各関数を次のキーワードで宣言する必要があります-

__declspec(dllexport)

アプリケーションでエクスポートされたDLL関数を使用するには、インポートする各関数を次のキーワードで宣言する必要があります-

__declspec(dllimport)

通常、次のような1つのヘッダーファイルを使用します。 define ステートメントと ifdef エクスポートステートメントとインポートステートメントを分離するステートメント。

モジュール定義ファイルを使用して、エクスポートされたDLL関数を宣言することもできます。モジュール定義ファイルを使用する場合、エクスポートされたDLL関数にfunctionキーワードを追加する必要はありません。モジュール定義ファイルで、次のように宣言します。LIBRARY ステートメントと EXPORTSDLLのステートメント。次のコードは、定義ファイルの例です。

// SampleDLL.def
//
LIBRARY "sampleDLL"

EXPORTS
   HelloWorld

サンプルDLLを作成する

Microsoft Visual C ++ 6.0では、次のいずれかを選択してDLLを作成できます。 Win32 Dynamic-Link Library プロジェクトタイプまたは MFC AppWizard (dll) プロジェクトタイプ。

次のコードは、Win32ダイナミックリンクライブラリプロジェクトタイプを使用してVisual C ++で作成されたDLLの例です。

// SampleDLL.cpp

#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
   return TRUE;
}

void HelloWorld()
{
   MessageBox( NULL, TEXT("Hello World"), 
   TEXT("In a DLL"), MB_OK);
}
// File: SampleDLL.h
//
#ifndef INDLL_H
#define INDLL_H

   #ifdef EXPORTING_DLL
      extern __declspec(dllexport) void HelloWorld() ;
   #else
      extern __declspec(dllimport) void HelloWorld() ;
   #endif

#endif

サンプルDLLの呼び出し

次のコードは、SampleDLLDLLでエクスポートされたDLL関数を呼び出すWin32アプリケーションプロジェクトの例です。

// SampleApp.cpp 

#include "stdafx.h"
#include "sampleDLL.h"

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ 	
   HelloWorld();
   return 0;
}

NOTE −ロード時のダイナミックリンクでは、SampleDLLプロジェクトのビルド時に作成されるSampleDLL.libインポートライブラリをリンクする必要があります。

ランタイムダイナミックリンクでは、次のコードのようなコードを使用して、SampleDLL.dllエクスポートDLL関数を呼び出します。

...
typedef VOID (*DLLPROC) (LPTSTR);
...
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;

hinstDLL = LoadLibrary("sampleDLL.dll");

if (hinstDLL != NULL)
{
   HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
	
   if (HelloWorld != NULL)
      (HelloWorld);

   fFreeDLL = FreeLibrary(hinstDLL);
}
...

SampleDLLアプリケーションをコンパイルしてリンクすると、Windowsオペレーティングシステムは次の場所でSampleDLLDLLをこの順序で検索します。

  • アプリケーションフォルダ

  • 現在のフォルダ

  • Windowsシステムフォルダ( GetSystemDirectory 関数は、Windowsシステムフォルダのパスを返します)。

  • Windowsフォルダ( GetWindowsDirectory 関数はWindowsフォルダのパスを返します)。