目标:
在另一个进程内运行时(...在Office
应用程序中说一个插件,如Word
),拦截对OleSetClipboard/OleGetClipboard
的调用并代理iDataObject
接口用我们控制的应用程序替换应用程序最初设置/获取的对象,根据需要将调用传递给原始对象。
基本上,我想控制对给定应用程序的剪贴板的访问。
进度:
我已成功连接OleSet/GetClipboard
函数(以及其他函数)并可以用我的"代理"替换对象。对象。我的代理对象使用IDataObject
(System.Runtime.InteropServices.ComTypes.IDataObject
)的.Net定义。
请注意,在这种情况下,我使用C#
,但在c++
重新实现时发生了类似的细分。
问题:
惊喜......很惊讶,当我代理OleSetClipboard
函数时,应用程序并不能完全正常运行。当我只挂钩OleGetClipboard
函数端时,它就可以了。
我可以看到我的代理对象在getter和setter方面相互调用。细分似乎是当olegetclipboard端的代理对象需要通过getdata调用时,olesetclipboard side代理将在尝试在setter端代理的原始对象上执行getdata时抛出异常。
Sudo示例:
请注意,此示例并非特定于此格式。大多数/所有非文本格式都以这种方式失败。
我看到set side从它从Word原始OleSetClipboard调用代理的对象上调用GetData:
代理对象(OLESETCLIPBOARD):GetData调用15
结构似乎是正确的:
FORMATETC Structure Info: cfFormat (15) dwAspect (DVASPECT_CONTENT) lindex (-1) ptd (0) tymed (TYMED_HGLOBAL)
但我得到一个例外:
System.Runtime.InteropServices.COMException:GetData:消息:无效 FORMATETC结构(HRESULT的异常:0x80040064 (DV_E_FORMATETC))
可能有什么问题?我没考虑过什么?其他想法?
谢谢!
更新
用于代理的示例类...
public enum DataObjectSource { OLESETCLIPBOARD = 0, OLEGETCLIPBOARD = 1};
public class DataObjectProxy System.Runtime.InteropServices.ComTypes.IDataObject
{
private System.Runtime.InteropServices.ComTypes.IDataObject OriginalClipboardObject;
private DataObjectSource eSource;
public DataObjectProxy(object OriginalObjectToWrap, DataObjectSource eType)
{
this.OriginalClipboardObject = (System.Runtime.InteropServices.ComTypes.IDataObject)OriginalObjectToWrap;
this.eSource = eType;
}
#region Utility Methods
public string GetSingleLineLoggableOutput(FORMATETC FormatToPrint)
{
return $"FORMATETC Structure Info: cfFormat ({(ushort)FormatToPrint.cfFormat} - {GetClipboardFormatName((uint)FormatToPrint.cfFormat)}) dwAspect ({FormatToPrint.dwAspect}) lindex ({FormatToPrint.lindex}) ptd ({FormatToPrint.ptd}) tymed ({FormatToPrint.tymed})";
}
private String GetClipboardFormatName(uint ClipboardFormat)
{
StringBuilder sb = new StringBuilder(1000);
GetClipboardFormatName(ClipboardFormat, sb, sb.Capacity);
return sb.ToString();
}
#endregion Utility Methods
#region pInvokes
[DllImport("user32.dll")]
static extern int GetClipboardFormatName(uint format, [Out] StringBuilder lpszFormatName, int cchMaxCount);
#endregion pInvokes
#region System.Runtime.InteropServices.ComTypes.IDataObject Interface Implemenation
public int DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): DAdvise Called");
return OriginalClipboardObject.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
}
public void DUnadvise(int connection)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): DUnadvise Called");
OriginalClipboardObject.DUnadvise(connection);
}
public int EnumDAdvise(out IEnumSTATDATA enumAdvise)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): EnumDAdvise Called");
return OriginalClipboardObject.EnumDAdvise(out enumAdvise);
}
public IEnumFORMATETC EnumFormatEtc(DATADIR direction)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): EnumFormatEtc Called");
return OriginalClipboardObject.EnumFormatEtc(direction);
}
public int GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): GetCanonicalFormatEtc Called");
return OriginalClipboardObject.GetCanonicalFormatEtc(ref formatIn, out formatOut);
}
public void GetData(ref FORMATETC format, out STGMEDIUM medium)
{
try
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): GetData Called for {(ushort)format.cfFormat}");
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, GetSingleLineLoggableOutput(format));
STGMEDIUM medTemp;
FORMATETC formatTemp = format;
OriginalClipboardObject.GetData(ref formatTemp, out medTemp);
medium = medTemp;
}
catch(Exception getException)
{
HookLogger.LogException(HookLogger.HOOK_CHANNEL, "GetData", getException);
throw;
}
}
public void GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): GetDataHere Called");
OriginalClipboardObject.GetDataHere(ref format, ref medium);
}
public int QueryGetData(ref FORMATETC format)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): QueryGetData Called");
return OriginalClipboardObject.QueryGetData(ref format);
}
public void SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): SetData Called");
OriginalClipboardObject.SetData(ref formatIn, ref medium, release);
}
#endregion System.Runtime.InteropServices.ComTypes.IDataObject Interface Implemenation
}