C++/CLI调用C# Clipboard.GetText()触发System.OutOfMemoryException求助
问题解决:C++/CLI剪贴板传文本导致C# OutOfMemoryException
我开发的C++/CLI应用包含MFC界面,需要从TextBox获取文本后调用C#方法处理,采用剪贴板传递文本。但将少量文本复制到剪贴板后,C#中执行Clipboard.GetText()时立即触发System.OutOfMemoryException(异常来自C#)。
原代码
C++代码
bool onclick(CStringW text){ if (!OpenClipboard(hwnd)) return false; if (!EmptyClipboard()) { CloseClipboard(); return false; } bool result; CAutoDelGlobalAlloc hMem(::GlobalAlloc(GMEM_MOVEABLE, (text.GetLength() + 1) * sizeof(WCHAR))); if (hMem) { CGlobalLock<WCHAR> lock(hMem); WCHAR* pText = lock; if (pText) { wcsncpy_s(pText, text.GetLength() + 1, text.GetString(), _TRUNCATE); pText[text.GetLength()] = L'\0'; ::SetClipboardData(CF_UNICODETEXT, hMem); result = true; } } CloseClipboard(); csharp_object.Process(); return result; }
C# DLL代码
public class CSharp_object{ public void Process(){ var text = Clipboard.GetText(); } }
问题根源
核心问题是剪贴板内存的所有权管理错误:
- 使用
CAutoDelGlobalAlloc这种自动释放的句柄包装类,在SetClipboardData调用后,该类会自动释放内存句柄,但SetClipboardData要求调用方传递句柄后放弃所有权——剪贴板会接管这块内存的生命周期,原调用方不能再释放它。自动释放逻辑导致剪贴板拿到的是已被释放的无效内存,C#读取时触发内存异常。 - 代码未检查
SetClipboardData的返回值,无法确认数据是否成功写入剪贴板。
解决方案
- 移除自动释放的内存句柄包装:改用普通
HGLOBAL,手动管理但不主动释放——剪贴板会在合适时机自行释放内存。 - 增加关键调用的错误检查:确认
SetClipboardData执行成功。 - 可选:C#端明确指定文本格式:避免格式匹配问题。
修改后的C++代码
bool onclick(CStringW text){ if (!OpenClipboard(hwnd)) return false; if (!EmptyClipboard()) { CloseClipboard(); return false; } bool result = false; HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (text.GetLength() + 1) * sizeof(WCHAR)); if (hMem) { WCHAR* pText = static_cast<WCHAR*>(::GlobalLock(hMem)); if (pText) { wcscpy_s(pText, text.GetLength() + 1, text.GetString()); ::GlobalUnlock(hMem); // 检查剪贴板数据是否设置成功 if (::SetClipboardData(CF_UNICODETEXT, hMem) != nullptr) { result = true; // 注意:SetClipboardData后,hMem的所有权已转移给剪贴板,不能再释放 } else { // 设置失败时,手动释放内存 ::GlobalFree(hMem); } } else { ::GlobalFree(hMem); } } CloseClipboard(); if (result) { csharp_object.Process(); } return result; }
修改后的C#代码(可选)
public class CSharp_object{ public void Process(){ var text = Clipboard.GetText(System.Windows.Forms.TextDataFormat.UnicodeText); } }
补充说明
GMEM_ZEROINIT可选,确保内存初始化为0,避免残留垃圾数据。wcscpy_s比wcsncpy_s更适合这里,因为已分配足够内存,无需截断逻辑。- 必须在
CloseClipboard之后调用C#的Process,否则剪贴板处于锁定状态,C#无法读取。
内容的提问来源于stack exchange,提问作者Vitaly
相关产品推荐
相关产品推荐

