WinsockAPIを使用して、UDPで文字列を受信するプログラムを
作成しているのですが、recvfrom関数を実行して受信待ちをしている時に、
ExcelやAccessなどのファイルが開けなくなることがあります。
開けなくなったファイルは、上記のプログラムが文字列を受信して、
recvfrom関数が終了したタイミングで、起動がかかっているようです。
Winsockコントロールを使用した場合、この問題は発生しないのですが、
環境的な事情があって、使用することができないのです。
原因・回避策などご存知の方、ご教授お願いします。
…まさかExcelがローカル使って通信してたりするわけでもない(というかそんなのではとまらないと思われる)ので、
きっとプログラムに問題があるんでしょう。
空ループで待ち続けているとか。
…Winsockが大丈夫ならWinsock使ったら?
いちゆさん、レスありがとうございます。
詳しいことは分からないんですけど、おそらく、問題のプログラムが
処理を占有してしまっているのが原因ではないかと思います。
他のアプリケーションに制御を渡すことができれば、解決できると思うのですが…。
受信待ちの処理はループで実行しているわけではなく、recvfrom関数をコールすると、
後はバインドしているポート番号に、何らかのデータを受信するまで、
そのステップで実行が止まっているようなイメージです。
(少なくとも、VBのエディタ上でステップ実行をするとそのように見えます。)
なので、「DoEventsを実行してOSに制御を渡す」といったこともでそうにありません。
ひとまず、ソースを記載致します。
解決策でなく、何かヒントになるような情報でも構いませんので、
みなさん、ご協力お願い致します。
'■Winsockの使用を開始
lngWin32apiResultCode = WSAStartup(MAKEWORD(1, 0), mudtWSAData)
'■ソケットを新規に作成
lngSocketDescriptor = socket(AF_INET, SOCK_DGRAM, 0)
'■ソケットの情報を指定
With udtSocketAddress
.sin_family = AF_INET
.sin_addr.S_addr = INADDR_ANY
.sin_port = htons("10364")
End With
'■自ネットワークアドレスとポート番号をソケットに結合
lngWin32apiResultCode = bind(lngSocketDescriptor, udtSocketAddress, Len(udtSocketAddress)) '■構造体の長さを指定
lngSocketAddressLen = Len(udtSocketAddress)
'■無限ループで受信を実行
Do Until blnDummyFlag = True
strReceiveData = ""
'■ソケットからデータグラムを受信
lngWin32apiResultCode = recvfrom(lngSocketDescriptor, strReceiveData, Len(strReceiveData), 0, _
udtSocketAddress, lngSocketAddressLen) '■受信したメッセージを表示
MsgBox Trim(strReceiveData), vbInformation + vbMsgBoxSetForeground, "MERS_DWH"
Loop
'■ソケットをクローズ
lngWin32apiResultCode = closesocket(lngSocketDescriptor)
'■Winsockの使用を終了
lngWin32apiResultCode = WSACleanup()
>受信待ちの処理はループで実行しているわけではなく、recvfrom関数をコールすると、
>後はバインドしているポート番号に、何らかのデータを受信するまで、
>そのステップで実行が止まっているようなイメージです。
>(少なくとも、VBのエディタ上でステップ実行をするとそのように見えます。)
>
非同期で処理すればいいのでは?
WSAAsyncSelectとか
GODさん、レスありがとうございます。
教えていただいたWSAAsyncSelectをテストしようとしているのですが、
能力不足のため、なかなか実行まで行き着きません…。
WSAAsyncSelect(sock, hWnd, uMsg, lEvent)
本関数は、2番目の引数としてウィンドウハンドルというものを渡さなければいけないようですが、
このウィンドウハンドルというものは、標準モジュールだけのプログラムにも存在するものなのでしょうか。
フォームの存在するプログラムのサンプルを見ると、Me.hWndのように取得しているようですが、
標準モジュールだけのプログラムの場合、どのように取得すればよいのかが分からず、苦戦しております。
Visible=Falseでフォームを作成すればいいのかな…。
すいません、的外れな質問をしているかもしれませんが、ご回答よろしくお願い致します。
ん〜、なんかちょっと調べたところによると、
WSAAsyncSelect(sock, hWnd, uMsg, lEvent)
はウィンドウメッセージとしてソケットイベントを通達するらしいな。
…つまりウィンドウは必要で、さらにウィンドウメッセージ処理をサブクラス化しないといけない。
多分。
WSAAsyncSelect関数はウィンドウがないと使用できません。(v_v)
↑lEventでほしいイベントを定義してそれが発生したときにuMsgをhWndに
発行するためです。(VBならサブクラス化してあげないと取れないですね。)
ウィンドウなしでやるならioctl(よく覚えていない^^)になるのかな。
検索ページなどで、recv, 非同期またはノンブロッキングなどで検索してみて
ください。
はっ!文章書いている間に更新が・・・
いちゆさんのおっしゃる通りです。
いちゆさん、GODさん、レスありがとうございます。
その後も、私なりにWSAAsyncSelectについて調べたのですが、
時間の関係もあり、私の力量では簡単には解決できそうにないと思いました。
仮に、動作がうまくいったとしても、サブクラス化などでロジックが複雑化した分、
他の部分への影響がないかどうかの確認が難しくなりますよね。
そこで、今回はWinsockコントロールに逃げようと思っています。
最初の書き込みで、Winsockコントロールを使用することは難しいと書きましたが、
その理由は以下のようなものです。
[理由1]
①Winsockコントロールを使用した場合、EXEファイルと一緒にOCXファイルを配布
しなければならない。
②コントロールを使用した場合、最初の実行時にOCXファイルの居場所が検索され、
そのパスがレジストリに書き込まれる。
③エンドユーザの大半が、レジストリへの変更権限のないユーザIDを使用しているため、
上記②のステップでエラーが発生してしまう。
[理由2]
①エンドユーザの端末に、Visual Basic(OCX)がインストールされていた場合、
配布したOCXではなく、元々インストールされていたOCXをコールしてしまう。
②上記のOCX間でバージョンに違いがあった場合に、EXEファイルとOCXファイルの間で、
整合性がとれず、エラーが発生してしまう可能性がある。
↑
(実は、前に1度ありました。)
ですが、上記の問題はなんとかクリアできそうですので、
今回はWinsockコントロールで行こうとおもっています。
その方が、私が付け焼刃でWSAAsyncSelectを使用するよりは、
信頼性が高いでしょうから。
ですが、せっかく情報を提供して頂きましたし、私自身も気になりますので、
個人的には勉強してみようと思います。
いちゆさん、GODさん、大変お世話になりました。
また何かありましたら、よろしくお願い致します。