Drag and Dropをサポートするものとして最終的にFMXプログラムとして統合
まだ修正すべき部分がありますが、一応一段落とします。さて、フォームは、

こんな感じです。上図のTCheckBoxはIsChecked=trueが吉ですかね。さて、Unit1.hは、
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <FMX.Controls.hpp>
#include <FMX.Forms.hpp>
#include <FMX.Controls.Presentation.hpp>
#include <FMX.Memo.hpp>
#include <FMX.Memo.Types.hpp>
#include <FMX.ScrollBox.hpp>
#include <FMX.Types.hpp>
#include <FMX.StdCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE で管理されるコンポーネント
TMemo *Memo1;
TCheckBox *RemoveExif;
TLabel *Label2;
private: // ユーザー宣言
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
virtual void __fastcall DragDrop(const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point);
virtual void __fastcall DragOver(const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point, Fmx::Types::TDragOperation &Operation);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endifUnit1.cppは、
//---------------------------------------------------------------------------
#include <fmx.h>
#pragma hdrstop
#include <windows.h>
#include <System.SysUtils.hpp>
#include <filesystem>
#include <System.SysUtils.hpp> // For StrToDateTime, Exception
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
namespace fs = std::filesystem;
TForm1 *Form1;
#define SOI 0xFFD8
#define EOI 0xFFD9
#define APP1 0xFFE1
//TForm1 *Form1;
TFileStream* fp;
bool BigEndian;
bool status;
TDateTime dt;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Memo1->Lines->Add("working....");
}
bool CreateProcess(String cmdline)
{
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
// 実行ファイルと引数(1つの文字列にまとめる)
//wchar_t cmdLine[] = L"notepad.exe C:\\Temp\\test.txt";
if (CreateProcessW(
NULL, // 実行ファイル名(NULLなら cmdLine から取得)
cmdline.w_str(), // コマンドライン
NULL, // プロセスセキュリティ属性
NULL, // スレッドセキュリティ属性
FALSE, // ハンドル継承
0, // 作成フラグ
NULL, // 環境変数
NULL, // カレントディレクトリ
&si, // STARTUPINFO
&pi // PROCESS_INFORMATION
)) {
// プロセス終了を待つ場合
//WaitForSingleObject(pi.hProcess, INFINITE);
// ハンドルを閉じる
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
} else {
ShowMessage(L"CreateProcess に失敗しました。");
return false;
}
}
bool SetFileCreationTime(String filePath, TDateTime dt) {
// Convert SYSTEMTIME to FILETIME
SYSTEMTIME newTime;
DateTimeToSystemTime(dt,newTime);
FILETIME ft;
if (!SystemTimeToFileTime(&newTime, &ft)) {
return false;
}
// Open file with write attributes permission
HANDLE hFile = CreateFileW(
filePath.w_str(),
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
return false;
}
// Set creation time (leave last access and write times unchanged)
BOOL result = SetFileTime(hFile, &ft, NULL, NULL);
CloseHandle(hFile);
return (result != 0);
}
void ProcessFile(String fname)
{
String comm = "c:\\program files\\python311\\exiv2.exe";
comm += " -k -d e ";
comm += "\"" + fname + "\"";
//Form1->Label1->Text = comm;
if( CreateProcess(comm) )
Form1->Label2->Text = "success";
else
Form1->Label2->Text = "failed";
}
void Read16(TFileStream* p,unsigned short* k)
{
unsigned char u,l;
p->Read(&u,1);
p->Read(&l,1);
*k = u*0x100 + l;
}
void BinDump_Buffer(Byte* start, unsigned length)
{
Byte it;
Byte* p;
char obuf[100];
int i;
UnicodeString str;
UnicodeString sub;
p = start;
for( i = 0 ; i < length ; i++ ){
sub.sprintf(L"%02x ",*p++);
str += sub;
}
//Form1->Memo1->Lines->Add(str);
}
void Dump_Buffer(Byte* start, unsigned length)
{
Byte it;
Byte* p;
char obuf[100];
int i;
p = start;
for( i = 0 ; i < length ; i++ ){
it = *p++;
if( it == 0x0 )
obuf[i] = '0';
else
obuf[i] = it;
}
obuf[i] = 0x0;
//Form1->Memo1->Lines->Add(UnicodeString(obuf));
}
void Detect_Endian(Byte* start, unsigned length)
{
unsigned char *p = start;
BigEndian = false;
if( *p == 'M' && *(p+1) == 'M' )
BigEndian = true;
else if( *p == 'I' && *(p+1) == 'I' )
BigEndian = false;
else
ShowMessage("Endian error");
/*
if( !BigEndian )
Form1->Label2->Caption = "Little_Endian";
else
Form1->Label2->Caption = "Big_Endian";
*/
return;
}
unsigned conv(Byte* p)
{
unsigned retval = 0;
if( BigEndian )
retval = *p*0x100000 + *(p+1)*0x10000 + *(p+2)*0x100 + *(p+3);
else
retval = *(p+3)*0x100000 + *(p+2)*0x10000 + *(p+1)*0x100 + *p;
return retval;
}
void IFD_Dump(Byte* address)
{
Byte* start;
char *p;
struct a_tag {
unsigned short tag;
unsigned short tag_type;
unsigned count;
unsigned val2;
} tags[100];
unsigned short ntag;
UnicodeString o;
UnicodeString o2;
start = address;
address += 8;
if(BigEndian ){
ntag = 0x100*(*address);
ntag += *++address;
}
else {
ntag = *address++;
ntag += 0x100*(*address);
}
address++;
//Read16(fp,&ntag);
o.sprintf(L"%u",ntag);
//Form1->Memo1->Lines->Add("# of tags " + o);
for( int i= 0; i < ntag ; i++ ){
if( BigEndian ){
tags[i].tag = 0x100*(*address);
tags[i].tag += *++address;
}
else{
tags[i].tag = *address++;
tags[i].tag += 0x100*(*address);
}
address++;
if( BigEndian ){
tags[i].tag_type = 0x100*(*address);
tags[i].tag_type += *++address;
}
else {
tags[i].tag_type = *address++;
tags[i].tag_type += 0x100*(*address);
}
address++;
//tags[i].count = *(unsigned*)(address);
tags[i].count = conv(address);
address += 4;
tags[i].val2 = conv(address);
if( tags[i].tag == 306 && tags[i].tag_type == 2 ){ // should get null terminated string
p= (char*)(start + tags[i].val2);
//o2.sprintf(L"%s",p);
o2 = UnicodeString(p);
Form1->Memo1->Lines->Add(o2); //original format
TFormatSettings fs;
fs = TFormatSettings::Create(); // Initialize with defaults
//fs.DateSeparator = ':';
fs.ShortDateFormat = "yyyy:mm:dd";
dt = StrToDateTime(o2,fs);
//dt = StrToDateTime(o2); // exception?
Form1->Memo1->Lines->Add(DateTimeToStr(dt));
}
/*
else
o.sprintf(L"%d %u %u %lu %lu",i,tags[i].tag,tags[i].tag_type,tags[i].count,tags[i].val2);
Form1->Memo1->Lines->Add(o);
*/
address += 4;
}
}
void Parse_Exif(Byte* buffer)
{
Byte* ptr;
Dump_Buffer(buffer,6); // "Exif"\0\0
Detect_Endian(buffer+6,2); //TIFF header "MM" or "II"
BinDump_Buffer(buffer+8,2); // TIFF code 002a
BinDump_Buffer(buffer+10,4); // pointer to oth IFD
IFD_Dump(buffer+6);
}
void Parse_File(String filename)
{
//Form1->Memo1->Lines->Add(filename);
//Form1->Label1->Caption = filename;
unsigned short sig;
UnicodeString out,out2;
Byte* buffer;
bool exif = false;
//Memo1->Lines->Clear();
fp = new TFileStream(filename, fmOpenRead);
if( !fp ){
ShowMessage("file not found");
Application->Terminate();
}
//fp->Read(&sig,2);
Read16(fp,&sig);
out.sprintf(L"%04X",sig);
//Form1->Memo1->Lines->Add(out);
if( sig != SOI ){
ShowMessage("sig error ");
return;
}
//unsigned short sig;
unsigned short length;
UnicodeString output;
int i = 0;
do {
Read16(fp,&sig);
output.sprintf(L"%d %04X",i++,sig);
//Form1->Memo1->Lines->Add(output);
Read16(fp,&length);
output.sprintf(L"length %04X",length);
//Form1->Memo1->Lines->Add(output);
if( sig == APP1 ){
buffer = new Byte[length]; // was Byte(length)
if( !buffer ){
ShowMessage("can't allocate memory");
Application->Terminate();
}
fp->Read(buffer,length);
Parse_Exif(buffer);
exif = true;
break; // exif block found
}
fp->Seek(length-2,soFromCurrent);
}while( sig != EOI && i < 10 );
delete fp;
if( exif ) {
delete buffer;
//SetFileTimeStamp(filename,dt);
if( SetFileCreationTime(filename,dt) )
Form1->Memo1->Lines->Add("set to " + dt.toAnsiString());
if( Form1->RemoveExif->IsChecked )
ProcessFile(filename);
}
}
bool TouchFileTimestamp(String path)
{
Form1->Memo1->Lines->Add(path);
// at first extract exif.recordedtime
Parse_File(path); // where is data Memo1->Lines->Add(o2) in function
return true;
}
void DigDirectory(String path)
{
fs::path target_path = path.c_str();
try {
// directory_iteratorを使用してディレクトリ内を走査
for (const fs::directory_entry& entry : fs::directory_iterator(target_path)) {
// entry.path() でフルパスを取得し、filename() でファイル名のみを抽出
//std::cout << entry.path().filename() << std::endl;
//Form1->Memo1->Lines->Add( path + "\\" + entry.path().filename().c_str());
//Form1->Memo1->Lines->Add(entry.path().c_str());
TouchFileTimestamp(entry.path().c_str());
}
} catch (const fs::filesystem_error& e) {
// ディレクトリが存在しない場合などのエラー処理
//std::cerr << "エラーが発生しました: " << e.what() << std::endl;
ShowMessage("error");
}
}
void __fastcall TForm1::DragDrop(const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point)
{
TForm::DragDrop(Data,Point);
for( int i = 0 ; i < Data.Files.Length ; i ++ ){
if(DirectoryExists(Data.Files[i]))
DigDirectory(Data.Files[i]);
else
//Memo1->Lines->Add(Data.Files[i]);
TouchFileTimestamp(Data.Files[i]);
}
}
void __fastcall TForm1::DragOver(const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point, Fmx::Types::TDragOperation &Operation)
{
TForm::DragOver(Data,Point,Operation);
Operation = TDragOperation::Copy;
}
起動すると、

フォーム全体でファイルエクスプローラーからのファイルないしフォルダーまるごとのDrag and Dropを受け付けます。以下のフォルダーを落としてみましょうか。

すると、

のように、Exif情報から撮影日時を抽出してファイル作成日時にセットして、その後でExif全体を削除しています。確認してみますか?

よいみたいですね。ただし、Exif情報全体を抜いてしまったので、下図のように画像の向きが変になることがあるようです。

書籍の画像なので、本来は縦長でした。GPS情報だけを削除すればいいので、現在修正中です。

コメント