🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

Q&A

解決済

1回答

4091閲覧

WindowsAPIでメールスロットからReadFileすると87エラーが出ます

puimol

総合スコア1

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

0グッド

0クリップ

投稿2021/01/22 14:38

前提・実現したいこと

C言語でWindowsAPIを使用し、メールスロットからメッセージを読み書きするプログラムを作成しています。
(メールスロットの実装方法が知りたいため、スレッドやプロセスは分けておりません)

メールスロットを作り、"Hello World!"を書き込んだ後、メールスロットからバッファに文字列を読み込み、
コンソールに出力させたいのですが、ReadFile関数実行時に、87エラー(パラメータが間違い)が出ています。
ReadFile関数のどのパラメータが間違っているのでしょうか。

以下、ソースコードです。

該当のソースコード

C

1#include <stdio.h> 2#include <Windows.h> 3#include <tchar.h> 4 5LPCTSTR slotName = TEXT("\\.\mailslot\sanple_mailslot"); 6 7int main() { 8 9 DWORD maxnum = 256; //メールスロット最大読み込み値 10 DWORD error = 0; 11 12 //メールスロットの作成 13 HANDLE handle1 = CreateMailslot( 14 slotName, 15 maxnum, 16 MAILSLOT_WAIT_FOREVER, 17 NULL 18 ); 19 20 if (handle1 == INVALID_HANDLE_VALUE) { 21 printf_s("CreateMailslotに失敗しました\n"); 22 return 1; 23 } 24 25 //メールスロットに書き込むメッセージを作成 26 LPCTSTR str1 = TEXT("Hello World!"); 27 //メールスロットに書き込んだバイト数 28 DWORD writedbyte = 0; 29 30 //メールスロットを書き込み権限でオープンする 31 HANDLE handle2 = CreateFile( 32 slotName, 33 GENERIC_WRITE, 34 FILE_SHARE_READ, 35 NULL, 36 OPEN_EXISTING, 37 FILE_ATTRIBUTE_NORMAL, 38 NULL 39 ); 40 41 if (handle2 == INVALID_HANDLE_VALUE) { 42 printf_s("メールスロットのオープンに失敗しました\n"); 43 error = GetLastError(); 44 printf_s("エラーコードは%dです\n",error); 45 return 1; 46 } 47 48 //メールスロットに書き込みする 49 bool writeResult = WriteFile( 50 handle2, 51 str1, 52 sizeof(str1), 53 &writedbyte, 54 NULL 55 ); 56 57 if (!writeResult) { 58 printf_s("メールスロットへの書き込みに失敗しました\n"); 59 error = GetLastError(); 60 printf_s("エラーコードは%dです\n"); 61 return 1; 62 } 63 64 CloseHandle(handle2); 65 66 //メールスロットを読み込みモードでオープンする 67 HANDLE handle3 = CreateFile( 68 slotName, 69 GENERIC_READ, 70 FILE_SHARE_DELETE, 71 NULL, 72 OPEN_EXISTING, 73 FILE_ATTRIBUTE_NORMAL, 74 NULL 75 ); 76 77 //次のメッセージサイズ 78 DWORD nextMsgByte = 0; 79 //メールスロット内の総メッセージ数 80 DWORD allMsgNum = 0; 81 82 //メールスロットにメッセージがあるか確認する 83 bool getInfoResult = GetMailslotInfo( 84 handle3, 85 &maxnum, 86 &nextMsgByte, 87 &allMsgNum, 88 NULL 89 ); 90 91 if (getInfoResult == false) { 92 printf_s("GetMailslotInfoに失敗しました\n"); 93 error = GetLastError(); 94 printf_s("エラーコードは%dです\n"); 95 return 1; 96 } 97 98 //次のメッセージバイト数、総メッセージ数を確認 99 printf_s("次のメッセージバイト数は%d,総メッセージ数は%dです\n", nextMsgByte, allMsgNum); 100 101 if (allMsgNum == 0) { 102 printf_s("メッセージが書き込まれていません\n"); 103 return 1; 104 } 105 106 //メールスロットから読みだしたバイト数 107 DWORD readByte = 0; 108 //メールスロットから読み込むバッファを確保する 109 LPTSTR readBuf; 110 readBuf = (LPTSTR)malloc(256); 111 if (readBuf == NULL) { 112 memset(readBuf, '\0', 256); 113 } 114 115 //メールスロットのファイルポインタをファイルの先頭に移動 116 DWORD fp = SetFilePointer( 117 handle3, 118 NULL, 119 NULL, 120 FILE_BEGIN); 121 122 printf_s("ファイルポインタの位置は%dです\n",fp); 123 124 125 //メールスロットのメッセージを読み込む 126 bool readResult = ReadFile( 127 handle3, 128 readBuf, 129 nextMsgByte, 130 &readByte, 131 NULL 132 ); 133 134 if (readResult == false) { 135 printf_s("ReadFileに失敗しました\n"); 136 error = GetLastError(); 137 printf_s("エラーコードは%dです\n", error); 138 return 1; 139 } 140 141 //メールスロットから受信したメッセージを出力する 142 _tprintf_s(TEXT("メッセージは「%s」です\n"), str1); 143 144 CloseHandle(handle3); 145 CloseHandle(handle1); 146 return 0; 147 148}

###コンソールに出力された内容
次のメッセージバイト数は4,総メッセージ数は1です
ファイルポインタの位置は0です
ReadFileに失敗しました
エラーコードは87です

試したこと

メールスロットからReadFileするときに87エラーが出るときは、
①CreateFile関数でFILE_FLAG_OVERLAPPEDで開いたときは、OVERLAPPED構造体へのポインタが必要
②ファイルポインタが終端まで来ている
などの場合があるようですが、どちらも原因ではなさそうです。

補足情報(FW/ツールのバージョンなど)

開発環境は、VisualStudio2019です。

初心者でわからない点たくさんあるのですが、ご指摘いただければありがたいです。
よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2021/01/22 18:16

> HANDLE handle3 = CreateFile( > slotName, > GENERIC_READ, > FILE_SHARE_DELETE, CreateFileで読み出し用のメールスロットを改めて開いていますが、そのようなサンプルがどこかにあって、それを参考にされたのでしょうか。このように読み出し用に新たに開くようなコードは見かけたことがないのですが。 [Reading from a Mailslot - Microsoft Docs] https://docs.microsoft.com/ja-jp/windows/win32/ipc/reading-from-a-mailslot
dodox86

2021/01/22 18:28

> 読み出し用に新たに開くようなコードは見かけたことがないのですが。 私が見かけたことがないだけかもしれないので、単なる確認です。 ちなみにHANDLE handle1 = CreateMailslot(...)で 生成したhandle1を使えばReadFile()で読み出せます。コードの他の部分で若干の問題があったりしますので、表示に不具合は出ますが。
puimol

2021/01/25 11:33 編集

ご回答ありがとうございます! >私が見かけたことがないだけかもしれないので、単なる確認です。 いえ、これはWriteFile()でファイルを開いていたので、読み出しの際にも必要になるのかと思い、 自分で勝手に付け足しておりました…。 ReadFileをhandle1で使ってみましたが、ReadFile関数でエラーが出なくなりました! >コードの他の部分で若干の問題があったりしますので、表示に不具合は出ますが。 ここに関してですが、ReadFileで取得したメッセージを出力する部分でしょうか。 _tprintf_s(TEXT("メッセージは「%s」です\n"), readBuf); (上記コードはstr1と書いておりました、間違いです) マルチバイト文字とワイド文字の違いを、TEXTマクロではコンパイラの設定を考慮して変換してくれ、それを_tprintf_s()で出力できるのかと考えていたのですが、どのように直せばメッセージを出力できますか?
dodox86

2021/01/23 07:36

回答しましたのでそちらをご覧ください。
guest

回答1

0

ベストアンサー

メールスロットからのReadFileによるデータ読出し時は、CreateMailslotで生成したハンドルを使えば良いということは「質問への追記・修正の依頼」で前述したとおりです。

Reading from a Mailslot - Microsoft Docs

LPTSTR系(?)のポインタの扱いに混乱があるように思えた部分と、その他、修正させてもらい、動作確認したコードを以下に全掲載します。"修正: "のコメントに注意してみてください。

C++

1 2#include <stdio.h> 3#include <Windows.h> 4#include <tchar.h> 5#include <locale.h> // 修正1: _tsetlocale() 6 7LPCTSTR slotName = TEXT("\\.\mailslot\sanple_mailslot"); 8 9int main() { 10 11 // 修正2: _tprintf_s, wsprintf_sで日本語出力するのに必要 12 _tsetlocale(LC_ALL, TEXT("")); 13 wprintf_s(L"スタート\n"); 14 15 DWORD maxnum = 256; //メールスロット最大読み込み値 16 DWORD error = 0; 17 18 //メールスロットの作成 19 HANDLE handle1 = CreateMailslot( 20 slotName, 21 maxnum, 22 MAILSLOT_WAIT_FOREVER, 23 NULL 24 ); 25 26 if (handle1 == INVALID_HANDLE_VALUE) { 27 printf_s("CreateMailslotに失敗しました\n"); 28 return 1; 29 } 30 31 //メールスロットに書き込むメッセージを作成 32 LPCTSTR str1 = TEXT("Hello World!"); 33 //メールスロットに書き込んだバイト数 34 DWORD writedbyte = 0; 35 36 //メールスロットを書き込み権限でオープンする 37 HANDLE handle2 = CreateFile( 38 slotName, 39 GENERIC_WRITE, 40 FILE_SHARE_READ, 41 NULL, 42 OPEN_EXISTING, 43 FILE_ATTRIBUTE_NORMAL, 44 NULL 45 ); 46 47 if (handle2 == INVALID_HANDLE_VALUE) { 48 printf_s("メールスロットのオープンに失敗:しました\n"); 49 error = GetLastError(); 50 printf_s("エラーコードは%dです\n",error); 51 return 1; 52 } 53 54 //メールスロットに書き込みする 55 bool writeResult = WriteFile( 56 handle2, 57 str1, 58 // 修正3: sizeof(str1), 59 // LPCTSTRはポインタの型なので、sizeof(str1)では正しくない。 60 // ここでは例として末端の'\0'を含んだバイト長をセット。 61 (_tcslen(str1) + 1) * sizeof(str1[0]), 62 &writedbyte, 63 NULL 64 ); 65 66 if (!writeResult) { 67 printf_s("メールスロットへの書き込みに失敗しました\n"); 68 error = GetLastError(); 69 // 修正4: 引数が足りない 70 // printf_s("エラーコードは%dです\n"); 71 printf_s("エラーコードは%dです\n", error); 72 return 1; 73 } 74 75 CloseHandle(handle2); 76 77 // 修正5: 使わないので無効化 78#if 0 79 //メールスロットを読み込みモードでオープンする 80 HANDLE handle3 = CreateFile( 81 slotName, 82 GENERIC_READ, 83 FILE_SHARE_DELETE, 84 NULL, 85 OPEN_EXISTING, 86 FILE_ATTRIBUTE_NORMAL, 87 NULL 88 ); 89#endif 90 91 //次のメッセージサイズ 92 DWORD nextMsgByte = 0; 93 //メールスロット内の総メッセージ数 94 DWORD allMsgNum = 0; 95 96 //メールスロットにメッセージがあるか確認する 97 bool getInfoResult = GetMailslotInfo( 98 // 修正6: handle1を使う 99 // handle3, 100 handle1, 101 &maxnum, 102 &nextMsgByte, 103 &allMsgNum, 104 NULL 105 ); 106 107 if (getInfoResult == false) { 108 printf_s("GetMailslotInfoに失敗しました\n"); 109 error = GetLastError(); 110 // 修正7: 引数が足りない 111 //printf_s("エラーコードは%dです\n"); 112 printf_s("エラーコードは%dです\n", error); 113 return 1; 114 } 115 116 //次のメッセージバイト数、総メッセージ数を確認 117 printf_s("次のメッセージバイト数は%d,総メッセージ数は%dです\n", nextMsgByte, allMsgNum); 118 119 if (allMsgNum == 0) { 120 printf_s("メッセージが書き込まれていません\n"); 121 return 1; 122 } 123 124 //メールスロットから読みだしたバイト数 125 DWORD readByte = 0; 126 //メールスロットから読み込むバッファを確保する 127 LPTSTR readBuf; 128 readBuf = (LPTSTR)malloc(256); 129 // 修正8: 判定誤り 130 // if (readBuf == NULL) { 131 if (readBuf != NULL) { 132 memset(readBuf, '\0', 256); 133 } 134 135 // 修正9: 使わないので無効化 136#if 0 137 //メールスロットのファイルポインタをファイルの先頭に移動 138 DWORD fp = SetFilePointer( 139 handle3, 140 NULL, 141 NULL, 142 FILE_BEGIN); 143 144 printf_s("ファイルポインタの位置は%dです\n",fp); 145#endif 146 147 //メールスロットのメッセージを読み込む 148 bool readResult = ReadFile( 149 // 修正10: CreateMailSlot()で獲得したhandleを利用 150 // handle3 151 handle1, 152 readBuf, 153 nextMsgByte, 154 &readByte, 155 NULL 156 ); 157 158 if (readResult == false) { 159 printf_s("ReadFileに失敗しました\n"); 160 error = GetLastError(); 161 printf_s("エラーコードは%dです\n", error); 162 return 1; 163 } 164 165 //メールスロットから受信したメッセージを出力する 166 // 修正11: 読み取ったバイト数も出力 167 _tprintf_s(TEXT("readByte=[%lu], メッセージは「%s」です\n"), readByte, readBuf); 168 169 // 修正12: malloc~free 170 free(readBuf); 171 172 // 修正13: 使わないので無効化 173 // CloseHandle(handle3); 174 CloseHandle(handle1); 175 176 return 0; 177} 178

これをコマンドプロンプトで実行すると以下のようになります。
※Visual Studio 2019 でWin32(32ビット)UNICODEビルド、Windows 10で実行しました。

イメージ説明

投稿2021/01/23 07:35

dodox86

総合スコア9256

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2021/01/23 07:51

ちなみにソースはほとんどC言語のプログラムではあるのですが、ファイルの拡張子を*.cにすると本当にC言語になるので、boolやtrue, falseの予約語は使えなくなり、コンパイルエラーとなります。より厳格にコーディングするのであれば、Windows APIに沿ってBOOLやTRUE, FALSEに直すべきかもしれません。細かい話であるのと、質問の本筋ではないのでこの辺で。
puimol

2021/01/25 11:30

dodox86さん、ありがとうございます!載せていただいたSRCを動かし、もう一度自分でもメールスロットを使用するプログラムを書いてみましたが、無事動かすことができました!こちらに載せる前にきちんと確認をしておらず、SRCの中に多くのミスがありました…。一つ一つ丁寧にご指摘くださり、ありがとうございます。勉強させていただきます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問