メニューの「オプション」「設定」を選択すると左の図のような
ダイアログボックスが出ます。SMTPアドレス、差出人メールアドレスを
入力します。「返信」先が差出人と同じでよい場合は何も入力しなくて
OKです。送信する場合パスワードは不要です。(最近これを利用して
スパムなどを送る悪質な業者などがいるので、自分の所の会員以外はメールを
送信できない設定にしているISPが多いようです。)
サブジェクトやらメール本文を入力するダイアログです。
本文は1行ずつ入力します。全文を入力し終わったらキャンセルボタンを
押します。
では、プログラムを見てみましょう。
メニューとダイアログボックスのリソース・スクリプトです。// mail01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "送信(&S)...", IDM_CONNECT MENUITEM SEPARATOR MENUITEM "終了(&X)...", IDM_END END POPUP "オプション(&O)" BEGIN MENUITEM "設定(&S)...", IDM_SETTING END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 187, 53 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "文字列入力" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,7,7,173,20,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,7,32,50,14 PUSHBUTTON "キャンセル",IDCANCEL,130,32,50,14 END MYSETDLG DIALOG DISCARDABLE 0, 0, 187, 89 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "設定" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,78,7,102,12,ES_AUTOHSCROLL EDITTEXT IDC_EDIT2,78,25,102,12,ES_AUTOHSCROLL EDITTEXT IDC_EDIT3,78,43,102,12,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,27,68,50,14 PUSHBUTTON "キャンセル",IDCANCEL,103,68,50,14 LTEXT "SMTPアドレス",IDC_STATIC,7,7,41,8 LTEXT "差出人メールアドレス",IDC_STATIC,7,29,63,8 LTEXT "返信",IDC_STATIC,7,47,15,8 END
VC++では、「プロジェクト」「設定」「C/C++」「プリプロセッサ」 のところの「プリプロセッサの定義」にWIN32_LEAN_AND_MEANを加えておけば ソースファイルでdefineしなくても大丈夫です。// mail01.cpp #ifndef STRICT #define STRICT #endif //#define WIN32_LEAN_AND_MEAN //「プリプロセッサの定義」に加えればdefineしなくてよい #include <windows.h> #include <winsock2.h> #include <windowsx.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MySettingProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); void MyConnect(HWND); char szClassName[] = "mail01"; //ウィンドウクラス char szStr[1024], szStrRcv[1024]; char szServerName[256], szFrom[256], szReplyTo[256]; HINSTANCE hInst;
Wsock32.libをリンクするのを忘れないでください。
いつもと同じですが、ここでインスタンスハンドルをグローバル変数hInstにコピーしています。int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
これは、いつもと同じです。//ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるメーラー", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
メニューからIDM_SETTINGが選択されたときは、"MYSETDLG"ダイアログを呼び出します。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_SETTING: DialogBox(hInst, "MYSETDLG", hWnd, (DLGPROC)MySettingProc); break; case IDM_CONNECT: MyConnect(hWnd); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
メニューからIDM_CONNECTが選択されたときは、MyConnect関数を呼び出します。
SMTPに接続するまでを復習するとvoid MyConnect(HWND hWnd) { WSADATA wsaData; LPHOSTENT lpHost; LPSERVENT lpServ; SOCKET s; int iProtocolPort, iRtn; SOCKADDR_IN sockadd; char szTo[256], szSubject[256]; if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) { MessageBox(hWnd, "エラーです", "Error", MB_OK); return; } if (strcmp(szServerName, "") == 0) { MessageBox(hWnd, "サーバーアドレスを入力してください。", "サーバー", MB_OK); DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); strcpy(szServerName, szStr); } lpHost = gethostbyname(szServerName); if (lpHost == NULL) { wsprintf(szStr, "%sが見つかりません", szServerName); MessageBox(hWnd, szStr, "Error", MB_OK); return; } s = socket(PF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { MessageBox(hWnd, "ソケットをオープンできません", "Error", MB_OK); return; } lpServ = getservbyname("mail", NULL); if (lpServ == NULL) { MessageBox(hWnd, "ポート指定がされていないので、デフォルトを使います", "OK", MB_OK); iProtocolPort = htons(IPPORT_SMTP); } else { iProtocolPort = lpServ->s_port; } sockadd.sin_family = AF_INET; sockadd.sin_port = iProtocolPort; sockadd.sin_addr = *((LPIN_ADDR)*lpHost->h_addr_list); if (connect(s, (PSOCKADDR)&sockadd, sizeof(sockadd))) { MessageBox(hWnd, "サーバーソケットに接続失敗", "Error", MB_OK); return; } memset(szStrRcv, '\0', sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv), 0); MessageBox(hWnd, szStrRcv, "Server", MB_OK); strcpy(szStr, "HELO "); strcat(szStr, szServerName); strcat(szStr, "\r\n"); send(s, szStr, strlen(szStr), 0); memset(szStrRcv, '\0', sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv), 0); MessageBox(hWnd, szStrRcv, "Server", MB_OK); if (strcmp(szFrom, "") == 0) { MessageBox(hWnd, "差出人メールアドレスを入力してください", "差出人", MB_OK); DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); strcpy(szFrom, szStr); } wsprintf(szStr, "MAIL FROM : <%s>\r\n", szFrom); send(s, szStr, strlen(szStr), 0); memset(szStrRcv, '\0', sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv), 0); MessageBox(hWnd, szStrRcv, "Server", MB_OK); MessageBox(hWnd, "あて先メールアドレスを入力してください", "あて先", MB_OK); DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); strcpy(szTo, szStr); wsprintf(szStr, "RCPT TO :<%s>\r\n", szTo); send(s, szStr, strlen(szStr), 0); memset(szStrRcv, '\0', sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv), 0); MessageBox(hWnd, szStrRcv, "Server", MB_OK); strcpy(szStr, "DATA\r\n"); send(s, szStr, strlen(szStr), 0); memset(szStrRcv, '\0', sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv), 0); MessageBox(hWnd, szStrRcv, "Server", MB_OK); strcpy(szStr, "X-Mailer: Nekodemo_Wakaru-Mailer\r\n"); send(s, szStr, strlen(szStr), 0); if (strcmp(szReplyTo, "") != 0) { wsprintf(szStr, "Reply-To: %s\r\n", szReplyTo); send(s, szStr, strlen(szStr), 0); } MessageBox(hWnd, "件名を入力してください", "件名", MB_OK); DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); strcpy(szSubject, szStr); wsprintf(szStr, "Subject: %s \r\n", szSubject); send(s, szStr, strlen(szStr), 0); //ここでもう一度「\r\n」を送信しておかないと //本文に日本語を使う場合1行目が文字化けします strcpy(szStr, "\r\n"); send(s, szStr, strlen(szStr), 0); while (1) { MessageBox(hWnd, "本文を1行ずつ記入して「OK」ボタンを押してください。\n" "記入が終わったら「キャンセル」ボタンを押してください。", "本文", MB_OK); iRtn = DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); if (iRtn == IDCANCEL) break; strcat(szStr, "\r\n"); send(s, szStr, strlen(szStr), 0); } strcpy(szStr, ".\r\n"); send(s, szStr, strlen(szStr), 0); memset(szStrRcv, '\0', sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv), 0); MessageBox(hWnd, szStrRcv, "Server", MB_OK); strcpy(szStr, "QUIT\r\n"); send(s, szStr, strlen(szStr), 0); memset(szStrRcv, '\0', sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv), 0); MessageBox(hWnd, szStrRcv, "Server", MB_OK); closesocket(s); WSACleanup(); MessageBox(hWnd, "通信を終了しました", "OK", MB_OK); return; }
の順で関数を呼び出します。connecct関数が成功したらSMTPとの対話を 開始します。sendでコマンドを送りrecvでサーバーの返事を受けます。 送信する文字列の最後に「\r\n」を付加するのを忘れないでください。1.WSAStartup関数 2.gethostbyname関数(これを実行するとインターネットに接続していないときは 接続のダイアログが自動的に出てきます) 3.socket関数 4.getservbyname関数 5.connect関数
DATAを送信する前に「Subject:件名」「Reply-To:返信先」「X-Mailer:メールソフト名」などを送信するとエラーが返ってきます。1.「HELO サーバー名」を送信、サーバーの返事を待つ 2.「MAIL FROM : <差出人アドレス>」を送信、返事を待つ 3.「RCPT TO : <あて先アドレス>」を送信、返事を待つ 4.「DATA」を送信、返事を待つ。 5.メールのその他のヘッダになる部分を送信(返事は待たない) 6.本文を1行ずつ送信(返事は待たない) 7.本文の最後に「.」(ピリオド)を送信、返事を待つ 8.「QUIT」を送信、返事を待つ 9.closesocket関数 10.WSACleanup関数
件名に日本語を使うと文字化けになります。
本文などを入力するダイアログボックスのプロシージャです。 OKボタンが押されたらエディットコントロールの内容をグローバル変数の szStrに取得します。LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hEdit; switch (msg) { case WM_INITDIALOG: hEdit = GetDlgItem(hDlg, IDC_EDIT1); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(hEdit, szStr, sizeof(szStr)); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }
設定ダイアログのプロシージャです。LRESULT CALLBACK MySettingProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hServerName, hFrom, hReplyTo; switch (msg) { case WM_INITDIALOG: hServerName = GetDlgItem(hDlg, IDC_EDIT1); hFrom = GetDlgItem(hDlg, IDC_EDIT2); hReplyTo = GetDlgItem(hDlg, IDC_EDIT3); Edit_SetText(hServerName, szServerName); Edit_SetText(hFrom, szFrom); Edit_SetText(hReplyTo, szReplyTo); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(hServerName, szServerName, sizeof(szServerName)); Edit_GetText(hFrom, szFrom, sizeof(szFrom)); Edit_GetText(hReplyTo, szReplyTo, sizeof(szReplyTo)); if (strcmp(szReplyTo, "") == 0) strcpy(szReplyTo, szFrom); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }
OKボタンが押されたとき返信先のアドレスが書き込まれていないときは 差出人と同じアドレスにしています。
さて、これで何とかメールだけは出せるようになりました。 しかし、まだまだ原始的な段階です。
Update 23/Sep/1999 By Y.Kumei