VC++の超初心者です。
今VC++でDLLを作成しています。
VC++で作成したDLLをVBで呼び出しiniファイルの値を取得したいのですが、
VC++でiniファイルの値(数値&文字列)を取得する事が旨くできません、
どうしたら取得出来るのでしょうか?
以下ソースです。
//----------------------------------------------------------------
// test.cpp
//----------------------------------------------------------------
#include stdafx.h
#include test.h
#define INI_FILENAME test.ini // INIファイル名
#define SECTION_NAME ProgramData // セクション名
#define KEY_NAME IntData // キー名
#define ERR_MES NO_CD // チェック失敗メッセージ
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
TEST_API int fnmsg(LPTSTR bak)
{
// GetPrivateProfileString用変数宣言
LPTSTR lpReturnedString="; // バッファへのポインタ
LPTSTR lpFileName="; // INIファイル名
LPCTSTR lpName=test.ini; // INIファイル名
GetPrivateProfileString(SECTION_NAME, // セクション[DISKCHECK]
KEY_NAME,
ERR_MES,
lpReturnedString,
256,
lpName);
bak = lpReturnedString;
return (0);
}
// これはエクスポートされたクラスのコンストラクタです。
// クラスの定義については test.h を参照してください。
CTest::CTest()
{
return;
}
//----------------------------------------------------------------
// test.h
//----------------------------------------------------------------
#ifdef TEST_EXPORTS
#define TEST_API __declspec(dllexport)
#else
#define TEST_API __declspec(dllimport)
#endif
// このクラスは test.dll からエクスポートされます
class TEST_API CTest {
public:
CTest(void);
// TODO: この位置にメソッドを追加してください。
};
//extern TEST_API int nTest;
TEST_API int fnmsg(LPTSTR bak);
> TEST_API int fnmsg(LPTSTR bak)
> {
> // GetPrivateProfileString用変数宣言
> LPTSTR lpReturnedString="; // バッファへのポインタ
> LPTSTR lpFileName="; // INIファイル名
> LPCTSTR lpName=test.ini; // INIファイル名
> GetPrivateProfileString(SECTION_NAME, // セクション[DISKCHECK]
> KEY_NAME,
> ERR_MES,
> lpReturnedString,
> 256,
> lpName);
> bak = lpReturnedString;
> return (0);
> }
lpReturnedStringが指しているメモリ実体はドコにあるのでしょうか?
現状のままでは、(最適化にもよると思いますが)lpFileNameと同じ場所を指していたりしませ
んか?
ちなみに、GetPrivateProfileString()の第5引数で指定している256バイト分のメモリが必要
です。
先ほどの例では、コンパイラが用意した"のあるアドレスから書き換えているハズです。
無論、書き換え用に用意された領域でもないでしょうし、後に続くデータが破壊されていると思
われます。
fnmsg()がポインタを受け取るようになっていますから、
受け取ったポインタをそのままGetPrivateProfileString()に渡せばいいのではないでしょう
か?
もちろん、そのポインタの指し示す先のメモリ実体は、必要なだけのサイズがないといけません
が。
> lpReturnedStringが指しているメモリ実体はドコにあるのでしょうか?
> 現状のままでは、(最適化にもよると思いますが)lpFileNameと同じ場所を指していたりしま
せ
> んか?
「メモリ実体」とはポインタのことを言っているのですか?
> ちなみに、GetPrivateProfileString()の第5引数で指定している256バイト分のメモリが
必要
> です。
VBで言う、Dim lpReturnedString As String * 256 と言うふうに宣言しなくてはいけない
のですか?
> fnmsg()がポインタを受け取るようになっていますから、
> 受け取ったポインタをそのままGetPrivateProfileString()に渡せばいいのではないでしょ
う
> か?
fnmsg()がポインタを受け取るようになっているかどうかはどうしてわかるのですか?
> もちろん、そのポインタの指し示す先のメモリ実体は、必要なだけのサイズがないといけませ
ん
> が。
必要なだけのサイズを指定するにはどのようにすればよろしいのですか?
> 「メモリ実体」とはポインタのことを言っているのですか?
バッファを保管する為に確保されたメモリの事だと思います。
> fnmsg()がポインタを受け取るようになっているかどうかはどうしてわかるのですか?
LPTSTRを調べてみましょう。
あ、ご自分でもコメントに書いているじゃないですか。
あとはVBを知らないのでわかりません(ToT)
>> 「メモリ実体」とはポインタのことを言っているのですか?
> バッファを保管する為に確保されたメモリの事だと思います。
メモリ実体とはメモリのアドレス見たいものを指定するのですか?
lpReturnedStringが指しているメモリ実体とlpFileNameの指しているメモリ実体
が同じ場所を指しているといけないのですか?
> fnmsg()がポインタを受け取るようになっているかどうかはどうしてわかるのですか?
LPTSTRを調べてみましょう。
あ、ご自分でもコメントに書いているじゃないですか。
あ、コメントについてはネットからサンプルを
コピーしてきただけなので意味がわからないのです。
よろしければ教えて下さい。
TEST_API int fnmsg(LPTSTR bak)
{
// GetPrivateProfileString用変数宣言
LPTSTR lpReturnedString="; //*1 バッファへのポインタ
LPTSTR lpFileName="; // INIファイル名
LPCTSTR lpName=test.ini; // INIファイル名
GetPrivateProfileString(SECTION_NAME, // セクション[DISKCHECK]
KEY_NAME,
ERR_MES,
lpReturnedString, //*2
256,
lpName);
bak = lpReturnedString; //*3
return (0);
}
*1でバッファへのポインタを入れるならば、
そのバッファをきちんと用意しましょう。
>「メモリ実体」とはポインタのことを言っているのですか?
この文字列を格納するバッファが「メモリ実体」ということです。
この場合、きちんとバッファを用意してないので
*2の部分でlpReturnedStringが指す場所に
無理矢理書き込まれるので危険だと思います。
*3では引数のbakに取得した文字列をコピーしようとしたのでしょうか。
もしそうならば、これではポインタの代入です。
文字列のコピーにはstrcpy()などを使うといいと思います。
また、瀬戸っぷさんも言っているように、bakにバッファへのポインタを渡すのならば、
最初からbakをGetPrivateProfileString()に渡すといいと思います。
言葉の使い方などには自信がありません。
間違ったことを言っていたら、どなたかフォローお願いします^^;
同じくVBは知りませんが…(ExcelのVBAをちょっといぢったコトがあるだけ)
………テキストファイルに書いていたらレスが増えていますな…
> 「メモリ実体」とはポインタのことを言っているのですか?
ポインタはあくまで「アドレス」を入れる入れ物でしかありません。
現状だとアドレスを示すのに32ビット(4バイト)必要ですので、
その分の変数の領域が用意されただけに過ぎません。
指し示すべき領域は別に用意する必要があります。
> LPTSTR lpReturnedString="; // バッファへのポインタ
> LPTSTR lpFileName="; // INIファイル名
の例の場合、コンパイラが用意した"のアドレスを指すようにしただけで、
必要な容量は確保されていません。
また、書き換えを想定して用意されているワケでもないと思われますので、
内容を破壊した場合の動作が保証できません。
また、最適化によっては上記のlpReturnedStringとlpFileNameが同じアドレスを
指している可能性があります。
もしかしたら次に続くtest.iniの終端コードのアドレスを指している可能性すらあります。
(コンパイラの実装次第ですが)
> LPCTSTR lpName=test.ini; // INIファイル名
こっちの場合は、コンパイラがどこかにtest.iniと入った領域を用意し、
そのアドレスを指すコトになります。
こちらも同じく、書き換えが想定されているワケではありません。
(用意された容量内での書き換えは可能でしょうが、好ましくありません。)
C++で適切な型であれば、上記の方法でも専用のメモリを確保してくれる場合もありますが…
(無論、そのように作られている場合だけですが)
VBでも似たようなモノですかね……
As Stringで作った文字列変数に追加していくと、
自動で必要な容量になるようにしてくれるのでしょう。
char Result[256];
等として、メモリ実体を確保した上でGetPrivateProfileString()に渡すコトになります。
ただしこの方法ではfnmsg()の動作に問題がありますが…。
ココで確保したResultをbak入れて(Resultの先頭アドレスをbakに代入)戻しても、
fnmsg()を抜けた後、そのメモリの内容は保証されません。
たまたま内容が残っているコトがあるかも知れませんが、
いつ何時書き換えられるか判りませんしそもそもそんなトコロをアクセスすべきではありませ
ん。
> VBで言う、Dim lpReturnedString As String * 256 と言うふうに
> 宣言しなくてはいけないのですか?
VBはよく判らないのでこちらはなんとも……
ボコノン教徒さんの示したリンク先を見ると、
Dim nName As String
nName = String(250, Chr(0))
で、250バイト分確保できるようですが……
受け渡し方法もそちらを参照してみてください。
> fnmsg()がポインタを受け取るようになっているかどうかはどうしてわかるのですか?
その様に作られているようだからです。
> TEST_API int fnmsg(LPTSTR bak)
にて、LPTSTR型の引数を取り、int型の戻り値を返す……と。
LPTSTR型は…NULLで終わるWindows文字列またはUnicode文字列を指すポインタ
ですよね。
> 必要なだけのサイズを指定するにはどのようにすればよろしいのですか?
上記のボコノン教徒さんの指摘のまま…ですね。
たいてい、ポインタを引数に渡して、ソコに結果を入れて貰う場合、
確保されているメモリの容量も引数で渡すコトが多いです。
ポインタだけでは、その先のメモリがどのくらいあるのか不明ですから。
4バイトしか確保されていないところに1024バイトも書き込んだら…
正常動作は期待できないでしょう?
VBなら多少わかるのですが、VC++の超初心者ですのですみません。
> *1でバッファへのポインタを入れるならば、
> そのバッファをきちんと用意しましょう。
> >「メモリ実体」とはポインタのことを言っているのですか?
> この文字列を格納するバッファが「メモリ実体」ということです。
> この場合、きちんとバッファを用意してないので
> *2の部分でlpReturnedStringが指す場所に
> 無理矢理書き込まれるので危険だと思います。
バッファを用意するにはどうしたらよいのですか?
> *3では引数のbakに取得した文字列をコピーしようとしたのでしょうか。
> もしそうならば、これではポインタの代入です。
> 文字列のコピーにはstrcpy()などを使うといいと思います。
strcpy(bak,lpReturnedString); ←こういう事ですか?
> また、瀬戸っぷさんも言っているように、bakにバッファへのポインタを渡すのならば、
> 最初からbakをGetPrivateProfileString()に渡すといいと思います。
ポインタはどのように渡せばよろしいのですか?
投稿文字数が多すぎるとのコトで分けます。
> メモリ実体とはメモリのアドレス見たいものを指定するのですか?
バッファのアドレスです。
コレに関しては上に書いた通りです。
> lpReturnedStringが指しているメモリ実体とlpFileNameの指しているメモリ実体
> が同じ場所を指しているといけないのですか?
こちらはあくまで可能性です。
が、どちらにしろ「書き換えていいメモリ」ではないでしょう。
ポインタはあくまでも「指し示すもの」なので、同じ文字列を指している場合、
全く同じアドレスになっている可能性がある……というコトです。
「文字列定数」のアドレスを指定しているので「書き換えられる」という
考えはしない可能性の方が高いですから。(定数ですからね)
実際にメモリ上にどう配置されるかは、コンパイラ次第です。
もし、同じアドレスに配置された場合一方の文字列を書き換えると
他方も書き換わるコトになります。
(もし、ROMに焼き込まれるプログラムだった場合、書き換え自体が不可能です)
> あ、コメントについてはネットからサンプルを
> コピーしてきただけなので意味がわからないのです。
どこからコピーしてきたのかちょっと興味はありますかね……
まさかそっちでも
> LPTSTR lpReturnedString="; // バッファへのポインタ
となっていたとは思いたくないですが……
Cの場合、VBとは違って実際のメモリの状態などをきちんと
イメージする必要があると思います。
アクセスが許可されていないメモリでもそのままアクセスしようとしますから。
自分のプロセス内の場合、たいていそのまま書き換えできます。
結果が破滅的なコトだったとしても。(プログラムコード部分の破壊すらあり得る)
自分のプロセス空間の外等、OSが保護している領域の場合は、
おなじみ(?)の「一般保護違反」になったりするでしょう。
無論、OSがそのように作られていれば……ですが。
(過去のMS-DOSならナニも言わずに暴走でしょう。)
> バッファを用意するにはどうしたらよいのですか?
例えば256バイトのメモリを確保するなら、
char sz[256]; とか、
char* psz=malloc(256); などでしょうか。
> strcpy(bak,lpReturnedString); ←こういう事ですか?
そういうことです。
ただ、lpReturnedStringを
完全に格納できるバッファをbakに用意しておく必要があります。
> ポインタはどのように渡せばよろしいのですか?
例えば↑で用意したバッファへのポインタなら sz, psz でポインタが取れるんで、
それを指定すればいいんです。
BASICからCに来た人にありがちな状況ですね。
BASICで文字列を扱う際には、文字列の連結やコピーが何気なく行えますが、C言語では文字列
の長さ分だけのメモリを自分で確保するなど、ちゃんと'メモリを意識する'必要があります。
今のいささんは、ポインタなどメモリまわりの知識が決定的に不足しているように見受けられ、
今回の件の解決法'だけ'知ったとしても、今後間違いなく同じ問題でつまることになるでしょ
う。
しっかりとC言語の勉強(VC++の勉強ではなく)をすべきかと。