カテゴリ:C/C++/Win32( 131 )

e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

エクスプローラ上でのファイル等のドラッグ&ドロップと同等のドラッグアンドドロップを開始する方法


エクスプローラ上でのファイル等のドラッグ&ドロップと同等のドラッグアンドドロップを開始する方法を、MFCを使用して実装する。
リストコントロールに登録されたファイルのドラッグアンドドロップが出来るようにした。


void CXXXListView::InitiateDragAndDropFiles()
{
    CListCtrl& list = GetListCtrl();

    // リストコントロールからファイルのリストを作成
    std::vector files;
    POSITION pos = list.GetFirstSelectedItemPosition();
    while(pos)
    {
        int nItem = list.GetNextSelectedItem(pos);
        // ここでは、各リストアイテムが CFileAttributes というクラスをデータとして持っており、
        // そのクラスが、ファイルのパスを提供する事を前提に作成しています。
        CFileAttributes* pAtt = (CFileAttributes*)list.GetItemData(nItem);
        if(pAtt)
        {
            CString strFilePath = pAtt->GetPathName();
            int nLen = strFilePath.GetLength();
            const char* top = strFilePath;
            files.insert(files.end(), top , top + nLen);
            files.push_back(0);    // 文字区切りとして、NULL文字(\0)を挿入します。
        }
    }
    files.push_back(0);    // ファイル名リストの終端は、 NULL NULL (\0\0) にします。

    // DROPFILES の データ構造は、
    // DROPFILES構造体と、構造体の先頭から pFilesバイト の位置から始まるファイル名リスト
    // から構成されているらしい。
    // ちなみに、pFilesを大きくすると、隙間に他のデータを埋め込む事が出来るっぽい。
    // それに新しくCFのIDを付ければ、HDROP互換のまま、お手軽にファイル+αの情報を
    // 伝えることが出来る。
    
    // グローバルメモリを割当
    HDROP hDrop = (HDROP)::GlobalAlloc(GHND, sizeof(DROPFILES) + files.size());
    if (hDrop == NULL)
        return;

    // グローバルメモリをロックして、ポインタを取得
    LPDROPFILES pDropFile = (LPDROPFILES)::GlobalLock(hDrop);
    pDropFile->pFiles = sizeof(DROPFILES);    
    pDropFile->pt.x = 0;
    pDropFile->pt.y = 0;
    pDropFile->fNC = FALSE;
    pDropFile->fWide = FALSE;

    // ファイル名リストをコピー
    char* lpNams = (char *)(pDropFile);
    lpNams += pDropFile->pFiles;
    ::memcpy(lpNams, &files[0], files.size());
    ::GlobalUnlock(hDrop);

    // ドラッグを開始
    COleDataSource oleDataSrc;
    oleDataSrc.CacheGlobalData(CF_HDROP, hDrop);
    DROPEFFECT dropEffect = oleDataSrc.DoDragDrop();

    if(dropEffect == DROPEFFECT_MOVE)
    {
        // なぜか、dropEffect が正しく返ってこない・・・不思議
    }
}

[PR]
by isoq | 2010-08-11 13:23 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

ImageJ の Analyze 形式のヘッダーファイルの解析 更新


enum ImageType
{
IM_P_8 = 0x0000,
IM_G_8 = 0x0002,
IM_G_16 = 0x0004,
IM_G_32 = 0x0010,
IM_RGB_24 = 0x0080
};


struct AnalyzeHDR
{
//0x00000000
unsigned char head[4];// 00 00 01 5C
char desc[12];// 長さが不明なので、unknown10 まで伸る可能性あり

//0x00000010
unsigned char unknown10[16];

//0x00000020
unsigned char unknown20[10];
unsigned short x;// xピクセル数
unsigned short y;// yピクセル数
unsigned short z;// zピクセル数

//0x00000030
unsigned char unknown30[8];
char unit[8];

//0x00000040
unsigned char unknown40_1[6];
unsigned short type;// 画像のタイプ
unsigned short depth;// ビット数
unsigned char unknown40_2[6];

//0x00000050
float px;// xピクセルサイズ
float py;// yピクセルサイズ
float pz;// zピクセルサイズ
unsigned char unknown50[4];

//0x00000060
unsigned char unknown60[16];

//0x00000070
unsigned char unknown70[16];

//0x00000080
unsigned char unknown80[16];

//0x00000090
unsigned char unknown90[16];

//0x000000A0
unsigned char unknownA0[16];

//0x000000B0
unsigned char unknownB0[16];

//0x000000C0
unsigned char unknownC0[16];

//0x000000D0
unsigned char unknownD0[16];

//0x000000E0
unsigned char unknownE0[16];

//0x000000F0
unsigned char unknownF0[16];

//0x00000100
unsigned char unknown100[16];

//0x00000110
unsigned char unknown110[16];

//0x00000120
unsigned char unknown120[16];

//0x00000130
unsigned char unknown130[16];

//0x00000140
unsigned char unknown140[16];

//0x00000150
unsigned char unknown150[12];
};


ImageJ の Analyze 形式ファイルを扱う事になったんだけど、
ヘッダーの情報が見つからなかったので、解析してみた。

不明なデータがたくさんだけど、なんとか必要な部分だけは取り出せた。
[PR]
by isoq | 2010-07-08 19:19 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

エンディアンを変換するためのコード

#include
void ReverseBytes(void *start, int size)
{
char *begin = (char*)start;
char *end = begin + size;
std::reverse(begin, end);
}
#define SwapEndian(a) ReverseBytes(&a, sizeof(a));


Big Endian -> Little Endian
Little Endian -> Big Endian

両方とも、いけます!
というか、データの並び方が違うだけだから、解釈の違いでしか無いから、
当たり前と言えば当たり前。
[PR]
by isoq | 2010-07-08 19:17 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

Visual Studio 2010 が MSDN で公開されました

まだ、英語版だけですが、
4月20日に日本語版が公開されるそうです。
楽しみですね。
[PR]
by isoq | 2010-04-14 08:32 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

Windows サービスを作成する方法 (Visual Studio 2008/2005 の Visual C++/CLR を使用)

デル株式会社


以前は、面倒な手順が必要だったWindows サービス プログラムを作成する方法も、
.NET Framework と 対応開発環境の登場によって、かなりスマートに掛けるようになった。

MSDNなどを見れば、すぐに出来ると思うけど、簡単にサービス プログラムをビルド、インストールする手順を覚え書き。


①Widnows サービス プロジェクトを作成する

新規プロジェクト作成のウィザードを開く。

プロジェクトの種類=『Visual C++』の『CLR』を選択し、『Windows サービス』を選択する。

プロジェクト名を入力して、『OK』ボタンをクリックして、プロジェクトを作成する。

②サービスのプロパティを設定する

ソリューションエクスプローラから、作成された XxxxxWinService.h をダブルクリックで開き(デザインビュー)、右クリックから「プロパティ」を選択。

プロパティページから、最低限以下の項目を設定する。

ServiceName - このサービスのサービス名
StartType - 自動/手動/無効など

その他、必要なプロパティを変更する。
デフォルトでは、開始・終了ができる標準的なサービスの設定がされている。

③インストーラを追加する

デザインビューを右クリックして、『インストーラの追加』を選択する。

ProjectInstaller クラスが追加され、デザインビューに ServiceProcessInstaller と ServiceInstaller のインスタンスが追加される。

④インストールの動作を変更する

ServiceInstaller のインスタンス(serviceInstaller1)を選択し、プロパティーページを表示する。

ServiceName が、サービス自体のServiceName(XxxxxWinService.hのプロパティ)と一致している事を確認する。
サービス名を変更した場合は、ここも変更する必要がある。

ServiceProcessInstaller のインスタンス(serviceProcessInstaller1)を選択し、プロパティーページを表示する。

ここで、必要な設定を変更する。

Account - サービスを実行するアカウント

⑤処理内容を記述する

XxxxxWinService.h のコードを表示して、OnStart() と OnStop() を編集して、サービスの開始・終了処理を記述する。

サービスは、一定時間以内に処理をサービスマネージャーに戻す必要があるので、ここでは、スレッドや外部プロセスの起動/終了処理のみを記述すること。

サービスが実際に行う処理は、他のスレッドや外部の実行ファイルに記述する。

ここで例外を発生させたりすると、サービスマネージャーから制御不能と怒られますので、注意が必要。

⑥ビルドする

ビルドして実行ファイルを作成します。

⑦サービスをインストールする

作成したサービスは、自己インストールができるようになっているので、次のコマンドラインでインストールが可能。

Xxxx.exe /Install

⑧サービスを起動する

サービスを起動するには、システムを再起動するか、つぎのコマンドを入力する。

net start "XxxxWinService"

ここで、"XxxxWinService" はサービス名。

Windowsの管理ツールのServiceからも起動・終了ができる。

⑨サービスを停止する

サービスを停止するには、次のコマンドを入力する。

net stop "XxxxWinService"

ここで、"XxxxWinService" はサービス名。

Windowsの管理ツールのサービスからも起動・終了ができる。

⑩サービスをアンインストールする

作成したサービスは、自己アンインストールができるようになっているので、次のコマンドラインでアンインストールが可能。

Xxxx.exe /Install /u

⑪サービスで実行しているプログラムのウインドウを表示できるようにする

サービスは、デフォルトでは、デスクトップとの対話を許可されていない。つまり、デスクトップにウインドウなどを表示する事ができない。(やろうとすると、かなり複雑な手続きが必要になる。)また、サービスが起動した外部プロセスも同じ制約課せられる。このため、次の手順で、マニュアルでデスクトップとの対話をサービスに許可する必要がある。

・管理ツールからサービスを開く
・XxxxWinSerice のプロパティを表示する
・ログオン タブの『デスクトップとの対話をサービスに許可』をチェックする
・『OK』をクリックする
・サービスを再起動する


必要な環境:

CLRによる Windows サービス プログラムの実行・インストール・アンインストールには、.NET Frameworkが必要。



参考URL

MSDN - .NET Framework 開発用 Visual Studio プログラマーズ ガイド - 方法 : Windows サービスを作成する

マイクロソフト サポート オンライン - [SDK32] サービスからアプリケーションデスクトップへのアクセス

@IT総合トップ > 旧@IT会議室 > Insider.NET > C#のWindowsサービスからのProcessによるEXE起動について

マイクロソフト サポート オンライン - CreateProcessAsUser() 関数とウィンドウ ステーションおよびデスクトップ
[PR]
by isoq | 2010-03-31 01:41 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

プロシージャ エントリ ポイント _except_handler4_common がダイナミックリンクライブラリ msvcrt.dll ...

 iTunes Store(Japan)

「プロシージャ エントリ ポイント _except_handler4_common がダイナミックリンクライブラリ msvcrt.dll から見つかりませんでした。」

自家制作のフリーウェアで、問題が発生してしまいました。
このエラーが出たのです。

今回から、開発環境をWindows 7/Visual Studio 2008 へ移行した
矢先の出来事でした。

原因は、Visual Studio のセットアップ プロジェクトが、OS依存のサテライトDLLを
セットアップに含めてしまっていたためでした。

今回の場合、oledlg.dll というDLLが含まれていて、インストーラによって
インストール先フォルダへSide-by-Side配置されるのですが、
このDLLが内部で、Windows 7に含まれるバージョンの msvcrt.dll を
要求してしまい、エラーが出てしまうというものでした。

Windows Vista以降のPCで開発を行い、Visual Studioのセットアップウィザードで
インストーラを構築すると、参照しているDLLファイルなどがインストールファイルとして追加されます。
OSのバージョン固有のDLL等も含まれてしまうため、不要なDLLは、手動で削除しないといけない。

たとえば、Windows XP でセットアッププロジェクトを作成し、
その後、セットアッププロジェクトを Windows 7 でビルドする場合。
Windows XPで見つかった依存関係ファイルを セットアッププロジェクトをビルドすると、
Windows XP依存のDLLがセットアップに含まれてしまう。
この場合、たいていはその後のWindows Vista や 7 でも実行可能。

が、Windows 7 で同じセットアッププロジェクトをビルドすると、
今度はWindows 7に依存したDLLがセットアップに含まれてしまう。
この場合、Windows XPにインストールしても、正しく動作しない可能性が高い。

しかも、セットアッププロジェクトは、勝手に依存するDLLをセットアップに含めてしまうが、
そのDLLのライセンスによっては、再配布が禁止されている可能性がある。
Visual Studioを持っていれば、開発環境の再配布可能ファイルは問題ないが、
OS依存ファイルの配布は、ライセンスに引っかかってしまうだろう。

この手のDLLの除外は、自動ではできないのだろうか。。。
[PR]
by isoq | 2010-03-20 00:18 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

list iterator not dereferencable の理由



std::list を使用していて、list iterator not dereferencable という意味不明のエラーが出た。
ランタイムのアサーション フェイリヤー(Assertion Failure) で、デバッグも不可能。。。

いろいろ考えてコードを直してみても、いっこうに修正ができる気配がない。

結局、原因は、次のコードだった。

std::list::iterator it2 = objList.end();
Obj* obj = *it2;
it2--;


end() で取得したイタレータは、データ要素を指していないで、
最後のデータ要素の1つ後ろを指している!

ということを忘れていました。。。

書いている途中は覚えていたんだけど、
書き終わった頃にはすっかり。。。

簡単な原因でも、エラーの意味が不明だと、解決に時間がかかる。。。
慣れだけど。
[PR]
by isoq | 2010-03-02 11:56 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

ドライブアイコンを取得してリストコントロールに表示する

// 次の2行は、通常ヘッダファイルに含めます。
CImageList m_imageList; // イメージリスト
CListCtrl m_listCtrl; // リストコントロール

// イメージリストを作成し、リストコントロールにセットする

m_imageList.Create(32, 32, ILC_COLOR32|ILC_MASK, 10, 10);
m_listCtrl.SetImageList(&m_imageList, LVSIL_NORMAL); // ※1

// ドライブアイコン(フォルダでも可:ファイルの場合は、フラグを変更の事)
// を表示したいパスを指定

CString strPath = "c:\\";

// アイコンを取得する

HICON hIcon = NULL;
SHFILEINFO sfi;
if(SHGetFileInfo(strPath, FILE_ATTRIBUTE_DIRECTORY, &sfi, sizeof(SHFILEINFO), SHGFI_ICON) != 0)
{
    // sfi.hIcon にアイコンハンドルが戻ってくる
    hIcon = sfi.hIcon;
}

int nImage = -1;
if(hIcon)
{
    // アイコンをイメージリストに追加
    nImage = m_imageList.Add(hIcon);
    
    // 使い終わったアイコンリソースを破棄
    DeleteObject(hIcon);

}

// リストコントロールに登録
nItem = m_listCtrl.InsertItem(++nItem, strPath, nImage);

// 上のちょっと回りくどい処理は、
// アイコンが取得出来なくても、
// イメージリストへの追加が出来なくても、
// とりあえず、リストコントロールにテキストだけでも
// 登録できるようにしています。



※1
CListCtrl::SetImageList() に渡す、引数によって、どのビューで使用されるアイコンか
設定する事が出来る。らしい。

LVSIL_NORMAL --- アイコンビュー
LVSIL_SMALL --- レポートビュー

これ、しっかりドキュメントしてくれ!!!!!!!!
[PR]
by isoq | 2010-01-12 17:06 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

GUI プログラムでコンソールを使用する(標準出力)

GUIプログラムでは、コンソールが無いため、

printf() でデバッグ情報などを出力することができない。

出せると、かなり便利なんだけど、、、

ということで、出し方。

AllocConsole() で、コンソールを表示して、

freopen() で、標準出力をコンソールにリダイレクトして、

printf() で、コンソールに文字列を出力できるようになる。

使い終わったら、 FreeConsole() でコンソールを破棄。

コンソールは、OSで管理されているので、

2回割り当てようとしても、失敗して AllocConsole() が FALSE を返すだけ。

結構気軽に使える。

使いやすいように、クラスにまとめてみました ↓

--- StdoutConsole.h ファイル
#pragma once

class CStdoutConsole
{
public:
    static bool Allocate()
    {
        if(m_stdfp == 0)
        {
            if(AllocConsole())
            {
                m_stdfp = freopen 括弧始め "CON", "w", stdout); // 標準出力の割り当て
                if(m_stdfp)
                {
                    return true;
                }
                else
                {
                    FreeConsole();
                }
            }
            return false;
        }
    }

    static bool Free()
    {
        if(m_stdfp != 0)
        {
            fclose(m_stdfp);
            m_stdfp = 0;

            if(FreeConsole())
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    
    static bool IsConsole()
    {
        return m_stdfp != 0;
    }
private:
    CStdoutConsole(){}    // 構築・破棄を不可能にする。スタティックのみを使わせるため。
    ~CStdoutConsole(){}
    
    static FILE* m_stdfp;
};


--- StdoutConsole.c ファイル

#include <Windows.h>
#include <stdio.h>
#include "StdoutConsole.h"
FILE* CStdoutConsole::m_stdfp = 0;


※括弧始め を ( に書き換える必要があります。
(エキブロの制限で open と 括弧を続けて書けないため)

ちなみに、クラスにしたのは、ファイルポインタのデータの置き場に困ったため。

関数だけだと、生成・破棄のときに同じファイルポインタを使わないといけないから。

グローバル変数は使いたくないし、、、

モジュールレベル変数(static)にするだけじゃ、芸が無いし、、、

ということで、パブリック・スタティックなメンバだけを公開したクラスにしてみました。

ちゃんちゃん。
[PR]
by isoq | 2010-01-04 19:55 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

strtok() のスマートな使い方

C の標準ライブラリに strtok() という関数がある。

文字列を区切り文字で区切って、要素ごとに取得できる便利な関数ではあるのですが、

スレッドセーフではない事と、オリジナルの文字列を改変してしまうため、

世間では、その使用をあまりお勧めされない関数です。

でも、便利は便利なので、小さなコンバータなどの簡単なプログラムでは良く使います。

(しっかりとしたプログラムでは、同じような処理を行う関数を別途作成して使用しています。)

で、Web検索でよく出てくる使い方(コード)を見ると、

「1回目に文字列を指定して、2回目からはNULLを指定する。」

という、オーソドックスな解説とサンプルコードがすぐに見つかりますが、

これをまじめに実装すると、同じ処理を2回書かなくてはいけなくなり、

コードが冗長(=見苦しくなって)してしまいます。

そこで、私は、次のように使用しています。

// buff に分割対象の文字列が入っている
char* t = buff;
while(t = strtok(t, "\t"))
{
    // t を何かしらに使う
    ・・・
    t = NULL;
}


まず、対象の文字列の先頭アドレスを指すポインタを作って、

最初にそのポインタを strtok() に渡します。

そして、そのポインタで、strtok() から、結果を受け取ります。

次からは、そのポインタを NULL に初期化して strtok() に渡します。

こうすると、コードの冗長化が防げます。

コードの冗長化を防いで、可読性を上げるのは、

こういった簡単な書き方の工夫の積み重ねです。
[PR]
by isoq | 2010-01-04 15:45 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇