登録不要の COM とドラッグ アンド ドロップ機能の間にどの関係が存在するか知っている人はいますか?
具体的には、多数の EXE と数百の DLL で構成される巨大な C++ CAD/CAM アプリケーションがあります。それらの多くは、COM サーバー (インプロセスとアウトプロセスの両方) および/またはクライアントとして機能し、ActiveX コントロールも実装します。
ほとんどの ActiveX コントロールと、CMDIFrameWnd
いずれかの EXE のメイン ベースのウィンドウは、ドラッグ アンド ドロップ機能を実装しています。ActiveX コントロールは、ドロップ ソースとドロップ ターゲットの両方を実装します。メイン ウィンドウは、特に Windows エクスプローラーからのファイルのドロップ ターゲットのみです。
COleDataSource
ドラッグ アンド ドロップの実装は非常に標準的で、それぞれドロップ ソースとCOleDropTarget
ドロップ ターゲットから派生した 2 つのデータ メンバーに基づいています。COleDropTarget
派生メンバーは、ウィンドウのメソッドでそれぞれのウィンドウに登録されますOnCreate
。OnDragEnter
、OnDragOver
およびOnDrop
メソッドも同様にオーバーライドします。つまり、システムが提供COleDataObject
するパラメータに特定の形式 (特に CF_HDROP) を尋ね、肯定的な回答の場合、データ (ファイル パスなど) をクリップボードから抽出します。コードは次のようになります。
static FORMATETC g_FileFmt = {CF_HDROP, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
....
// Inside OnDragEnter, OnDragOver or OnDrop method
STGMEDIUM stgmedium = {0,0,0};
if (pDataObject->IsDataAvailable(g_FileFmt.cfFormat))
{
HRESULT hr = pDataObject->GetData(g_FileFmt.cfFormat, &stgmedium);
HDROP hdrop = (HDROP)GlobalLock(stgmedium.hGlobal);
if (hdrop != 0)
{
int FilesCount = DragQueryFile(hdrop, (UINT)-1, 0, 0);
if (FilesCount != 0)
{
TCHAR FileName[_MAX_PATH];
DragQueryFile(hdrop, 0, FileName, _MAX_PATH);
// Check file extension and store the file name for farther use.
}
GlobalUnlock(hdrop);
}
}
ドロップ ソースの実装も簡単で、次のようになります。
void CDmDocListCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (pNMListView->iItem != -1 && m_pOleDataSource && prv_BeginDrag())
{
DROPEFFECT DE = m_pOleDataSource->DoDragDrop(
DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK, 0);
}
*pResult = 0;
}
ここで、関数はドラッグされたデータを収集し、パックして、オブジェクトのインターフェイスからメソッドをprv_BeginDrag()
呼び出してクリップボードに置きます。SetData
m_pOleDataSource
IDataObject
アプリケーション全体を登録不要にすることが決定されるまで、これらすべては完全に機能していました。マニフェストを埋め込み、アウトプロセス COM サーバーをオンデマンドで起動し、いくつかのクラスの CLSID を変更して、起動された同じサーバーのインスタンスを別のサーバーから分離することにより、アプリケーションを (COM コンポーネントの登録なしで) 強制的に分離して実行するのに 3 か月かかりました。フォルダ。ついに機能し始めましたが、私の変更に触れていなかったにもかかわらず、ドラッグ/ドロップ機能はありませんでした。
ドロップ ターゲット側では、Windows エクスプローラーからファイルをドラッグすると、上の呼び出しでCOleDataObject::IsDataAvailable
false が返されますが、変更前は true が返されます。同時に、メイン ウィンドウの OnCreate メソッドに 1 行のコード " " を追加すると、標準の CFrameWnd のメッセージ ハンドラDragAcceptFiles();
を介してドラッグ アンド ドロップが機能し始めます。WM_DROPFILE
ドロップ ソース側では、ドラッグされたデータは正常にパックされてクリップボードに配置されますが、 MFC 実装内の API への呼び出しが REGDB_E_CLASSNOTREG "クラスが登録されていません" という結果を返すCOleDataSource::DoDragDrop
ため、メソッドは失敗します。::DoDragDrop
これは、COM アクティベーションの変更が何らかの形でドラッグ/ドロップ動作に影響することを意味します。どのように?
PS 1) Windows エクスプローラーからファイルをドラッグする EXE のプロジェクト プロパティには、「UAC 実行レベル = asInvoker」があります。私が理解している限りでは、ファイルをダブルクリックして起動すると、EXE が Windows エクスプローラーと同じ UAC レベルで実行されることがわかります。
2) 非常に驚くべきことに、ドラッグ アンド ドロップは上記の症状で機能しなくなりましたが、コピー アンド ペーストは、両方のテクノロジの実装が類似しているにもかかわらず、引き続き正常に機能します。
3) ::DoDragDrop API がいつ「クラスが登録されていません」というエラーを返すのか、どのクラスを探しているのかがわかれば、問題を解決できると思います。
助けてくれてありがとう、イリア。