はじめまして。
VB6(SP6)のアプリに文字列の配列を渡すDLLをVC6 + WinXPで作ってます。
配列の要素数は状況によって変わるので、VBの動的配列に入れて渡すことしました。
下のようにコーディングしてみましたが、実行するとVB側には一部の文字が
「?」(クエスチョンマーク)に文字化けして表示されてしまいます。
これを文字化けしないように直したいのですが、どうしたらよいのでしょうか?
(化ける文字と化けない文字があるのは何故?)
下の例ですと、VBのListBox(List1)には
ABC
123456
あいうえお
?????
?うこそ
?しいy
今日の夕食はカ??にス??
と表示されてしまいます。「都」は「y」に化けます。
======== VBアプリ側 ========
Declare Function ListTest Lib "xxxxxx.dll" (ByRef list0() As String) As Long
Private Sub Command1_Click()
Dim i As Integer
Dim ref As Long
Dim stlist() As String
Dim st As String
ref = ListTest(stlist())
For i = 0 To UBound(stlist)
st = StrConv(stlist(i), vbFromUnicode)
List1.AddItem (st)
Next i
End Sub
======== VC DLL側 ========
// _MBCSでコンパイルしてます
typedef std::basic_string<_TCHAR> STR; // _TCHAR string型
__declspec(dllexport) bool __stdcall ListTest(LPSAFEARRAY *ppsa)
{
BSTR item0;
long rglndices[1];
std::vector<STR> itemlist;
// 質問用にハードコーディングしてます
itemlist.push_back(_T("ABC"));
itemlist.push_back(_T("123456"));
itemlist.push_back(_T("あいうえお"));
itemlist.push_back(_T("らりるれろ"));
itemlist.push_back(_T("ようこそ"));
itemlist.push_back(_T("美しい都"));
itemlist.push_back(_T("カレーにスルー"));
// 既に領域が確保されている配列が渡されたら、その領域は破棄する
if (*ppsa != NULL) {
if (FAILED(::SafeArrayDestroy(*ppsa))) return false;
}
setlocale(LC_ALL, "Japanese");
if (FAILED(::SafeArrayAllocDescriptor(1, ppsa))) return false;
(*ppsa)->cbElements = sizeof(BSTR);
(*ppsa)->fFeatures = FADF_STATIC;
(*ppsa)->rgsabound[0].lLbound = 0;
(*ppsa)->rgsabound[0].cElements = itemlist.size();
if (::SafeArrayAllocData((*ppsa)) != S_OK) return false;
::SafeArrayLock(*ppsa);
setlocale(LC_ALL, "Japanese");
for (int cnt = 0; cnt < itemlist.size(); cnt++) {
rglndices[0] = cnt;
_bstr_t bstr1(itemlist[cnt].c_str());
item0 = bstr1.copy();
::SafeArrayPutElement(*ppsa, rglndices, &item0);
}
::SafeArrayUnlock(*ppsa);
return true;
}
よろしくお願いします。
どうでもいいことですが。。
>今日の夕食はカ??にス??
は
カ??にス??
です。すみません。
>st = StrConv(stlist(i), vbFromUnicode)
は不要では。stlist(i)はUnicodeで格納されているはずです。
というか、VB側では文字列はUnicodeでないといけないのにもかかわらず、
いちいちCP932(Shift_JIS)に変換しているようですけど。
回答ありがとうございます。
>>st = StrConv(stlist(i), vbFromUnicode)
>は不要では。stlist(i)はUnicodeで格納されているはずです。
そう思って、
For i = 0 To UBound(stlist)
List1.AddItem (stlist(i))
Next i
としてたのですが、これだとVB側の表示が
A
1
B0D0F0H0J0
・・・・・
・F0S0]0
・W0D0
ォ0・・k0ケ0・・
となってしまうのです。
>というか、VB側では文字列はUnicodeでないといけないのにもかかわらず、
>いちいちCP932(Shift_JIS)に変換しているようですけど。
これはStrConv(stlist(i), vbFromUnicode)のことをおっしゃってるのですよね。
あと念のため、VC側のプリプロセッサの定義を_MBCSから_UNICODEに変えてみても同じ結果になりました。
>・W0D0
HTMLの実体参照に変換されちゃってますが、&#63729;(また変換されるとイヤなのでわざと全角にしました)の部分は実際は半角の黒い四角です。
どうも文字列配列のばあい、CP932で配列に入れないとだめそうです。
よって
>item0 = bstr1.copy();
は
LPCSTR temp = bstr1;
item0 = ::SysAllocStringByteLen(temp, strlen(temp));
1次元の文字型配列であればSafeArrayCreateVectorとSafeArrayAccessData
を使うほうがコードが簡単になります。
HRESULT WINAPI Sample(LPSAFEARRAY* ppsa)
{
HRESULT hr = S_OK;
if (!ppsa) return E_INVALIDARG; // 引数エラー
if (!*ppsa)
{
// 既存配列の削除
hr = ::SafeArrayDestroy(*ppsa);
if (FAILED(hr)) return hr;
}
static const LPCSTR Foot_bola[3] = {"中村俊輔", "松井大輔", "高原直泰"};
const long size = sizeof(Foot_bola) / sizeof(Foot_bola[0]);
/*
// 配列の再確保
hr = ::SafeArrayAllocDescriptor(1, ppsa);
if (FAILED(hr)) return hr;
// 要素のサイズを設定
(*ppsa)->cbElements = sizeof(BSTR);
(*ppsa)->fFeatures = FADF_STATIC;
// 配列のサイズを設定
(*ppsa)->rgsabound[0].lLbound = 0;
(*ppsa)->rgsabound[0].cElements = size;
// 格納領域の確保
hr = ::SafeArrayAllocData(*ppsa);
if (FAILED(hr))
{
::SafeArrayDestroyDescriptor((*ppsa));
return hr;
}
// 要素の設定
::SafeArrayLock(*ppsa);
BSTR item;
for (long index = 0; index < size; ++index)
{
::SafeArrayGetElement(*ppsa, &index, &item);
::SysFreeString(item);
// 文字列配列の場合マルチバイト文字列として格納する
item = ::SysAllocStringByteLen(Foot_bola[index], strlen(Foot_bola[index]));
::SafeArrayPutElement(*ppsa, &index, &item);
}
::SafeArrayUnlock(*ppsa);
*/
*ppsa = ::SafeArrayCreateVector(VT_BSTR, 0L, size);
if (!*ppsa) return E_OUTOFMEMORY; // メモリ不足
// 要素の設定
BSTR* items;
::SafeArrayAccessData(*ppsa, (LPVOID*)&items);
for (long index = 0; index < size; ++index)
{
::SysFreeString(items[index]);
// 文字列配列の場合マルチバイト文字列として格納する
items[index] = ::SysAllocStringByteLen(Foot_bola[index], strlen(Foot_bola[index]));
}
::SafeArrayUnaccessData(*ppsa);
return hr;
}
それとこのコードは、からの配列を渡す分にはきちんと格納されるようですけど、
配列が既にある(*ppsa!=NULL)パターンのときちゃんと格納できないような
気がします。
>どうも文字列配列のばあい、CP932で配列に入れないとだめそうです。
うわ〜ん。ややこしいですね。。
>LPCSTR temp = bstr1;
>item0 = ::SysAllocStringByteLen(temp, strlen(temp));
これで意図通りに動くようになりました。
けど、なんでしょうか、自分で作った小包を自分でほどいてるような、そんな空しさがありますね。。
教えてもらっておきながらすみません。
ともかくありがとうございました!!
あっ、2007/04/19(木) 11:38:53の書き込みがあるのに気づかずに送ってしまいました。
2007/04/19(木) 11:38:53の記事について今から検討します。すみません
>if (!*ppsa)
条件式が間違っていた。
if (*ppsa)
です。
ちなみにVARIANT型配列で受け渡しする場合はもっと簡単になります。
HRESULT WINAPI Sample(LPSAFEARRAY* ppsa)
{
HRESULT hr = S_OK;
if (!ppsa) return E_INVALIDARG; // 引数エラー
if (*ppsa)
{
// 既存配列の削除
hr = ::SafeArrayDestroy(*ppsa);
if (FAILED(hr)) return hr;
}
static const LPCTSTR Foot_bola[3] = {_T("中村俊輔"), _T("松井大輔"), _T("高原直泰")};
const long size = sizeof(Foot_bola) / sizeof(Foot_bola[0]);
*ppsa = ::SafeArrayCreateVector(VT_VARIANT, 0L, size);
if (!*ppsa) return E_OUTOFMEMORY; // メモリ不足
// 要素の設定
VARIANT* items;
::SafeArrayAccessData(*ppsa, (LPVOID*)&items);
for (long index = 0; index < size; ++index)
{
::VariantClear(&items[index]);
_variant_t temp(Foot_bola[index]);
items[index] = temp.Detach();
}
::SafeArrayUnaccessData(*ppsa);
return hr;
}
ありがとうございます。
>それとこのコードは、からの配列を渡す分にはきちんと格納されるようですけど、
>配列が既にある(*ppsa!=NULL)パターンのときちゃんと格納できないような
>気がします。
ええっと、これは
>条件式が間違っていた。
>
>if (*ppsa)
で解決でしょうか?
あらかじめ配列に何か入れておいてから、上の関数(条件式を直した物)を
呼び出しても見かけ上、異常動作しているようには見えないのですが。。
>1次元の文字型配列であればSafeArrayCreateVectorとSafeArrayAccessData
確かにこの方がすっきりしてますね。
>ちなみにVARIANT型配列で受け渡しする場合はもっと簡単になります。
VARIANT型の扱いって、なんか難しそうでいままでずっと敬遠していました。
_variant_tなんてラッパークラスもあるんですね。
>で解決でしょうか?
実行してみるとわかると思いますが、解決ではないです。
単にロジックが違っていたから変えただけです。
Stringの場合
VB→CのDLL
VB←CのDLL
のときに暗黙的に文字コードが変わるようです。
(変わらない環境もあるとかないとか。詳しくないです)
逆にVariantの場合は暗黙的に文字コードが変わらないので
こちらをつかったほうがよいかもしれませんね。
(でもWinAPIをVBから使う場合 LPCSTR の引数には普通に ByVal XXX As String
になっているんですよねぇ。。
VB.NETのようにマーシャラに文字コードを教えるみたいなことはできないし。)
>>で解決でしょうか?
>実行してみるとわかると思いますが、解決ではないです。
う〜ん。。
VB側で
Dim Foot_bola_A() As String
としておいて後のほうで
ReDim Foot_bola_A(2)
Foot_bola_A(0) = "稲本潤一"
Foot_bola_A(1) = "中田浩二"
Foot_bola_A(2) = "森本貴幸"
Sample(Foot_bola_A())
とかやると期待通り動いてるように見えるんですが
代わりに
Dim Foot_bola_B(2) As String
Foot_bola_B(0) = "小笠原満男"
Foot_bola_B(1) = "宮本恒靖"
Foot_bola_B(2) = "中田英寿"
Sample(Foot_bola_B())
普通の配列、動的配列に対して静的配列っていうんですか?
これで試すとうまく格納できませんねぇ。。
でも、「動的配列で」という前提なんでこれは仕様の範囲内
じゃないかと思ってます。
>逆にVariantの場合は暗黙的に文字コードが変わらないので
>こちらをつかったほうがよいかもしれませんね。
そうなんですか。VARIANT型を使うように変更しようかな。
ありがとうございました。
とりあえず、お昼にググってたらこんな文書を見つけたので
これを読んで勉強します。
http://www.microsoft.com/japan/msdn/vs_previous/vbasic/docs/dll/
ツイート | ![]() |