WM_COPYDATAメッセージを送ります。
using namespace System::Runtime::Serialization::Formatters::Binary;
using namespace System::IO;
using namespace System::Runtime::InteropServices;
using namespace System::Diagnostics;
// 実行中の同じアプリケーションのプロセスを取得する
System::Diagnostics::Process *GetPreviousProcess()
{
Process *curProcess = Process::GetCurrentProcess();
Process *allProcesses[] = Process::GetProcessesByName (curProcess->ProcessName);
for (int i = 0; i < allProcesses->Length; i++)
{
Process *checkProcess = allProcesses[i];
// 自分自身のプロセスIDは無視する
if (checkProcess->Id != curProcess->Id)
{
// プロセスのフルパス名を比較して同じアプリケーションか検証
if (String::Compare(checkProcess->MainModule->ModuleName, curProcess->MainModule->ModuleName, true) == 0)
{
// 同じフルパス名のプロセスを取得
return checkProcess;
}
}
}
// 同じアプリケーションのプロセスが見つからない!
return 0;
}
//Stringの配列をWM_COPYDATAで送信する関数
void SendData(System::IntPtr hdl, String *str[])
{
BinaryFormatter *bf = new BinaryFormatter;
MemoryStream *ms = new MemoryStream;
bf->Serialize(ms, str);
size_t size = static_cast<size_t>(ms->Length);
char *data = new char[size];
Marshal::Copy(ms->GetBuffer(), 0, data, size);
COPYDATASTRUCT ds;
ds.dwData = WM_COPYDATA;
ds.cbData = size;
ds.lpData = data;
SendMessage((HWND)hdl.ToPointer(), WM_COPYDATA, (WPARAM)0, (LPARAM)&ds);
delete []data;
}
//オーバーライドしたフォームのWndProc関数でWM_COPYDATAを受信
void Form1::WndProc(System::Windows::Forms::Message *msg)
{
if (msg->Msg == WM_COPYDATA)
{
COPYDATASTRUCT *ds = reinterpret_cast<COPYDATASTRUCT *>(msg->LParam.ToPointer());
BinaryFormatter *bf = new BinaryFormatter;
MemoryStream *ms = new MemoryStream;
unsigned char data __gc[] = new unsigned char __gc[ds->cbData];
Marshal::Copy(ds->lpData, data, 0, ds->cbData);
ms->Write(data, 0, ds->cbData);
ms->Seek(0, System::IO::SeekOrigin::Begin);
String *files[] = __try_cast<String * __gc[]>(bf->Deserialize(ms));
ReadFromFiles(files);
return;
}
__super::WndProc(msg);
}
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
System::Threading::Thread::CurrentThread->ApartmentState = System::Threading::ApartmentState::STA;
//起動済みプロセスのチェック
System::Diagnostics::Process *prev = GetPreviousProcess();
if (prev)
{
SendData(prev->MainWindowHandle, Environment::GetCommandLineArgs());
return 0;
}
Application::Run(new Form1());
return 0;
}
lpCmdLineをWM_COPYDATAで送信すればもっと簡単なのではないかと思うかもしれません。
しかしコマンドラインに渡されるファイル名はロングファイル名なので、スペースを単純にコマンドの区切りとして解釈できません。
せっかくEnvironment::GetCommandLineArgsで文字列の配列として取得できるのですから、これをそのまま送信できれば、"の範囲がどうとか面倒なことは考えなくても済むわけです。
そこでシリアライザを利用してStringの配列オブジェクトをシリアル化しています。シリアライザには、XML、SOAP、バイナリの3種類があり、ここではバイナリのシリアライザを使用しています。
出力先となるストリームはMemoryStreamです。しかしここでまた問題が。MemoryStreamに出力されたByteの配列のポインタは、CLIで管理されている仮想的なポインタなので、このままではプロセスを超えられません。そこでマーシャリングを使用して、アンマネージドな配列にコピーしています。
マーシャリングというのはこのような非CLI環境とのデータ交換のことらしいです。Marshalクラスにはそのための謎な機能がたくさんあります。
送信したメッセージはFormのWndProcをオーバーライドして受信します。ゲットしたデータをStringの配列に戻すためにデシリアライズしています。