2016-12-02 7 views
0

起動引数を解析し、いくつかのDLLのうちの1つを「起動」する非常に小さなプログラムを書きたいと思います。実行時に実行可能ファイルのようにDLLを「開始」するにはどうすればよいですか?

私はすでにアプリケーションとしてアプリケーションを作成し、代わりにDLLとしてビルドするようにVisual Studioのプロジェクトプロパティを変更することで、DLLとして「実行」したいアプリケーションを作成しました。私はLoadLibraryとGetProcAddressをコンサートで使用して必要な機能を得る必要があることを知っていますが、この事実の多くはユースケースではないので、これに関する明確で包括的なドキュメントを見つけるのは難しいです。また、プロジェクトとプラットフォームの制限に基づいてこのルートに進む必要があります。

私はthis pageを見つけましたが、これにはいくつかの情報がありますが、私の目的に適応するには十分ではありません。

編集:今ここにいるのです。

__declspec(dllexport) int cdecl main(int argc, char *argv[]) 

私もDLLをロードし、上記の関数を実行する試みは、次のようになりますアプリケーションプロジェクトを持っている:

は、私は、その主な機能の署名次のようになりますDLLプロジェクトを持っていますtypedef int (CALLBACK* LPFNDLLFUNC1)(int, char *);

...

 HMODULE dllHandle = NULL; 
     BOOL freeResult, runTimeLinkSuccess = FALSE; 
     LPFNDLLFUNC1 lpfnDllFunc1; // Function pointer 
     if (args->IsEmpty()) 
     { 
      dllHandle = LoadLibrary(L"TrueApplication.dll"); 
      if (NULL != dllHandle) 
      { 
       lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(dllHandle, "main"); 
       if (lpfnDllFunc1) 
       { 
        int retVal = lpfnDllFunc1(0, "1"); 
       } 

現在、LoadLibrary呼び出しは機能しますが、GetProcAddressは機能しません。

+2

EXEではなくDLLにすることのメリットは何ですか?あなたの推論を知る必要があるので、あなたが探しているDLLの利点のいくつかを「取り消す」ソリューションを推奨しません。 –

+0

この方法を使用することは私の選択でも私の考えでもありませんが、プラットフォームとプロジェクトの制限を考慮すると、私はこれを実行する必要があります。基本的に、DLLの代わりに複数の実行可能ファイルを使用する場合、このプラットフォームは各実行可能ファイルごとに異なるアプリケーション・エントリを持つ必要があります。たとえば、ユーザーは「ランチャー」アプリと「アプリ1」、「アプリ2」などをインストールする必要があります。 – Roderick

答えて

1

まず、プロジェクトタイプを実行可能ファイルからDLLに変更するだけでは、DLLを作成するには不十分です。また、APIを作成するためにいくつかのシンボルをエクスポートする必要があります。少なくとも、エクスポートする関数を__declspec(dllexport)で飾る必要があります。ただし、C APIをエクスポートすることをお勧めします。つまり、extern "C"C互換の引数で機能します。したがって、エクスポートする関数の先頭にextern "C" __declspec(dllexport)を付ける必要があります。このソリューションは、ジェリーの棺で示さ/delayloadと静荷重よりも多くの手間がかかります

const char* dllname = "myfile.dll"; 
    h = LoadLibrary(dllname); 
    if(h == nullptr) 
    { 
     /*handle error*/ 
    } 

    using myfunc_type = bool (*)(int x, double y); //example 
    auto myfunc = reinterpret_cast<myfunc_type>(GetProcAddress(h, "myfunc"));  
    //...... 
    myfunc(x,y); //call the imported function 

が、それは利点があります:DLL場合は、あなたが動的にこのようなあなたのDLLを読み込むことができることを行っているたら

が必要ですが見つからない場合、Windowsからのメッセージに頼るのではなく、独自のエラーメッセージをユーザーに与えることができます(これは技術者以外の人には受け入れられないことがよくあります)。独自のカスタムエラーメッセージとともにAPIバージョンの検証をAPIに含めることもできます。

編集:コードサンプルが動作するあなたはどのように理解すればそのアプローチは、(オプション#2)単純ですが、あなたがこの

extern "C" __declspec(dllexport) int main(int argc, char *argv[]){...} 
typedef int (* LPFNDLLFUNC1)(int, char **); 
+0

遅延ロードによって、エラーをトラップしてカスタムメッセージを表示することもできます。 –

+0

@BenVoigtあなたはそれを行うためのガイドに私を指摘できますか?また、(DLLのAPIが時間とともに変化する)バージョンの不一致に対するカスタムエラーメッセージを作成することは可能ですか? – Eugene

+0

私がしたいと思うものは、DLLのmain関数のようなものでしょうか? 'myfunc_type = int(*)(int x、double y)を使用します。 //例 自動myfunc = reinterpret_cast (GetProcAddress(h、 "main")); ' – Roderick

2

あなたはDLLで機能を呼び出すためにLoadLibraryGetProcAddressを使用する必要はありません

多くの場合、独自のエントリポイントを持つDLLを作成します。今のところ、コマンドラインを解析し、DLLを選択し、引数なしでそのエントリポイントを呼び出すと仮定しましょう。あなたのメインをリンクすると

void DLL_a(); 
void DLL_b(); 
void DLL_c(); 

int main(int argc, char **argv) { 
    // we'll assume DLL_a is the default: 
    if (argc < 2) 
     DLL_a(); 

    // For now, we'll do a *really* trivial version of parsing the command line 
    // to choose the right DLL: 
    if (argv[1][0] == 'A') 
     DLL_a(); 
    else if (argv[1]][0] == 'B') 
     DLL_b(); 
    else if (argv[1][0] == 'C') 
     DLL_c(); 
    else { 
     std::cerr << "Unrecognized argument\n"; 
     return 1; 
    } 
} 

は、あなたが各DLLに対応する.libを指定します、そして、あなたはおそらく、リンカに/delayloadフラグを指定したいと思う:あなたはこのようなものになってしまいます。つまり、DLL内の関数が実際に呼び出されるまで、DLLはロードされません。たとえば、DLL Aのみを含むプログラムの機能制限の低いバージョンを配布する場合、DLL Bからの関数がない限り(DLL BまたはCはユーザーのシステムには存在しません)またはCはこれまでに呼び出されています。 /delayloadを指定しない場合、ローダーはプログラムの起動時にすべてのDLLをRAMにマップし、使用するためにDllMainを実行し、使用するすべてのDLLに対して同じ再帰を実行します。

/delayloadにはもう1つの利点があります。使用されないと、他のDLLをアドレスにマッピングしないようにします。どのような呼び出しでも1つのDLLしか使用されないように思えるので、おそらくあなたの場合は勝利です。

+0

メモリマッピングだけでなく、DllMain(およびグローバルオブジェクトのコンストラクタ)も、選択されていないDLLに対しては実行されません。 –

1

のようにそれを変更する場合はは、これを行うにはGetProcAddress (...)を必要としないコンパイラはシンボル名を生成します。


のDllMainがメインスレッド

はDllMain内の内部に複雑な何かを決して生成します。オプション#1

、あなたのソフトウェアをデッドロックがあります。

DLLが(それは本当に忙しい機能だ...と出口ポイントと取り付け点をスレッド)自身のエントリポイントを持っています。あなたのDLLにLoadLibrary (...)を呼び出すだけで、少なくともプロセスアタッチのためにDllMain (...)が呼び出されます。

BOOL 
APIENTRY 
DllMain (HMODULE hModule, 
      DWORD ul_reason_for_call, 
      LPVOID lpReserved) 

あなたが実際にそれはあなたのプログラムの主な機能であるかのようにDllMainを実行する権限としてul_reason_for_call == DLL_PROCESS_ATTACHを扱うことができます。

実際にここでプログラムループを開始する必要はありません...いつでもDllMainは非常に重要なオペレーティングシステムロック(DLLローダー)を保持しており、通常のプログラム操作のために戻すことで解放する必要があります。

あなたは、プログラムのエントリポイントとしてDllMainを使用したい場合は、それがスレッドを生成する必要があり、そのスレッドが終了するまで、あなたの元main方法は戻らないしなければならないことを意味

...


オプション#2

DLLはmain関数をエクスポートします。

呼び出し規約の非常に注意して、コンパイラはあなたのためのシンボルの名前を変更し、直感的なよりGetProcAddress少ないとDLL内の関数を見つけるようになります。あなたのDLLで

輸出main:あなたのプログラムで

__declspec (dllexport) 
int 
__cdecl main (int argc, char *argv []) 
{ 
    printf ("foobar"); 
    return 0; 
} 

輸入main DLLから:

// Need a typedef for the function you are going to get from the DLL 
typedef int (__cdecl *main_pfn)(int argc, char *argv[]); 

int main (int argc, char *argv[]) 
{ 
    HMODULE hModMyDLL = LoadLibraryA ("MyDll.dll"); 

    if (hModMyDLL != 0) { 
    // 
    // The preceding underscore deals with automatic decorations 
    // the compiler added to the __cdecl function name. 
    // 
    // It is possible to do away with this completely if you use a .def 
    // file to assign export names and ordinals manually, but then you 
    //  lose the ability to tell a function's calling convention by its 
    //  name alone. 
    // 
    main_pfn MyMain = (main_pfn) 
     GetProcAddress (hModMyDLL, "_main"); 

    // Call the main function in your DLL and return when it does 
    if (MyMain != nullptr) 
     return MyMain (argc, argv); 
    } 

    return -1; 
} 

どちらのアプローチは、自分の長所を持っています。 DllMainからスレッドを産卵

は、ロードするDLLがある方法については全く何も知ら回避実装し、それはまた、返すために決してあなたのmain機能を設計しないためにあなたを必要としている - DLLはExitProcess (...)を呼び出します。

関数をエクスポートし、後でそれらを名前でインポートすると、Windows DLLローダーロックの周りに厄介なことを避けることができます。しかし、あなたが明示的にエクスポートされたシンボルに名前を付け.defファイルを使用しない場合、コンパイラは、このような_...などの装飾を追加する予定です(__cdecl)または[email protected]__stdcall)名に、あなたはこれらの規則を学ばなければなりませんGetProcAddressと一緒に有用なことをしてください。

+0

私は、(WINAPIが** __ stdcall **に定義されているにもかかわらず)未修飾の名前を使ってWin32 API関数で 'GetProcAddress(...)'を使用できる理由は、Microsoftが '.def' 'user32'のようなDLLにエクスポート名を与えます。彼らは意図的に呼び出し規約の装飾を取り除いて、 'GetProcAddress'が使用する負担を軽減します。あなたもこれを自由にすることができます。 –

関連する問題