ActiveX から IIS(ASP.NET) への WinInet を 使用した HTTP通信

ActiveX と IIS(ASP.NET)間で、HTTP、又はHTTPSを使用したバイナリ通信を行う場合

  1. ActiveX 側で WinInet モジュール
  2. IIS(ASP.NET)側で、HTTP ハンドラ

を使用し下記コードのように実装すると、Webサービス並みの手軽さでHTTP通信を実現することが可能だ。

1. [ ActiveX ]

SampleCommand.h

#pragma once

#include <afxinet.h>

#define READ_BUF (8192)        // 受信時読取りバッファサイズ
#define MSG_SIZE (1024)        // メッセージサイズ

// エラーメッセージ
#define NO_ERROR (0)       
#define ERROR_PARAMETOR (1)       
#define ERROR_NET_API (2)

// 通信コマンド基本クラス
class CSampleCommand
{
public:
    CSampleCommand(void); // コンストラクタ
    CSampleCommand(LPCTSTR lpszConnectUrl) { m_strConnectUrl = lpszConnectUrl; } // コンストラクタ
    virtual ~CSampleCommand(void); // デストラクタ
    virtual ULONG Execute(BYTE* pSendBuf, ULONG nSendSize, BYTE** pRecvBuf, ULONG* pnRecvSize);  // 通信処理

protected:
    CString    m_strConnectUrl;    // 通信時接続先URL           
};

SampleCommand.cpp

#include "StdAfx.h"
#include "SampleCommand.h"

// CSampleCommand

CSampleCommand::CSampleCommand(void)
{
    m_strConnectUrl.Empty();
}

CSampleCommand::~CSampleCommand(void)
{
}

#define CLOSE_HTTPSESSION(pFile, pConn, pSess) { pFile->Close(); pConn->Close(); pSess->Close(); }
#define DELETE_HTTPSESSION(pFile, pConn) { delete pFile; delete pConn; }

ULONG CSampleCommand::Execute(BYTE* pSendBuf, ULONG nSendSize, BYTE** pRecvBuf, ULONG* pnRecvSize)
{
    *pRecvBuf = NULL;
    *pnRecvSize = 0;
    LONG nRet;

    // URLの解析
    DWORD dwServiceType;
    CString strServer;
    CString strObject;
    INTERNET_PORT nPort;
    if (!::AfxParseURL(m_strConnectUrl, dwServiceType, strServer, strObject, nPort))
    {
        // ASSERT(0);
        return ERROR_PARAMETOR;
    }

    CHttpConnection* pConnection = NULL;
    CInternetSession session;
    CHttpFile* pFile = NULL;
    try
    {
        pConnection = session.GetHttpConnection(
            strServer,
            INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE ,
            nPort,
            NULL,
            NULL
        );

        DWORD dwFlags = INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE;
        BOOL bSecure = m_strConnectUrl.MakeLower().Find(_T("https")) == 0;
        if (bSecure)
        {
            dwFlags |= INTERNET_FLAG_SECURE;
#if _DEBUG
            // 自己署名証明書(オレオレ証明書)と期限切れ証明書を認める
            dwFlags |= INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_IGNORE_CERT_CN_INVALID;
#endif
        }

        pFile = pConnection->OpenRequest(
            CHttpConnection::HTTP_VERB_POST,
            strObject,
            NULL,
            1,
            NULL,
            _T("HTTP/1.1"),
            dwFlags
        );

        if (bSecure)
        {
#if _DEBUG
            // 自己署名証明書(オレオレ証明書)を認める
            DWORD dwFlagSec;
            pFile->QueryOption(INTERNET_OPTION_SECURITY_FLAGS, dwFlagSec);
            dwFlagSec |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
            pFile->SetOption(INTERNET_OPTION_SECURITY_FLAGS, dwFlagSec);
#endif
        }

        if (!pFile->AddRequestHeaders(
                _T("Accept: */*\r\nContent-Type: application/octet-stream\r\n")))
        {
            ASSERT(0);
            CLOSE_HTTPSESSION(pFile, pConnection, (&session));
            DELETE_HTTPSESSION(pFile, pConnection);
            return ERROR_NET_API;           
        }

        // リクエストの送信とデータの書込み ① or ②
#if 0
        // ①いくつかに分けて送る
        pFile->SendRequestEx(nSendSize);
        pFile->Write(pSendBuf, nSendSize);
        pFile->EndRequest();
#else
        // ②一度に送る
        if (!pFile->SendRequest(NULL, 0, pSendBuf, nSendSize))
        {
            // ASSERT(0);
            CLOSE_HTTPSESSION(pFile, pConnection, (&session));
            DELETE_HTTPSESSION(pFile, pConnection);
            return ERROR_NET_API;   
        }
#endif
        DWORD dwStatusCode;
        if (!pFile->QueryInfoStatusCode(dwStatusCode))
        {
            // ASSERT(0);
            CLOSE_HTTPSESSION(pFile, pConnection, (&session));
            DELETE_HTTPSESSION(pFile, pConnection);
            return ERROR_NET_API;   
        }

        if (dwStatusCode >= 300)
        {
            // ASSERT(0);
            CLOSE_HTTPSESSION(pFile, pConnection, (&session));
            DELETE_HTTPSESSION(pFile, pConnection);
            return ERROR_NET_API;   
        }

        // 受信サイズ
        DWORD dwLength;
        pFile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, dwLength);
        *pnRecvSize = dwLength;

        // 受信バッファ
        *pRecvBuf = new BYTE [dwLength];
        // データの読み込み ① or ②
#if 1    // ①一気読み
        int nRead = pFile->Read(*pRecvBuf, dwLength);
        ASSERT(nRead == dwLength);
#else    // ②バッファ読み
        BYTE* pBuf = *pRecvBuf;
        BYTE buf[READ_BUF];
        int nRead;
        do
        {
            nRead = pFile->Read(buf, READ_BUF);
            memcpy_s(pBuf, nRead, buf, nRead);
            pBuf += nRead;
        }
        while (nRead == READ_BUF);
#endif
        CLOSE_HTTPSESSION(pFile, pConnection, (&session));
        DELETE_HTTPSESSION(pFile, pConnection);
        nRet = NO_ERROR;
    }
    catch (CInternetException *pe)
    {
#if _DEBUG
        pe->ReportError();
#else
        CString strBuf;
        pe->GetErrorMessage(strBuf.GetBuffer(MSG_SIZE + 1), MSG_SIZE);
        strBuf.ReleaseBuffer();

        // ログ出力
#endif
        nRet = pe->m_dwError;    
        pe->Delete();
        DELETE_HTTPSESSION(pFile, pConnection);
        nRet = ERROR_NET_API;
        return nRet;
    }
    return nRet;
}

2. [ ASP.NET (C#) ]

Handler.cs

using System;
using System.Diagnostics;
using System.IO;
using System.Web;
using System.Web.SessionState;

namespace Sample.SI
{
    public class Handler : IHttpHandler, IRequiresSessionState
    {
        #region IHttpHandler メンバ

        public bool IsReusable
        {
            get { return true; }
        }

        /// <summary>
        /// HTTP Web 要求の処理を行う
        /// </summary>
        /// <param name="context">HTTP 要求を処理するために使用する、サーバー オブジェクト (Request、Response、Session、Server など) への参照</param>
        public void ProcessRequest(HttpContext context)
        {
            Debug.WriteLine("Handler::ProcessRequest() Start: " + context.Session.SessionID);

            Stream stream = context.Request.InputStream;
            int sendLength = Convert.ToInt32(stream.Length);
            byte[] sendBuf = new byte[sendLength];
            int read = stream.Read(sendBuf, 0, sendLength);

            HttpResponse response = context.Response;
            try
            {
                // ここで独自処理をおこなう ////////////////////////////
                byte[] recvBuf = ….内部処理;
                Debug.WriteLine(String.Format("RecvSize={0}", recvBuf.Length));
                // (ここまで独自処理) ////////////////////////////
                response.BinaryWrite(BitConverter.GetBytes((int)recvBuf.Length));
                response.BinaryWrite(recvBuf);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                response.BinaryWrite(BitConverter.GetBytes((int)sizeof(int)));
                response.BinaryWrite(BitConverter.GetBytes((int)-1));
            }
            response.End();
        }

        #endregion
    }
}

Web.config

<configuration>
    <system.web>
        <httpHandlers>
            <add verb="*" path="si/*.aspx" type="Sample.SI.Handler,Sample.SI" />
        </httpHandlers>
    </system.web>
</configuration>

◎ IISの設定で*.aspx拡張子への「ファイルの存在を確認する」処理をオフにします。
IIS6.0以降の場合、存在しないファイルへのリクエストはデフォルトで”404.2エラー”になるため、下記の設定ページを参考に *.aspx拡張子ファイルの [ファイルの存在を確認する] チェック ボックスをオフにします。

IIS 6.0 におけるアプリケーション マッピングを設定する
http://technet.microsoft.com/ja-jp/library/cc783552(WS.10).aspx

参考情報:

典型的な HTTP クライアント アプリケーションの作成手順
http://msdn.microsoft.com/ja-jp/library/8yh4zs9e.aspx

Visual C# .NET を使用して ASP.NET HTTP ハンドラを作成する方法
http://support.microsoft.com/kb/308001/ja

[INFO] ASP.NET の HTTP モジュールと HTTP ハンドラの概要
http://support.microsoft.com/kb/307985/

広告

コメントを残す

コメントを投稿するには、以下のいずれかでログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中