janeyjhon 发表于 2025-8-17 11:02:29

这段mfc代码为什么word显示不了公式

#include "pch.h"
#include "FounderToOmmlConverter.h"
#include <oleauto.h>
#include <msxml6.h>
#include <afx.h>
#include <vector>

#define CLSID_WordApplication _T("{000209FF-0000-0000-C000-000000000046}")

bool FounderToOmmlConverter::InsertToWord(const CString& ommlXml)
{
    if (ommlXml.IsEmpty())
    {
      TRACE("Empty OMML content\n");
      return false;
    }

    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
      TRACE("CoInitialize failed: 0x%08X\n", hr);
      return false;
    }

    CComPtr<IDispatch> pWordApp;
    try {
      // 创建Word实例
      CLSID clsid;
      if (FAILED(CLSIDFromString(CLSID_WordApplication, &clsid))) {
            TRACE("Failed to get CLSID for Word.Application\n");
            CoUninitialize();
            return false;
      }

      hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,
            IID_IDispatch, (void**)&pWordApp);
      if (FAILED(hr)) {
            TRACE("CoCreateInstance failed: 0x%08X (Is Word installed?)\n", hr);
            CoUninitialize();
            return false;
      }

      // 显示Word窗口
      CComVariant varVisible(true);
      PutProperty(pWordApp, L"Visible", &varVisible);

      // 获取Documents集合
      CComVariant varDocs;
      if (FAILED(GetProperty(pWordApp, L"Documents", &varDocs))) {
            TRACE("Failed to get Documents property\n");
            CoUninitialize();
            return false;
      }
      CComPtr<IDispatch> pDocuments = varDocs.pdispVal;

      // 添加新文档
      CComVariant varDoc;
      if (FAILED(InvokeMethod(pDocuments, L"Add", &varDoc, NULL))) {
            TRACE("Failed to add new document\n");
            if (FAILED(GetProperty(pWordApp, L"ActiveDocument", &varDoc))) {
                TRACE("Failed to get active document\n");
                CoUninitialize();
                return false;
            }
      }
      CComPtr<IDispatch> pDoc = varDoc.pdispVal;

      // 获取Selection对象
      CComVariant varSelection;
      if (FAILED(GetProperty(pWordApp, L"Selection", &varSelection))) {
            TRACE("Failed to get Selection\n");
            CoUninitialize();
            return false;
      }
      CComPtr<IDispatch> pSelection = varSelection.pdispVal;

      // 插入提示文本
      CComVariant testText(L"数学公式:");
      InvokeMethod(pSelection, L"TypeText", NULL, &testText, NULL);

      // 尝试插入数学公式的方法 - 使用OMath对象而不是InsertXML
      bool insertSuccess = InsertOmmlUsingOMath(pSelection, ommlXml);

      if (!insertSuccess) {
            // 备用方法:使用InsertXML
            insertSuccess = InsertOmmlUsingXML(pSelection, ommlXml);
      }

      //if (!insertSuccess) {
      //    // 最后的备用方法:插入为普通文本以便调试
      //    TRACE("All insertion methods failed, inserting as text for debugging\n");
      //    CComVariant fallbackText(L"\n\n");
      //    InvokeMethod(pSelection, L"TypeText", NULL, &fallbackText, NULL);

      //    CString correctOmmlXml = CreateCorrectOmmlXml(ommlXml);
      //    CComVariant ommlTextVar(correctOmmlXml);
      //    InvokeMethod(pSelection, L"TypeText", NULL, &ommlTextVar, NULL);
      //}

      CoUninitialize();
      return insertSuccess;
    }
    catch (...) {
      TRACE("Exception occurred during Word automation\n");
      CoUninitialize();
      return false;
    }
}

// 新方法:使用Word的OMath对象插入数学公式
bool FounderToOmmlConverter::InsertOmmlUsingOMath(IDispatch* pSelection, const CString& ommlXml)
{
    try {
      // 获取OMath对象集合
      CComVariant varOMaths;
      if (FAILED(GetProperty(pSelection, L"OMaths", &varOMaths))) {
            TRACE("Failed to get OMaths property\n");
            return false;
      }
      CComPtr<IDispatch> pOMaths = varOMaths.pdispVal;

      // 添加新的数学公式对象
      CComVariant varOMath;
      CComVariant varRange;
      GetProperty(pSelection, L"Range", &varRange);

      if (FAILED(InvokeMethod(pOMaths, L"Add", &varOMath, &varRange, NULL))) {
            TRACE("Failed to add OMath object\n");
            return false;
      }
      CComPtr<IDispatch> pOMath = varOMath.pdispVal;

      // 创建正确的OMML内容(去掉外层的oMath标签,因为OMath对象会自动添加)
      CString innerOmml = ExtractInnerOmml(ommlXml);

      // 获取Range对象并设置XML
      CComVariant varMathRange;
      if (FAILED(GetProperty(pOMath, L"Range", &varMathRange))) {
            TRACE("Failed to get OMath Range\n");
            return false;
      }
      CComPtr<IDispatch> pMathRange = varMathRange.pdispVal;

      // 设置数学公式的XML内容
      CComVariant xmlVar(innerOmml);
      if (SUCCEEDED(InvokeMethod(pMathRange, L"InsertXML", NULL, &xmlVar, NULL))) {
            TRACE("Successfully inserted OMML using OMath object\n");
            return true;
      }

      TRACE("Failed to insert XML into OMath Range\n");
      return false;
    }
    catch (...) {
      TRACE("Exception in InsertOmmlUsingOMath\n");
      return false;
    }
}

// 备用方法:使用InsertXML
bool FounderToOmmlConverter::InsertOmmlUsingXML(IDispatch* pSelection, const CString& ommlXml)
{
    try {
      // 创建正确的OMML XML格式
      CString correctOmmlXml = CreateCorrectOmmlXml(ommlXml);
      TRACE("Generated OMML XML: %s\n", correctOmmlXml.GetString());

      //// 方法1:直接插入OMML
      CComVariant xmlVar(correctOmmlXml);
      HRESULT hr = InvokeMethod(pSelection, L"InsertXML", NULL, &xmlVar, NULL);
      if (SUCCEEDED(hr)) {
            TRACE("Successfully inserted OMML directly\n");
            return true;
      }

      // 方法2:使用完整的WordProcessingML包装
      //CString wrappedXML = CreateWordProcessingMLDocument(correctOmmlXml);
      //CComVariant wrappedVar(wrappedXML);
      //HRESULT hr = InvokeMethod(pSelection, L"InsertXML", NULL, &wrappedVar, NULL);
      //if (SUCCEEDED(hr)) {
      //    TRACE("Successfully inserted wrapped OMML\n");
      //    return true;
      //}

      // 方法3:通过Range插入
      //CComVariant varRange;
      //CComVariant xmlVar(correctOmmlXml);
      //if (SUCCEEDED(GetProperty(pSelection, L"Range", &varRange))) {
      //    CComPtr<IDispatch> pRange = varRange.pdispVal;
      //    HRESULT hr = InvokeMethod(pRange, L"InsertXML", NULL, &xmlVar, NULL);
      //    if (SUCCEEDED(hr)) {
      //      TRACE("Successfully inserted via Range\n");
      //      return true;
      //    }
      //}
      else {
            TRACE("Failed to insert OMML\n");
            return false;
      }
      
    }
    catch (...) {
      TRACE("Exception in InsertOmmlUsingXML\n");
      return false;
    }
}

// 提取OMML内部内容(去掉外层的oMath标签)
CString FounderToOmmlConverter::ExtractInnerOmml(const CString& ommlXml)
{
    //CString inner = ommlXml;

    //// 简单的字符串处理,实际应该使用XML解析
    //int startPos = inner.Find(L"<m:oMath");
    //if (startPos != -1) {
    //    int endTag = inner.Find(L">", startPos);
    //    if (endTag != -1) {
    //      inner = inner.Mid(endTag + 1);
    //      int endPos = inner.ReverseFind(L"</m:oMath>");
    //      if (endPos != -1) {
    //            inner = inner.Left(endPos);
    //      }
    //    }
    //}

    //return inner;

    CString inner = ommlXml;

    // 移除开始标签
    int startPos = inner.Find(L"<m:oMath");
    if (startPos != -1) {
      int endTag = inner.Find(L">", startPos);
      if (endTag != -1) {
            inner = inner.Mid(endTag + 1);
      }
    }

    // 移除结束标签
    inner.Replace(L"</m:oMath>", L"");

    return inner.Trim(); // 去除首尾空白
}

// 创建正确的OMML XML格式
CString FounderToOmmlConverter::CreateCorrectOmmlXml(const CString& inputOmml)
{
    // 如果输入已经是正确格式,直接返回
    //if (inputOmml.Find(L"xmlns:m=") != -1 && inputOmml.Find(L"m:oMath") != -1) {
    //    return inputOmml;
    //}

    //// 创建一个简单但正确的数学公式 x = a² + b
    //CString correctedOmml =
    //    L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
    //    L"<m:r>"
    //    L"<m:t>x</m:t>"
    //    L"</m:r>"
    //    L"<m:r>"
    //    L"<m:t>=</m:t>"
    //    L"</m:r>"
    //    L"<m:sSup>"
    //    L"<m:e>"
    //    L"<m:r>"
    //    L"<m:t>a</m:t>"
    //    L"</m:r>"
    //    L"</m:e>"
    //    L"<m:sup>"
    //    L"<m:r>"
    //    L"<m:t>2</m:t>"
    //    L"</m:r>"
    //    L"</m:sup>"
    //    L"</m:sSup>"
    //    L"<m:r>"
    //    L"<m:t>+</m:t>"
    //    L"</m:r>"
    //    L"<m:r>"
    //    L"<m:t>b</m:t>"
    //    L"</m:r>"
    //    L"</m:oMath>";

    //return correctedOmml;

    // 创建一个正确的分式:1/2
    CString correctedOmml =
      L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      L"<m:f>"// 分式元素
      L"<m:num>"// 分子
      L"<m:r>"
      L"<m:t>1</m:t>"
      L"</m:r>"
      L"</m:num>"
      L"<m:den>"// 分母
      L"<m:r>"
      L"<m:t>2</m:t>"
      L"</m:r>"
      L"</m:den>"
      L"</m:f>"
      L"</m:oMath>";

    return correctedOmml;
}

// 创建WordProcessingML文档
CString FounderToOmmlConverter::CreateWordProcessingMLDocument(const CString& ommlContent)
{
    CString document;
    document.Format(
      L"<w:document xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" "
      L"xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      L"<w:body>"
      L"<w:p>"
      L"<w:r>"
      L"<w:rPr></w:rPr>"
      L"%s"
      L"</w:r>"
      L"</w:p>"
      L"</w:body>"
      L"</w:document>",
      ommlContent.GetString()
    );
    return document;
}

// COM辅助函数实现(保持不变)
HRESULT FounderToOmmlConverter::GetProperty(IDispatch* pDisp, LPCOLESTR propertyName, VARIANT* pResult)
{
    if (!pDisp || !propertyName || !pResult) return E_INVALIDARG;

    DISPID dispid;
    HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, (LPOLESTR*)&propertyName, 1,
      LOCALE_USER_DEFAULT, &dispid);
    if (FAILED(hr)) return hr;

    DISPPARAMS dispparams = { NULL, NULL, 0, 0 };
    return pDisp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
      DISPATCH_PROPERTYGET, &dispparams, pResult, NULL, NULL);
}

HRESULT FounderToOmmlConverter::InvokeMethod(IDispatch* pDisp, LPCOLESTR methodName, VARIANT* pResult, ...)
{
    if (!pDisp || !methodName) return E_INVALIDARG;

    DISPID dispid;
    HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, (LPOLESTR*)&methodName, 1,
      LOCALE_USER_DEFAULT, &dispid);
    if (FAILED(hr)) return hr;

    va_list args;
    va_start(args, pResult);

    VARIANT varArgs;
    int argCount = 0;

    VARIANT* pArg = va_arg(args, VARIANT*);
    while (pArg && argCount < 10) {
      varArgs = *pArg;
      pArg = va_arg(args, VARIANT*);
    }
    va_end(args);

    // 反转参数顺序
    if (argCount > 0) {
      for (int i = 0; i < argCount / 2; i++) {
            VARIANT temp = varArgs;
            varArgs = varArgs;
            varArgs = temp;
      }
    }

    DISPPARAMS dispparams = { varArgs, NULL, argCount, 0 };
    return pDisp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
      DISPATCH_METHOD, &dispparams, pResult, NULL, NULL);
}

HRESULT FounderToOmmlConverter::PutProperty(IDispatch* pDisp, LPCOLESTR propertyName, VARIANT* pValue)
{
    if (!pDisp || !propertyName || !pValue) return E_INVALIDARG;

    DISPID dispid;
    HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, (LPOLESTR*)&propertyName, 1,
      LOCALE_USER_DEFAULT, &dispid);
    if (FAILED(hr)) return hr;

    DISPID dispidNamed = DISPID_PROPERTYPUT;
    DISPPARAMS dispparams = { pValue, &dispidNamed, 1, 1 };
    return pDisp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
      DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
}

// 转换函数(保持原有逻辑)
CString FounderToOmmlConverter::ConvertToOmml(const CString& founderFormula)
{
    //CString ommlXml;
    //ommlXml.Format(
    //    L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
    //    L"<m:r>"
    //    L"<m:t>%s</m:t>"
    //    L"</m:r>"
    //    L"</m:oMath>",
    //    founderFormula.GetString());

    //return ommlXml;

    CString ommlXml;

    // 检查是否是分式格式(例如 "1/2" 或 "a/b")
    int slashPos = founderFormula.Find(L"/");
    if (slashPos != -1 && slashPos > 0 && slashPos < founderFormula.GetLength() - 1) {
      // 提取分子和分母
      CString numerator = founderFormula.Left(slashPos).Trim();
      CString denominator = founderFormula.Mid(slashPos + 1).Trim();

      // 创建分式OMML
      ommlXml.Format(
            L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
            L"<m:f>"
            L"<m:num>"
            L"<m:r>"
            L"<m:t>%s</m:t>"
            L"</m:r>"
            L"</m:num>"
            L"<m:den>"
            L"<m:r>"
            L"<m:t>%s</m:t>"
            L"</m:r>"
            L"</m:den>"
            L"</m:f>"
            L"</m:oMath>",
            numerator.GetString(),
            denominator.GetString());
    }
    else
    {
      // 普通文本公式
      ommlXml.Format(
            L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
            L"<m:r>"
            L"<m:t>%s</m:t>"
            L"</m:r>"
            L"</m:oMath>",
            founderFormula.GetString());
    }

    return ommlXml;
}

// 其他辅助函数(保持不变)
CString FounderToOmmlConverter::ProcessFounderFormula(const CString& formula)
{
    return formula;
}

CString FounderToOmmlConverter::GetEndBracket(const CString& startBracket)
{
    if (startBracket == L"(") return L")";
    if (startBracket == L"[") return L"]";
    if (startBracket == L"{") return L"}";
    return L"";
}

CString FounderToOmmlConverter::WrapOMMLWithWordNamespace(const CString& rawOMML)
{
    if (rawOMML.Find(L"xmlns:m=") == -1) {
      CString wrapped;
      wrapped.Format(L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">%s</m:oMath>", rawOMML);
      return wrapped;
    }
    return rawOMML;
}


// 在cpp文件中实现
CString FounderToOmmlConverter::CreateFractionOmml(const CString& numerator, const CString& denominator)
{
    CString ommlXml;
    ommlXml.Format(
      L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      L"<m:f>"
      L"<m:num>"
      L"<m:r>"
      L"<m:t>%s</m:t>"
      L"</m:r>"
      L"</m:num>"
      L"<m:den>"
      L"<m:r>"
      L"<m:t>%s</m:t>"
      L"</m:r>"
      L"</m:den>"
      L"</m:f>"
      L"</m:oMath>",
      numerator.GetString(),
      denominator.GetString());

    return ommlXml;
}

// 创建更复杂的数学表达式
CString FounderToOmmlConverter::CreateComplexOmml(const CString& formula)
{
    // 示例:创建 x = a²/b + c 这样的复杂公式
    CString ommlXml =
      L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      L"<m:r>"
      L"<m:t>x</m:t>"
      L"</m:r>"
      L"<m:r>"
      L"<m:t>=</m:t>"
      L"</m:r>"
      L"<m:f>"// 分式 a²/b
      L"<m:num>"
      L"<m:sSup>"// 上标 a²
      L"<m:e>"
      L"<m:r>"
      L"<m:t>a</m:t>"
      L"</m:r>"
      L"</m:e>"
      L"<m:sup>"
      L"<m:r>"
      L"<m:t>2</m:t>"
      L"</m:r>"
      L"</m:sup>"
      L"</m:sSup>"
      L"</m:num>"
      L"<m:den>"
      L"<m:r>"
      L"<m:t>b</m:t>"
      L"</m:r>"
      L"</m:den>"
      L"</m:f>"
      L"<m:r>"
      L"<m:t>+</m:t>"
      L"</m:r>"
      L"<m:r>"
      L"<m:t>c</m:t>"
      L"</m:r>"
      L"</m:oMath>";

    return ommlXml;
}

// MFCApplication1Dlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "MFCApplication1.h"
#include "MFCApplication1Dlg.h"
#include "afxdialogex.h"
#include "FounderToOmmlConverter.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
        CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
        enum { IDD = IDD_ABOUTBOX };
#endif

        protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
        DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
        CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CMFCApplication1Dlg 对话框



CMFCApplication1Dlg::CMFCApplication1Dlg(CWnd* pParent /*=nullptr*/)
        : CDialogEx(IDD_MFCAPPLICATION1_DIALOG, pParent)
{
        m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMFCApplication1Dlg::DoDataExchange(CDataExchange* pDX)
{
        CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
        ON_WM_SYSCOMMAND()
        ON_WM_PAINT()
        ON_WM_QUERYDRAGICON()
        ON_BN_CLICKED(IDC_COVENT, &CMFCApplication1Dlg::OnBnClickedCovent)
        ON_BN_CLICKED(IDC_INSERT, &CMFCApplication1Dlg::OnBnClickedInsert)
END_MESSAGE_MAP()


// CMFCApplication1Dlg 消息处理程序

BOOL CMFCApplication1Dlg::OnInitDialog()
{
        CDialogEx::OnInitDialog();

        // 将“关于...”菜单项添加到系统菜单中。

        // IDM_ABOUTBOX 必须在系统命令范围内。
        ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
        ASSERT(IDM_ABOUTBOX < 0xF000);

        CMenu* pSysMenu = GetSystemMenu(FALSE);
        if (pSysMenu != nullptr)
        {
                BOOL bNameValid;
                CString strAboutMenu;
                bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
                ASSERT(bNameValid);
                if (!strAboutMenu.IsEmpty())
                {
                        pSysMenu->AppendMenu(MF_SEPARATOR);
                        pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
                }
        }

        // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
        //执行此操作
        SetIcon(m_hIcon, TRUE);                        // 设置大图标
        SetIcon(m_hIcon, FALSE);                // 设置小图标

        // TODO: 在此添加额外的初始化代码


        // 设置示例文本
        m_strFounder = _T("〖SX(〗A〖〗B〖SX)〗");
        UpdateData(FALSE);

        return TRUE;// 除非将焦点设置到控件,否则返回 TRUE
}

void CMFCApplication1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
        if ((nID & 0xFFF0) == IDM_ABOUTBOX)
        {
                CAboutDlg dlgAbout;
                dlgAbout.DoModal();
        }
        else
        {
                CDialogEx::OnSysCommand(nID, lParam);
        }
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//这将由框架自动完成。

void CMFCApplication1Dlg::OnPaint()
{
        if (IsIconic())
        {
                CPaintDC dc(this); // 用于绘制的设备上下文

                SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

                // 使图标在工作区矩形中居中
                int cxIcon = GetSystemMetrics(SM_CXICON);
                int cyIcon = GetSystemMetrics(SM_CYICON);
                CRect rect;
                GetClientRect(&rect);
                int x = (rect.Width() - cxIcon + 1) / 2;
                int y = (rect.Height() - cyIcon + 1) / 2;

                // 绘制图标
                dc.DrawIcon(x, y, m_hIcon);
        }
        else
        {
                CDialogEx::OnPaint();
        }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCApplication1Dlg::OnQueryDragIcon()
{
        return static_cast<HCURSOR>(m_hIcon);
}



void CMFCApplication1Dlg::OnBnClickedCovent()
{
        // TODO: 在此添加控件通知处理程序代码

        if (m_strFounder.IsEmpty()) {
                AfxMessageBox(_T("请输入方正公式!"));
                return;
        }

        m_strOmml = FounderToOmmlConverter::ConvertToOmml(m_strFounder);

       
       
       
       
       
       
        UpdateData(FALSE);

        AfxMessageBox(_T("转换完成!"));
}


void CMFCApplication1Dlg::OnBnClickedInsert()
{
        // TODO: 在此添加控件通知处理程序代码
        if (m_strOmml.IsEmpty()) {
                AfxMessageBox(_T("请先转换公式!"));
                return;
        }

        CString simpleOmml = FounderToOmmlConverter::CreateFractionOmml(L"1", L"2");
       

        if (FounderToOmmlConverter::InsertToWord(simpleOmml)) {
                AfxMessageBox(_T("插入Word成功!"));
        }
        /*if (FounderToOmmlConverter::InsertToWord(m_strOmml)) {
                AfxMessageBox(_T("插入Word成功!"));
        }*/
        else
        {
                AfxMessageBox(_T("插入Word失败!请确保Word已打开。"));
        }
}
这段mfc代码为什么word显示不了公式

// MFCApplication1Dlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "MFCApplication1.h"
#include "MFCApplication1Dlg.h"
#include "afxdialogex.h"
#include "FounderToOmmlConverter.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
        CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
        enum { IDD = IDD_ABOUTBOX };
#endif

        protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
        DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
        CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CMFCApplication1Dlg 对话框



CMFCApplication1Dlg::CMFCApplication1Dlg(CWnd* pParent /*=nullptr*/)
        : CDialogEx(IDD_MFCAPPLICATION1_DIALOG, pParent)
{
        m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMFCApplication1Dlg::DoDataExchange(CDataExchange* pDX)
{
        CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
        ON_WM_SYSCOMMAND()
        ON_WM_PAINT()
        ON_WM_QUERYDRAGICON()
        ON_BN_CLICKED(IDC_COVENT, &CMFCApplication1Dlg::OnBnClickedCovent)
        ON_BN_CLICKED(IDC_INSERT, &CMFCApplication1Dlg::OnBnClickedInsert)
END_MESSAGE_MAP()


// CMFCApplication1Dlg 消息处理程序

BOOL CMFCApplication1Dlg::OnInitDialog()
{
        CDialogEx::OnInitDialog();

        // 将“关于...”菜单项添加到系统菜单中。

        // IDM_ABOUTBOX 必须在系统命令范围内。
        ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
        ASSERT(IDM_ABOUTBOX < 0xF000);

        CMenu* pSysMenu = GetSystemMenu(FALSE);
        if (pSysMenu != nullptr)
        {
                BOOL bNameValid;
                CString strAboutMenu;
                bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
                ASSERT(bNameValid);
                if (!strAboutMenu.IsEmpty())
                {
                        pSysMenu->AppendMenu(MF_SEPARATOR);
                        pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
                }
        }

        // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
        //执行此操作
        SetIcon(m_hIcon, TRUE);                        // 设置大图标
        SetIcon(m_hIcon, FALSE);                // 设置小图标

        // TODO: 在此添加额外的初始化代码


        // 设置示例文本
        m_strFounder = _T("〖SX(〗A〖〗B〖SX)〗");
        UpdateData(FALSE);

        return TRUE;// 除非将焦点设置到控件,否则返回 TRUE
}

void CMFCApplication1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
        if ((nID & 0xFFF0) == IDM_ABOUTBOX)
        {
                CAboutDlg dlgAbout;
                dlgAbout.DoModal();
        }
        else
        {
                CDialogEx::OnSysCommand(nID, lParam);
        }
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//这将由框架自动完成。

void CMFCApplication1Dlg::OnPaint()
{
        if (IsIconic())
        {
                CPaintDC dc(this); // 用于绘制的设备上下文

                SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

                // 使图标在工作区矩形中居中
                int cxIcon = GetSystemMetrics(SM_CXICON);
                int cyIcon = GetSystemMetrics(SM_CYICON);
                CRect rect;
                GetClientRect(&rect);
                int x = (rect.Width() - cxIcon + 1) / 2;
                int y = (rect.Height() - cyIcon + 1) / 2;

                // 绘制图标
                dc.DrawIcon(x, y, m_hIcon);
        }
        else
        {
                CDialogEx::OnPaint();
        }
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCApplication1Dlg::OnQueryDragIcon()
{
        return static_cast<HCURSOR>(m_hIcon);
}



void CMFCApplication1Dlg::OnBnClickedCovent()
{
        // TODO: 在此添加控件通知处理程序代码

        if (m_strFounder.IsEmpty()) {
                AfxMessageBox(_T("请输入方正公式!"));
                return;
        }

        m_strOmml = FounderToOmmlConverter::ConvertToOmml(m_strFounder);

       
       
       
       
       
       
        UpdateData(FALSE);

        AfxMessageBox(_T("转换完成!"));
}


void CMFCApplication1Dlg::OnBnClickedInsert()
{
        // TODO: 在此添加控件通知处理程序代码
        if (m_strOmml.IsEmpty()) {
                AfxMessageBox(_T("请先转换公式!"));
                return;
        }

        CString simpleOmml = FounderToOmmlConverter::CreateFractionOmml(L"1", L"2");
       

        if (FounderToOmmlConverter::InsertToWord(simpleOmml)) {
                AfxMessageBox(_T("插入Word成功!"));
        }
        /*if (FounderToOmmlConverter::InsertToWord(m_strOmml)) {
                AfxMessageBox(_T("插入Word成功!"));
        }*/
        else
        {
                AfxMessageBox(_T("插入Word失败!请确保Word已打开。"));
        }
}

FishC 发表于 2025-8-17 11:04:47

根据提供的代码,Word无法显示公式的主要原因有以下几点:

### 1. 方正公式转换问题
在 `ConvertToOmml` 方法中,方正公式 `〖SX(〗A〖〗B〖SX)〗` 未被正确解析:
// 当前转换逻辑错误地依赖 "/" 符号
int slashPos = founderFormula.Find(L"/"); // 方正公式中没有斜杠
if (slashPos != -1) { ... } // 条件永远不成立
else {
    // 错误地生成了普通文本OMML
    ommlXml.Format(L"<m:oMath><m:r>%s</m:r></m:oMath>", ...);
}

### 2. OMML生成格式错误
生成的OMML缺少关键命名空间声明:
// 缺少必要的命名空间 xmlns:m
CString ommlXml = L"<m:oMath>...</m:oMath>"; // 无效格式

// 正确格式应包含命名空间
CString correctOmml =
    L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
    L"<m:f>...</m:f></m:oMath>";

### 3. 插入逻辑冲突
在插入按钮事件中,实际插入的是测试公式而非转换结果:
void CMFCApplication1Dlg::OnBnClickedInsert() {
    // 错误:插入的是硬编码的测试公式
    CString simpleOmml = FounderToOmmlConverter::CreateFractionOmml(L"1", L"2");
    FounderToOmmlConverter::InsertToWord(simpleOmml); // 应使用 m_strOmml
}

### 4. COM方法调用问题
`InsertOmmlUsingXML` 方法存在逻辑缺陷:
bool InsertOmmlUsingXML(IDispatch* pSelection, const CString& ommlXml) {
    // 错误:直接使用未验证的原始XML
    CComVariant xmlVar(ommlXml); // 可能包含无效XML
    HRESULT hr = InvokeMethod(pSelection, L"InsertXML", NULL, &xmlVar, NULL);
    // 未处理失败时的备用方案
}

---

### 解决方案
#### 1. 修复方正公式解析
CString FounderToOmmlConverter::ConvertToOmml(const CString& founderFormula) {
    // 解析方正分式标记 〖SX(〗A〖〗B〖SX)〗
    if (founderFormula.Find(L"〖SX(〗") != -1) {
      int start = 6;// "〖SX(〗"长度
      int mid = founderFormula.Find(L"〖〗", start);
      int end = founderFormula.Find(L"〖SX)〗", mid);
      
      CString num = founderFormula.Mid(start, mid - start);
      CString den = founderFormula.Mid(mid + 2, end - mid - 2);
      
      return CreateFractionOmml(num, den); // 生成正确OMML
    }
    // 其他公式处理...
}

#### 2. 生成有效OMML
CString FounderToOmmlConverter::CreateFractionOmml(const CString& num, const CString& den) {
    return
      L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      L"<m:f><m:num><m:r>" + num + L"</m:r></m:num>"
      L"<m:den><m:r>" + den + L"</m:r></m:den></m:f>"
      L"</m:oMath>";
}

#### 3. 修正插入逻辑
void CMFCApplication1Dlg::OnBnClickedInsert() {
    if (m_strOmml.IsEmpty()) return;
   
    // 正确使用转换后的公式
    if (FounderToOmmlConverter::InsertToWord(m_strOmml)) {
      AfxMessageBox(_T("插入成功!"));
    }
}

#### 4. 增强XML插入健壮性
bool InsertOmmlUsingXML(IDispatch* pSelection, const CString& ommlXml) {
    // 添加XML声明和命名空间
    CString validXml =
      L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
      L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      + ommlXml + L"</m:oMath>";
   
    CComVariant xmlVar(validXml);
    return SUCCEEDED(InvokeMethod(pSelection, L"InsertXML", NULL, &xmlVar, NULL));
}

### 验证步骤
1. 确保输入方正公式格式正确,如 `〖SX(〗分子〖〗分母〖SX)〗`
2. 在转换后检查生成的OMML是否包含完整命名空间
3. 使用Word的XML验证工具检查生成的XML有效性
4. 在`InsertToWord`中添加TRACE输出HRESULT错误代码

> 关键提示:Office 2007+ 才支持OMML,请确认Word版本符合要求。调试时可在`InsertXML`调用后添加`TRACE("InsertXML result: 0x%X\n", hr);`查看具体错误代码。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

janeyjhon 发表于 2025-8-17 11:35:53

CString FounderToOmmlConverter::CreateFractionOmml(const CString& num, const CString& den) {
    return
      L"<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">"
      L"<m:f><m:num><m:r>" + num + L"</m:r></m:num>"
      L"<m:den><m:r>" + den + L"</m:r></m:den></m:f>"
      L"</m:oMath>";
} 这一行的代码显示报错

janeyjhon 发表于 2025-8-17 11:36:13

找不到文本运算符

FishC 发表于 2025-8-17 11:37:41

janeyjhon 发表于 2025-08-17 11:35
CString FounderToOmmlConverter::CreateFractionOmml ...

你的代码报错是因为在拼接XML字符串时使用了错误的语法。C++中不能直接用 `+` 操作符连接字符串字面量和CString对象,需要显式转换。以下是修正后的代码:

CString FounderToOmmlConverter::CreateFractionOmml(const CString& num, const CString& den) {
    return
      L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      L"<m:f>"
      L"<m:num>" + num + L"</m:num>"
      L"<m:den>" + den + L"</m:den>"
      L"</m:f>"
      L"</m:oMath>";
}

关键修正点:

1. 字符串字面量需要显式转换为CString:
   // 错误写法
   L"text" + cstringVar + L"text"
   
   // 正确写法
   CString(L"text") + cstringVar + CString(L"text")

2. 更推荐的格式化写法(避免拼接错误):
CString FounderToOmmlConverter::CreateFractionOmml(const CString& num, const CString& den) {
    CString xml;
    xml.Format(
      L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      L"<m:f>"
      L"<m:num>%s</m:num>"
      L"<m:den>%s</m:den>"
      L"</m:f>"
      L"</m:oMath>",
      num.GetString(), den.GetString());
    return xml;
}

3. 必须包含XML命名空间声明:
// 缺少这个会导致Word无法识别公式
xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\"

4. 完整的OMML结构应该是:
<m:oMath xmlns:m="命名空间">
<m:f> <!-- 分式 -->
    <m:num>分子</m:num>
    <m:den>分母</m:den>
</m:f>
</m:oMath>

验证建议:
1. 调试时输出生成的XML:
TRACE(L"Generated OMML: %s\n", xml);
2. 将输出的XML复制到文本编辑器,检查标签是否闭合
3. 在Word中手动粘贴XML测试(通过"插入-对象-公式")

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

janeyjhon 发表于 2025-8-17 13:28:00

CString ommlXml;
ommlXml.Format(
   L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
   
      L"<m:f>"
   L"<m:num>"
   L"<m:r>"
   L"<m:t>%s</m:t>"
   L"</m:r>"
   L"</m:num>"
   L"<m:den>"
   L"<m:r>"
   L"<m:t>%s</m:t>"
   L"</m:r>"
   L"</m:den>"
   L"</m:f>"
   L"</m:oMath>",   
   numerator.GetString(),
   denominator.GetString());这样写对吗

FishC 发表于 2025-8-17 13:29:38

janeyjhon 发表于 2025-08-17 13:28
CString ommlXml;
ommlXml.Format(
   L""
   ...

你的代码结构基本正确,但需要注意以下关键点:

CString FounderToOmmlConverter::CreateFractionOmml(const CString& numerator, const CString& denominator) {
    CString ommlXml;
    ommlXml.Format(
      L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      L"<m:f>"// 分式开始
      L"<m:num>"// 分子开始
      L"<m:r><m:t>%s</m:t></m:r>"// 分子内容
      L"</m:num>"// 分子结束
      L"<m:den>"// 分母开始
      L"<m:r><m:t>%s</m:t></m:r>"// 分母内容
      L"</m:den>"// 分母结束
      L"</m:f>"// 分式结束
      L"</m:oMath>",// 公式结束
      numerator.GetString(),
      denominator.GetString());
    return ommlXml;
}

### 需要修正的关键点:

1. **XML命名空间声明**:
   // 必须包含此命名空间声明
   L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"

2. **标签闭合问题**:
   // 错误:缺少闭合标签
   L"<m:num><m:r><m:t>%s</m:t></m:r>"
   
   // 正确:添加分子结束标签
   L"<m:num><m:r><m:t>%s</m:t></m:r></m:num>"

3. **特殊字符处理**(重要):
   如果分子/分母包含 `<`, `>`, `&` 等字符,需要转义:
   CString escapedNum = numerator;
   escapedNum.Replace(L"&", L"&amp;");
   escapedNum.Replace(L"<", L"&lt;");
   escapedNum.Replace(L">", L"&gt;");

### 完整修正版:
CString FounderToOmmlConverter::CreateFractionOmml(const CString& numerator, const CString& denominator) {
    // 转义特殊字符
    CString safeNum = EscapeXml(numerator);
    CString safeDen = EscapeXml(denominator);

    CString ommlXml;
    ommlXml.Format(
      L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"
      L"<m:f>"
      L"<m:num><m:r><m:t>%s</m:t></m:r></m:num>"
      L"<m:den><m:r><m:t>%s</m:t></m:r></m:den>"
      L"</m:f>"
      L"</m:oMath>",
      safeNum.GetString(),
      safeDen.GetString());
   
    return ommlXml;
}

// XML转义辅助函数
CString FounderToOmmlConverter::EscapeXml(const CString& input) {
    CString result = input;
    result.Replace(L"&", L"&amp;");
    result.Replace(L"<", L"&lt;");
    result.Replace(L">", L"&gt;");
    result.Replace(L"\"", L"&quot;");
    return result;
}

### 验证方法:
在调用处添加调试输出:
// 在调用CreateFractionOmml后添加:
CString generated = CreateFractionOmml(L"1/2", L"x+y");
TRACE(L"Generated OMML:\n%s\n", generated);

输出应该类似:
<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">
<m:f>
    <m:num><m:r><m:t>1/2</m:t></m:r></m:num>
    <m:den><m:r><m:t>x+y</m:t></m:r></m:den>
</m:f>
</m:oMath>

> **重要提示**:Word对OMML的解析非常严格,必须确保:
> 1. 所有标签正确闭合
> 2. 命名空间正确定义
> 3. 特殊字符正确转义
> 4. 整个XML是单根结构(如使用`<m:oMath>`作为根元素)

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

janeyjhon 发表于 2025-8-17 13:34:14

L"<m:oMath xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\">"这个里面写成这样没报错写成这样<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">报错

janeyjhon 发表于 2025-8-17 13:35:09

出现红色波浪线

FishC 发表于 2025-8-17 13:38:57

janeyjhon 发表于 2025-08-17 13:34
L""这个里面写成这样没报错写成这样报错

您遇到的问题是C++字符串拼接的语法问题。让我解释清楚正确的写法:

### 正确写法(不会报错):
CString ommlXml;
ommlXml.Format(
    L"" // 注意这里没有逗号
    L""
    L""
    L"%s" // 分子
    L""
    L""
    L""
    L""
    L"%s" // 分母
    L""
    L""
    L""
    L"",
    numerator.GetString(),
    denominator.GetString());

### 错误写法(会报错):
CString ommlXml;
ommlXml.Format(
    L"", // 这里有逗号
    L"", // 这里有逗号
    L"", // 这里有逗号
    L"%s", // 这里有逗号
    ...); // 错误:Format函数不接受多个字符串参数

### 关键区别:
1. **没有逗号分隔**:所有XML字符串必须写在一个连续的大字符串中,不能在每个L""后面加逗号
2. **整体是一个字符串**:编译器会将相邻的字符串字面量自动拼接成一个字符串
3. **Format参数对应**:`%s`的数量必须与后面的参数数量一致

### 更安全的写法(推荐):
CString ommlXml;
ommlXml.Format(
    L""
    L""
    L"%s"
    L""
    L""
    L""
    L"%s"
    L""
    L"",
    numerator.GetString(),
    denominator.GetString());

### 调试技巧:
如果仍然报错,可以简化测试:
// 简化测试1:纯文本
CString test;
test.Format(L"第一部分" L"第二部分 %s" L"第三部分", L"测试");
TRACE(L"测试结果: %s\n", test.GetString());

// 简化测试2:XML结构
CString xmlTest =
    L""
    L""
    L"分子"
    L""
    L""
    L""
    L"分母"
    L""
    L"";
TRACE(L"XML测试:\n%s\n", xmlTest.GetString());

### 常见错误原因:
1. 在L""之间添加了逗号(最常见错误)
2. 漏掉了某个字符串的L前缀
3. 标签没有正确闭合(如缺少`</m:num>`)
4. 特殊字符未转义(特别是`<`、`>`、`&`)
5. 命名空间声明不正确

> 提示:在VS中,将鼠标悬停在Format方法上,会显示函数签名:`void Format(LPCTSTR lpszFormat, ...);` 这证明它只接受一个格式字符串和可变参数。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: 这段mfc代码为什么word显示不了公式