鱼C论坛

 找回密码
 立即注册
查看: 97|回复: 9

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

[复制链接]
发表于 2025-8-17 11:02:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
#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[OMML Code Below]\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&#178; + 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[10];
    int argCount = 0;

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

    // 反转参数顺序
    if (argCount > 0) {
        for (int i = 0; i < argCount / 2; i++) {
            VARIANT temp = varArgs[i];
            varArgs[i] = varArgs[argCount - 1 - i];
            varArgs[argCount - 1 - i] = 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&#178;/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&#178;/b
        L"<m:num>"
        L"<m:sSup>"  // 上标 a&#178;
        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已打开。"));
        }
}
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-8-17 11:04:47 | 显示全部楼层
根据提供的代码,Word无法显示公式的主要原因有以下几点:

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


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

  3. // 正确格式应包含命名空间
  4. CString correctOmml =
  5.     L"<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">"
  6.     L"<m:f>...</m:f></m:oMath>";
复制代码


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


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


---

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


#### 2. 生成有效OMML
  1. CString FounderToOmmlConverter::CreateFractionOmml(const CString& num, const CString& den) {
  2.     return
  3.         L"<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">"
  4.         L"<m:f><m:num><m:r>" + num + L"</m:r></m:num>"
  5.         L"<m:den><m:r>" + den + L"</m:r></m:den></m:f>"
  6.         L"</m:oMath>";
  7. }
复制代码


#### 3. 修正插入逻辑
  1. void CMFCApplication1Dlg::OnBnClickedInsert() {
  2.     if (m_strOmml.IsEmpty()) return;
  3.    
  4.     // 正确使用转换后的公式
  5.     if (FounderToOmmlConverter::InsertToWord(m_strOmml)) {
  6.         AfxMessageBox(_T("插入成功!"));
  7.     }
  8. }
复制代码


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


### 验证步骤
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 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 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>";
} 这一行的代码显示报错
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-8-17 11:36:13 | 显示全部楼层
找不到文本运算符
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-8-17 11:37:41 | 显示全部楼层
janeyjhon 发表于 2025-08-17 11:35
CString FounderToOmmlConverter::CreateFractionOmml ...


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

  1. CString FounderToOmmlConverter::CreateFractionOmml(const CString& num, const CString& den) {
  2.     return
  3.         L"<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">"
  4.         L"<m:f>"
  5.         L"<m:num>" + num + L"</m:num>"
  6.         L"<m:den>" + den + L"</m:den>"
  7.         L"</m:f>"
  8.         L"</m:oMath>";
  9. }
复制代码


关键修正点:

1. 字符串字面量需要显式转换为CString:
  
  1. // 错误写法
  2.    L"text" + cstringVar + L"text"
  3.    
  4.    // 正确写法
  5.    CString(L"text") + cstringVar + CString(L"text")
复制代码


2. 更推荐的格式化写法(避免拼接错误):
  1. CString FounderToOmmlConverter::CreateFractionOmml(const CString& num, const CString& den) {
  2.     CString xml;
  3.     xml.Format(
  4.         L"<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">"
  5.         L"<m:f>"
  6.         L"<m:num>%s</m:num>"
  7.         L"<m:den>%s</m:den>"
  8.         L"</m:f>"
  9.         L"</m:oMath>",
  10.         num.GetString(), den.GetString());
  11.     return xml;
  12. }
复制代码


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


4. 完整的OMML结构应该是:
  1. <m:oMath xmlns:m="命名空间">
  2.   <m:f> <!-- 分式 -->
  3.     <m:num>分子</m:num>
  4.     <m:den>分母</m:den>
  5.   </m:f>
  6. </m:oMath>
复制代码


验证建议:
1. 调试时输出生成的XML:
  1. TRACE(L"Generated OMML: %s\n", xml);
复制代码

2. 将输出的XML复制到文本编辑器,检查标签是否闭合
3. 在Word中手动粘贴XML测试(通过"插入-对象-公式")

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 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());这样写对吗
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-8-17 13:29:38 | 显示全部楼层
janeyjhon 发表于 2025-08-17 13:28
CString ommlXml;
ommlXml.Format(
     L""
     ...


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

  1. CString FounderToOmmlConverter::CreateFractionOmml(const CString& numerator, const CString& denominator) {
  2.     CString ommlXml;
  3.     ommlXml.Format(
  4.         L"<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">"
  5.         L"<m:f>"  // 分式开始
  6.         L"<m:num>"  // 分子开始
  7.         L"<m:r><m:t>%s</m:t></m:r>"  // 分子内容
  8.         L"</m:num>"  // 分子结束
  9.         L"<m:den>"  // 分母开始
  10.         L"<m:r><m:t>%s</m:t></m:r>"  // 分母内容
  11.         L"</m:den>"  // 分母结束
  12.         L"</m:f>"  // 分式结束
  13.         L"</m:oMath>",  // 公式结束
  14.         numerator.GetString(),
  15.         denominator.GetString());
  16.     return ommlXml;
  17. }
复制代码


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

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


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


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


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

  5.     CString ommlXml;
  6.     ommlXml.Format(
  7.         L"<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">"
  8.         L"<m:f>"
  9.         L"<m:num><m:r><m:t>%s</m:t></m:r></m:num>"
  10.         L"<m:den><m:r><m:t>%s</m:t></m:r></m:den>"
  11.         L"</m:f>"
  12.         L"</m:oMath>",
  13.         safeNum.GetString(),
  14.         safeDen.GetString());
  15.    
  16.     return ommlXml;
  17. }

  18. // XML转义辅助函数
  19. CString FounderToOmmlConverter::EscapeXml(const CString& input) {
  20.     CString result = input;
  21.     result.Replace(L"&", L"&amp;");
  22.     result.Replace(L"<", L"&lt;");
  23.     result.Replace(L">", L"&gt;");
  24.     result.Replace(L""", L"&quot;");
  25.     return result;
  26. }
复制代码


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


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


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

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 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">报错
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-8-17 13:35:09 | 显示全部楼层
出现红色波浪线
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-8-17 13:38:57 | 显示全部楼层
janeyjhon 发表于 2025-08-17 13:34
L""这个里面写成这样没报错写成这样报错


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

### 正确写法(不会报错):
  1. CString ommlXml;
  2. ommlXml.Format(
  3.     L"" // 注意这里没有逗号
  4.     L""
  5.     L""
  6.     L"%s" // 分子
  7.     L""
  8.     L""
  9.     L""
  10.     L""
  11.     L"%s" // 分母
  12.     L""
  13.     L""
  14.     L""
  15.     L"",
  16.     numerator.GetString(),
  17.     denominator.GetString());
复制代码


### 错误写法(会报错):
  1. CString ommlXml;
  2. ommlXml.Format(
  3.     L"", // 这里有逗号
  4.     L"", // 这里有逗号
  5.     L"", // 这里有逗号
  6.     L"%s", // 这里有逗号
  7.     ...); // 错误:Format函数不接受多个字符串参数
复制代码


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

### 更安全的写法(推荐):
  1. CString ommlXml;
  2. ommlXml.Format(
  3.     L""
  4.     L""
  5.     L"%s"
  6.     L""
  7.     L""
  8.     L""
  9.     L"%s"
  10.     L""
  11.     L"",
  12.     numerator.GetString(),
  13.     denominator.GetString());
复制代码


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

  5. // 简化测试2:XML结构
  6. CString xmlTest =
  7.     L""
  8.     L""
  9.     L"分子"
  10.     L""
  11.     L""
  12.     L""
  13.     L"分母"
  14.     L""
  15.     L"";
  16. 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 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-8-26 02:52

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表