C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[10]: vb.net 構造体を配列にした場合の固定長文字列の引渡し


(過去ログ 83 を表示中)

[トピック内 16 記事 (1 - 16 表示)]  << 0 >>

■48891 / inTopicNo.1)  vb.net 構造体を配列にした場合の固定長文字列の引渡し
  
□投稿者/ Morio (1回)-(2010/04/15(Thu) 21:15:45)

分類:[.NET 全般] 

VS6.0からのマイグレーションにて、VS2008に変換しています。

vb.netで定義した構造体を配列化したデータを、VC++で作成したDLLに引渡しをしていますが、値が正しくわたってくれません。
ご教授願えませんでしょうか?

vb.net
------------------------------------
 Structure ptMenu
    Dim iEnable As Short 
    <VBFixedString(12), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=12)> Public iName As String 
    Dim iBackColor As Integer 
    Dim iMenuFlag As Short
  End Structure

  Dim gtmenu(63 * 9)  As ptMenu
------------------------------------

VC++
------------------------------------
#define		TMENUMAX	63 * 9
typedef struct {
	short	iEnable;		
	char	Name[12];	
	long	iBackColor;
	short	iMenuFlag;
} stMenu;
typedef struct {
	stMenu	sdProgMenu[TMENUMAX];
} vbProgMenu;


extern "C" void WINAPI DbRGetMenuMsg( vbProgMenu *buf )
{
}
------------------------------------
このような宣言を行い、VB.netから、API関数を呼び出ししても
vb.net側でセットした値が正しくVC側に反映されません。
0番目の配列の値は、VC側に正しくわたりますが、1番目以降の
配列の値がずれているようです。
 構造体に、アライメントをする必要がありそうですが、
どなたか、ご教授いただけませんでしょうか?


(例)
  gtmenu(1).iBackColor = 15 ←この値は、VC側で正しく参照できない
  gtmenu(0).iName = "12345678901" ←この値は、VC側で正しく参照できる
  call DbRGetMenuMsg(gtmenu(0))
 
 ちなみに、iNameの変数をVCも、VB.netもコメントアウトすると、正しくなります。
 String文字列宣言方法に、何か必要でしょうか?


よろしくお願いいたします。

引用返信 編集キー/
■48903 / inTopicNo.2)  Re[1]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ Jitta on the way (611回)-(2010/04/16(Fri) 07:44:13)
No48891 (Morio さん) に返信
>  構造体に、アライメントをする必要がありそうですが、
> どなたか、ご教授いただけませんでしょうか?

構造体の中で使っている値型のサイズを調べてください。構造体で宣言している変数の型が変わるところで、そのサイズに合うよう、ダミーデータを入れてください。
C++ を使ったり、他のプラットフォームと通信するときは必須の調整事項なので、苦労して身に付けてください。
引用返信 編集キー/
■48908 / inTopicNo.3)  Re[2]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ よねKEN (472回)-(2010/04/16(Fri) 09:39:41)
No48903 (Jitta on the way さん) に返信
> ■No48891 (Morio さん) に返信
>> 構造体に、アライメントをする必要がありそうですが、
>>どなたか、ご教授いただけませんでしょうか?
>
> 構造体の中で使っている値型のサイズを調べてください。構造体で宣言している変数の型が変わるところで、そのサイズに合うよう、ダミーデータを入れてください。

StructLayout属性のPack指定でアラインメントの調整ができると思いますが、それでは調整できないのでしょうか?
(VC++側の知識が足りないので、この質問の構造体の場合にPack指定で対応のかどうかは
私にはちょっとわからないのですが)

Packでダメな場合でも、LayoutKind.Explicitを指定して、FieldOffset属性で構造体の各メンバーの配置を指定することもできますので、ダミーデータを入れる必要はないのではと思うのですがいかがでしょうか。

--
以下はたぶん本題には影響しないところなので、ついでですが・・・。

>char Name[12];
とあるので、

> <VBFixedString(12), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=12)> Public iName As String
ここでのUnmanagedType.ByValTStrの文字コードがANSIになるように、
StructLayout属性のCharSetを明示的にAnsiで指定しておいた方がよいと思います。

> Dim gtmenu(63 * 9) As ptMenu

VC++側と一致させるなら、以下のように-1しておかないと1つ構造体が余分ですね。
(ご質問の状況では実害はないかもしれませんが)

Dim gtmenu(63 * 9 - 1) As ptMenu

引用返信 編集キー/
■48930 / inTopicNo.4)  Re[3]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ Jitta on the way (612回)-(2010/04/16(Fri) 19:36:36)
No48908 (よねKEN さん) に返信
> ■No48903 (Jitta on the way さん) に返信
>>■No48891 (Morio さん) に返信
> >> 構造体に、アライメントをする必要がありそうですが、
> >>どなたか、ご教授いただけませんでしょうか?
>>
>>構造体の中で使っている値型のサイズを調べてください。構造体で宣言している変数の型が変わるところで、そのサイズに合うよう、ダミーデータを入れてください。
>
> StructLayout属性のPack指定でアラインメントの調整ができると思いますが、それでは調整できないのでしょうか?

私の勉強不足です。


> Packでダメな場合でも、LayoutKind.Explicitを指定して、FieldOffset属性で構造体の各メンバーの配置を指定することもできますので、ダミーデータを入れる必要はないのではと思うのですがいかがでしょうか。

.NET 側はそれでいいとして、VC++(unmanaged)に、そういったオプションがあるでしょうか。私は、調整をコンパイラに任せず、自分で行います。C++ の構造体を作った人にそういうクセがあれば、この質問はなかったかも知れません。言葉が足りませんでしたが、C++側の構造体こそ、手を入れるべきと考えています。(short が2つあるのだから、まとめて置くべき。そうしてあれば、やはり問題は発生していないかもしれない。)
引用返信 編集キー/
■48934 / inTopicNo.5)  Re[4]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ Morio (2回)-(2010/04/16(Fri) 23:11:38)
No48930 (Jitta on the way さん) に返信
> ■No48908 (よねKEN さん) に返信
>>■No48903 (Jitta on the way さん) に返信
> >>■No48891 (Morio さん) に返信
>>>> 構造体に、アライメントをする必要がありそうですが、
>>>>どなたか、ご教授いただけませんでしょうか?
> >>
> >>構造体の中で使っている値型のサイズを調べてください。構造体で宣言している変数の型が変わるところで、そのサイズに合うよう、ダミーデータを入れてください。
>>
>>StructLayout属性のPack指定でアラインメントの調整ができると思いますが、それでは調整できないのでしょうか?


Jitta on the way さん,よねKEN さん、ありがとうございます。
<StructLayout(LayoutKind.Sequential, Pack:=8, CharSet:=CharSet.Auto)>
この記述ですよね?

 vb.net−−−−−−−−−−−−−
<StructLayout(LayoutKind.Sequential, Pack:=8, CharSet:=CharSet.Auto)> _
Public Structure ptMenu
Dim iEnable As Short

上記のように宣言した場合、型'StructLayout'が定義されていませんとなり、エラーとなります。
事前に宣言が必要になりますか?


> .NET 側はそれでいいとして、VC++(unmanaged)に、そういったオプションがあるでしょうか。私は、調整をコンパイラに任せず、自分で行います。C++ の構造体を作った人にそういうクセがあれば、この質問はなかったかも知れません。言葉が足りませんでしたが、C++側の構造体こそ、手を入れるべきと考えています。(short が2つあるのだから、まとめて置くべき。そうしてあれば、やはり問題は発生していないかもしれない。)

他のサイトなどを探してると、文字列を構造体に含めた状態で、配列化されたものを、unmanagedのDLLに引渡しするのが、できなかったというような記載がありました。
その場合に、VC++DLLをmanagedで作成し、managedで引渡しされた値をunmanagedに変換する・・・と。
極力VC++側は手を入れずに構成したいと思っています。(VCの構造体は、データベースのカラムとあわせていたりと、複雑になっているためです)



引用返信 編集キー/
■48936 / inTopicNo.6)  Re[4]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ よねKEN (473回)-(2010/04/17(Sat) 00:21:38)
No48930 (Jitta on the way さん) に返信
> .NET 側はそれでいいとして、

.NET側でVC++側のアラインメントに合わせてやればいいのかなと思ったのですが、
そう単純な話でもないのですかね。
#コンパイラのアラインメント調整に依存してるぞ、という点を気にされているのだと思いますが、
#もともとのソースコードがコンパイラのデフォルトのアライメント調整に従っているのであれば、
#それはそれでありなのかなぁと・・・

> VC++(unmanaged)に、そういったオプションがあるでしょうか。

先ほどもちょっと書いたように私はVC++は全然といっていいほどわかっていないので、
眉唾ものとして疑ってかかって聞いて欲しいのですが、
「#pragma pack 〜」を使えば指定できそう?な感じ…<教えて、えらい人!

--
No48934 (Morio さん) に返信
> <StructLayout(LayoutKind.Sequential, Pack:=8, CharSet:=CharSet.Auto)>
> この記述ですよね?

はい。そこのLayoutKindの指定だったり、Packだったり、CharSetだったりを調整します。

> 上記のように宣言した場合、型'StructLayout'が定義されていませんとなり、エラーとなります。
> 事前に宣言が必要になりますか?

はい。StructLayout属性の実体は、StructLayoutAttributeクラスです。
http://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.structlayoutattribute(VS.80).aspx

おそらく、ファイルの先頭に
Imports System.Runtime.InteropServices
のように名前空間のインポート指定が不足していると思います。

引用返信 編集キー/
■48938 / inTopicNo.7)  Re[5]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ Jitta (637回)-(2010/04/17(Sat) 00:35:19)
No48936 (よねKEN さん) に返信
> .NET側でVC++側のアラインメントに合わせてやればいいのかなと思ったのですが、
> そう単純な話でもないのですかね。
> #コンパイラのアラインメント調整に依存してるぞ、という点を気にされているのだと思いますが、
> #もともとのソースコードがコンパイラのデフォルトのアライメント調整に従っているのであれば、
> #それはそれでありなのかなぁと・・・

 いえ、「C++ のコードが調整してあれば、VBer がつまずくことはなかったのではないか」ということです。
私がいた部署では、次のように書くことを推奨していました。

typedef struct {
  short iEnable;
  short iMenuFlag;
  char dummy1[4]; // 調整
  long iBackColor;
  char Name[12];
  char dummy2[4]; // 調整
} stMenu;
# サイズは、不安を残す(^-^;
# そういや、いつも main() {printf("%d\n",sizeof(int));} ってやって調べてた;

 こうすることで、sizeof 演算子の結果と、コードを"見て"計算したサイズが一致します。
引用返信 編集キー/
■48940 / inTopicNo.8)  Re[6]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ ちゃっぴ (4回)-(2010/04/17(Sat) 01:31:07)
ちゃっぴ さんの Web サイト
> いえ、「C++ のコードが調整してあれば、VBer がつまずくことはなかったのではないか」ということです。

それ言うなら C++/CLI で manage にしてやる方が。
C++/CLI で wrapper 作ってやる方法もありますが、この定義ものすごく manage にしづらいんですよね。
引用返信 編集キー/
■48941 / inTopicNo.9)  Re[7]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ ちゃっぴ (5回)-(2010/04/17(Sat) 02:53:33)
ちゃっぴ さんの Web サイト
これかな?

Public Structure <StructLayout(LayoutKind.Sequential)> ProgMenu
Public <MarshalAs(UnmanagedType.ByValArray, SizeConst:=63 * 9)> _
s1() As ptMenu
End Structure


配列に対する既定のマーシャリング
構造体の中の配列
http://msdn.microsoft.com/ja-jp/library/z6cfh6e6.aspx

検証まったくやっていませんし、確証もないのでその点よろしく。
引用返信 編集キー/
■48962 / inTopicNo.10)  Re[5]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ Jitta on the way (613回)-(2010/04/19(Mon) 07:44:59)
No48934 (Morio さん) に返信
> <StructLayout(LayoutKind.Sequential, Pack:=8, CharSet:=CharSet.Auto)>
> この記述ですよね?

TCHAR ではなく、char で宣言しているので、Ansi だったか Ascii だったかで。
引用返信 編集キー/
■48964 / inTopicNo.11)  Re[6]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ よねKEN (476回)-(2010/04/19(Mon) 09:22:47)
No48962 (Jitta on the way さん) に返信
> ■No48934 (Morio さん) に返信
>><StructLayout(LayoutKind.Sequential, Pack:=8, CharSet:=CharSet.Auto)>
>>この記述ですよね?
>
> TCHAR ではなく、char で宣言しているので、Ansi だったか Ascii だったかで。

Ansiですね。私の投稿の以下の箇所で既に指摘していますよ。

No48908 の投稿から抜粋
>>char Name[12];
> とあるので、
>
>> <VBFixedString(12), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=12)> Public iName As String
> ここでのUnmanagedType.ByValTStrの文字コードがANSIになるように、
> StructLayout属性のCharSetを明示的にAnsiで指定しておいた方がよいと思います。

No48936 の投稿から抜粋
>> <StructLayout(LayoutKind.Sequential, Pack:=8, CharSet:=CharSet.Auto)>
>> この記述ですよね?
>
>はい。そこのLayoutKindの指定だったり、Packだったり、CharSetだったりを調整します。

引用返信 編集キー/
■48966 / inTopicNo.12)  Re[8]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ よねKEN (477回)-(2010/04/19(Mon) 10:50:08)
2010/04/19(Mon) 11:08:21 編集(投稿者)
検証してみました。簡単な動作確認しかしてませんので、本当に問題ないかは十分な検証が必要です。
環境:
OS:WinXP SP2 (32bit)
・VBはVB9.0のコンパイラでコンパイル
・VC++はVC++2008でコンパイル
  (VC++側は、明示的なアラインメントの調整の指定はしていません
   (コンパイルのオプションや#pragma packの指定))

※StructLayoutのPackの指定を1,2,4,8で試してみましたが、
 以下のコードの範囲では、4と8なら一応うまく行くようです。
 (VC++側のデフォルトのアラインメント調整のバイト数を確認する必要がありますね。
   32bit/64bit環境のどちらで動くのか、というのも重要そう)

--
' VB側の実験コード コンソールアプリとしてvbc.exeでコンパイル
Imports System
Imports System.Runtime.InteropServices

Module Program
    <DllImport("DllTest.dll", CharSet:=CharSet.Ansi, ExactSpelling:=true)> _
    Public Sub DbRGetMenuMsg(ByRef buf As ProgMenu)
    End Sub

    Sub Main()
        Dim pm As ProgMenu = new ProgMenu()
        pm.s1 = new ptMenu(63 * 9-1){}
        pm.s1(0).iEnable = 12
        pm.s1(0).iName = "a123456789b"
        pm.s1(0).iBackColor = 345
        pm.s1(0).iMenuFlag = 6789

        pm.s1(63 * 9 - 1).iEnable = 21
        pm.s1(63 * 9 - 1).iName = "b123456789a"
        pm.s1(63 * 9 - 1).iBackColor = 543
        pm.s1(63 * 9 - 1).iMenuFlag = 9876

        DbRGetMenuMsg(pm)

        Console.ReadKey()
	End Sub
End Module

<StructLayout(LayoutKind.Sequential, Pack:=4, CharSet:=CharSet.Ansi)> _
Structure ptMenu
    Public iEnable As Short
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=12)> public iName As String
    Public iBackColor As Integer
    Public iMenuFlag As Short
End Structure

<StructLayout(LayoutKind.Sequential, Pack:=4)> _
Structure ProgMenu
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=63 * 9)> Public s1 As ptMenu()
End Structure

--
/* VC++側のコード DLL用のプロジェクトを新規作成して以下のコードを記述 */
/* DEFファイルを定義してリンカーオプションで指定 */
// DllTest.cpp : DLL アプリケーション用にエクスポートされる関数を定義します。

#include "stdafx.h"
#include <stdio.h>

#define		TMENUMAX	63 * 9

typedef struct {
	short	iEnable;		
	char	Name[12];	
	long	iBackColor;
	short	iMenuFlag;
} stMenu;
typedef struct {
	stMenu	sdProgMenu[TMENUMAX];
} vbProgMenu;


extern "C" void WINAPI DbRGetMenuMsg( vbProgMenu *buf )
{
         /* 動作確認のため構造体の中身を確認 */
         /* 量が多いので最初と最後の要素だけ */
	int i = 0;
	for (i = 0; i < TMENUMAX; i++)
	{
		if (i ==0 || i == TMENUMAX-1)
		{
			printf("i=%d\n", i);
			printf("iEnable=%d\n", buf->sdProgMenu[i].iEnable);
			printf("Name[12]=%s\n", buf->sdProgMenu[i].Name);
			printf("iBackColor=%d\n", buf->sdProgMenu[i].iBackColor);
			printf("iMenuFlag=%d\n", buf->sdProgMenu[i].iMenuFlag);
		}
	}
}


--
<修正>
環境まわりの説明を補足
</修正>

引用返信 編集キー/
■48985 / inTopicNo.13)  Re[7]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ Jitta on the way (614回)-(2010/04/19(Mon) 18:17:30)
No48940 (ちゃっぴ さん) に返信
>>いえ、「C++ のコードが調整してあれば、VBer がつまずくことはなかったのではないか」ということです。
>
> それ言うなら C++/CLI で manage にしてやる方が。
> C++/CLI で wrapper 作ってやる方法もありますが、この定義ものすごく manage にしづらいんですよね。

はい。VB6からのマイグレーションということなので、VC6のものをそのまま使う、と、勝手に想像しました。

MSDN、Not Found になりますねぇ。どうしたんだろう?
引用返信 編集キー/
■49007 / inTopicNo.14)  Re[8]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ ちゃっぴ (7回)-(2010/04/20(Tue) 12:26:48)
ちゃっぴ さんの Web サイト
No48985 (Jitta on the way さん) に返信
> MSDN、Not Found になりますねぇ。どうしたんだろう?

VS2010 用の document が無いようですね。
ということで張りなおし。

http://msdn.microsoft.com/ja-jp/library/z6cfh6e6(VS.90).aspx
引用返信 編集キー/
■49038 / inTopicNo.15)  Re[9]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ Morio (3回)-(2010/04/20(Tue) 22:33:24)
No49007 (ちゃっぴ さん) に返信
> ■No48985 (Jitta on the way さん) に返信
>>MSDN、Not Found になりますねぇ。どうしたんだろう?
>
> VS2010 用の document が無いようですね。
> ということで張りなおし。
>
> http://msdn.microsoft.com/ja-jp/library/z6cfh6e6(VS.90).aspx

皆さん、ありがとうございます!!

Public Structure <StructLayout(LayoutKind.Sequential)> ProgMenu
Public <MarshalAs(UnmanagedType.ByValArray, SizeConst:=63 * 9)> _
この対応を行うことで、VCのDLLへ値の引渡し、VCのDLLからの値の取得ができるようになりました。
(VC側の修正はなし)
ありがとうございます。


ちなみになんですが、この配列化された構造体を、2次元配列にする場合は、

<StructLayout(LayoutKind.Sequential, Pack:=4)> _
Structure ProgMenu
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=63 * 9,5)> Public s1(,) As ptMenu()
End Structure
こんな風に定義をしたいのですが、うまく定義ができません。
よき、手法ありましら、ご教授お願いいたします。

VB.netのみで、定義するのであれば、
<VBFixedArray(63 * 9,5)> Public s1(,) As ptMenu
と記載ができるのですが。
引用返信 編集キー/
■49049 / inTopicNo.16)  Re[10]: vb.net 構造体を配列にした場合の固定長文字列の引渡し
□投稿者/ ちゃっぴ (8回)-(2010/04/21(Wed) 02:17:40)
ちゃっぴ さんの Web サイト
「配列に対する既定のマーシャリング」より

アンマネージ配列
アンマネージ配列は、COM スタイルのセーフ配列か、または固定長または可変長の C スタイル配列です。セーフ配列は、関連付けられている配列データの型、ランク、および上下限を共に伝達する自己記述型の配列です。C スタイルの配列は、下限が 0 で固定され、型が指定されている 1 次元の配列です。マーシャリング サービスは両方の配列型を制限付きでサポートします。

ということなので、1 次元でやるより仕方ないです。
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -