如何在MFC中获取Firefox的URL?原DDE方案在50+版本失效
我之前使用DDE(Dynamic Data Exchange,动态数据交换)方案获取Firefox的URL,该方案在Firefox 49版本中运行正常,但在50及以上版本中失效。现在希望针对以下问题获取解决方案:
- Firefox 49版本与50版本之间是否存在重大变更?
- 为何DDE方案在Firefox 50及以上版本中失效?
- 在MFC中获取Firefox URL的最优方案是什么?(注:无法使用C#等其他编程语言)
以下是我近期使用的DDE相关代码:
wchar_t szApp[] = L"Firefox"; wchar_t szTopic[] = L"WWW_GetWindowInfo"; //DDE Initialization DWORD idInst=0; UINT iReturn; iReturn = DdeInitialize(&idInst, (PFNCALLBACK)DdeCallback, APPCLASS_STANDARD | APPCMD_CLIENTONLY, 0 ); if (iReturn!=DMLERR_NO_ERROR) { _TM(L"DDE Initialization Failed: 0x%04x\n", iReturn); Sleep(1500); break; } //DDE Connect to Server using given AppName and topic. HSZ hszApp, hszTopic; HCONV hConv; hszApp = DdeCreateStringHandle(idInst, szApp, 0); hszTopic = DdeCreateStringHandle(idInst, szTopic, 0); hConv = DdeConnect(idInst, hszApp, hszTopic, NULL); DdeFreeStringHandle(idInst, hszApp); DdeFreeStringHandle(idInst, hszTopic); if (hConv == NULL) { _TM(L"DDE Connection Failed.\n"); Sleep(1500); DdeUninitialize(idInst); break; } wchar_t szItem1[100]; wchar_t szDesc1[DDEREQUEST_ITEM_LENGTH]; memset(szItem1,0,sizeof(szItem1)); memset(szDesc1,0,sizeof(szDesc1)); wsprintf(szItem1,L"URL"); //Execute commands/requests specific to the DDE Server. DDERequest(idInst, hConv, szItem1, szDesc1); //DDE Disconnect and Uninitialize. DdeDisconnect(hConv); DdeUninitialize(idInst); CString strData; strData.Format(L"%s",szDesc1); strData.MakeLower(); _TM(L"firefox Start : %s\n",strData); int nStartPos = strData.Find(L"http"); if (nStartPos!=-1) { int nEndPos = strData.Find(L"\"",nStartPos+1); if (nEndPos!=-1) { strReturnUrl = strData.Mid(nStartPos,nEndPos-1); } else { nEndPos = strData.Find(L"/",nStartPos+9); strReturnUrl = strData.Mid(nStartPos,nEndPos-1); } } _TM(L"firefox End : %s\n",strReturnUrl); if (strReturnUrl.IsEmpty()) { _TM(L"firefox chrome Start\n"); strReturnUrl = GetChromeWndAddress(hWnd); _TM(L"firefox chrome End : %s\n",strReturnUrl); }
针对问题的解答
1. Firefox 49与50之间的重大变更
Firefox 50确实带来了不少关键变更,其中和你的问题直接相关的是DDE服务的彻底移除。此外,这个版本还全面启用了多进程架构(Electrolysis,e10s),默认让每个标签页运行在独立的内容进程中,同时重构了内部的进程间通信(IPC)机制,这些都彻底改变了传统进程间数据交互的基础。
2. DDE方案在Firefox 50+失效的原因
Mozilla官方在Firefox 50版本中完全废弃了对DDE协议的支持。此前Firefox提供的WWW_GetWindowInfo这类DDE主题是单进程时代的遗留特性,随着多进程架构的普及,DDE这种老旧、安全性不足且维护成本高的通信方式,已经无法适配新的进程隔离模型,因此被官方彻底移除。这直接导致你的DDE连接请求会失败,自然无法获取到URL。
3. MFC中获取Firefox URL的最优方案
由于不能使用C#,这里推荐两种可靠的替代方案:
方案一:Firefox远程调试API(推荐)
Firefox提供了官方支持的Remote Debugging Protocol,可以通过本地TCP连接与Firefox通信,获取标签页的URL信息。步骤如下:
- 让用户在Firefox中开启远程调试:打开
about:config,设置devtools.debugger.remote-enabled=true,可选设置devtools.debugger.prompt-connection=false避免连接弹窗; - 在MFC程序中用WinSock建立TCP连接到
localhost:6000(默认端口,可在Firefox设置中修改); - 发送符合协议规范的JSON请求,获取标签页列表并提取URL。
这种方案兼容性好、官方维护,只要调试协议不发生突破性变更就能稳定工作。以下是简化的MFC代码片段:
// 初始化WinSock WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); // 创建socket SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(6000); serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 连接到Firefox调试服务 connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr)); // 发送获取标签列表的请求 const char* request = "{\"id\":1,\"method\":\"Target.getTargets\"}"; send(sock, request, strlen(request), 0); // 接收响应 char buffer[4096]; int bytesRead = recv(sock, buffer, sizeof(buffer)-1, 0); buffer[bytesRead] = '\0'; // 解析JSON响应提取URL(可使用cppjson等轻量JSON库,或手动解析) // ... // 清理资源 closesocket(sock); WSACleanup();
方案二:Windows UI自动化(Accessibility API)
使用Windows的IUIAutomation接口遍历Firefox的UI元素,找到地址栏控件并读取其文本内容。这种方案不需要用户修改Firefox设置,但需要适配不同版本的Firefox UI结构:
// 初始化UI自动化 CoInitialize(NULL); IUIAutomation* pUIAutomation = NULL; CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER, IID_IUIAutomation, (void**)&pUIAutomation); // 查找Firefox主窗口 IUIAutomationElement* pFirefoxWindow = NULL; VARIANT varProp; varProp.vt = VT_BSTR; varProp.bstrVal = SysAllocString(L"Mozilla Firefox"); pUIAutomation->FindFirst(TreeScope_Children, pUIAutomation->CreatePropertyCondition(UIA_NamePropertyId, varProp), &pFirefoxWindow); SysFreeString(varProp.bstrVal); // 查找地址栏元素(需根据Firefox版本调整名称属性) IUIAutomationElement* pAddressBar = NULL; varProp.bstrVal = SysAllocString(L"Address and search bar"); pUIAutomation->FindFirst(TreeScope_Subtree, pUIAutomation->CreatePropertyCondition(UIA_NamePropertyId, varProp), &pAddressBar); SysFreeString(varProp.bstrVal); // 读取地址栏文本 if (pAddressBar) { VARIANT varValue; pAddressBar->GetCurrentPropertyValue(UIA_ValueValuePropertyId, &varValue); if (varValue.vt == VT_BSTR) { CString url(varValue.bstrVal); // 使用获取到的URL SysFreeString(varValue.bstrVal); } pAddressBar->Release(); } // 清理资源 pFirefoxWindow->Release(); pUIAutomation->Release(); CoUninitialize();
综合来看,远程调试API是最稳定、长期可维护的方案;UI自动化则更适合对用户体验要求高、不希望用户修改浏览器设置的场景。
内容的提问来源于stack exchange,提问作者HANS

