DOBON.NET DOBON.NETプログラミング掲示板過去ログ

C#でwinsock

  • 題名: C#でwinsock
  • 著者: み
  • 日時: 2009/06/23 11:40:45
  • ID: 24802
  • この記事の返信元:
    • (なし)
  • この記事への返信:
  • ツリーを表示
環境/言語:[C#2 XP]
分類:[.NET]

お世話になっております。
C#にてリモートIPアドレス取得するため以下のコードでテストコードを
作成しましたがWSAStartupでAccessViolationException例外が発生していまいます。
すいませんがご教授をお願いします。
namespace ws2_32
{
public partial class Form1 : Form
{
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern Int32 WSAStartup(Int16 wVersionRequested, ref WSAData wsaData);

[DllImport("Ws2_32.DLL", CharSet = CharSet.Auto,SetLastError = true)]
private extern static Int32 WSACleanup();

public const int SUCCESS = 0;
public const int HIGH_VERSION = 2;
public const int LOW_VERSION = 2;
public const Int16 WORD_VERSION = 36;

public const int MAX_WSA_DESCRIPTION = 256;
public const int MAX_WSA_SYS_STATUS = 128;


public struct WSAData
{
public Int16 wVersion;
public Int16 wHighVersion;
public String szDescription;
public String szSystemStatus;
public Int16 iMaxSockets;
public Int16 iMaxUdpDg;
public IntPtr lpVendorInfo;
}

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
WSAData data = new WSAData();
int result = 0;

data.wHighVersion = HIGH_VERSION;
data.wVersion = LOW_VERSION;

result = WSAStartup(WORD_VERSION, ref data);

if (result == SUCCESS)
{
Console.WriteLine("szDescription:" + data.szDescription);
WSACleanup();
}
Console.ReadLine();
}
}
}
  • 題名: Re[1]: C#でwinsock
  • 著者: 魔界の仮面弁士
  • 日時: 2009/06/23 12:52:15
  • ID: 24803
  • この記事の返信元:
  • この記事への返信:
  • ツリーを表示
2009/06/23(Tue) 15:47:46 編集(投稿者)

■No24802に返信(みさんの記事)
> [DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
> static extern Int32 WSAStartup(Int16 wVersionRequested, ref WSAData wsaData);
今回、WSAData は struct として宣言していますよね。
この場合、(ref でも動作しますが)本来は out です。

> public struct WSAData
> {
構造体には StructLayoutAttribute を付与した方が安全です。今回の場合、
指定せずとも動くとは思いますが、WinSock2.h には、『#include <pshpack4.h>』の
記述がありますので、今回は、Pack = 4 を指定しておいた方が良いでしょう。

> public String szDescription;
> public String szSystemStatus;
この部分を System.String にて宣言する場合には、
MarshalAsAttribute で ByValTStr を指定して、SizeConst に適切な値
(WSADESCRIPTION_LEN + 1 / WSASYSSTATUS_LEN + 1)を渡す必要があります。

> public Int16 iMaxSockets;
> public Int16 iMaxUdpDg;
Int16 でも良いですが、本来は UInt16 です。

> WSAData data = new WSAData();
> data.wHighVersion = HIGH_VERSION;
> data.wVersion = LOW_VERSION;
WSAStartup の第2引数(WSADATA 構造体)は出力引数なので、事前に値を
セットしておく必要は無いと思います。呼び出し時に上書きされてしまいますから。

> public const Int16 WORD_VERSION = 36;
…36、ですか?

> result = WSAStartup(WORD_VERSION, ref data);
WSAStartup の第1引数には、呼びだしたプロセスが使える Windows Sockets の
いちばん高いバージョンを渡すことになっています。
上位バイトがマイナーバージョン、下位バイトがメジャーバージョンなので、
1.1 以下を呼び出す場合には 0x101(= (1 << 8) + 2)、
2.2 以下を呼び出す場合には 0x202(= (2 << 8) + 2)を渡すべきかと。

http://members.jcom.home.ne.jp/toya.hiroshi/winsock2/index.html?wsastartup_2.html
http://msdn.microsoft.com/en-us/library/ms742213.aspx
  • 題名: Re[2]: C#でwinsock
  • 著者: み
  • 日時: 2009/06/23 14:43:49
  • ID: 24805
  • この記事の返信元:
  • この記事への返信:
  • ツリーを表示
2009/06/23(Tue) 14:44:39 編集(投稿者)

魔界の仮面弁士さん
返信ありがとうございます。
一応修正しましまところ
デバッグに”szDescription:WinSock 2.0”と出力され
正常に?動いた感じです。ありがとうございます。


>>public const Int16 WORD_VERSION = 36;
> …36、ですか?
どこからかの拝借なので36の意味はわかりません。


> 1.1 以下を呼び出す場合には 0x101(= (1 << 8) + 2)、
> 2.2 以下を呼び出す場合には 0x202(= (2 << 8) + 2)を渡すべきかと。

public const Int16 WORD_VERSION = 0x202;
っと記述すればよいのでしょうか?
これで実行してもWinSock 2.0と返ります。
  • 題名: Re[3]: C#でwinsock
  • 著者: 魔界の仮面弁士
  • 日時: 2009/06/23 16:43:03
  • ID: 24807
  • この記事の返信元:
  • この記事への返信:
  • ツリーを表示
■No24805に返信(みさんの記事)
> これで実行してもWinSock 2.0と返ります。
バージョン確認に使うのは szDescription ではなく、wVersion の方だと思います。

今回の「36」を含め、バージョン指定を幾つか変更してみました。


WSAData data = new WSAData();
foreach (short ver in new short[] { 36, 0x0, 0x1, 0x2, 0x100, 0x101, 0x102, 0x200, 0x201, 0x202 })
{
    Console.Write("reqVer={0,3:d}.{1:d3}(0x{2:x4}): ", ver & 0xff, ver >> 8, ver);
    int ret = WSAStartup(ver, out data);
    if (ret == SUCCESS)
    {
        Console.WriteLine("ver=0x{0:x4}, highVer=0x{1:x4}, [{2}]",
            data.wVersion, data.wHighVersion, data.szDescription);
        WSACleanup();
    }
    else
    {
        Console.WriteLine("Error 0x{1:x8}", ver, ret);
    }
}
Console.ReadLine();


当方での実行結果は下記の通り。なお、ws2_32.dll ファイルのバージョンは 5.1.2600.5512 です。


reqVer= 36.000(0x0024): ver=0x0202, highVer=0x0202, [WinSock 2.0]
reqVer=  0.000(0x0000): Error 0x0000276c
reqVer=  1.000(0x0001): ver=0x0001, highVer=0x0202, [WinSock 2.0]
reqVer=  2.000(0x0002): ver=0x0002, highVer=0x0202, [WinSock 2.0]
reqVer=  0.001(0x0100): Error 0x0000276c
reqVer=  1.001(0x0101): ver=0x0101, highVer=0x0202, [WinSock 2.0]
reqVer=  2.001(0x0102): ver=0x0102, highVer=0x0202, [WinSock 2.0]
reqVer=  0.002(0x0200): Error 0x0000276c
reqVer=  1.002(0x0201): ver=0x0101, highVer=0x0202, [WinSock 2.0]
reqVer=  2.002(0x0202): ver=0x0202, highVer=0x0202, [WinSock 2.0]


これを見ると、WSAStartup(36, out data) を指定した場合というのは、
 「ver 36.0 を要求したが、2.2 しか返されなかった」
という状況に相当しますので、本来はアプリ側でサポート外として扱うべきかと思います。


一方、WSAStartup(0x202, out data) として指定した場合には、wVersion も
0x202 となりますので、アプリが想定されるバージョンが使用可能ということを意味し、
そのまま処理を続行させる事ができます。
  • 題名: Re[4]: C#でwinsock
  • 著者: み
  • 日時: 2009/06/24 11:06:18
  • ID: 24815
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
魔界の仮面弁士さん
お忙しいなか検証コード書いていただいてありがとうございます。
やっと次のステップなんですがまた悩んでます・・

ホスト名とIPアドレスの取得なんですが

[DllImport("Ws2_32.DLL", CharSet = CharSet.Auto, SetLastError = true)]
private extern static Int32 gethostname(StringBuilder name, int length);


[DllImport("Ws2_32.DLL", CharSet = CharSet.Auto, SetLastError = true)]
private extern static Int32 gethostbyname( StringBuilder szHost);


//ホストの取得
if (gethostname(hostname, 0x100) != 0)
{
WSACleanup();
return;
}
Console.WriteLine(hostname.ToString());


//ホストからIPアドレスへ変換
StringBuilder hostname2 = new StringBuilder("www.yahoo.co.jp");
int hostEnvPtr = gethostbyname(hostname2);


ホスト名の取得ですが取得しているのか文字化けになっています。
取得に失敗しているのか・・・


またホストからIPアドレスへの変換ですが
gethostbynameからの先がまだ調査中です
なにかサンプルがあればと検索中です。

ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板