Life is Real.

ぐだぐだと。

‘Windows API’ カテゴリーのアーカイブ

モニターの電源を切れ

投稿者: なかがわ : 2009年 10月 17日

今日、iPod Touchの車載用FMトランスミッター兼充電器買ってきました。エンジンの始動とともに音楽が始まるところは便利です(^^

バッファローコクヨ・FMトランスミッター

雑音(ノイズ)も入るのかな~、と思ってましたが、そんなこともなく、なかなかです。明日のドライブに重宝しそうです(^^

で、本題です。

この投稿の続きを読む »

カテゴリー: Programing..., Windows API, 日記 | コメントする »

VC++ 2008EE で COMサーバーをカンタンに作成

投稿者: なかがわ : 2009年 6月 26日

備忘録のエントリです。

フリーの開発環境でラクにCOMサーバーを作ろう、という主旨です(^^;;;

キーワードは「C++属性」です。この属性を利用することで手作業であっても退屈なメンドクサイ、コードの大部分(ほとんど)が自動的に作成されます。コーディングするのは、実装するインターフェイスのメソッドの中身だけ。簡単です。ただ・・・ATLだけはどうしても必要です。

しかし・・・Visual Studio 2008 Express EditionにはATLは付属していません。

ATLのヘッダーファイルとライブラリファイルをどこぞから調達しなくてはいけませんが、幸いなことにマイクロソフトがこれまた無料で配布しているWDK(Windows Driver Kit)をインストールすることにより利用することが可能です。WDKはかつては DDK(Driver Developper Kit)と呼ばれていたモノです。WDKにはVC++2008と同じコンパイラ・リンカなどの開発ツール、ATL/MFCやWindowsでのカーネルモードドライバを作成するために必要なすべてのファイル・ドキュメントが同梱されています。

無料で手に入れることができるのですが・・・一点、不安なことがあります。

それは・・・Visual Studio 2008 Express Editionと、WDK内の構成ファイル(ATLヘッダーファイルとライブラリファイル)を混ぜてコードをビルドするのがライセンス的に良いのか駄目なのか・・・等は、調査不足で分かりません。Visual Studio 2008 Express Editionで作成したアプリケーションは商用・非営利を問わず配布できたかと思いますが、WDKはそのあたり分かりません。まぁ非営利・個人的な利用なら問題はないかと思いますが・・・。

(WDKのダウンロードにはWindows Liveやマイクロソフト・パスポートのアカウントが必要です)

で、話を本題に戻します。 このWDKをインストール後、VC++ 2008 Express Editionのディレクトリ設定にこれらのパスを追加登録してあげればATLを利用したアプリケーション開発が可能となります。ただし、ATL関連ファイルがあるというだけで、便利なプロジェクト・ウィザードはないので全部手作業です(^^;;; やっぱりラクはできません(笑)。

さて、COMサーバーの実際の手順なんですが・・・これは日本語ドキュメントが既にあります(^^
MSDNライブラリの「COM属性によるCOM DLLの開発」です。

サンプルがありますのでこれを自分用に修正していけばいいだけ。僕は以下のようにMyServerというCOMオブジェクトをテストとして作成することにしました。見れば分かりますが・・・[ ] でくくっているところが属性と呼ばれるマイクロソフトの独自の拡張です。かなり違和感のあるコーディングです。が、これをコンパイルすることで必要なファイル、足りないコードを自動生成してくれます。理屈は分かりません(笑)  ある程度理解するにはやはりCOM/ATLの知識が必要です。魔法(ブラックボックス)として扱うのがよさそうです。

/*
 compiler command line :
   cl /LD MyServer.cpp /link /TLBOUT:MyServer.tlb
*/

#define STRICT
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif
#define _ATL_ATTRIBUTES
#define _ATL_APARTMENT_THREADED
#define _ATL_NO_AUTOMATIC_NAMESPACE
#include <atlbase.h>
#include <atlcom.h>
#include <atlwin.h>
#include <atltypes.h>
#include <atlctl.h>
#include <atlhost.h>
using namespace ATL;

//#include <windows.h>
//#include <comutil.h>

// DllMain,DllCanUnloadNow,DllRegisterServer and DllUnregisterServer
[ module(dll, name = "MyServer", helpstring = "MyServer 1.0 Type Library") ];
[ emitidl ];

/////////////////////////////////////////////////////////////////////////////
// IMyServer
[
   object,
   oleautomation,
   dual,
   helpstring("IMyServer Interface"),
   pointer_default(unique)
]
__interface IMyServer : IDispatch
{
   //メソッドを定義
   HRESULT CurrentDirectory([out,retval]BSTR *sPath);
};

/////////////////////////////////////////////////////////////////////////////
// CMyServer
[
   coclass,
   threading(apartment),
   vi_progid("MyServer.Utility.1"),
   progid("MyServer.Utility.1"),
   version(1.0),
   helpstring("MyServer Class")
]
class ATL_NO_VTABLE CMyServer :
   public IMyServer
{
public:
   CMyServer()
     {
       //コンストラクタ
     }

  //メソッドの実装
  HRESULT CurrentDirectory(BSTR *sPath)
    {
      WCHAR pBuffer[MAX_PATH+1] = {0};
      ::GetCurrentDirectoryW(MAX_PATH+1,pBuffer);

      *sPath = ::SysAllocString(pBuffer);

      return S_OK;
    }
  DECLARE_PROTECT_FINAL_CONSTRUCT()
  HRESULT FinalConstruct()
    {
      return S_OK;
    }

  void FinalRelease()
    {
      return;
    }
};

IMyServerインターフェイスを定義して、CMyServerクラスで実装。インターフェイスメソッド、CurrentDirectory()を定義。これは現在のカレントディレクトリを文字列で返すメソッドをサンプルとして定義しました。必要に応じてインターフェイスやメソッドを増やしたい場合もチョチョッと手を加えるだけで済みます。

んで、これだけのコードで完璧なオートメーションサーバーができてしまいます。属性の詳細は・・・あまり知る必要はないかと。詳細はかなり難解です。何度も書きますが、理解するにはCOMとかATLに関する中級~上級レベルの知識が必須かと。ただ公開するものの実装さえしてやればいいので、あとのCOMに関連するおきまりのコードはコンパイラとリンカが勝手にやってくれますのでかなりラクです。従来ならIDLファイルを書いて、IDispatch,IUnknowを実装して、レジストリへの登録処理を書いて・・・などが必要でしたが・・・。

このファイルをたとえば  cl /LD MyServer.cpp /link /TLBOUT:MyServer.tlb  みたいにコンパイルしてやればインプロセスサーバー(DLL)、タイプライブラリが生成されます。

あとはRegsvr32.exe でDLLファイルを登録し、以下のようなテストコードで動作確認。

===MyClient.wsf===
<?xml version="1.0" encoding="Shift-JIS"?>
<job id="myScript">
<script type="text/jscript">
<![CDATA[
try
{
  var obj = new ActiveXObject("MyServer.Utility.1");
  WScript.Echo(obj.CurrentDirectory());
}
catch(e)
{
  WScript.Echo(e.message);
}
]]>
</script>
</job>

パフォーマンスにシビアなコードはC++で実装して、あとはC#やJScriptでお手軽に・・・というような用途にいいかもです。

カテゴリー: C++, Programing..., Windows, Windows API | コメントする »

簡易メモリアロケータ

投稿者: なかがわ : 2009年 3月 20日

最近面白そうな機能満載の携帯がいっぱい出てきて、意味無く携帯を新しくしたいです。どっちかっていうと、時間潰しができて遊べる携帯(^^;;; 早くDOCOMOからiPhone出てくれないかな~。坂本龍一がCMやってたSamsungも良いなぁ。面白そうなやつは全部ソフトバンクなんだなぁ~。DOCOMOももっと変わったもん出して欲しいなぁ。

そんなことはさておき、

ローカルスコープ内で、使い捨てみたいな使い方をするような小さなメモリを確保するとき、たとえば、Win32APIでなんらかの文字列を受け取るときのバッファとか、いちいちnewでメモリ確保して、使い終わったらdeleteして・・・が面倒くさい。特にC#を覚えてnewしっぱなしでdeleteする必要がない言語に慣れきってしまうとC++は非常にやりづらい。

と、かなり前に思って、以前ずっとかなり前にWin32APIの勉強のため書いていた簡易メモリアロケータを引っ張り出してきて、ちょこっと手直し。

正直、メモリアロケータなんて大層なもんじゃなくて、コンストラクタでnewしてメモリを確保して、スコープから抜けるとデストラクタでdeleteする、という超簡単なもん。

手直ししたのは、コンストラクタとデストラクタでメモリの確保と解放する際に、newとdelete演算子を使わず、Windowsの Heap系のAPIで置き換えたことと、& 演算子をオーバーロードして、内部バッファのポインタを返すようにしたことの2点。Advanced Windows のメモリ管理を参考に参照カウンタも付けてみました(^^;

/*
  若干修正(2009/3/20)
  メモリ確保は一回だけ(2009/3/21)
  キャスト演算子のオーバーロード追加(2009/3/21)
*/
#define HEAP_INITIAL_SIZE 0
#define HEAP_MAX_SIZE 0
#include <windows.h>
class CAllocator
{
private:
  //コピーと代入は禁止!
  CAllocator &operator=(const CAllocator& rhs){}
  CAllocator(const CAllocator& src){}

  //static なヒープハンドルと参照カウンタ
  static HANDLE s_hHeap;
  static UINT s_cbRef;

  LPVOID m_lpBlock;

  void Init()
    {
      if(s_hHeap == NULL)
        s_hHeap = HeapCreate(HEAP_NO_SERIALIZE,
                             HEAP_INITIAL_SIZE,
                             HEAP_MAX_SIZE);
    }

public:
  //コンストラクタとデストラクタ
  CAllocator() //
       :m_lpBlock(NULL)
    {
      Init();
    }

  CAllocator(UINT size)
       :m_lpBlock(NULL)
    {
      Init();
      Alloc(size);
    }

  virtual ~CAllocator()
    {
      if(m_lpBlock != NULL)
        {
          if(TRUE == HeapFree(s_hHeap,HEAP_NO_SERIALIZE,m_lpBlock))
            {
              m_lpBlock = NULL;

              if(s_hHeap != NULL && --s_cbRef == 0)
                {
                  if(TRUE == HeapDestroy(s_hHeap))
                    s_hHeap = NULL;
                }
            }
        }
    }

  //プライベートヒープからメモリ確保
  void Alloc(UINT size)
    {
      if((m_lpBlock == NULL) &&
         ((m_lpBlock = HeapAlloc(s_hHeap,HEAP_NO_SERIALIZE,size)) != NULL))
        s_cbRef++;
    }

  LPVOID operator&()
  LPVOID operator*()
    {
      return m_lpBlock;
    }

  operator LPVOID()
    {
      return m_lpBlock;
    }
};

HANDLE CAllocator::s_hHeap = NULL;
UINT CAllocator::s_cbRef = 0;

ほとんどサンプルと同じになっちゃった(^^;

具体的には・・・

CAllocator alloc(MAX_PATH); //MAX_PATHバイトのメモリ確保
CAllocator allocs[5] = {100,100,100,100,100}; //配列とか、
CAllocator alloc; alloc->Alloc(100); //などでメモリ確保
CAllocator alloc; alloc.Alloc(100); //などでメモリ確保

メモリブロックのポインタを得るには、*演算子。

LPSTR lpStr = reinterpret_cast<LPSTR>(*alloc);

スコープを抜けるとデストラクタがコールされて、メモリが解放。参照数がゼロになると、プライベートヒープハンドルも解放。

修正すべき欠点は、スレッドセーフじゃない。複数スレッドで使用すると参照カウンタとかで競合が起きるし。大きなメモリ(たとえば1MBとか)を確保したいときとかは、VirtualAlloc系を使うべき、らしい。
まぁ・・・スレッドをおこすようなプログラムは複雑になればなるほどC#の方が安全で簡単なんで。

追記) &演算子で確保したメモリのポインタをリターンしてますが、スコープを抜けると問答無用でメモリ解放してしまうので、気をつけないといけません。CAllocatorを動的に確保すればいいんでしょうけど、それだと意味がなくなる(^^;

これをベースに実用的に使えるまで改良していきたいと思います。


8月30日、ところどころ修正しました。

カテゴリー: C++, Windows API | コメントする »

寒すぎですねぇ

投稿者: なかがわ : 2009年 1月 12日

数年前なら平気で撮影会とか行ってましたが(笑)

原点回帰して、C++とWin32APIで組むのが最近はまりつつあります。まぁ、当たり前ですけど、同じ機能のものをC#やPerlとPerlモジュール、WSHで組めば圧倒的に早くバグがない安全なものが完成するんですけど、家で使うツールぐらいはねぇ・・・少々バグがあっても無問題だし・・・ってことで、ほとんど忘れかけていたC++を持ち出して夜遅くまでエディタとデバッガと睨めっこです。

昨日から書き始めたのが、

  • クリップボードの内容(CF_TEXT)をファイルに書き出す。
  • テキストファイルの内容をクリップボードに転送する。

ただ、これだけ。非常に簡単。

何に使うのかというと・・・、ブラウザなどでテキストをコピーしてどっかに保存するとき・・・

  1. デスクトップを右クリック→新規作成→テキストドキュメント でファイル作成
  2. 作成したファイルをエディタで開いてペースト(貼り付け)
  3. 保存する。

という、一連のステップが非常にメンドクサイ(^^;; ただ、一回だけなら・・・別に良いんだけど・・・。

というわけで・・・レジストリを弄って「デスクトップを右クリック→新規作成」のメニューに「クリップボード」項目を作成。これが選択されたら、もしクリップボードにテキスト形式(CF_TEXT)のデータがあれば、新規にファイルを作成してそこにペースト&保存するようにした。

e5908de7a7b0e69caae8a8ade5ae9a-1

プログラム的には、一個関数を作ればいい程度のものですが、再利用したいので強引にC++風にする。

class CClipboardText
{
  //コピーコンストラクタと代入演算子は使用禁止
  CClipboardText(const CClipboardText& rhs);
  CClipboardTexT& operator=(const CClipboardText& rhs);

protected:
  LPBYTE lpData; //内部データのためのバッファ
  size_t cbLen; //バッファのサイズ

public:
  //コンストラクタとデストラクタ
  CClipboardText();
  virtual ~CClipboardText();

  //公開メソッド
  virtual bool Save(LPTSTR lpOut);
  virtual bool Load(LPTSTR lpIn);
}

.
要は、コンストラクタで Win32 APIのOpenClipboard()とGetClipboardData()を呼んで内部データバッファにデータを読み出し、Saveメソッドでファイルに書き出す。デストラクタで内部データバッファの解放とWin32APIのCloseClipboard()をコールするだけ。
あとは、適当なタイミングでインスタンスを作成して、save()すればいい。

//クリップボードのテキストデータをファイルに書き出す。
//使い捨て?のオブジェクトなので変数を用意せず、無名のまま使用(^^;
CClipboardText().Save(TEXT("ファイル名"));

.
まだLoadメソッドが未実装。今日中に完成して連休は終わりです(^^;;;

追記)
・・・と、思ったけど、ContextmenuHandlerにしてシェルエクステンションで実装する方が、よりスマートで、いいかもしれない。でもVistaだとエクスプローラ・シェルがさらに重くなりそうな悪寒が・・・。

カテゴリー: C++, Windows, Windows API, 日記 | コメントする »