はじめまして、offと申します。
早速ですが、質問です。
Cで作ったDLLからログインしているユーザ名を取得するPGを作っているのですがうまくユーザ名が取れないのです。
Cのソースは、
TEST_API CHAR* __stdcall UserName(void){
char lpBuffer[256];
DWORD dwSize ;
dwSize = sizeof(lpBuffer) ;
memset( lpBuffer, '\0', dwSize ) ;
GetUserName(lpBuffer, &dwSize );
return lpBuffer ;
}
VBのソースは、
Private Declare Function UserName Lib "TEST.dll" () As Long
Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" ( ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private Sub Command1_Click()
Dim p As Long
Dim b(256) As Byte
Dim s As String
p = UserName
Call MoveMemory(b(0), ByVal p, 256)
s = StrConv(b(), vbUnicode)
s = Left$(s, InStr(s, vbNullChar) - 1)
MsgBox "現在のユーザ:" & s & "です。"
です。
UserName からはアドレスらしき数値が戻ってきてるのですが、
そのあとのMoveMemoryを呼んでもbには何も入らないのです。
どうすれば、Cからcharの文字列を
VB側のString変数に渡せばよいのでしょうか?
どうか、よろしくお願いします。
スタック上に確保されたメモリ領域はDLLを抜けると不定になってしまうんじゃ?
DLLないでGlobalAllocを使用してメモリを確保するか、VBから確保したメモリに
DLL内にて文字列を書き込むといった処理にしてみて下さい。
> スタック上に確保されたメモリ領域はDLLを抜けると不定になってしまうんじゃ?
その通りですね。
一番簡明なのはWinAPIのように、VB側であらかじめ文字列領域を確保して、
C側でその領域に設定する方法があります。
また、String変数をVARIANT*型で受取り、C側でSysAllocString関数で設定しする
方法があります。
いずれにせよ、戻り値でないほうがやりやすいと思います。
(戻り値は成功、失敗等の結果を示す値にしたほうがいいかと)
というか、
> GetUserName(lpBuffer, &dwSize );
をそのままVBから使えるようにすればいいのでは。
TEST_API void __stdcall GetUserName( LPCSTR lpUserName, DWORD dwSize )
・・・
Private Declare Sub GetUserName Lib "TEST.dll" (ByVal username As String, ByRef size As Integer)
Private Sub Command1_Click()
Dim s As String
Dim n As Integer
n = 256
s = String(n,vbNullChar)
Call GetUserName(s,n)
s = Left(s,InStr(s,vbNullChar)-1)
MsgBox "現在のユーザ:" & s & "です。"
End Sub
> TEST_API void __stdcall GetUserName( LPCSTR lpUserName, DWORD dwSize )
TEST_API void __stdcall GetUserName( LPCSTR lpUserName, DWORD* dwSize )
第2引数はポインタですた。。。orz
どうもです。
Blueさんのように、
Cは、
TEST_API void __stdcall UserName(LPCSTR lpBuffer, DWORD* dwSize)
にかえ、
VBは、
Private Declare Function UserName2 Lib "ClientInfo.dll"
(ByVal username As String, ByRef size As Integer) As Long
に変えてみました。
しかし、C側でWinAPIのGetUserNameで文字化けして、
VBではユーザ名をちゃんと取ることができません。
GetUserName((char *)&lpBuffer, dwSize );
これは間違った書き方なのでしょうか?
> GetUserName((char *)&lpBuffer, dwSize );
これってどこの処理?
TEST_API void __stdcall UserName(LPCSTR lpBuffer, DWORD* dwSize)
{
GetUserName((char *)&lpBuffer, dwSize );
}
ってこと?
それならば、
GetUserName( lpBuffer, dwSize );
でいいです。
# > TEST_API void __stdcall UserName(LPCSTR lpBuffer, DWORD* dwSize)
# は
# ハンガリアンに従うならば、
# TEST_API void __stdcall UserName(LPCSTR lpBuffer, DWORD* pdwSize)
# でした。
> GetUserName
って、WinAPIなのね。。。いちいちDLL作らんでもいいような。。。
しかも戻り値あるし。
そうです。
TEST_API void __stdcall UserName(LPCSTR lpBuffer, DWORD* dwSize)
{
GetUserName((char *)&lpBuffer, dwSize );
// ここでlpBufferの中身をテキストに出力したら文字化けしてましたです。
}
GetUserName( lpBuffer, dwSize );をビルドすると、
'const char *' 型は 'char *' 型に変換できない
(関数 __stdcall fnGetUserName2(const char *,unsigned long *) )
のエラーが出ます。
>TEST_API void __stdcall UserName(LPCSTR lpBuffer, DWORD* dwSize)
^^^^^^
型がちがってました。スイマセン。
LPSTRでした。
LPCSTRだとconstになるので変更が利かないのでした。
LPCSTR = const char *
なんだから&lpBufferは
const char **
になってしまうでしょ。
波線 おもいっきりずれた。orz
# ここの掲示板はプロポーショナルフォントなのね。(´△` )アァー
ん?なんか変なこと書いたかも
解決しました。
LPCSTRでユーザ名が取れました。
どうも、Blueさん、9566さん、
ありがとうございました。
> LPCSTRでユーザ名が取れました。
ホントでっか?
Cだからconst_cast出来ないとおもうのだが。。。
Blueさん。
間違えました。
「LPSTRでいけました。」の書き間違えです。
どうも、すみません。
あれから、
> Cで作ったDLLから戻り値(文字列)の取得について
ついて、いろいろとやってみました。
SysAllocStringByteLen関数を使うといとも簡単に出来ました。
(解答した時は知識不足でした。)
__declspec( dllexport ) BSTR UserName()
{
char szBuff[ 256 ] = { 0 };
DWORD dwSize = 256;
if ( GetUserNameA( szBuff, &dwSize ) )
{
return SysAllocStringByteLen( szBuff, dwSize - 1 );
}
return NULL;
}
> return NULL;
NULLじゃないほうがいいかも。
return SysAllocString( L"" );
Blueさん、こんにちは。
SysAllocStringByteLen関数版ですか?
そんなやり方があったんですね。
早速、DLLに追加してみます。
質問内容とは違うことなのですが、また、質問してもよろしいでしょうか?
9X系、NT系の両方で使えるDLLを作ろうとしているのですが、
VBからDLLの中の関数を呼んだとき、NT系であればNT系にしか無い
APIを使って、そうでなければ、9X系のAPIを使っています。
このDLLを9X系で動かすと、「ファイルが見つかりません」の
エラーが出てしまいます(ロードエラー)。
そこで、NT系のAPIをLoadLibraryで動的に呼ぶように変更したのですが
結果はエラーのままなのです。
こんなとき、9X系、NT系の両方でも使えるようにするにはどうすれば
良いのでしょうか?
どうか、よろしくお願いします。
> 質問内容とは違うことなのですが、また、質問してもよろしいでしょうか?
改めて、新しいスレを建てたほうがよいです。
それと、完全にVBとCで切り分けられて、Cの話になってしまっているので、
ここの掲示板では場違いでしょう。
Visual C++ Q & A 掲示板を使ったほうがよさそう。
http://madia.world.coocan.jp/cgi-bin/Vcbbs/wwwlng.cgi
# VCで開発していないならば、別の掲示板探すのかな。。。
質問の際には、環境(OS,コンパイラの種類)を明示したほうがベターです。
> こんなとき、9X系、NT系の両方でも使えるようにするにはどうすれば
> 良いのでしょうか?
ちなみに、私はわかりません。
>こんなとき、9X系、NT系の両方でも使えるようにするにはどうすれば
>良いのでしょうか?
On Error Resume Nextを使ってください。
> >こんなとき、9X系、NT系の両方でも使えるようにするにはどうすれば
> >良いのでしょうか?
> On Error Resume Nextを使ってください。
DLLのロードの失敗はOnErrorでは補足できなかったと思います。
APIを環境ごとにActiveXDLLにラップして、その都度ロードするのを変えてみては?
>DLLのロードの失敗はOnErrorでは補足できなかったと思います。
このDLLを9X系で動かすと、「ファイルが見つかりません」の
エラーが出てしまいます(ロードエラー)。
↑
これがVBのON ERRORでは補足できないということですか?
以前、マイクロソフトから
WindowsXP以上からしかないDLLを動かす方法として
(Windows2000でも動くようにする)
On Error Resume Nextを教えてもらったんですが・・
Blueさん、あんさん、もさん、こんにちは。
VBに関係ない質問を投げてしまってすみません。
別の掲示板で問い掛けてみます。
それと、ActiveXDLLではレジストリに登録しないといけない?のは
避けたいので、別の方法を考えてみます。
どうも、みなさんお騒がせ致しました。
> VBに関係ない質問を投げてしまってすみません。
なんかそうでもないような気がしてきた。
DLLは結局のところ1つしかつくらないのですか?
ある特定の関数を使おうとするとエラーが発生し、それ以外の関数は
正常に使えるのでしょうか?
ちなみに、
>9X系、NT系の両方で使えるDLLを作ろうとしているのですが、
>VBからDLLの中の関数を呼んだとき、NT系であればNT系にしか無い
>APIを使って、そうでなければ、9X系のAPIを使っています。
なんてAPIでしょうか?
>>DLLのロードの失敗はOnErrorでは補足できなかったと思います。
>以前、マイクロソフトから
>WindowsXP以上からしかないDLLを動かす方法として
>(Windows2000でも動くようにする)
>On Error Resume Nextを教えてもらったんですが・・
すみません、
今OnErrorを使ったテストプログラムを作ってみたところ、
ちゃんと補足できる例外が発生していました。
でも存在しないときどうやって分岐させればいいのかな…
Blueさん、もさんこんにちは。
私の環境では、OnErrorでエラーを引っ掛けることが出来ました。
エラー番号48:ファイルがみつかりません。TEST.DLL
Blueさん、そうです、DLLは1つしか作らず、
それをサーバにEXEと同じフォルダに置いて、
どのプラットフォームからでも利用できるアプリを作ろうとしています。
以前は、VBでActiveXDLLを作っていたのですが、
各端末にレジストリ登録をしなければならなかったので、
今回はCでDLLを作って挑戦しています。
他のAPIでも同様にエラーになります。これは単純にDLLが
端末に存在しないからだと思います。
因みに動かないAPIは、WTSQuerySessionInformationです。
9X系にはこのAPIが入っているWTSAPI32.DLLがないのです。
----------------------------------------------------------
もさん、「存在しないときどうやって…」っていうのは、
本当にDLLがないのかロードできないのか判断できない
ってことなのでしょうか?
> WTSQuerySessionInformation
のみを使おうとすると、その関数だけエラーが発生するのですね?
> そこで、NT系のAPIをLoadLibraryで動的に呼ぶように変更したのですが
> 結果はエラーのままなのです。
ということなので、WTSQuerySessionInformationを直接使わなくても
(LoadLibrary + GetProcAddressでWTSQuerySessionInformationの関数のアドレスにしても)
エラーがでるのですね?
>もさん、「存在しないときどうやって…」っていうのは、
>本当にDLLがないのかロードできないのか判断できない
>ってことなのでしょうか?
いえ、DLLファイルが無い、DLLにエントリが無いといった例外は補足できました。
しかし例外を補足したとして、
回復して代わりに何をするのかが思い浮かびません。
Cだと明示的にリンクできるのですけどね…
>のみを使おうとすると、その関数だけエラーが発生するのですね?
そうです。
>ということなので、WTSQuerySessionInformationを直接使わなくても
>(LoadLibrary + GetProcAddressでWTSQuerySessionInformationの関数のアド>レスにしても)
>エラーがでるのですね?
そうです、呼ばなくてもエラーになリます。
もさん、勘違いをしてすみません。
>しかし例外を補足したとして、
>回復して代わりに何をするのかが思い浮かびません。
>Cだと明示的にリンクできるのですけどね…
例外で引っかかってくれれば、
エラーMSGを出して、その内容をログに出力するだけ。
あとは、DBなどを正常に切断させるように組めば
それだけでOKです(そんなに大きなシステムじゃないので)。
> そうです、呼ばなくてもエラーになリます。
LoadLibrary + GetProcAddressの実際のソースが見たいかも。
# ここまでくるとやっぱりCの話なのかなぁ。。。と
# 焦点は、9X系、NT系 をどこて判定するかがVBでできるかC側でできるかなのかなぁ。
> それと、ActiveXDLLではレジストリに登録しないといけない?のは
最近のOSに限定するなら、SxSにてこの制限を回避できる可能性があります。
http://www.microsoft.com/japan/msdn/windows/windowsxp/sidexsidewinxp.asp
> いえ、DLLファイルが無い、DLLにエントリが無いといった例外は補足できました。
> しかし例外を補足したとして、
> 回復して代わりに何をするのかが思い浮かびません。
そもそも9x系ならば、WTSQuerySessionInformationを呼ぶ必要自体が無いの
ですから、事前にOSの確認処理を行っておけば、そのような回復処理を
考慮しなくて済むような気もしますが……(そういう話ではないのかな?)
Cのソースです。
int fnWTSQuerySessionInformation(LPSTR lpBuffer, int WTSInfo){
typedef BOOL (WINAPI *LPWTSQUERYSESSIONINFORMATION)(HANDLE, DWORD, int, LPTSTR*, DWORD* );
DWORD dwProccessId ;
DWORD SessionId ;
BOOL rtncd ;
LPSTR ppBuffer ;
DWORD pBytesReturned ;
dwProccessId = GetCurrentProcessId();
rtncd = ProcessIdToSessionId( dwProccessId, &SessionId ) ;
// DLLをロード
HINSTANCE hLib = LoadLibrary("wtsapi32.dll");
if(!hLib) {
FreeLibrary(hLib); return 1;
}
// アドレスを取得
LPWTSQUERYSESSIONINFORMATION lpWTSAPIEntryPoint; // ポインタ
lpWTSAPIEntryPoint = (LPWTSQUERYSESSIONINFORMATION)GetProcAddress(hLib, "WTSQuerySessionInformationA");
if(!lpWTSAPIEntryPoint) {
FreeLibrary(hLib);
return 1;
}
// 呼び出し
BOOL bRet = (*lpWTSAPIEntryPoint)(0, SessionId, WTSInfo, &ppBuffer, &pBytesReturned );
FreeLibrary(hLib);
strcpy(lpBuffer, ppBuffer); //ここにユーザ名が入る
fclose(fp);
return 0;
}
TEST_API int __stdcall fnUserName(LPSTR lpBuffer, DWORD* dwSize){
if( GetSystemMetrics( SM_REMOTESESSION ) )
lfnWTSQuerySessionInformation( lpBuffer, WTSUserName );
else
GetUserName(lpBuffer, dwSize );
return 0;
}
です。NT系か9X系かでは判断せずに、
ターミナルサービス上で動いているかでやっています。
>Cだと明示的にリンクできるのですけどね…
VBでもできると思いますが・・
VBではAPIを呼ぶところでDLLをリンクするので
そのAPIを呼ばなければリンクしない!
あんさん。先ほどのレスありがとうございました。
危うくDLLの読み込み時の例外を補足出来ないと思い続けていくところでした;
>>Cだと明示的にリンクできるのですけどね…
>VBでもできると思いますが・・
>VBではAPIを呼ぶところでDLLをリンクするので
>そのAPIを呼ばなければリンクしない!
たしかに、VB内部では呼び出し時に動的に解決をしているようですね。
リンクに失敗した後の回復方法として、Cで扱うような明示的リンクを想像したのですが…
> そもそも9x系ならば、WTSQuerySessionInformationを呼ぶ必要自体が無いの
> ですから、事前にOSの確認処理を行っておけば、そのような回復処理を
> 考慮しなくて済むような気もしますが……(そういう話ではないのかな?)
私もこれがいいと思います。(VB側でGetVersionEx関数あたりを使うのかな)
おはようございます。
>そもそも9x系ならば、WTSQuerySessionInformationを呼ぶ必要自体が無い…
呼び方が2パターンありまして、
ひとつは、C/S環境でDLLをロードし、ユーザ名を取得。
このときは、GetUserNameを呼ぶ。
もうひとつは、WTS、RDP環境で、ユーザ名(接続元)を取得。
このときは、WTSQuerySessionInformationを呼ぶ。
NT系は両方できたのですが、
C/S環境の9x系のとき、どうしてもロードエラーが回避ないのです。
ツイート | ![]() |