2014-11-14

Could not load file or assembly Microsoft.Office.BusinessApplications.Fba VSTO error

Here is an error that plagued me (occasionally) for months.  Since Office 2013 was rolled out, we would sometimes get a call from a user trying to install an older Visual Studio Tools for Office add-in. Not sure how much of the specifics are at play here, but these were the facts in this situation:

Windows version: Windows 7
New Office version: Office 2013
VSTO Add-In version: Office 2007
VSTO Add-In application: Excel

The full text of the error was:

The value of the property 'type' cannot be parsed.  The error is: Could not load file or assembly 'Microsoft.Office.BusinessApplications.Fba, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9r29c' or one of its dependencies. The system cannot find the file specified. (C:\Program Files (x86)\Common Files\Microsoft Shared\VSTO\10.0\VSTOInstaller.exe.Config line 10)

clip_image002

For months, all I could find was this post suggesting to repair Office.  Unfortunately, it failed to correct the issue and we were forced to uninstall and reinstall Office 2013 on a number of machines.  After a successful reinstall of Office, the VSTO installation ran to completion. This is a pain for both the support personnel and the users.

Today I found and successfully used the post at the end of this thread.  My experience wasn’t quite as clean as described, but it worked.  Here’s what I did:

  1. I right-clicked on setup.exe (installer for VSTO application) and chose ‘Run as administrator’. Answered [Yes] at the UAC prompt.
  2. I received a message informing that I already had the VSTO application installed.  The application was not showing in Excel, however this was already a step further than previous installation attempts.
  3. I pushed forward, found the VSTO application in Programs and Features and uninstalled it.
  4. Finally, I ran setup.exe again - this time not using ‘Run as administrator’.  It installed successfully and runs as expected. (Plan was to use Run as admin again if it failed, but it wasn’t required.)

Hope this helps others, too.

Later.

2014-01-06

Improvement to SAP JSON RFC service

In my last post, I described an essential piece of the SAP UI5 development as being the JSON HTTP handler.  This week I enhanced the service to allow more complex data types to be passed into the RFC.

The issue I encountered was this: the function module I was calling had input values that were more than simple data types.  It appears that the JSON HTTP handler as designed by Cesar would allow me to pass the JSON data directly in from HTTP.  However, since my SAP UI5 in JavaScript skills are currently much weaker than my ABAP I wanted a simple way to pass this data as a URL parameter to the SAP HTTP handler and let it do the work of converting the input to JSON.  Here’s what I did. 

I added two new private methods to the HTTP hander class:

MAP_QS2JSON: A recursive routine that reads the query string and determines whether the name is a table/structure/simple type.  This determination is made by appending [], {}, or nothing to the name of the query parameter.

METHOD map_qs2json.
*****************************************************************zkegm**
* Purpose: Map query string name/values to JSON format.  Rewrote this  *
*          to handle table data input also.                            *
*----------------------------------------------------------------------*
* -Changed by-       -Date-     -Req #-  -Action-                      *
* Jeff Woehler       2014.01.01 PLM8772  Initial.                      *
************************************************************************
 
* We map the query string to a simple JSON input. Handy for REST style queries.
* The query string may come from GET requests in the url and content data in
* POST request in x-www-form-urlencoded. ICF handles this perfectly and mixes both!! Great!!
 
  DATA: lv_lines                      TYPE i,
        lv_idx                        TYPE i.
  DATA: ls_save_nvp                   TYPE ihttpnvp,
        lt_nvp                        TYPE tihttpnvp,
        lt_tr_nvp                     TYPE tihttpnvp,
        lv_tr_json                    TYPE string,
        lv_name                       TYPE string,
        lv_len                        TYPE i,
        lv_table                      TYPE xfeld.
  FIELD-SYMBOLS <ls_nvp> TYPE ihttpnvp.
 
  lt_nvp[] = it_qs_nvp[].
  SORT lt_nvp[] BY name.
 
  lv_lines = lines( lt_nvp[] ).
  CLEAR: lv_idx, lv_table.
 
  LOOP AT lt_nvp ASSIGNING <ls_nvp>.
    ADD 1 TO lv_idx.
 
    " ABAP is upper case internally anyway.
    TRANSLATE <ls_nvp>-name TO UPPER CASE.
 
    IF lv_table = abap_true.
      IF <ls_nvp>-name NE ls_save_nvp-name.
        CONCATENATE rv_json ']'
               INTO rv_json RESPECTING BLANKS.
        lv_table = abap_false.
      ENDIF.
    ENDIF.
 
    IF lv_idx NE 1.
      CONCATENATE rv_json ','
             INTO rv_json RESPECTING BLANKS.
    ENDIF.
 
    IF <ls_nvp>-name CS '[]'.
      lv_len = strlen( <ls_nvp>-name ) - 2.
      lv_name = <ls_nvp>-name(lv_len).
 
      IF <ls_nvp>-name NE ls_save_nvp-name.
        lv_table = abap_true.
        CONCATENATE rv_json '"' lv_name '":['
               INTO rv_json RESPECTING BLANKS.
      ENDIF.
 
      lt_tr_nvp = map_tblrow2nvp( iv_value      = <ls_nvp>-value
                                  iv_delimiter  = ';' ).
      lv_tr_json = map_qs2json( lt_tr_nvp ).
      CONCATENATE rv_json lv_tr_json
             INTO rv_json RESPECTING BLANKS.
    ELSEIF <ls_nvp>-name CS '{}'.
      lv_len = strlen( <ls_nvp>-name ) - 2.
      lv_name = <ls_nvp>-name(lv_len).
      lt_tr_nvp = map_tblrow2nvp( iv_value      = <ls_nvp>-value
                                  iv_delimiter  = ';' ).
      lv_tr_json = map_qs2json( lt_tr_nvp ).
      CONCATENATE rv_json '"' lv_name '":' lv_tr_json
             INTO rv_json RESPECTING BLANKS.
    ELSE.
      CONCATENATE rv_json '"' <ls_nvp>-name '":"' <ls_nvp>-value '"'
             INTO rv_json RESPECTING BLANKS.
    ENDIF.
 
    IF lv_idx < lv_lines.
    ENDIF.
 
    ls_save_nvp = <ls_nvp>.
  ENDLOOP.
 
  CONCATENATE '{' rv_json '}' INTO rv_json.
 
ENDMETHOD.

MAP_TBLROW2NVP: This routine splits the input structure value at a ‘;’ and creates a new table of name/value pairs for which to generate a JSON string.



METHOD map_tblrow2nvp.
*****************************************************************zkegm**
* Purpose: Map table-row to name/value pair.                           *
*----------------------------------------------------------------------*
* -Changed by-       -Date-     -Req #-  -Action-                      *
* Jeff Woehler       2014.01.01 PLM8772  Initial.                      *
************************************************************************
 
  DATA: lt_params                     TYPE TABLE OF string,
        lv_param                      TYPE string,
        ls_nvp                        TYPE ihttpnvp.
 
  SPLIT iv_value AT iv_delimiter INTO TABLE lt_params[].
 
  LOOP AT lt_params INTO lv_param.
    SPLIT lv_param AT '=' INTO ls_nvp-name ls_nvp-value.
    APPEND ls_nvp TO rt_nvp[].
  ENDLOOP.
 
ENDMETHOD.

Caveat: this improvement obviously wouldn’t handle nested levels of structure - example: a structure containing a field that is a structure.  But this is (I hope) a tiny improvement to the the wonderful work that Cesar Martin put together.


Now I can have an SAP UI5 refresh routine like this and ABAP will handle the JSON. 



  • I_TEMPORARY_FLAG: simple value
  • I_SEARCH_DATE: structure of fields – single row
  • IT_WERKS: table of data – multiple rows


refresh: function(event) {
    sap.ui.getCore().setModel(undefined);
    
        var url = 'http://kww-h10s.kimball.com:8002/keg/rfc/Z_E_RFC_GCS_AVG_TURN_TIME/lc?I_TEMPLATE=00001';
      
        var cbTemp = this.byId('cbTemp');
        if (cbTemp.getChecked() == true) {
            url += '&I_TEMPORARY_FLAG=X';
        } 
        
        var date1 = this.byId('date1');
        var date2 = this.byId('date2');
        url += '&I_SEARCH_DATE{}=SIGN=I;OPTION=BT;LOW=' + date1.getYyyymmdd() + ';HIGH=' + date2.getYyyymmdd();
        
        var lstPlant = this.byId('lstPlant');
        var plants = lstPlant.getSelectedKeys();
        for (var idx = 0; idx < plants.length; idx++) {
            url += '&IT_WERKS[]=SIGN=I;OPTION=EQ;WERKS_LOW=' + plants[idx];
        }
      
      var oModel4 = new sap.ui.model.json.JSONModel(url);
      sap.ui.getCore().setModel(oModel4);
}

Get my update to the handler here.  Be sure to read the spec and documentation from the original developer here.


Enjoy!