【问题标题】:How to read/write excel checkbox status in c++如何在 C++ 中读取/写入 excel 复选框状态
【发布时间】:2013-03-27 21:48:49
【问题描述】:

我正在处理一个问题,该问题需要我在 Excel 中的给定单元格中读取/写入复选框的状态。

我知道你可以访问activeX控件,可以使用COM/OLE访问。但是,我无法找到任何解决此问题的方法。事实上,我什至不确定您是否可以使用行列访问复选框。我研究了activeX复选框的属性。找到了 top 和 left 属性,但没有找到 row-column

我想问一下: 1.有没有直接的方法可以做到这一点。 2.如果不是,任何间接方式,比如以某种方式获取顶部/左列,然后获取行/列位置并比较两者。 3. 有没有办法对表单控件做同样的事情?

【问题讨论】:

    标签: c++ excel com ole


    【解决方案1】:

    感谢大家的回复。我在 MSDN 上发现了一些有用的东西。稍微修改了一下,分享给大家。。谢谢大家的支持

    (原谅格式)

    msdn 中的 Autowrap() 函数完全未经编辑:

    HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, LPOLESTR ptName, int cArgs...) {
    // Begin variable-argument list...
    va_list marker;
    va_start(marker, cArgs);
    
    if(!pDisp) {
        MessageBox(NULL, "NULL IDispatch passed to AutoWrap()", "Error", 0x10010);
        _exit(0);
    }
    
    // Variables used...
    DISPPARAMS dp = { NULL, NULL, 0, 0 };
    DISPID dispidNamed = DISPID_PROPERTYPUT;
    DISPID dispID;
    HRESULT hr;
    char buf[200];
    char szName[200];
    
    
    // Convert down to ANSI
    WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);
    
    // Get DISPID for name passed...
    hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
    if(FAILED(hr)) {
        sprintf(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx", szName, hr);
        MessageBox(NULL, buf, "AutoWrap()", 0x10010);
        _exit(0);
        return hr;
    }
    
    // Allocate memory for arguments...
    VARIANT *pArgs = new VARIANT[cArgs+1];
    // Extract arguments...
    for(int i=0; i<cArgs; i++) {
        pArgs[i] = va_arg(marker, VARIANT);
    }
    
    // Build DISPPARAMS
    dp.cArgs = cArgs;
    dp.rgvarg = pArgs;
    
    // Handle special-case for property-puts!
    if(autoType & DISPATCH_PROPERTYPUT) {
        dp.cNamedArgs = 1;
        dp.rgdispidNamedArgs = &dispidNamed;
    }
    
    // Make the call!
    hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &dp, pvResult, NULL, NULL);
    if(FAILED(hr)) {
        sprintf(buf, "IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx", szName, dispID, hr);
        MessageBox(NULL, buf, "AutoWrap()", 0x10010);
        _exit(0);
        return hr;
    }
    // End variable-argument section...
    va_end(marker);
    
    delete [] pArgs;
    
    return hr;
    }
    

    这是编辑后的 ​​main() 函数:

    int main()
    {
     // Initialize COM for this thread...
       CoInitialize(NULL);
    
       // Get CLSID for our server...
       CLSID clsid;
       HRESULT hr = CLSIDFromProgID(L"Excel.Application", &clsid);
    
       if(FAILED(hr)) {
    
          ::MessageBox(NULL, "CLSIDFromProgID() failed", "Error", 0x10010);
          return -1;
       }
    
       // Start server and get IDispatch...
       IDispatch *pXlApp;
       hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pXlApp);
    
       if(FAILED(hr)) {
          ::MessageBox(NULL, "Excel not registered properly", "Error", 0x10010);
          return -2;
       }
    
       // Make it visible (i.e. app.visible = 1)
       {
          VARIANT x;
          x.vt = VT_I4;
          x.lVal = 1;
          AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, L"Visible", 1, x);
       }
    
       // Make it visible (i.e. app.visible = 1)
       {
          VARIANT x;
          x.vt = VT_BSTR;
          x.bstrVal = ::SysAllocString (L"D:\\");
          AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, L"DefaultFilePath", 1, x);
       }
    
       // Get Workbooks collection
       IDispatch *pXlBooks;
       {
          VARIANT result;
      VariantInit(&result);
      AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, L"Workbooks", 0);
      pXlBooks = result.pdispVal;
       }
    
       // Call Workbooks.Add() to get a new workbook...
       IDispatch *pXlBook;
       {
          VARIANT parm;
      parm.vt = VT_BSTR;
      parm.bstrVal = ::SysAllocString(L"a.xlsx");
    
      VARIANT result;
      VariantInit(&result);
      AutoWrap(DISPATCH_METHOD, &result, pXlBooks, L"Open", 1,parm);
      pXlBook = result.pdispVal;
        }
    
       // Get ActiveSheet object
       IDispatch *pXlSheet;
       {
    enter code here
      VARIANT result;
      VariantInit(&result);
      AutoWrap(DISPATCH_PROPERTYGET, &result, pXlBook, L"ActiveSheet",0);
      if(result.pdispVal != NULL)
        pXlSheet = result.pdispVal;
       }
    
       //Get shapes collection..
        IDispatch *pShapes;
        {
           VARIANT result;
       VariantInit(&result);
       AutoWrap(DISPATCH_PROPERTYGET, &result, pXlSheet, L"Shapes",0);
       pShapes = result.pdispVal;
    }
    
    int nControls;  
    {
       VARIANT result;
       VariantInit(&result);
       AutoWrap(DISPATCH_PROPERTYGET, &result, pShapes, L"Count",0);
       if(result.vt == VT_I4)
           nControls = result.lVal;
    }
    
    for(int i = 1; i <= nControls; ++i) {
    
        IDispatch * pShape;
    
        {  
            VARIANT x;
            x.vt = VT_I4;
            x.lVal = i;
    
            VARIANT result;
            VariantInit(&result);
            AutoWrap(DISPATCH_METHOD, &result, pShapes, L"Item",1, x);
            pShape = result.pdispVal;
        }   
    
        IDispatch *pControlFormat = NULL;
        {
            VARIANT result;
            VariantInit(&result);
            AutoWrap(DISPATCH_PROPERTYGET, &result, pShape, L"Type",0);
    
            // msoFormControl
            if(result.lVal == 8)
            {
                VARIANT result0;
                VariantInit(&result0);
                AutoWrap(DISPATCH_PROPERTYGET, &result0, pShape, L"FormControlType",0);
    
                // xlCheckBox
                if(result0.lVal == 1) {
    
                    // Get range containing cell to be tested
                    VARIANT result2;
                    VariantInit(&result2);
                    IDispatch * range;
                    {
                        VARIANT param1;
                        param1.vt= VT_BSTR;
                        param1.bstrVal = ::SysAllocString(L"F5");
    
                        VARIANT result3;
                        VariantInit(&result3);
                        AutoWrap(DISPATCH_PROPERTYGET, &result3, pXlSheet,L"Range",1,param1);
                        range = result3.pdispVal;
                    }
    
                    // Get the top, left, bottom, right of cell
                    DOUBLE top, left, bottom, right;
                    {
                        VARIANT result4;
                        VariantInit(&result4);
    
                        AutoWrap(DISPATCH_PROPERTYGET,&result4, range,L"Top",0);
                        top = result4.dblVal;
    
                        AutoWrap(DISPATCH_PROPERTYGET,&result4, range,L"Left",0);
                        left = result4.dblVal;
    
                        AutoWrap(DISPATCH_PROPERTYGET,&result4, range,L"Height",0);
                        bottom = top + result4.dblVal;
    
                        AutoWrap(DISPATCH_PROPERTYGET,&result4, range,L"Width",0);
                        right = left + result4.dblVal;
                    }
                    range->Release();
    
                    // Get the top, left of checkbox
                    FLOAT shapetop, shapeleft;
                    {
                        VARIANT result5;
                        VariantInit(&result5);
    
                        AutoWrap(DISPATCH_PROPERTYGET,&result5, pShape,L"Top",0);
                        shapetop = result5.fltVal;
    
                        AutoWrap(DISPATCH_PROPERTYGET,&result5, pShape,L"Left",0);
                        shapeleft = result5.fltVal;
                    }
    
                    // Get hold of control format
                    VARIANT result1;
                    VariantInit(&result1);
                    AutoWrap(DISPATCH_PROPERTYGET, &result1, pShape, L"ControlFormat",0);
                    pControlFormat = result1.pdispVal;
    
    
    
                    // Check if the checkbox if within the range boundary. If yes, then check it, else dont
                    if((top <= shapetop) && (bottom >= shapetop) && (left <= shapeleft) && (right >= shapeleft)) {
    
                        // Perpare the parameter
                        VARIANT parm;
                        VARIANT_BOOL t = VARIANT_TRUE;;
                        parm.vt = VT_BOOL;
                        parm.pboolVal = &t;
    
                        AutoWrap(DISPATCH_PROPERTYPUT, NULL, pControlFormat, L"Value", 1, parm);
                    }                   
                }
            }
        }
    
        if(pControlFormat != NULL) pControlFormat->Release();
        pShape->Release();
    }
    
       // to save when we tell Excel to quit...
       {
           VARIANT x;
      x.vt = VT_I4;
      x.lVal = 1;
      AutoWrap(DISPATCH_METHOD, NULL, pXlBook, L"Save", 0);
        }
    
       // Wait for user...
       ::MessageBox(NULL, "All done.", "Notice", 0x10000);
    
       // Tell Excel to quit (i.e. App.Quit)
       AutoWrap(DISPATCH_METHOD, NULL, pXlApp, L"Quit", 0);
    
       // Release references...
    
       pShapes->Release();
    
       pXlSheet->Release();
       pXlBook->Release();
       pXlBooks->Release();
       pXlApp->Release();
    
    
       // Uninitialize COM for this thread...
       CoUninitialize();
    
       return 0;
    }
    

    更多详情:
    http://support.microsoft.com/kb/216686
    http://msdn.microsoft.com/en-us/library/office/bb149081%28v=office.12%29.aspx

    【讨论】:

      【解决方案2】:

      尽管看起来,电子表格中不包含复选框;它们被画在上面。所以他们对行或列一无所知,你不能那样引用他们。

      复选框保存在OLEObjects 集合中。这个来自here 的示例应该为您提供所需的信息:

      Private Sub chkFinished_Click()
          ActiveSheet.OLEObjects("CheckBox1").Object.Value = 1
      End Sub
      

      【讨论】:

        【解决方案3】:

        我不认为复选框是“在单元格中”,有几种方法(我能想到的)可以获得值。

        首先:不要检查复选框的值,检查其链接单元格的值(如果已分配)

        第二:使用类似下面的伪代码来确定哪个控件在哪里。

        ForEach Sheet.Controls As Control
          If Control.Type = Checkbox Then
            Row = 1
            YPos = 0
            Do
              If YPos + Sheet.Rows(Row).RowHeight > Control.Top Then
                Exit Do
              Else
                YPos = YPos + Sheets.Rows(Row).RowHeight
              End If
            Loop
            Column = 1
            XPos = 0
            Do
              If XPos + Sheet.Columns(Column).ColumnWidth > Control.Left Then
                Exit Do
              Else
                XPos = XPos + Sheets.Columns(Column).ColumnWidth
              End If
            Loop
            Debug.Print Control.Name & " is in Cell(" & XPos & ", " & YPos & ")"
          End If
        End ForEach
        

        【讨论】:

        • 嗨@nick,感谢您对算法的帮助..正如您所见,我在我的问题中提到了类似的技术。我正在寻找的是有关 C++ 中 COM 的一些帮助。您能帮我处理用于运行上述算法的消息吗?我自己找不到任何相关的东西..
        • 我从来没有真正使用过 com,所以不确定。您在使用 excel 互操作吗?我的代码只会在你的脑海中运行,伪代码也是如此。我假设 C++ 有某种对象库,比如 VBA?您需要查看控件集合的可用对象以及与行高和宽度有关的内容。
        猜你喜欢
        • 2014-02-15
        • 1970-01-01
        • 2013-05-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-15
        • 2013-10-05
        • 1970-01-01
        相关资源
        最近更新 更多