Webサービス で ASP.NET アプリケーション と セッションを共有する

 

ASP.NETアプリケーション と 同じ仮想ディレクトリ内にある Webサービス(*.asmx等) 呼び出しで どうしてもセッションを共有したい場合、下記のようにすれば共有が可能になる。

Web サービスで ASP.NET セッション状態を使用する
http://msdn.microsoft.com/ja-jp/library/aa480509.aspx

> HTTP サーバーの観点からは、 ASP.NET セッションのスコープは指定した ASP.NET アプリケーション内でのみ有効です。つまり、特定のユーザーの単一の仮想ディレクトリ内のすべてのセッション対応の ASP.NET 要求では、 HttpSessionState クラスの同じインスタンスが使用されます。(省略) ASP.NET はセッションに関する限り、ASPX 要求と ASMX 要求を区別しません。そのため、理論上は Web メソッド呼び出しと通常の ASPX ファイル間でセッション状態を共有できます。

① Webサービス

 

namespace ASPNETWebService
{
    /// <summary>
/// EnableSession=true(セッションを利用する Webサービス)
/// </summary>

[WebService(Namespace = “http://tempuri.org/&#8221;)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Service1 : System.Web.Services.WebService
{

        [WebMethod(EnableSession = true)]
public string HelloWorld()
{
return String.Format(“Process={0}, Thread={1}, SessionID={2}”,
Process.GetCurrentProcess().Id,
Thread.CurrentThread.ManagedThreadId,
Session.SessionID);
}
}
}

 

② Webアプリケーション

namespace ASPNETWebApplication
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Session[“Dummy”] = “1”;
}

protected void ButtonCallWebService_Click(object sender, EventArgs e)
{
            HttpCookie cookie = Request.Cookies[“ASP.NET_SessionId“];

            WSHelloWorld.Service1 service = new WSHelloWorld.Service1();
service.CookieContainer = new CookieContainer();
service.CookieContainer.Add(new Uri(service.Url), new System.Net.Cookie(cookie.Name, cookie.Value));

string resultWebSearvice = service.HelloWorld();

Debug.WriteLine(“WebService=” + resultWebSearvice);

string resultWebApplication = String.Format(“Process={0}, Thread={1}, SessionID={2}”,
Process.GetCurrentProcess().Id,
Thread.CurrentThread.ManagedThreadId,
Session.SessionID);

Debug.WriteLine(“WebApplication=” + resultWebApplication);
}
}
}

 

 

③ ActiveX
※ActiveXからクッキーを参照する場合

◎Win32 API

#define MAX_COOKIE_SIZE 4096

TCHAR szCookie[MAX_COOKIE_SIZE + 1];
DWORD dwSize = sizeof (szCookie) / sizeof (TCHAR);
if (InternetGetCookie(_T(“http://www.testsite.local/&#8221;), _T(“ASP.NET_SessionId“), szCookie, &dwSize))
{
TRACE(“Cookie=%s\n”, szCookie);
}

 

InternetGetCookie Function
http://msdn.microsoft.com/en-us/library/aa384710%28VS.85%29.aspx

◎MFC

CString strCookieData;
if (CInternetSession::GetCookie(_T(“http://www.testsite.local/&#8221;), _T(“ASP.NET_SessionId“), strCookieData))
{
TRACE(“Cookie=%s\n”, (LPCTSTR)strCookieData);
}

 

 

CInternetSession::GetCookie
http://msdn.microsoft.com/ja-jp/library/cff9kt47.aspx

Livedoor タグ: ,

 

WinInet (MFC) の SSL通信 で 開発中に自己署名証明書(オレオレ証明書)を利用する場合のコード

サーバー等とSSL通信を利用するアプリケーションやシステムで、ローカルでの開発中や、ステージング環境でのテスト中は正規のSSLサーバー証明書を利用できない場合が多いと思う。
WinInetを使用した通信 で MFC ライブラリを使用する場合のサンプルコードを下記に記載する。

開発中に、自己署名証明書(オレオレ証明書) と 期限切れ証明書を認める場合のコード。
※例外処理などは含めてない。 もう少しまとまったコードは 最後の 詳細記事 を 参照。

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;           
        }

詳細記事:

ActiveX から IIS(ASP.NET) への WinInet を 使用した HTTP通信
https://techwing.wordpress.com/2009/06/21/activex-%e3%81%8b%e3%82%89-iisasp-net-%e3%81%b8%e3%81%ae-wininet-%e3%82%92-%e4%bd%bf%e7%94%a8%e3%81%97%e3%81%9f-http%e9%80%9a%e4%bf%a1/

リボンUIをインプレースで実装する時の注意点と対応策

リボンのようなUIをインプレースで実装する場合の注意点が「Tech Fielders コラム」で記載されている。

Windows 7 : 製品ソフト開発者から出た“その疑問”にズバリお答えします ! | Tech Fielders コラム
http://www.microsoft.com/japan/powerpro/TF/column/ro2_02_3.mspx

第 2 回 プログラミングに関する Tips – リボンで OLE を実装する際の注意点

リボンを使ったアプリケーションは、OLE のインプレースアクティベートをサポートしていません。リボンを使ったアプリケーションを OLE 経由でアクティベートする場合には、アウトプレースで起動 (アクティベーション) するようにしてください。

2009年11月現在、MFC ActiveX上に標準リボンUIは実装できないと認識している。
無理やりCWinAppExやCFrameWndExクラスを継承するようなクラスを作っても難しいだろう。
それでもリボン同様のコントロールバーをActiveXコントロールやインプレースで実装したいなら下記のようなサードパーティ製コントロールの利用がお勧めだ。
標準リボンUI以上(?)のパフォーマンスを持つコントロールを、ごく簡単なコードで実装することが出来る。

Codejock Xtreme Command Bars ActiveX
http://www.codejock.com/

国内販売:

Codejock Xtreme Command Bars ActiveX
http://www.componentsource.co.jp/ese/products/codejock-xtreme-commandbars-activex/index.html

関連記事:

ハンズオン ラボ: Windows Ribbon ~ Win32 ネイティブ アプリケーションへのリボンの追加 ~
http://msdn.microsoft.com/ja-jp/windows/ee817147.aspx?rss_fdn=MSDNTopNewInfo

Windows 7 で ボタン へ 盾アイコンを付ける

Windows Vista や Windows 7 では

  • Button
  • Hyperlink
  • Commandlink

等のコントロールに盾アイコンを表示することができるようになっている。

Window 7 からは アイコンのデザインが少し変わったようだ。

Windows VISTA

Windows 7

ボタンへ盾アイコンをつけるには簡単だ。下記のマクロを使用して対象のウィンドウへメッセージを送れば良い。

// 盾アイコンを付ける
void CSetElevationDlg::OnBnClickedCommand1()
{
    Button_SetElevationRequiredState(GetDlgItem(IDOK)->m_hWnd, TRUE);
}

// 盾アイコンを外す
void CSetElevationDlg::OnBnClickedCommand2()
{
    Button_SetElevationRequiredState(GetDlgItem(IDOK)->m_hWnd, FALSE);
}

盾アイコンは自分や、自分の子ウィンドウだけではなく、他のウィンドウにも付けるが出来る。(※ただしボタンに無暗に付けてもUACの動作には関係ない)
テーマがクラシックスタイルの場合は、下記のようにWindowsのスタートボタン等にもつけることが出来るようになっている。

ちなみに”デスクトップに表示されている全てのボタンに盾アイコンを付けたい”場合、次を参考に実装すれば良い。

#define BTN_OFF 0
#define BTN_ON 1

// 個別の子ウィンドウへの処理を行う、アプリケーション定義のコールバック関数
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)   
{
    const ATOM wButtonAtom = 0xC017; // "Button"クラスのグローバルATOM   
    WINDOWINFO info;
    GetWindowInfo(hwnd, &info);
    if (info.atomWindowType == wButtonAtom && IsWindowVisible(hwnd))
    {
        Button_SetElevationRequiredState(hwnd, lParam == BTN_ON);
        InvalidateRect(hwnd, NULL, TRUE);
    }
    EnumChildWindows(hwnd, EnumWindowsProc, lParam);
    return TRUE;
}

// 盾アイコンを付ける
void CSetElevationDlg::OnBnClickedCommand1()
{
    Button_SetElevationRequiredState(GetDlgItem(IDOK)->m_hWnd, TRUE);
    CWaitCursor wait;
    EnumChildWindows(NULL, EnumWindowsProc, BTN_ON); // 子ウィンドウを列挙する。
}

// 盾アイコンを外す
void CSetElevationDlg::OnBnClickedCommand2()
{
    Button_SetElevationRequiredState(GetDlgItem(IDOK)->m_hWnd, FALSE);
    CWaitCursor wait;
    EnumChildWindows(NULL, EnumWindowsProc, BTN_OFF);    // 子ウィンドウを列挙する。
    Button_SetElevationRequiredState(GetDlgItem(IDC_COMMAND1)->m_hWnd, TRUE);
}

Before (Normal)

After (Custom)

 

参照情報:

アプリケーション開発者向け Microsoft® Windows 7 対応アプリケーションの互換性
http://msdn.microsoft.com/ja-jp/windows/dd883236.aspx

Livedoor タグ:

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/