DLLのインポートについて

[戻る]
mmm [E-Mail] 2006/01/21(土) 23:43:31
初めまして。vc初心者です。SDK勉強中です
あるメーカーからデータ読み込み用のAPIを頂きました。
資料によると以下の内容でした。

//xxx.h
#ifdef DATAFILE_EXPORTS
#define DATAFILE_API __declspec(dllexport)
#else
#define DATAFILE_API __declspec(dllimport)
#endif

//DATAFILE.h
enum File_TYPE {AAA = 0};
DATAFILEI_API BOOL WINAPI OpenData(LPCTSTR lpFileName, ULONG ulFileType);

//API名(adddef.dll)
BOOL WINAPI OpenData(LPCTSTR lpFileName, ULONG ulFileType);
lpFileName: ファイル名
File_TYPE: ファイルタイプ(AAA = 0)
戻り値:正常時 TRUE

このDLLを利用してデータファイルを開き、TRUEが戻ってくるかテストしていますが
らららのホームページや猫でもわかるプログラミングで参照して

//test.c
#include "windows.h"
#include "stdio.h"

typedef BOOL(WINAPI* *MYFUNC)(LPCTSTR lpFileName, ULONG ulFileType);
MYFUNC pFunc;

void main()
{
    HINSTANCE hDLL;

    hDLL = LoadLibrary("adddef");    // DLLのロード
    if (hDLL != NULL)
    {
        pFunc = (MYFUNC)GetProcAddress(hDLL,"?add@xcvfdg");    // 関数ポインタ取
得
        if (!pFunc)
        {
            FreeLibrary(hDLL);    // 解放
            return;
        }
        ret = (*pFunc)("c:\\test.dat",0);
        sprintf(str,"%d",ret);
        MessageBox(NULL,str,"メッセージ",MB_OK);
    }
    FreeLibrary(hDLL);
}

として見たのですが、「アプリケーションエラー・・・メモリがreadになることはありませんでした」の
エラーが表示してうまく行きません。
どこがどうおかしいのか理解出来ません。
どなたか分かる方初心者の私に教えていただけないでしょうか?

KING・王 2006/01/21(土) 23:54:15
> として見たのですが、「アプリケーションエラー・・・メモリがreadになることはあり
ませんでした」の
> エラーが表示してうまく行きません。
プログラムのどの行を実行したときに、このエラーが発生しましたか?

ぱっと見、気になったのは、関数ポインタの使い方も間違っているような気が。

mmm [E-Mail] 2006/01/22(日) 00:01:00
mmmです。早速のレスありがとうございます。
pFunc = (MYFUNC)GetProcAddress(hDLL,"?add@xcvfdg");
のpFuncはなにやら245631とか数字が入っているようです。(0以外)
ret = (*pFunc)("c:\\test.dat",0);
を実行後エラーになっているようです。
よろしくお願いします。

KING・王 2006/01/22(日) 00:13:00
> ret = (*pFunc)("c:\\test.dat",0);
> を実行後エラーになっているようです。

だったっら、やっぱり関数ポインタの使い方が間違っているのかな。

とりあえず、適当にGoogleで検索したのですが、下記ページを参考に、試してみてくださ
い。

> http://www2.muroran-it.ac.jp/circle/mpc/old/qanda/prog0000.html

mmm [E-Mail] 2006/01/22(日) 00:38:39
LPCTSTR lpFileName に 直接 "c:\\test.dat" と
入力していいんでしょうか?
どこかのHPみたら、lpFileName は文字列のポインタとか
書いてありました。
でも、sprintf(filename ,"c:\\test.dat");
ret = (*pFunc)(filename 0);でも×
typedef BOOL(*POPENDATA)(LPCTSTR *, DWORD);
ret = (*pFunc)(&filename 0);だと戻り値0です。
やっぱり基本的にポインタの勉強をしないと駄目みたいです。

KING・王 2006/01/22(日) 01:02:33
> LPCTSTR lpFileName に 直接 "c:\\test.dat" と
> 入力していいんでしょうか?
> どこかのHPみたら、lpFileName は文字列のポインタとか
> 書いてありました。

あまり自信がないのですが・・・
直接 "c:\\test.dat"とすると、まず"c:\\test.dat"という文字列が、
文字リテラル(?)として作成され、"c:\\test.dat"とした部分には、
その文字リテラルへのポインタとなります。
#識者の方、詳細のフォローお願いします。

え〜と、とりあえず、私の示したリンク先などは見ていただけましたか?
そこを見ると、関数ポインタの使い方が間違っているようなのですが。。。

mmm [E-Mail] 2006/01/22(日) 01:50:07
すいません
ret = (*pFunc)(&filename 0);だと
この関数内でfilename名を取得する事になるような
気がしますので、戻り値0になるのかな?
もうちょっと、
http://www2.muroran-it.ac.jp/circle/mpc/old/qanda/prog0000.html
のリンク先やC言語の本を読み直して
関数ポインタの使い方を勉強してみます。

Ban 2006/01/22(日) 02:01:12
> typedef BOOL(WINAPI* *MYFUNC)(LPCTSTR lpFileName, ULONG ulFileType);

既に指摘されてますが、'*'がひとつ余分では?

> LPCTSTR lpFileName に 直接 "c:\\test.dat" と
> 入力していいんでしょうか?

'T'と書いてある以上、TCHARが要求されてますから、だめでしょう。
少なくとも、TEXT("c:\\test.dat") または _T("c:\\test.dat")。
# 逆に言えば、TCHARになってればリテラルを直接書いてもオッケーです。

KING・王 2006/01/22(日) 02:03:46
はぁ〜。
私が指摘しているのは、とりあえず、リンク先の2つ目の白抜きの部分の2行目を見て、
mmmさんが記載されている
> ret = (*pFunc)("c:\\test.dat",0);
と比較してみてくださいということです。

Blue 2006/01/22(日) 02:06:18
typedef BOOL ( WINAPI *MYFUNC )( LPCTSTR, ULONG );

pFunc = ( MYFUNC )GetProcAddress( hDLL, "OpenData" );    
TCHAR filename[] = "c:\\test.dat";
ret = ( *pFunc )( filename, 0 );

としてもダメでしょうか?

Blue 2006/01/22(日) 02:07:23
_Tマクロが抜けていました。
> TCHAR filename[] = "c:\\test.dat";
TCHAR filename[] = _T( "c:\\test.dat" );

mmm [E-Mail] 2006/01/22(日) 02:28:23
KING・王さん、Blueさん レスありがとうございます。

Blueさんの指摘の様に実行してみましたが

c:\tmp\vc_project\test3\xxxx.c(52) : error C2275: 'TCHAR' : typedef 識別子に、クラ
ス メンバ アクセス演算子 (->) を使用しました。
c:\program files\microsoft visual studio\vc98\include\winnt.h(189) : 'TCHAR' の
宣言を確認してください。

とエラーが表示されます。根本的にtypedefとかTCHARの意味を完全には
把握しきれていないので(勉強不足)自分であーこうすればいいのかー
という風に出来ません。
申し訳ありません。よろしくご指導お願いします。

Blue 2006/01/22(日) 02:31:51
#include <tchar.h>
を入れたらどうなりますか?

mmm [E-Mail] 2006/01/22(日) 02:40:16
Blueさん、ありがとうございます。

#include <tchar.h>をインクルードしても駄目でした。

もしかしてMFCでないとエラーになるとかあるんでしょうか

申し訳ありません。よろしくご指導お願いします。

Blue 2006/01/22(日) 02:53:41
> もしかしてMFCでないとエラーになるとかあるんでしょうか
そんなことないはずですが。。。

とりあえず、修正したソースを再度載せてもらえますでしょうか?
エラーが出た場合は、お手数ですがもう一度貼ってください。

それと、
>#include "windows.h"
>#include "stdio.h"
一応
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
のほうが良いと思います。
# "〜"と<〜>の違いはヘッダファイルの探し方です。詳しくはMSDNで。

> void main()
も、
int main( void ) か int main( int argc, char* argv[] ) 
として,戻り値を返すようにしたほうがベターです。
参考)
ロベールのC++教室 - 第41章 私はαでありωである -
http://www1.kcn.ne.jp/~robe/cpphtml/html03/cpp03041.html

Ban 2006/01/22(日) 03:17:22
> はぁ〜。

う......orz

> typedef BOOL(WINAPI* *MYFUNC)(LPCTSTR lpFileName, ULONG ulFileType);
この宣言だと、MYFUNCは関数ポインタのポインタなので、
>       ret = (*pFunc)("c:\\test.dat",0);
この呼び出し方自体は妥当だが、
>     pFunc = (MYFUNC)GetProcAddress(hDLL,"?add@xcvfdg");    // 関数ポインタ取
GetProcAddressが返してくるのは、関数ポインタのポインタではなく関数ポインタなの
で、
この(無理な)キャストで参照数がずれ、結果として、関数の先頭コードをアドレスと見
て、
その位置を実行しようとしてしまい不正参照、というのが最初のエラー原因だと
思うのですが、何か間違ってましたでしょうか。

KING・王 2006/01/22(日) 08:13:23
>> typedef BOOL(WINAPI* *MYFUNC)(LPCTSTR lpFileName, ULONG ulFileType);
>この宣言だと、MYFUNCは関数ポインタのポインタなので、
>>       ret = (*pFunc)("c:\\test.dat",0);
>この呼び出し方自体は妥当だが、

あっ、すいません。
typedefの内容を詳しく見ていませんでしたorz。
単純にMYFUNCは関数ポインタだと認識していたので、
私の指摘は間違っていたかも知れません。

すいませんが、後は識者の方々、フォローをよろしくお願いします。

mmmさん、混乱させて申し訳ありませんでした。

mmm [E-Mail] 2006/01/22(日) 10:56:45
Blueさん・Banさん・KING・王さん、レスありがとうございます。
突然、VCがロックしてビルドの中止押しても、復帰しなくなったので
色々調べていたら、VCのSP6を当てたら復旧しました。

Banさんのご指摘どおり
typedef BOOL(WINAPI* *MYFUNC)(LPCTSTR lpFileName, ULONG ulFileType);
をtypedef BOOL(WINAPI *MYFUNC)(LPCTSTR lpFileName, ULONG ulFileType);に
変更したら、実行後エラーは無くなりました。

ret = (*pFunc)("c:\\test.dat",0);
switch( ret ){
    case FALSE: MessageBox(NULL,"FALSE","メッセージ",MB_OK);break;
    case TRUE:  MessageBox(NULL,"TRUE","メッセージ",MB_OK);break;
}
で確認してもFALSEしか帰ってきません。
もう少しソースを見てどこか抜けている所とかないか確認してみます。

Ban 2006/01/22(日) 15:08:41
> ret = (*pFunc)("c:\\test.dat",0);

前述の通り、LPCTSTR に渡す文字列をリテラルで書く場合、TEXT or _T が囲む
必要があります。
# これは、UNICODEの扱いに関するWindows固有のルールです。

相手がUNICODEを期待している場合、文字列はUNICODEで渡してあげなければならない、
逆に相手がShiftJISなどを期待している場合、文字列はそのように渡してあげなければ
ならない、ということです。
文字コードが違うと、当然そのファイルを探すことはできません。


あと、大前提として、読み出しするデータの妥当性と、DLLの妥当性についても
考慮はしておいたほうがよいかと思います。

メーカの開発者がWindowsの作法に詳しくなくて、
LPCSTR でなく LPCTSTR なのは、サンプルにした他の関数にあわせただけで、
単に「Windowsではそう書くものだと思ってた」とかだと泣けます。
# そして、TCHARの認知度などを考えると、ありえない話ではない、かも...

mmm [E-Mail] 2006/01/23(月) 21:06:28
mmmです。DATAFILE.h の最後を見てみたら
以下の様に書いてありました。

//// This class is exported from the DataFileIo.dll
//class DATAFILEIO_API DataFileIo {
//public:
//    DataFileIo(void);
//    // TODO: add your methods here.
//};

//extern DATAFILEIO_API int nDataFileIo;
//
//DATAFILEIO_API int fnDataFileIo(void);

これは、やっぱりクラスで渡すから、クラスで受け取れと
言う事でしょうか?
ちなみに新たに

dllCall.cpp
#include "DATAFILE.h"

void main()
{
    DataFileIo my;
    my.OpenData(LPCTSTR lpFileName, ULONG ulFileType);

}
としてコンパイルしたら
error C2065: 'DataFileIo' : 定義されていない識別子です。とか
色々エラーが帰ってきました。
クラスを勉強していない私には無理でしょうか?

よろしくご指導の程よろしくお願いします。

Ban 2006/01/23(月) 22:33:18
// の後ろはただのコメントですね。
そのままでは通らないでしょう。
#固有の用法に関する質問は提供元のサポートに聞かれた方がよいのでは?

mmm [E-Mail] 2006/01/23(月) 22:49:58
mmmです。
提供元のサポートに何とかお願いして聞いてみます。

ITO 2006/01/23(月) 23:23:15
大変そうですね。
VCなど、Cまたは、C++で作られたDLLならライブラリはないですか?
メーカ提供なら付属しているはずですよね。
なければ、提供してもらうとか出来ないですか。
ここまで大変なら、ライブラリをリンクしたほうが早いと思うのですが。
VB用でしたらすみません。

mmm [E-Mail] 2006/01/25(水) 00:07:00
mmmです。
提供元のサポートに聞いて見た所、

開発言語:C#

ITOさん、レスありがとうございます。
ライブラリ自体はありましたのでリンクしています。

もしかして、ライブラリがあると解決するような
ヒントがあるのでしょうか。

よろしくご指導の程お願いします。

RAPT 2006/01/25(水) 00:21:23
VC++用のライブラリが用意されているなら、静的リンクして、ヘッダファイルを
#includeするだけで使えませんか?

Blue 2006/01/25(水) 00:21:35
libファイルをつかいDLLとリンクと仕方と
LoadLibrary関数を使いDLLとリンクする方法はそれぞ違いがあります。
(作業としてはlibファイルでリンクするほうが格段に楽ちん)

関連スレ
DLL 暗黙的リンク 明示的リンク の差異
http://www.vcppclub.com/bbs1/wforum.cgi?
mode=allread&pastlog=0004&no=2522&page=20&act=past#2522

> 開発言語:C#
とは?なにが C# になるのしょうか?

ITO 2006/01/25(水) 16:29:01
らららのホームページや猫でもわかるプログラミングにも説明があります。
猫でもわかるプログラミングは、119章からきちんと見ましょう

古いスレッドには追加発言できません。