前回の記事(1)はこちら↓

https://ameblo.jp/devprogc15/entry-12674298187.html

 
class C*1 を定義。インターネットを開く関数だけを作成してある。
 
この記事では、インターネット上のテキストファイルを開いて読み込んでみる。
バイト列として取得。

 

宣言部分(コンストラクタなど一部略)

class C*2 : public C*1
{
protected:
    // 1. URL 上のテキストファイルを開く //
    static BOOL OpenURL(
                HINTERNET   hInternet       ,
                LPCTSTR     lpszURL         ,
                CByteArray& arrayBytes      ,
                int         nMaxByteToAbort
            ) ;
private:
    // 2-(1). (静的関数) (private) 全体を読み取り //
    static BOOL ReadFile(
                HINTERNET   hConnect ,

                CByteArray& arrayBytes
            ) ;
    // 2-(2). (静的関数) (private) 指定された長さだけ読み取り //
    static DWORD ReadBytes(
                HINTERNET   hConnect        ,
                DWORD       dwBufferSize    ,
                CByteArray& arrayResultRead
            ) ;
};

関数の中身は以下の感じ。 

// Macro //
#define DEFAULT_BUFFER_SIZE   1024

// 1. URL 上のテキストファイルを開く //
BOOL C*2::OpenURL(
                HINTERNET   hInternet ,
                LPCTSTR     lpszURL     ,
                CByteArray& arrayBytes    )
{

    // これは意味がわからない //

    TCHAR pszHeader[] = _T("Accept: */*\r\n\r\n") ;

    // 1. インターネット URL へのアクセス //
    /**************************************************
    HINTERNET InternetOpenUrl(
        IN HINTERNET hInternetSession,
        IN LPCSTR lpszUrl,
        IN LPCSTR lpszHeaders,
        IN DWORD dwHeadersLength,
        IN DWORD dwFlags,
        IN DWORD dwContext
    );
    Begins reading a complete FTP, Gopher, or HTTP URL.

    Use InternetCanonicalizeUrl first if the URL being used contains

    a relative URL and a base URL separated by blank spaces.
   **************************************************/
    // (1). 接続 //

    HINTERNET hConnect = ::InternetOpenUrl(
                            hInternet ,
                            lpszURL ,
                            pszHeader , // NULL でも良い //
                            -1 , // 自動で長さ判定 //
                            INTERNET_FLAG_DONT_CACHE ,
                            0
                        );
    // (2). 失敗した場合 //
    if(hConnect == NULL) return FALSE ;

    // 2. 全体を読み取り //
    BOOL bResult = ReadFile(hConnect , arrayBytes) ;

    // 3. 終了処理 //
    ::InternetCloseHandle(hConnect) ;

    return bResult ;
}

// 2-(1). (静的関数) (private) 全体を読み取り //
BOOL C*2::ReadFile(
                HINTERNET   hConnect  ,
                CByteArray& arrayBytes )
{
    CByteArray arrayResultRead ;


    // 1. 合計サイズ //
    DWORD dwTotalSize = 0 ;

    // 2. 読み取り //
    while(1)
    {
        // (1). 指定された長さだけ読み取り //
        DWORD dwResultReadSize = ReadBytes(
                         hConnect ,
                         (DWORD)DEFAULT_BUFFER_SIZE ,
                         arrayResultRead
                    ) ;

        // (2). 終了する場合 //
        if (dwResultReadSize == 0) break ;

        // (3). 合計サイズを加算 //
        dwTotalSize += dwResultReadSize ;

        // (4). バイト列を追加 //
        arrayBytes.Append(arrayResultRead) ;
    }

    return TRUE ;
}

// 2-(2). (静的関数) (private) 指定された長さだけ読み取り //
DWORD C*2::ReadBytes(
                HINTERNET   hConnect        ,
                DWORD       dwBufferSize    ,
                CByteArray& arrayResultRead   )
{
    DWORD dwResultReadSize = 0 ;

    // 1. 初期設定 //
    // (1). 配列を初期化 //

    arrayResultRead.SetSize(0) ;

    // (2). データ領域を確保 //
    BYTE* pcbBuffer = new BYTE[ dwBufferSize ] ;

    // 2. インターネット上のファイルからバイト列を読み取り //
    BOOL bRet = ::InternetReadFile(
                              hConnect ,
                              pcbBuffer ,
                              dwBufferSize ,
                              &dwResultReadSize
                          ) ;

    // 3. 成功した場合 //
    if (bRet && dwResultReadSize > 0)
    {
        // (1). 配列のサイズを確保 //
        arrayResultRead.SetSize( (int)dwResultReadSize ) ;

        // (2). 要素をコピー //
        // これ、何とかならなかったか? //

        for(int k = 0 ; k < (int)dwResultReadSize ; k++)
        {
              arrayResultRead.SetAt( k , pcbBuffer[k] ) ;
        }
    }

    // 4. メモリ解放 //
    delete pcbBuffer ;

    return dwResultReadSize ;
}

 

以上の感じ。

 

これで、どう使うか。

 

CByteArray arrayBytes ;

 

// ファイルを開いて読み取り //
// (1). インターネット開始 //
HINTERNET hInternet = C*1::InternetOpen() ;
if (hInternet == NULL) return ;

// (2). URL上のファイルデータを読み取り //
BOOL bResult = C*2::OpenURL(

                                  hInternet ,

                                  lpszURL ,

                                  arrayBytes

                               ) ;

// (3). 終了処理 //
::InternetCloseHandle(hInternet) ;

 
 
最終的に、こんな自動アップデートを目指します。
(Google Chrome や Firefox などのように)
 
 
 
 
**************************************************************


 

 

 
自分でホームページなどを用意し、その中にファイルをアップロードしておきます。
 
クラス C*1 (名前は各自決めてください)は、どの派生クラスでも使用する関数を集めます。
その中で、必要なものだけ。
 

宣言部分(コンストラクタなど一部略)

#include "wininet.h"

class C****1
{
protected:
    //   文字列 (メモリ管理)
    static WCHAR* MemoryWCharStr(int nLength) ;
    static WCHAR* MemoryWideCharFromMultiChar(

        const char* lpszMultiByteStr ,

        UINT uCodePage = CP_ACP

    ) ;
    //   基本関数 (共通処理)
    static HINTERNET InternetOpen(

        DWORD dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG

    ) ;
};

関数の中身は以下の感じ。

#pragma comment(lib,"wininet.lib")
/////////////////////////////////////////////////////////////////////
//   基本関数 (共通処理)
/////////////////////////////////////////////////////////////////////

// 1. (静的関数) (protected) インターネット使用開始 (Win32初期化) //
HINTERNET C*1::InternetOpen(DWORD dwAccessType)
{

    /*************************************************
    HINTERNET InternetOpen(
        IN LPCSTR lpszAgent,
        IN DWORD dwAccessType,
        IN LPCSTR lpszProxyName,
        IN LPCSTR lpszProxyBypass,
        IN DWORD dwFlags
    );
    Initializes an application's use of the Win32 Internet functions.
    *************************************************/

    return ::InternetOpen(
                _T("class name") ,
                dwAccessType , // INTERNET_OPEN_TYPE_PRECONFIG
                NULL ,
                NULL ,
                0
            ) ;
}

 

/////////////////////////////////////////////////////////////////////
//   文字列 (メモリ管理)
/////////////////////////////////////////////////////////////////////
// 1-(1). (静的関数) (protected) WCHAR [] のメモリ確保 //

WCHAR* C*1::MemoryWCharStr(int nLength)
{
    // 1. メモリ確保 //
    WCHAR* pText = new WCHAR [nLength] ;

 

    // 2. 初期化 //
    ::memset(pText , 0 , sizeof(WCHAR) * nLength) ;

 

    return pText ;
}

// 1-(2). (静的関数) マルチバイト文字列 (char*) を

//           ワイド文字列 (UNICODE) に変換してメモリ確保 //
// ※ UINT uCodePage = CP_ACP ; // または CP_THREAD_ACP

WCHAR* C*1::MemoryWideCharFromMultiChar(

        const char* lpszMultiByteStr ,

        UINT uCodePage)
{
    /**************************************************
    MultiByteToWideChar() : マルチバイト文字列をワイド文字列に変換
    ------------------------------------------------------------------------
    int MultiByteToWideChar(
        UINT CodePage,         // コードページ
        DWORD dwFlags,         // 文字の種類を指定するフラグ
        LPCSTR lpMultiByteStr, // マップ元文字列のアドレス
        int cchMultiByte,      // マップ元文字列のバイト数
        LPWSTR lpWideCharStr,  // マップ先ワイド文字列を入れる

                                            //バッファのアドレス
        int cchWideChar        // バッファのサイズ
    );
    **************************************************/

    // (UINT)uCodePage = CP_ACP または CP_THREAD_ACP
    DWORD dwFlags = MB_PRECOMPOSED ;

    // 1. 必要なサイズを計算して取得 //
    int retSize = ::MultiByteToWideChar(
        uCodePage        ,
        dwFlags          ,
        lpszMultiByteStr ,
        -1        ,
        NULL    , // lpMultiByteStr が指すバッファは使われません
        0            // 0 を指定するとバッファに必要なバイト数が返ります
    );

    // 2. メモリ確保 //
    WCHAR* pWCharMemory = MemoryWCharStr(retSize + 1) ;

    // 3. 変換 //
    int ret = ::MultiByteToWideChar(
        uCodePage        ,
        dwFlags          ,
        lpszMultiByteStr ,
        -1               ,
        pWCharMemory     ,
        retSize
    );

    return pWCharMemory;
}

 
従来のC言語でなかなか痛いところが、文字コードの違い。
バイト文字 char 型と、ワイド文字とか、Unicodeとか、変換が必要だったりするので、きめ細かく対応しないと正しく動作しない。
 
 
インターネット上のファイルへのアクセスの基本的な流れは以下の通り。

// (1). インターネット開始 //

//      (ハンドルを取得)

HINTERNET hInternet = C*1::InternetOpen() ;
if (hInternet == NULL) return 0 ;

 
// (2). 読み取りなどの処理 //
    ....

 

// (3). 終了処理 //
::InternetCloseHandle(hInternet) ;
 
 
ワイド文字に変換した場合、必ずメモリ解放を行う必要がある。

// (1). 変換 (メモリ確保) //

WCHAR* pszWBuffer = C*1::MemoryWideCharFromMultiChar(

        "..."         , // const char* lpszMultiByteStr

         CP_ACP    // UINT uCodePage

) ;

 

// (2). 処理 //
    ....

 

// (3). メモリ解放 //

delete [] pszWBuffer ;

 
 
最終的に、こんな自動アップデートを目指します。
(Google Chrome や Firefox などのように)
 
 
 
 
**************************************************************

 数独を解く時、セル(マス)に入力可能な数値を、列挙することが多々ある。

 

 いつも、Excelで数字を代入して、解いているのだが・・・正直面倒。

 マウスとキーボード、面倒くさいし、見づらい。

 

 世の中には、Excelで作ってくれてある、数独専用のメモ帳がある。

 けれど、やはりExcelなので、両手を使ってマウスとキーボードを操作しなければならない。

 

 それで、自分でC++で作ってみた。

 ここ数年でプログラミング技術がより高くなったと感じる。

 

 こんな感じ。

 

 

 Visual Studio 2015 C++ (Windows) で開発。

 

 既にある問題を入力し、メモ帳として数値を入力したり確定したり。

 正解を自動計算する機能を搭載している。けれど、今のバージョンでは表示しないし、正誤判定も行わないようにしている(商標権に抵触するかもしれないので)。

 

 出来映えは良い方と思っているけれど、どうなのか。

 基本、無料。

 

 バージョンアップも、自動でネット接続して実行するようにした。

 これが、相当うまくいった。

 

公式HP

http://yoyontek-develop.minim.ne.jp/num-place-memo/

 

 
 
YouTubeでの紹介動画(1分)