質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.78%

GDI+での キーワードタグ削除について

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,061

score 2744

C/C++ でやっています。

基本的には MinGW でやっているのですが、

Jpegタグ で Windows用(?) の "キーワードタグ" ってありますよね。
( 呼び名がライブラリ等によって変わりますが。 )

画像や実行ファイル等 のファイルを閲覧するときに使う Explorer で

jpegファイル を右クリック -> "プロパティ(R)" を選択 -> "詳細" タブ を選択 -> "タグ"

で編集できるやつです。

これを編集できるソフトを作ろうと思い、調べてみたところ、

GDI+ というライブラリでできるようです。

ですが libファイルで VC++向けらしいので

VC++ で DLL化 し、MinGWで作成した実行ファイルからアクセスし使用する

という方法を取ろうと思っています。

一応最低限 ( タグ追加 ) はできたのですが、

タグ削除ができません。

"コメント"タグ は NULL を追加したら "削除"扱いにできるようなのでいいですが、
"キーワード"タグ が...

[ DLL側 ( 一部 ) ]

/* GDI+ で必要最低限のもの */
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
#include<string>
using namespace Gdiplus;
using namespace std;

/* ライブラリ内の関数をオープンにするためのもの */
#define FUNCTION_DEFINITION extern "C" __declspec(dllexport)

/* GDI+ をリンクする */
#pragma comment (lib, "gdiplus.lib")


#define MAX_TAG  2000


/* コール時に受け取る可能性のある種類 */
#define ID_KEYWORD_TAG        (0)
#define ID_COMMENT_TAG        (1)
#define ID_USER_COMMNET_TAG   (1)


/*
    マクロ GDIPLUS_INITIALIZE は GDI+を使うときに書く GdiplusStartupInput gdiplusStartupInput; ... で、
    マクロ GDIPLUS_UNINITIALIZE は GdiplusShutdown(gdiplusToken) とする。
*/


// 時数制限のため、プロトタイプのみの記載。
// char型配列からWCHAR型配列に変換
void CStringToWCHAR( const char *cStr, WCHAR *wStr );


// 時数制限のため、プロトタイプのみの記載。
// WCHAR型配列からchar型配列に変換
void WcharToCString( const WCHAR *wStr, char *cStr );


// 時数制限のため、プロトタイプのみの記載。
// GDI+ のヘルプファイルから流用。
// Helper function
void PropertyTypeFromWORD(WORD index, WCHAR* string);


// 時数制限のため、プロトタイプのみの記載。
// GDI+ のヘルプファイルから流用。
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);



bool WriteTag( WCHAR *JpgFilePath, int TagID, const char *str, WCHAR *NewjpgPathName )
{
   // Initialize GDI+.
   GDIPLUS_INITIALIZE;

   bool bStatus = false;

   Status stat;
   CLSID  clsid;
   WCHAR   wPropertyValue[6000];

   Bitmap* bitmap = new Bitmap(JpgFilePath);
   PropertyItem* propertyItem = new PropertyItem;

   // Get the CLSID of the JPEG encoder.
   GetEncoderClsid(L"image/jpeg", &clsid);



   if( TagID == KEYWORD_TAG )
   {
              CStringToWCHAR( str, wPropertyValue );
              propertyItem->id = KEYWORD_TAG;
              propertyItem->length = MAX_TAG*2;  // string length including NULL terminator
              propertyItem->type = PropertyTagTypeByte; 
              propertyItem->value = wPropertyValue;
   }
   else if( TagID == COMMENT_TAG )
   {
             CStringToWCHAR( str, wPropertyValue );
             propertyItem->id = COMMENT_TAG;
             propertyItem->length = MAX_PATH;  // string length including NULL terminator
             propertyItem->type = PropertyTagTypeByte; 
             propertyItem->value = wPropertyValue;
   }
   else
   {

   }
   bitmap->SetPropertyItem(propertyItem);

   stat = bitmap->Save(NewjpgPathName, &clsid, NULL);
   if(stat == Ok) bStatus = true;

   delete propertyItem;
   delete bitmap;

   GDIPLUS_UNINITIALIZE;
 return bStatus;
}


// これにアクセスする。そのままでもいいが、WCHAR型なので扱いにくいため、
// char型 -> WHCAR型 に変換して代理でやってくれるようにする。
FUNCTION_DEFINITION
bool WriteTagEx( const char *JpgFilePath, int iKind, const char *str, const char *NewJpgFilePath )
{
      WCHAR wJpgFilePath[MAX_PATH+1];
      WCHAR wJpgNewFilePath[MAX_PATH+1];
      int TagID;

      CStringToWCHAR( JpgFilePath, wJpgFilePath );
      CStringToWCHAR( NewJpgFilePath, wJpgNewFilePath );

      if( iKind == ID_KEYWORD_TAG )
      {
                TagID = KEYWORD_TAG;
      }
      else if( iKind == ID_COMMENT_TAG || iKind == ID_USER_COMMNET_TAG )
      {
                TagID = COMMENT_TAG;
      }
return WriteTag( wJpgFilePath, TagID, str, wJpgNewFilePath );
}

( 実際は main.cpp に ReadTagEx関数を、read.h に WriteTag関数のプロトタイプ, read.cpp に WriteTag関数の定義... と
分割しているが、すべて記述するのは大変(主に関係性が。)なので一つにまとめます。 )

[ MinGWで作成する実行ファイル側 ( 一部 ) ]

/*
    時数制限のため、簡略化する。

    CPath::... とあるのは 自作クラス ( ただし、static付き ) で ファイルパスからディレクトリパスを取得したりする。

    CSystem::... とあるのも自作クラス ( 同じくstatic ) で、システム関連操作を行う。

    CJpgTag は Read関数と Write関数, コンストラクタ ... とあるとします。
*/

bool CJpgTag::Write( const std::string JpgFilePath, const std::string tag ){

     string tag1; // 編集前のタグ
     string tag2; // 編集後のタグ
     string dir;  // 対象ファイルのディレクトリ
     string filename; // 対象ファイルのファイル名+拡張子
     string new1; // 一時ファイル
     int    AddOrDelete; // 追加か削除か


     // 同じクラスのメンバ。現在の値を取得。
     this->Read( JpgFilePath, tag1 );

     // 第2引数が empty の場合、"タグの削除" とみなす
     if( tag.empty() ){
             AddOrDelete = DELETE_TAG;
     }else{
             AddOrDelete = ADD_TAG;
     }

     if( AddOrDelete == ADD_TAG ){
            if( this->TagID == ID_KEYWORD_TAG ){
                   // キーワードタグがない場合は 引数の tag, ある場合は 既存のタグ+tag とする
                   if( tag1.empty() ){
                          tag2 = tag;
                   }else{
                          tag2 = tag1 + string( ";" ) + tag; 
                   }
            }else{
                   tag2 = tag;
            }

            // 一時ファイルのパスを生成する
            if( CPath::GetDirPath( JpgFilePath, dir ) ){
                   new1 = dir + string("\\temp.jpg");
            }else{
                   new1 = string("temp.jpg");
            }

            CPath::GetFileEx( JpgFilePath, filename );

            if( WriteTagEx( JpgFilePath.c_str(), this->TagID, tag2.c_str(), new1.c_str() ) == true ){
                   CSystem::Del( JpgFilePath );
                   CSystem::Ren( new1, filename );
                   return true;
            }else{
                   return false;
            }
     }else{
            tag2 = "";

            // 一時ファイルのパスを生成する
            if( CPath::GetDirPath( JpgFilePath, dir ) ){
                   new1 = dir + string("\\temp.jpg");
            }else{
                   new1 = string("temp.jpg");
            }

            CPath::GetFileEx( JpgFilePath, filename );

            if( WriteTagEx( JpgFilePath.c_str(), this->TagID, tag2.c_str(), new1.c_str() ) == true ){
                   CSystem::Del( JpgFilePath );
                   CSystem::Ren( new1, filename );
                   return true;
            }else{
                   return false;
            }
     }
}


という感じです。

動かしてみると、

追加のときや、 コメントタグ初期化 はうまくいくのですが、

キーワードタグ初期化のときは なぜか元のデータが残っています。

どうすればいいでしょうか。

[環境等]
言語: C/C++ ( WindowsAPI あり )
コンパイラ: ( 実行ファイル ) MinGW, ( DLL ) VC++ 2010 Express

宜しくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

propertyItem->lengthにMAX_PATHなどの固定値をセットしているのが気になります。
正確な文字長(ヌル文字含む)をセットしてはどうでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る