画像のプリロード

一昔前、Webページ上での画像のロールオーバー(画像の上にマウスポインタを重ねると画像が変わるアレです)にJavascriptが使われていましたね。でも、今は、Javascriptではなく、CSSで実現する方法が標準となりつつあります。
たとえば・・・

a      {background: url(xxx.jpg);}
a:hover{background: url(yyy.jpg);}

こんな感じですかね。IE6では、アンカータグ(<a>)しか対応してませんが、他のブラウザなら他のタグでも可能です。

で、ここで僕がはまったのが、ページに初めてアクセスしたとき、ロールオーバーさせる要素にマウスを持っていくと一瞬空白になってしまうんですよねぇ・・・。初めてページを表示させたときまだロールオーバーさせる画像が読み込まれていないのが原因で、解決方法は簡単、画像を先読みさせてブラウザのキャッシュに入れてしまえばいい。要するにプリロードさせればいい。んな、こたぁ、言われなくても分かるんです。

その方法は・・・頭の悪い僕では2つぐらいしか思いつきません・・・

  1. javascriptを使って (new Image()).src = “yyy.jpg”; とかしておく。
  2. HTML内に幅/高さを1にした<img>タグを書いておく。
  3. 画像を一枚用意して、width,height,background-positionを駆使して切り分ける

無駄なタグを含める後者は論外。画像を一枚に・・・というのは新規につくるページだと最適ですが、既存の組みあがっているページとなると無駄に工数がかかってしまう。一番簡単にできそうなのは・・・やはりスクリプトで。・・・せっかくロールオーバー処理からJavascriptを排除したのに・・・(--;;;
ブラウザの最適化処理で自動的に読み込んでくれれば問題ないんですけどね・・・。そもそも、css内にある画像は先読みしてほしい気もします。まぁ、無駄にメモリー消費が増えてしまう、パフォーマンスの問題も絡むので無理か・・・。

結局、

/**********************************************
  画像を読み込んでおく関数
  preload(画像1,画像2,....);
***********************************************/
function preload()
{
  var len = 0;

  if((len = arguments.length) &lt;= 0)
    return;

   for(var i=0;i&lt;len;i++)
    (new Image()).src = arguments[i];
}

preload('yyy.jpg','zzz.png');

とかやってしまうんだけど・・・。

う~ん・・・スクリプトを使わない上手いやり方はないものか・・・。

コマンドプロンプトのログを取る (その2)

コンソール(CMD.EXE)のログを取る (不完全版) の続きです。

標準出力と標準エラー出力をファイルに書き出せたところまではできてタイムアップ。標準入力が書き出せていませんでした。今回は、入力バッファをファイルに書き出してから、リダイレクトしたパイプハンドルに書き出せば事は足ります。

で、あまりにもソースが汚くなってきたので整理して、テストコードが完成。パイプハンドルをラップした構造体とスレッドに渡すコンテキストを別ファイルにして整理。

/*******************************************************************

 ■コマンドプロンプトのすべての操作をテキストファイルへ書き出します。

  ※ほとんどエラーチェック・処理をしていないテストコード
  ※書き出すファイルは、#define LOGFILE TEXT("~") で指定。

********************************************************************/

#include <windows.h>

//Cランタイム
#include <tchar.h>
#include <stdio.h>

//パイプ、スレッドに渡すパラメータをラップした構造体を定義したヘッダー
#include "pipe.h"
#include "context.h"

#define CLOSEHANDLE(X) {if(X)CloseHandle(X);}
#define BUFFER_SIZE 1024
#define SPIN_COUNT 4000

//書き出すファイル名の指定
#define LOGFILE TEXT("cmdlog.txt")

//標準入出力・ファイルハンドルへの読取と書込スレッド
DWORD WINAPI ReadAndWriteProc(LPVOID lpParameter)
{
  Context *context = (Context*)lpParameter;

  TCHAR pBuffer[BUFFER_SIZE] = {0};
  DWORD dwRead = 0,dwWrite = 0;
  BOOL bRet = FALSE;
  while(!(context->bThreadMustTerminate))
    {
      do
        {
          bRet = ReadFile(context->hRead,(PVOID)pBuffer,BUFFER_SIZE,&dwRead,NULL);
          if(dwRead > 0)
            {
              EnterCriticalSection(&(context->CriticalSection));
              if(context->hFile)
                WriteFile(context->hFile,(PVOID)pBuffer,dwRead,&dwWrite,NULL);
              if(context->hWrite)
                WriteFile(context->hWrite,(PVOID)pBuffer,dwRead,&dwWrite,NULL);
              LeaveCriticalSection(&(context->CriticalSection));
            }
        }
      while(bRet == TRUE && dwRead > 0);
    }

  return 0;
}

//ファイルを書込用にオープン(同期)
HANDLE OpenLogFile(PCTSTR szLogName)
{
  return CreateFile(szLogName,
                    GENERIC_WRITE,
                    0,
                    NULL,
                    CREATE_ALWAYS,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);
}

//環境変数COMSPECからコマンドプロンプトのパスを得て起動
BOOL CreateCommandPrompt(STARTUPINFO& si,PROCESS_INFORMATION& pi)
{
  BOOL bRetVal = FALSE;

  //CMD.EXEのパスを格納するバッファ
  TCHAR szCmdPath[MAX_PATH+1] = {0};

  //CMD.EXEのパスを環境(システム)変数から得る。
  if(0 == GetEnvironmentVariable(TEXT("COMSPEC"),szCmdPath,MAX_PATH+1))
    goto cleanup;
  
  bRetVal = CreateProcess(NULL,
                          szCmdPath,
                          NULL,
                          NULL,
                          TRUE,
                          NORMAL_PRIORITY_CLASS,
                          NULL,
                          NULL,
                          &si,
                          &pi);

cleanup:
  return bRetVal;
}

//スタートアップ
int _tmain(int argc,_TCHAR **argv)
{
  UNREFERENCED_PARAMETER(argc);
  UNREFERENCED_PARAMETER(argv);
  
  /*------------------------------------------------------------
    スレッドハンドル、パイプ、スレッドに渡すコンテキスト
    配列はそれぞれ、0:標準入力用 1:標準出力用 2:標準エラー出力用
  --------------------------------------------------------------*/
  HANDLE hThreads[3] = {NULL};
  Pipe Std[3] = {PIPE_READ_INHERIT,PIPE_WRITE_INHERIT,PIPE_WRITE_INHERIT};
  Context ctx[3];

  //ログを書き込むファイル
  HANDLE hFile = NULL;
  
  //CreateProcessに必要な構造体
  STARTUPINFO si = {sizeof(STARTUPINFO)};
  PROCESS_INFORMATION pi = {NULL};

  //クリティカルセクション初期化
  InitializeCriticalSectionAndSpinCount(&Context::CriticalSection,SPIN_COUNT);

  if((hFile = OpenLogFile(LOGFILE)) == INVALID_HANDLE_VALUE)
    {
      _tprintf(TEXT("ログファイルのオープンが失敗しました"));
      goto cleanup;
    }

  //入出力ハンドルをリダイレクトしたコマンドプロンプトを生成
  si.dwFlags    = STARTF_USESTDHANDLES;
  si.hStdInput  = Std[0].hRead;   //パイプにリダイレクト
  si.hStdOutput = Std[1].hWrite; //パイプにリダイレクト
  si.hStdError  = Std[2].hWrite; //パイプにリダイレクト

  if(FALSE == CreateCommandPrompt(si,pi))
    {	
      _tprintf(TEXT("CreateProcess 失敗 : %d"),GetLastError());
      goto cleanup;
    }

  //コンテキスト構造体のメンバをセット
  ctx[0].Set(GetStdHandle(STD_INPUT_HANDLE),Std[0].hWrite);
  ctx[1].Set(Std[1].hRead,GetStdHandle(STD_OUTPUT_HANDLE),hFile);
  ctx[2].Set(Std[2].hRead,GetStdHandle(STD_ERROR_HANDLE),hFile);
  
  //スレッド作成
  for(int i=0;i<3;i++)
    hThreads[i] = CreateThread(NULL,0,ReadAndWriteProc,&ctx[i],0,NULL);
  
  //新しいコマンドプロンプトが終了するまで待機
  WaitForSingleObject(pi.hProcess,INFINITE);
  
  //スレッド終了通知をセット
  Context::bThreadMustTerminate = TRUE;

  //パイプハンドルのクローズ
  for(int i=0;i<3;i++)
    Std[i].Close();

  CloseHandle(GetStdHandle(STD_INPUT_HANDLE));

  //起動したスレッドが終了するまで待機
  WaitForMultipleObjects(3,hThreads,TRUE,INFINITE);

  //クリティカルセクションオブジェクトを削除
  DeleteCriticalSection(&Context::CriticalSection);

cleanup:
  //ハンドルを閉じる
  CLOSEHANDLE(hFile);
  CLOSEHANDLE(pi.hThread);
  
  for(int i=0;i<3;i++)
    CLOSEHANDLE(hThreads[i]);
  
  CLOSEHANDLE(pi.hProcess);

  return 0;
}

すべてのコードを固めたものはこちら。
全てのソースはこっちに移動
nmakeでcmdlog.exeが生成されます。

ただ、これは CTRL-Cとかすると・・・コマンドプロンプト自体がガサッと終了してしまいますし、入力した内容がエコーされるし(ファイルには出力されないので別に良いのですが・・・)、まだまだ不完全。
書き出すファイルパスはハードコードしとるし、エラー処理していないし、ファイルの書き出しは同期処理なので、遅いデバイスを指定すると(今更ないと思うけどフロッピーとか)書出処理から制御が戻ってこないので非常にマズイことになってます・・・。

また暇なときに完成度を高めたい・・・といっててそのままでずっと使うことが多いんですが(ーー;


コンソール(CMD.EXE)のログを取る (不完全版)
コマンドプロンプトのログを取る(その2)
コマンドプロンプトのログを取る(その3)
コマンドプロンプトのログを取る(完結)

コンソール(CMD.EXE)のログを取る (不完全版)

コマンドプロンプト(CMD.EXE)の操作や出力ログを全部取りたい、と思っては見たものの、Windows標準の機能だけでは完全なログを取ることができません。リダイレクトしても入力したログが記録されないし、何より、コンソール画面に出力されないので入力しつつ、出力も表示しながら、かつすべてのログをとる、ということが標準でできません。

確かUnix系のOSなら、teeコマンドというものでできるらしいのですが・・・Windowsにはありません。そういう需要がWindowsには少ないのか、ネットで探すと、VBSスクリプトで実装しているもの(Tee.vbs)もありますが・・・。

これが Cygwin であれば、TeratermやPedorosaのようなターミナルエミュレータでCygwinに接続することですべてのログを取ることが可能ですが、残念ながらこれらのターミナルからCMD.EXEには接続できません。

というわけで、C++で強引に作ってみよう、というわけで、とりあえず動作チェック・テスト用コードを書いてみました。

仕組み自体は、CreateProcessで標準入出力をリダイレクトしたCMD.EXEのプロセスを作って、匿名パイプで出力を取得してファイルと画面に出力する、という至って普通のやり方です。

なんとか強引にコーディングして、標準出力と標準エラー出力はファイルに落とすことはできましたが、まだ入力のログがとれてません。また来週にでも完全版を書きたいと思ってます。。。

/****************************************************************

  ■CMD.EXE のログをとる不完全版テストコード
     (入力がログに落ちません)

****************************************************************/

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

#define CLOSEHANDLE(X) {if(X)CloseHandle(X);}
#define SAFEDELETE(X) {if(X)delete (X);}
#define BUFFER_SIZE 1024
#define SPIN_COUNT 4000
#define LOGFILE_PATH TEXT("cmdlog.log")

//匿名パイプをラップしたもの
struct PipeHandle
{
  HANDLE hRead;
  HANDLE hWrite;
  SECURITY_ATTRIBUTES sa;

  PipeHandle() : hRead(NULL),hWrite(NULL)
    {
      sa.nLength = sizeof(SECURITY_ATTRIBUTES);
      sa.lpSecurityDescriptor = NULL;
      sa.bInheritHandle = TRUE;

      ::CreatePipe(&hRead,&hWrite,&sa,0);
    }
  ~PipeHandle()
    {
      CLOSEHANDLE(hWrite);
      CLOSEHANDLE(hRead);
    }
};

//スレッド関数に各種変数を渡すために使用するコンテキスト
struct Context
{
  PipeHandle *lpStdHandle;
  HANDLE hFile;
  HANDLE hStdio;
  LPCRITICAL_SECTION lpCriticalSection;
  PBOOL pThreadMustTerminate;

  Context(LPCRITICAL_SECTION lpcs,PBOOL ptht) 
    : lpCriticalSection(lpcs),pThreadMustTerminate(ptht)
    {}
};

//パイプから出力を読み取るスレッド関数
DWORD WINAPI ReadOutputProc(LPVOID lpParameter)
{
  Context *context = (Context*)lpParameter;

  TCHAR pBuffer[BUFFER_SIZE] = {0};
  DWORD dwRead = 0,dwWrite = 0;

  while(!(*context->pThreadMustTerminate))
    {
      BOOL b = FALSE;
      DWORD dwReadAvail = 0;
      do
        {
          b = ReadFile(context->lpStdHandle->hRead,(PVOID)pBuffer,BUFFER_SIZE,&dwRead,NULL);
          if(dwRead > 0)
            {
              EnterCriticalSection(context->lpCriticalSection);
              WriteFile(context->hFile,(PVOID)pBuffer,dwRead,&dwWrite,NULL);
              WriteFile(context->hStdio,(PVOID)pBuffer,dwRead,&dwWrite,NULL);
              LeaveCriticalSection(context->lpCriticalSection);
            }
          if(*context->pThreadMustTerminate == TRUE)
            break;
        }
      while(b == TRUE && dwRead > 0);
    }

  return 0;
}

//スタートアップ
int _tmain(int argc,_TCHAR **argv)
{
  //CMD.EXEのパスを格納するバッファ
  TCHAR szCmdPath[MAX_PATH+1] = {0};
  
  //ログを書き込むファイル
  HANDLE hFile = NULL;
  
  //CreateProcessに必要な構造体
  STARTUPINFO si;
  PROCESS_INFORMATION pi;

  //パイプ
  PipeHandle *ppStdOut = new PipeHandle();
  PipeHandle *ppStdErr = new PipeHandle();

  //スレッドの同期処理に必要
  CRITICAL_SECTION cs;
  BOOL bThreadMustTerminate = FALSE;

  //標準入力コンソールハンドル
  HANDLE hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  
  //スレッドに渡すコンテキスト
  Context ctxOut(&cs,&bThreadMustTerminate);
  Context ctxErr(&cs,&bThreadMustTerminate);

  //クリティカルセクションオブジェクト初期化
  InitializeCriticalSectionAndSpinCount(&cs,SPIN_COUNT);

  //CMD.EXEのパスを環境(システム)変数から得る。
  if(0 == GetEnvironmentVariable(TEXT("COMSPEC"),szCmdPath,MAX_PATH+1))
    {
      _tprintf(TEXT("環境変数 COMSPEC 取得失敗\n"));
      return 0;
    }

  //ログファイルをオープン
  if((hFile = CreateFile(LOGFILE_PATH,
                         GENERIC_WRITE,
                         0,
                         NULL,
                         CREATE_ALWAYS,
                         FILE_ATTRIBUTE_NORMAL,
                         NULL)) == INVALID_HANDLE_VALUE)
    {
      _tprintf(TEXT("CreateFile が失敗しました"));
      goto cleanup;
    }
  

  // CMD.EXEのプロセスを生成
  ZeroMemory((LPVOID)&si,sizeof(STARTUPINFO));
  si.cb = sizeof(STARTUPINFO);
  si.dwFlags    = STARTF_USESTDHANDLES;
  si.hStdInput  = hStdInput;
  si.hStdOutput = ppStdOut->hWrite; //パイプにリダイレクト
  si.hStdError  = ppStdErr->hWrite; //パイプにリダイレクト

  if(FALSE == CreateProcess(NULL,
                            szCmdPath,
                            NULL,
                            NULL,
                            TRUE,
                            NORMAL_PRIORITY_CLASS,
                            NULL,
                            NULL,
                            &si,
                            &pi))
    {	
      _tprintf(TEXT("CreateProcess 失敗 : %d"),GetLastError());
      goto cleanup;
    }
  
  //スレッドに渡すコンテキストのメンバをセット
  ctxOut.lpStdHandle = ppStdOut;
  ctxOut.hFile = hFile;
  ctxOut.hStdio = GetStdHandle(STD_OUTPUT_HANDLE);
  
  ctxErr.lpStdHandle = ppStdErr;
  ctxErr.hFile = hFile;
  ctxErr.hStdio = GetStdHandle(STD_ERROR_HANDLE);

  /* スレッド作成 */
  HANDLE hThread1 = CreateThread(NULL,0,ReadOutputProc,&ctxOut,0,NULL);
  HANDLE hThread2 = CreateThread(NULL,0,ReadOutputProc,&ctxErr,0,NULL);

  //CMDプロセス終了まで待機
  WaitForSingleObject(pi.hProcess,INFINITE);
  
  //スレッド終了通知
  bThreadMustTerminate = TRUE;

  //パイプの削除
  SAFEDELETE(ppStdOut);
  SAFEDELETE(ppStdErr);
  
  //クリティカルセクションオブジェクトを削除
  DeleteCriticalSection(&cs);

cleanup:
  //ハンドルを閉じる
  CLOSEHANDLE(hFile);
  CLOSEHANDLE(pi.hThread);
  CLOSEHANDLE(pi.hProcess);
  CLOSEHANDLE(hThread1);
  CLOSEHANDLE(hThread2);

  return 0;
}

なんか・・・汚いコードだな、相変わらず・・・。
これだけなら、

CMD.EXE 2>&1 |  xxxxxx

と変わらないなぁ・・・。


コマンドプロンプトのログを取る(完結)
コマンドプロンプトのログを取る(その3)
コマンドプロンプトのログを取る (その2)

ImageFlow and HighSlideJS

今日、仕事で画像中心のギャラリーページのためのギャラリー・スクリプトをネットで探してましたら・・・もう完璧じゃね?的なクールなJavascriptライブラリを発見。結構有名なものみたいです。

その名は ImageFlow

iPhoneなどでおなじみの CoverFlow を Javascript で再現したライブラリです。ただ残念なことに、営利サイト(会社のサイトとか)で使用するにはライセンスが必要・・・とのことで採用するのは却下。

でも、サイトのドキュメントを読むと、HighSlideJSと組み合わせて利用する方法が書いてあって、これがなかなかいいので個人で使おうと・・・思ってたんだけど・・・PHP環境が必要なんで無理っぽそうです。今度DTIの格安のVPSサービスを申し込もうと思っているので、もうちょっとしたら使えそうです。

で、ローカル環境のサーバーにインストールしてみました。
 

マウスホイールも効きます

 
クリックすると・・・

HighSlideJSと併用

使うときも簡単で、簡単なサンプルのHTMLファイルがあるのですぐ分かります。
が、いちいち <img>タグを列挙するのも面倒くさいというわけで、特定のディレクトリをスキャンしてjpegファイルを列挙させるphpファイルを書けばラクになります。

<?php
function DirectoryScanAndWrite($img_dir)
{
  if ($handle = opendir($img_dir))
    {
      while (false !== ($file = readdir($handle)))
        {
          if(preg_match("/^\.{1,2}$/",$file) || preg_match("/^refl_(.+)$/i",$file))
            continue;
          
          $path = "$img_dir/$file";
          print "<img src=\"$path\" longdesc=\"$path\" alt=\"$file\">\n";
        }
    }
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
	<head>
		<meta http-equiv="content-type" content="text/html; charset=shift_jis">
		<title>ImageFlow</title>
		<meta name="robots" content="index, follow, noarchive" />
		<link rel="stylesheet" href="style.css" type="text/css" />

		<!-- This includes the ImageFlow CSS and JavaScript -->
		<link rel="stylesheet" href="imageflow.packed.css" type="text/css">
		<link rel="stylesheet" href="highslide.css" type="text/css">
		<script type="text/javascript" src="imageflow.packed.js"></script>
		<script type="text/javascript" src="highslide.packed.js"></script>
		<script type="text/javascript">
hs.graphicsDir = './graphics/';

domReady(function()
         {
           var Highslide = new ImageFlow();
           Highslide.init({ ImageFlowID: 'Highslide',
           opacity: true,
           onClick: function() 
             { 
               return hs.expand(this, { src: this.getAttribute('longdesc'),
               outlineType: 'rounded-white',
               fadeInOut: true } ); 
             } 
           });
         });
		</script>
		
		<style type="text/css">
			.highslide-container {z-index:10003 !important;}
			.highslide-credits {display:none !important; }
		</style>
	</head>
	<body>
		<h1>ImageFlow with HighSlide JS</h1>

		<!-- This is all the XHTML ImageFlow needs -->
		<div id="Highslide" class="imageflow">
			<?php DirectoryScanAndWrite('./img2/'); ?>
		</div>

	</body>
</html>

で、PHPは、画像の鏡像反射効果を作成するために必要みたいで同じディレクトリに反射効果が適用されたものが画像ファイルと同じ数だけ生成されます。

ImageFlowのインスタンスを作成するときに、いろいろなオプションが指定できたり、CSSファイルを編集することでかなり自由にデザインできそうです。PHP環境が使えるサイトでは、写真のデキがそこそこでも、かなりクール?な見せ方ができるので、僕にピッタリかもしれません(^^;;;

ヒープとshared_ptr

先日ファイルシステムが壊れてます、みたいなエラーが記録されているのを発見。
ググったらデフラグかけたらエラーがなくなった、みたいな記事を見つけました。

いくらなんでもそれはないだろう・・・と思ったけど・・・、
まぁ、エントロピーが増大しまくっているHDDを久しぶりにデフラグをかけたら
ちょっとは速くなるだろうと思い3時間かけてデフラグ終了。
エラーが記録されているHDDにデフラグをかけるなんて、傷に塩を塗り込む行為だとは思いつつ(^^;

で、今日イベントビューア見たらエラーが消えてました・・・どーなってんの、コレ?(笑)
まぁいいや。

で、本題です。

Windowsで動的にメモリブロックを確保するには、Heap APIを使うか、もっと原始的に?VirtualAlloc()などの仮想メモリを直接確保するAPIを駆使するか・・・と思います。でも仮想メモリを直接いじると何でもできちゃう反面、ちょっと大げさですし・・・。ということで、よほど大きなメモリブロックを確保するほどでもないサイズだとヒープAPIを使用するほうがお手軽です。

そんなの malloc()で十分・・・という感じもしますが・・・ちょっとした備忘録のメモも兼ねています。

で、確保したメモリブロックの寿命管理をスマートポインタ、shared_ptr に任せてしまおう、というわけです。

プライベートヒープを使用する上で、HeapCreate()とHeapDestroy()を呼ばないといけないわけで、ヒープハンドルをいつ廃棄するか(HeapDestroy()を呼ぶか)を参照カウンタで管理したいと思い次のようなコードを思いつきました。

//-----shared_heap.h-------

#include <windows.h>

//shared_ptrはboost版でもいいですが、
//Visual C++ 2008SP1にはTR1の実装があるのでそれを使います。
#include <memory>

using namespace std::tr1;

class shared_heap
{
public:
  typedef std::tr1::shared_ptr<VOID> return_type;

private:
	//CCriticalSectionは CRITICAL_SECTION構造体を単純にラップしたクラスです。
  static CCriticalSection _criticalsection;
  static HANDLE _sHeap;
  static UINT _sCount;
  
  class HeapDeleter
    {
    public:
      void operator()(PVOID ptr)
        {
          _criticalsection.Enter();
          if(::HeapFree(_sHeap,HEAP_NO_SERIALIZE,ptr))
            {
              if((--_sCount == 0) && _sHeap)
                {
                  ::HeapDestroy(_sHeap);
                  _sHeap = NULL;
                }
            }
          _criticalsection.Leave();
        }
    };
  
  shared_heap(){};
  ~shared_heap(){};

public:
  static return_type alloc(size_t size)
    {
      return_type retVal;
      
      _criticalsection.Enter();
      if(!_sHeap)
        _sHeap = ::HeapCreate(HEAP_NO_SERIALIZE,0,0);
      
      retVal = return_type(::HeapAlloc(_sHeap,HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY,size),HeapDeleter());
      _sCount++;
      
      _criticalsection.Leave();
      
      return retVal;
    }
};

CCriticalSection shared_heap::_criticalsection;
HANDLE shared_heap::_sHeap  = NULL;
UINT   shared_heap::_sCount = 0;

この shared_heapクラスは単純に静的関数を集めたもので、インスタンス化しても意味はないのでコンストラクタとデストラクタはプライベートにしています。実際には、各静的メンバ関数およびカスタムデリータの定義(.h)と実装(.cpp)を分ける必要がありますし、静的メンバ変数の初期化は実装側(.cpp)に書く必要もありますねぇ・・・。

で、まぁ、要点はshared_heap::alloc()で、HeapAlloc()APIをコールして得たメモリブロックのポインタをカスタムデリータとともにshared_ptrのコンストラクタに渡して、shared_ptrを返しています。スレッドセーフにするためクリティカルセクションを使用しています。

非常に単純なコードですが、これで廃棄処理コードを書く手間が省けます。shared_ptr 万歳!(^_^;;;

//使用例
#include "shared_heap.h"
int _tmain(int argc,_TCHAR **argv)
{
  shared_heap::return_type sptr = shared_heap::alloc(512);

  //なんかの処理

  return 0;
}

※僕の備忘録ですので、多々間違いがあるかもしれません。誰もいないと思いますが、そのままをコピペして使用するのは危険です。ご了承を。