OS:WinXPPro SP2
VC++.NET
dllで文字列配列を戻し、他のexeで受取る方法が
分からなくて困っています。
dllでsplit関数を作成し、使用したいのですが...
(dll側)
extern "C" __declspec(dllexport) SAFEARRAY* __stdcall getSplitText(LPCTSTR pszTarget, LPCTSTR pszDelimiter);
extern "C" __declspec(dllexport) SAFEARRAY* __stdcall getSplitText(LPCTSTR pszTarget, LPCTSTR pszDelimiter)
{
///// 配列を用意
CStringArray* arrResult = new CStringArray();
int nDelimiter;
int nStart;
int nEnd;
CString strTarget = pszTarget;
CString strDelimiter = pszDelimiter;
strTarget = strTarget + strDelimiter;
nDelimiter = strDelimiter.GetLength();
nStart = 0;
while(nEnd = strTarget.Find(strDelimiter, nStart), nEnd >= 0)
{
arrResult->Add(strTarget.Mid(nStart, nEnd - nStart));
nStart = nEnd + nDelimiter;
}
SAFEARRAY* pSa;
SAFEARRAYBOUND bound;
bound.cElements = (ULONG)arrResult->GetSize();
bound.lLbound = 0;
pSa = SafeArrayCreate(VT_BSTR, 1, &bound);
// SAFEARRAYにアクセスする.
LPCTSTR* sArray;
SafeArrayAccessData(pSa, (void**)&sArray);
for(int i = 0; i < arrResult->GetSize(); i++)
{
sArray[i] = arrResult->operator [](i);
}
SafeArrayUnaccessData(pSa);
delete arrResult;
return pSa;
}
(exe側)
typedef SAFEARRAY* (__stdcall *DISPLAYMESSAGE)(LPCTSTR, LPCTSTR);
void CTest2View::OnBnClickedButton2()
{
HINSTANCE hDllInstance;
DISPLAYMESSAGE Split; // 関数アドレス
hDllInstance = ::LoadLibrary("D:\\xxx\\yyy\\ComProc.dll");
if( hDllInstance != NULL )
{
// エクスポートされた関数のアドレスを取得
Split = (DISPLAYMESSAGE)(::GetProcAddress(hDllInstance, "getSplitText"));
if( Split != NULL )
{
CStringArray* arrResult = new CStringArray();
SAFEARRAY* pSa;
pSa = Dll_TestMessageBox("123,456,789", ",");
// SAFEARRAYにアクセスする.
LPCTSTR* sArray;
SafeArrayAccessData(pSa, (void**)&sArray);
for(int i = 0; i < 3; i++)
{
arrResult->Add(sArray[i]);
}
SafeArrayUnaccessData(pSa);
AfxMessageBox(arrResult->operator [](0));
delete arrResult;
}
}
FreeLibrary(hDllInstance);
}
だとアクセス違反になります。
SAFEARRAYからの取り出し方に問題があるのでしょうか?
よろしくお願いします。
>sArray[i] = arrResult->operator [](i);
文字列領域を確保していないのでは?
>LPCTSTR* sArray;
BSTR* sArray;
なんじゃないかな。
んで、
格納するときは、SysAllocStringかSysAllocStringLengthをつかうのかな。
>格納するときは、SysAllocStringかSysAllocStringLengthをつかうのかな。
CString の AllocSysString を使うほうがべんりですな。
サンプル)
// DLL側
// 本来ならば、HRESULTで成功したかどうかを返し、引数に LPSAFEARRAY* を指定して格納するほうがよい
SAFEARRAY* WINAPI Sample21()
{
SAFEARRAYBOUND sb;
sb.lLbound = 0;
sb.cElements = 3;
SAFEARRAY* psa = ::SafeArrayCreate( VT_BSTR, 1, &sb );
BSTR* data;
::SafeArrayAccessData( psa, ( void** )&data );
data[ 0 ] = ::SysAllocString( L"イチロー" );
data[ 1 ] = ::SysAllocString( L"松井秀喜" );
data[ 2 ] = ::SysAllocString( L"松坂大輔" );
::SafeArrayUnaccessData( psa );
return psa;
}
// exe側
#include <windows.h>
int main()
{
HMODULE hDLL = ::LoadLibrary( TEXT( "C:\\XXX\\YYY\\hoge.dll" ) );
if ( hDLL )
{
SAFEARRAY* ( WINAPI *func )();
func = ( SAFEARRAY* ( WINAPI * )() )::GetProcAddress( hDLL, TEXT( "Sample21" ) );
SAFEARRAY* psa = ( *func )();
long lb, ub;
BSTR data;
::SafeArrayGetLBound( psa, 1, &lb );
::SafeArrayGetUBound( psa, 1, &ub );
for ( long i = lb; i <= ub; i++ )
{
::SafeArrayGetElement( psa, &i, &data );
::MessageBoxW( NULL, data, L"てすと", MB_OK );
::SysFreeString( data );
}
// 後処理
// DLL側で処理させる関数を作るべき
for ( long j = lb; j <= ub; j++ )
{
::SafeArrayGetElement( psa, &j, &data );
::SysFreeString( data );
}
::SafeArrayDestroy( psa );
::FreeLibrary( hDLL );
}
return 0;
}
ちなみに、C++⇔C++であればSafeArrayを使わなくてもいいような気もする。
→APIでよくある、\0で配列を区切り、終端を\0\0にしたり。
(MFCのDLLを使えば、CStringArrayを普通に使えたような。)
Blueさんいつもありがとうございます。
(dll側)
BSTR* sArray;
SafeArrayAccessData(pSa, (void**)&sArray);
BSTR bstr = SysAllocString(L"");
BSTR bs;
for(int i = 0; i < arrResult->GetSize(); i++)
{
bs = arrResult->operator [](i).SetSysString(&bstr);
sArray[i] = bs;
}
(exe側)
#include <AfxPriv2.h>
BSTR* sArray;
SafeArrayAccessData(pSa, (void**)&sArray);
CString Str;
for(int i = 0; i < 3; i++)
{
AfxBSTR2CString(&Str, sArray[i]);
arrResult->Add(Str);
}
エラーは出なくなり、メッセージボックスは表示されますが
789しか表示されません。
取出し方に問題があるのでしょうか?
よろしくお願いします。
すみません、Blueさんの次の書込みを見ていませんでした。
(exe側)
SAFEARRAY* psa = Dll_TestMessageBox("123,456,789", ",");
long lb, ub;
BSTR data;
::SafeArrayGetLBound( psa, 1, &lb );
::SafeArrayGetUBound( psa, 1, &ub );
for ( long i = lb; i <= ub; i++ )
{
::SafeArrayGetElement( psa, &i, &data );
::MessageBoxW( NULL, data, L"てすと", MB_OK );
::SysFreeString( data );
}
for ( long j = lb; j <= ub; j++ )
{
::SafeArrayGetElement( psa, &j, &data );
::SysFreeString( data );
}
::SafeArrayDestroy( psa );
でも、789しか表示されません。
>bs = arrResult->operator [](i).SetSysString(&bstr);
ちがうっぽ。
//BSTR bstr = SysAllocString(L"");
//BSTR bs;
for(int i = 0; i < arrResult->GetSize(); i++)
{
//bs = arrResult->operator [](i).SetSysString(&bstr);
sArray[i] = arrResult->GetAt(i).AllocSysString();
}
それと気になったこと
>CStringArray* arrResult = new CStringArray();
なんで new するのでしょうか?
単に
CStringArray arrResult;
で十分でしょう。
それと、ソースコードをのせるときは、タブを半角空白に変換してから載せてください。
インデントが消えると非常に読みにくいですので。
Blueさん、いつもいつもありがとうございます。
SAFEARRAYを使用したのはVBでも使いたかったからです。
VBのテストもOKでした。
ソースの載せ方、気をつけます。
ツイート | ![]() |