DLL-クイックガイド

ダイナミックリンクは、実行時にアプリケーションをライブラリにリンクするメカニズムです。ライブラリは独自のファイルに残り、アプリケーションの実行可能ファイルにコピーされません。DLLは、アプリケーションが作成されたときではなく、実行されたときにアプリケーションにリンクします。DLLには、他のDLLへのリンクが含まれている場合があります。

多くの場合、DLLは、.EXE、.DRV、.DLLなどの異なる拡張子を持つファイルに配置されます。

DLLの利点

DLLファイルを持つことのいくつかの利点を以下に示します。

より少ないリソースを使用します

DLLファイルはメインプログラムと一緒にRAMにロードされません。必要な場合を除いて、スペースを占有しません。DLLファイルが必要な場合は、ロードして実行します。たとえば、Microsoft Wordのユーザーがドキュメントを編集している限り、RAMにプリンタのDLLファイルは必要ありません。ユーザーがドキュメントを印刷することを決定した場合、WordアプリケーションはプリンターDLLファイルをロードして実行します。

モジュラーアーキテクチャを促進します

DLLは、モジュラープログラムの開発を促進するのに役立ちます。多言語バージョンを必要とする大規模なプログラムや、モジュラーアーキテクチャを必要とするプログラムの開発に役立ちます。モジュラープログラムの例は、実行時に動的にロードできる多くのモジュールを持つ会計プログラムです。

簡単な展開とインストールを支援

DLL内の関数の更新または修正が必要な場合、DLLの展開とインストールでは、プログラムをDLLに再リンクする必要はありません。さらに、複数のプログラムが同じDLLを使用している場合、それらすべてが更新または修正の恩恵を受けます。この問題は、定期的に更新または修正されるサードパーティのDLLを使用する場合に頻繁に発生する可能性があります。

コンパイルの一部としてモジュール定義ファイルのIMPORTSセクションでDLLリンケージが指定されている場合、アプリケーションとDLLは他のDLLに自動的にリンクできます。それ以外の場合は、WindowsのLoadLibrary関数を使用して明示的にロードできます。

重要なDLLファイル

  • COMDLG32.DLL -ダイアログボックスを制御します。

  • GDI32.DLL -グラフィックの描画、テキストの表示、フォントの管理のための多数の機能が含まれています。

  • KERNEL32.DLL -メモリとさまざまなプロセスを管理するための何百もの機能が含まれています。

  • USER32.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)ファイルを作成します。

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

__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フォルダのパスを返します)。

DLLを使用するには、レジストリに適切な参照を入力して登録する必要があります。レジストリ参照が破損し、DLLの機能が使用できなくなる場合があります。DLLは、Start-Runを開き、次のコマンドを入力することで再登録できます。

regsvr32 somefile.dll

このコマンドは、somefile.dllがPATHにあるディレクトリまたはフォルダにあることを前提としています。それ以外の場合は、DLLのフルパスを使用する必要があります。DLLファイルは、以下に示すようにスイッチ「/ u」を使用して登録を解除することもできます。

regsvr32 /u somefile.dll

これを使用して、サービスのオンとオフを切り替えることができます。

DLLの問題のトラブルシューティングに役立ついくつかのツールを利用できます。それらのいくつかを以下で説明します。

Dependency Walker

Dependency Walkerツール(depends.exe)プログラムによって使用されるすべての依存DLLを再帰的にスキャンできます。Dependency Walkerでプログラムを開くと、DependencyWalkerは次のチェックを実行します。

  • 欠落しているDLLをチェックします。
  • 無効なプログラムファイルまたはDLLをチェックします。
  • インポート機能とエクスポート機能が一致していることを確認します。
  • 循環依存エラーをチェックします。
  • モジュールが別のオペレーティングシステム用であるために無効なモジュールをチェックします。

Dependency Walkerを使用すると、プログラムが使用するすべてのDLLを文書化できます。これは、将来発生する可能性のあるDLLの問題を防止および修正するのに役立つ場合があります。Microsoft Visual Studio 6.0をインストールすると、DependencyWalkerは次のディレクトリに配置されます。

drive\Program Files\Microsoft Visual Studio\Common\Tools

DLLユニバーサル問題ソルバー

DLL Universal Problem Solver(DUPS)ツールは、DLL情報の監査、比較、文書化、および表示に使用されます。次のリストは、DUPSツールを構成するユーティリティについて説明しています。

  • Dlister.exe -このユーティリティは、コンピュータ上のすべてのDLLを列挙し、その情報をテキストファイルまたはデータベースファイルに記録します。

  • Dcomp.exe -このユーティリティは、2つのテキストファイルにリストされているDLLを比較し、違いを含む3番目のテキストファイルを生成します。

  • Dtxt2DB.exe -このユーティリティは、Dlister.exeユーティリティとDcomp.exeユーティリティを使用して作成されたテキストファイルをdllHellデータベースにロードします。

  • DlgDtxt2DB.exe -このユーティリティは、Dtxt2DB.exeユーティリティのグラフィカルユーザーインターフェイス(GUI)バージョンを提供します。

DLLを作成するときは、次のヒントに注意してください。

  • 適切な呼び出し規約(Cまたはstdcall)を使用します。

  • 関数に渡される引数の正しい順序に注意してください。

  • 関数に直接渡された引数を使用して、配列のサイズを変更したり、文字列を連結したりしないでください。渡すパラメータはLabVIEWデータであることを忘れないでください。配列または文字列のサイズを変更すると、LabVIEWメモリに保存されている他のデータが上書きされてクラッシュする可能性があります。LabVIEW配列ハンドルまたはLabVIEW文字列ハンドルを渡し、Visual C ++コンパイラまたはSymantecコンパイラを使用してDLLをコンパイルしている場合は、配列のサイズを変更したり、文字列を連結したりできます。

  • 関数に文字列を渡すときに、渡す文字列の正しいタイプを選択します。CまたはPascalまたはLabVIEW文字列ハンドル。

  • Pascal文字列の長さは255文字に制限されています。

  • C文字列はNULLで終了します。DLL関数が数値データをバイナリ文字列形式で返す場合(たとえば、GPIBまたはシリアルポート経由)、データ文字列の一部としてNULL値を返す場合があります。このような場合、短い(8ビット)整数の配列を渡すのが最も信頼できます。

  • データの配列または文字列を操作している場合は、LabVIEWハンドルとして渡さない限り、関数によってバッファに配置された結果を保持するのに十分な大きさのバッファまたは配列を常に渡します。その場合、CINを使用してサイズを変更できます。 Visual C ++またはSymantecコンパイラで機能します。

  • _stdcallを使用している場合は、モジュール定義ファイルのEXPORTSセクションにDLL関数をリストします。

  • 他のアプリケーションが呼び出すDLL関数をモジュール定義ファイルのEXPORTSセクションにリストするか、関数宣言に_declspec(dllexport)キーワードを含めます。

  • C ++コンパイラを使用する場合は、名前のマングリングを防ぐために、ヘッダーファイルでextern.C。{}ステートメントを使用して関数をエクスポートします。

  • 独自のDLLを作成している場合は、DLLが別のアプリケーションによってメモリにロードされている間はDLLを再コンパイルしないでください。DLLを再コンパイルする前に、その特定のDLLを使用するすべてのアプリケーションがメモリからアンロードされていることを確認してください。DLL自体がメモリにロードされないようにします。これを忘れてコンパイラが警告しない場合、正しく再構築できない可能性があります。

  • 別のプログラムでDLLをテストして、関数(およびDLL)が正しく動作することを確認します。コンパイラのデバッガまたはDLL内の関数を呼び出すことができる単純なCプログラムでテストすると、考えられる問題がDLLまたはLabVIEW関連に固有のものであるかどうかを特定するのに役立ちます。

DLLの作成方法と、「HelloWorld」プログラムの作成方法を見てきました。その例は、DLLを作成する基本的な概念についてのアイデアを与えたに違いありません。

ここでは、Delphi、Borland C ++、およびVC ++を使用してDLLを作成する方法について説明します。

これらの例を1つずつ見ていきましょう。

  • Delphi内でDLLを記述および呼び出す方法

  • Borland C ++ BuilderIDEからDLLを作成する

  • Microsoft Visual C ++ 6.0でDLLを作成する