10821


C#内からC++の自作DLLにダウンロードURLを送り、  
ダウンロード完了後、中身のtarファイルを展開するために  
動的にTAR32.DLLを読み込んだところdownLoadTestに  
Falseが返ってきました。

C++側のみをDLL化せずMFCでテストした時には  
testretにTrueが返ってきていてDLLがロードできた事が  
確認できたのですが、DLL内から呼んだ時には  
DLLのロードに失敗しているようでFalseになります。

同じディレクトリに２つのDLLを置くのではなく、  
参照先のようなものを持つ必要があるのでしょうか？

使用しているTar32.DLLは下記から取得しました。  
<http://www.csdinc.co.jp/archiver/lib/tar32.html>

[追記]

* * *

フォルダ構成  
Assets  
|---------Plaugins  
| |------CallDll.cs  
| |------DllHttpDownLoadUntar.dll  
| |------tar32.dll  
|  
|  
|---------Scripts

開発環境  
・Windoes7 64bit  
・Visual Studio Community 2013

* * *

C#側

**CallDll.cs**

[code]

    [DllImport("DllHttpDownLoadUntar")]
    private static extern bool HttpDwonLoad(string geturl);
    
    public void AddUrl()
    {
        bool downLoadTest = false;
        downLoadTest = HttpDwonLoad("http://IPアドレス/Tar/Android.tar");
    
    }  
    
[/code]

C++DLL側

**DllHttpDownLoadUntar.h**

[code]

    #ifdef DLLHTTPDOWNLOADUNTAR_EXPORTS
    #define DLLHTTPDOWNLOADUNTAR_API __declspec(dllexport) 
    #else
    #define DLLHTTPDOWNLOADUNTAR_API __declspec(dllimport) 
    #endif
    
    #include <string>
    
    typedef int (WINAPI *LPUNTAREXTRACTMEM)(const HWND, LPCSTR, LPBYTE, const DWORD, int *, LPWORD, LPDWORD);
    LPUNTAREXTRACTMEM lpTarExtractMem = NULL;
    HMODULE hTar32 = 0;
    
    extern "C"{
        DLLHTTPDOWNLOADUNTAR_API bool HttpDwonLoad(char* geturl);
        DLLHTTPDOWNLOADUNTAR_API void HttpDownLoadAction(std::string url);
    }
    
[/code]

**DllHttpDownLoadUntar.cpp**

[code]

    #include "stdafx.h"
    #include "DllHttpDownLoadUntar.h"
    #include "wininet.h"
    
    extern "C"{
    // 受け取ったURLのダウンロード
    bool HttpDwonLoad(char* geturl){
    
        HttpDownLoadAction(geturl);
    
        std::string DllName = "TAR32.DLL";
    
        size_t origsize = DllName.size() + 1;
        const size_t newsize = 100;
        size_t convertedChars = 0;
        wchar_t wcstring[newsize];
        mbstowcs_s(&convertedChars, wcstring, origsize, DllName.c_str(), _TRUNCATE);
    
        bool testret = true;
        hTar32 = LoadLibrary(wcstring);
    
        if (!hTar32) {
            testret = false;
        }
        else
        {
            lpTarExtractMem = (LPUNTAREXTRACTMEM)GetProcAddress(hTar32, "TarExtractMem");
        }
    
        return testret;
    }
    
    void HttpDownLoadAction(std::string url){
    
            HINTERNET hInet, hUrl;
            char szBuf[128], *lpszSrc;
    
            DWORD dwRead, dwTotal = 0;  // ファイルサイズ
            HGLOBAL hMem;
            //URLの入力
            //m_edit_url.GetWindowTextA(ss);
            char szUrl[128];
            strcpy_s(szUrl, url.c_str());
            //インターネットの開始
            hInet = InternetOpen((LPCWSTR)"test-program",
                INTERNET_OPEN_TYPE_PRECONFIG,
                NULL, NULL, 0);
            if (hInet == NULL){
                return;
            }
    
            size_t origsize = url.size() + 1;
            const size_t newsize = 100;
            size_t convertedChars = 0;
            wchar_t wcstring[newsize];
            mbstowcs_s(&convertedChars, wcstring, origsize, szUrl, _TRUNCATE);
    
    
    
            //HTTPセッションの開始、指定のURLオープン
            hUrl = InternetOpenUrl(hInet, wcstring, NULL, 0, 0, 0);
            if (hUrl == NULL){
                return;
            }
            hMem = GlobalAlloc(GHND, 1);
            lpszSrc = (char*)GlobalLock(hMem);
            while (1){
                InternetReadFile(
                    hUrl, szBuf, (DWORD)sizeof(szBuf) - 1, &dwRead);
                szBuf[dwRead] = '\0';
                if (dwRead == 0)break;
                dwTotal += dwRead;
                hMem = GlobalReAlloc(hMem, dwTotal + 1, GMEM_MOVEABLE);
                if (hMem == NULL){
                    return;
                }
                lpszSrc = (char*)GlobalLock(hMem);
                if (lpszSrc == NULL){
                    break;
                }
                strcat_s(lpszSrc, dwTotal + 1, szBuf);
            }
            //メモリの解放
            GlobalUnlock(hMem);
            GlobalFree(hMem);
            //インターネットハンドルの解放
            InternetCloseHandle(hUrl);
            InternetCloseHandle(hInet);
        }
    }
    
[/code]


CC BY-SA 3.0
2015-05-29T06:36:46.037
10748
2015-05-31T14:17:05.983
2015-05-29T07:50:24.377
5261
5261
question
c# c++
C++で自作DLL内から同じディレクトリにあるDLLを呼ぶには？


> 同じディレクトリに２つのDLLを置くのではなく、  
>  参照先のようなものを持つ必要があるのでしょうか？

質問するのであれば現在のディレクトリ配置（実行ファイル、DllHttpDownLoadUntar.dll、TAR32.DLLの位置関係）を述べるべきです。ところでこの環境は64bit
OSだったりしますか？ その影響も受けます。

[LoadLibraryのドキュメント](https://msdn.microsoft.com/en-
us/library/ms684175\(v=vs.85\).aspx)には[Standard Search Order for Desktop
Applications](https://msdn.microsoft.com/en-
us/library/ms682586\(v=vs.85\).aspx#standard_search_order_for_desktop_applications)へのリンクも示されています。ここに挙げられているディレクトリに配置されていない場合はLoadLibraryは失敗します。適切なディレクトリに配置するかフルパスを指定しましょう。

* * *

蛇足ですが、mbstowcs_s()はmbstowcs()のセキュリティ強化バージョンです。第３引数でバッファサイズを指定することでバッファオーバーフローを検出します。origsize＝入力サイズを指定しては全く無意味です。  
またLoadLibrary()の引数はTCHAR文字列を要求します。wchar_t文字列を指定するのであればLoadLibraryW()を使用すべきですし、LoadLibraryA()を使えばchar文字列がそのまま使えます。


CC BY-SA 3.0
2015-05-29T07:31:18.117
10749
2015-05-29T07:31:18.117
4236
10748
answer


DLL内部からDLLを呼び出す際、パスの解決で悩まされることがあります。DLL同士を同じディレクトリに配置したとしても、最初のDLLを呼び出したモジュールから内部で呼び出すDLLが見つからないことがあるためです。

Tar32.dllは普通はSYSTEMフォルダに配置して使うように、と指定されていますが、今回は質問の文面からそういう使い方をされていないと思います。SYSTEMフォルダに配置してあれば、PATHが通っている場所ですので、呼び出し元がどこからでも読み込むことが可能となります。  
(この場合のSYSTEMフォルダはC:\Widows\System32になります)

ただ、今回のように同じフォルダにDLLを配置して使いたいというような場面は結構あって都度SYSTEMフォルダに配置するわけにはいかないと思います。その場合の解決方法として[DLLからDLLを呼ぶのはこんなに難しい](http://d.hatena.ne.jp/m_kawanon/20110731/1312116123)というサイトでDllMain内に処理を追加する方法が解説されていますので、参考にしてください。


CC BY-SA 3.0
2015-05-29T07:56:35.290
10753
2015-05-29T07:56:35.290
9515
10748
answer


ほかの回答者も述べているように、TAR32.DLLをフルパスで読み込むようにするのが最も単純な方法だと思います。

次のことを実際に試してみました。

  1. フルパス指定だとうまくいくこと
  2. そうでないとうまくいかないこと

ほかのかたが試しやすいように、一部単純にして以下のような構成にしました。

  * Plugins/TAR32.DLL (C++)
  * Plugins/DllHttpDownLoadUntar.dll (C++)
  * Plugins/CallDll.dll (C#)
  * App.exe (C#)
  * App.exe.config

App.exeがCallDll.dllのメソッドを呼び出し、CallDll.dllがDllHttpDownLoadUntar.dllの関数を呼び出し、DllHttpDownLoadUntar.dllがTAR32.DLLの関数を呼び出すようにしています。

この順番にソースコードを載せます。

まず、tar32.dllです。

[code]

    // TAR32.DLLの代用
    // cl tar32.cpp /LD /link /export:TarExtractMem
    
    #include <stdio.h>
    #include <windows.h>
    
    extern "C"
    int WINAPI TarExtractMem(
        const HWND hWnd,LPCSTR szCmdLine,
        LPBYTE szBuffer,const DWORD dwSize,time_t *lpTime,
        LPWORD lpwAttr,LPDWORD lpdwWriteSize)
    {
        puts("TarExtractMem");
        return 0;
    }
    
[/code]

次にDllHttpDownLoadUntar.dllです。余計なお節介かもしれませんが、`HttpDwonLoad`を`HttpDownLoad`に直しています。

[code]

    // cl DllHttpDownLoadUntar.cpp shlwapi.lib /LD /link /export:HttpDownLoad
    
    #include <windows.h>
    #include <shlwapi.h>
    
    int WINAPI TarExtractMem(
        const HWND hWnd,LPCSTR szCmdLine,
        LPBYTE szBuffer,const DWORD dwSize,time_t *lpTime,
        LPWORD lpwAttr,LPDWORD lpdwWriteSize);
    typedef decltype(&TarExtractMem) LPUNTAREXTRACTMEM;
    
    // 自分自身のモジュールハンドルを取得する元になる。
    // http://blogs.msdn.com/b/oldnewthing/archive/2004/10/25/247180.aspx
    extern "C" IMAGE_DOS_HEADER __ImageBase;
    
    extern "C" {
    bool WINAPI HttpDownLoad(const char* geturl){
        // HttpDownLoadAction(geturl);
    
    #if 1
        WCHAR modulePath[MAX_PATH]{};
        auto hmod = reinterpret_cast<HMODULE>(&__ImageBase);
        if (!GetModuleFileNameW(hmod, modulePath, ARRAYSIZE(modulePath))) {
            return false;
        }
        PathRemoveFileSpecW(modulePath);
        PathAppendW(modulePath, L"TAR32.DLL");
        auto hTar32 = LoadLibraryW(modulePath);
    #else
        auto hTar32 = LoadLibraryW(L"TAR32.DLL");
    #endif
    
        if (!hTar32) {
            return false;
        }
        else
        {
            auto lpTarExtractMem = reinterpret_cast<LPUNTAREXTRACTMEM>(GetProcAddress(hTar32, "TarExtractMem"));
            lpTarExtractMem(nullptr, nullptr, nullptr, 0, nullptr, nullptr, nullptr);
            return true;
        }
    }
    }
    
[/code]

次にCallDll.dllです。

[code]

    // csc /target:library /platform:x86 CallDll.cs
    
    using System;
    using System.Runtime.InteropServices;
    
    public class CallDll
    {
        [DllImport("DllHttpDownLoadUntar")]
        [return: MarshalAs(UnmanagedType.I1)]
        private static extern bool HttpDownLoad([MarshalAs(UnmanagedType.LPStr)] string geturl);
    
        public static void Download()
        {
            bool downLoadTest = false;
            downLoadTest = HttpDownLoad("http://IPアドレス/Tar/Android.tar");
            Console.WriteLine(downLoadTest);
        }
    }
    
[/code]

そして、App.exeです。

[code]

    using System;
    
    class App
    {
        static void Main()
        {
            CallDll.Download();
        }
    }
    
[/code]

最後に、App.exe.configです。

[code]

    <configuration>
       <runtime>
          <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
             <probing privatePath="Plugins"/>
          </assemblyBinding>
       </runtime>
    </configuration>
    
[/code]

DllHttpDownLoadUntar.dllのソースファイルDllHttpDownLoadUntar.cppの`#if
0`を切り換えると、LoadLibraryWをTAR32.DLLで呼び出す場合を試せます。

当初の質問者はこの問題を解決する必要が無くなったようですが、質問としては有意義だと思うので、実際に試してみました。


CC BY-SA 3.0
2015-05-31T14:17:05.983
10821
2015-05-31T14:17:05.983
8616
10748
answer