תיקון שגיאות בקוד שהומר

תוסף Macro Converter ל-Google Workspace מבצע אוטומטית את רוב תהליך ההמרה, אבל יכול להיות שתצטרכו לבצע התאמות בחלק מממשקי ה-API ובפריטים אחרים כדי להשלים את הקוד.

המדריך הזה יעזור לכם להבין את קובצי Apps Script (קובצי GS) שנוספו לפרויקט, לפרש את סוגי השגיאות השונים וללמוד איך לתקן שגיאות.

הסבר על קובצי Apps Script שנוספו לפרויקט

קבצים נוספים של GS מתווספים לפרויקט Apps Script כדי לעזור לכם:

  • הגדרת קבועים וערכים של VBA שלא קיימים ב-Apps Script.
  • הטמעה של ממשקי API שלא הומרו.
  • פתרון וריאציות.

הקבצים הבאים של GS מתווספים לפרויקט Apps Script:

  • Library.gs
  • Unimplemented_constructs.gs
  • Variant_resolutions.gs

Library.gs

באופן כללי, לא צריך לשנות שום דבר בקובץ library.gs.

בקובץ library.gs מוגדרות פונקציות וקבועים שנעשה בהם שימוש בקוד VBA שלא קיימים ב-Apps Script. כך קוד Apps Script החדש דומה יותר לקוד VBA. בנוסף, לא צריך לחזור על ההגדרות בכל פעם שמשתמשים בפונקציות או בקבועים מהקובץ library.gs.

Unimplemented_constructs.gs

בקובץ unimplemented_constructs.gs מפורטים מבנים או ממשקי API שלא ניתן היה להמיר באמצעות Macro Converter. כנראה שתצטרכו לשנות את הקובץ הזה כדי שהקוד יפעל כמו שרציתם.

דוגמה: Window.Activate

הנה דוגמה ל-API לא נתמך בשם Window.Activate. הכלי Macro Converter יוצר פונקציית Apps Script חדשה עם שם דומה ומגדיר אותה בקובץ unimplemented_constructs.gs. מכיוון שהפונקציה של VBA לא נתמכת, הפונקציה החדשה של Apps Script מחזירה חריגה.

הפונקציה החדשה מתווספת לקוד Apps Script שהומר בכל מקום שבו נעשה שימוש ב-API המקורי בקוד VBA.

אם מצאתם פתרון עקיף ליצירת ההתנהגות של ה-API המקורי, אתם צריכים לעדכן רק את ההגדרה של הפונקציה בקובץ unimplemented_constructs.gs. אחרי שמגדירים את הפונקציה, היא חלה בכל מקום שבו היא מופיעה בפרויקט Apps Script.

הנה דוגמה לקוד:

קוד VBA מקורי

Window.activate()

קוד Apps Script שהומר, נוסף בתוך השורה

_api_window_activate();

הגדרה של פונקציה נוספה לקובץ unimplemented_constructs.gs

/**
 * Could not convert window.activate API. Please add relevant code in the
 * following function to implement it.
 * This API has been used at the following locations in the VBA script.
 *     module1 : line 3
 *
 * We couldn't find an equivalent API in Apps Script for this VBA API. Please
 * reconsider if this function call is critical, otherwise consider implementing
 * it in a different way.
 */
function _api_window_activate(CallingObject) {
  ThrowException("API window.activate not supported yet.");
}

Variant_resolutions.gs

הקובץ variant_resolutions.gs נוסף לפרויקט Apps Script אם אי אפשר לקבוע את הסוג של אובייקט. זה יכול לקרות מכמה סיבות, למשל אם ל-API יש כמה סוגי החזרה או אם האובייקט מוצהר כמשתנה בעצמו.

הכלי להמרת פקודות מאקרו מוסיף לקובץ זה פונקציה חדשה בשם __handle_resolve_<api> שמחליפה את ה-API הרלוונטי ועוזרת לקבוע את סוג האובייקט.

במקרים מסוימים, יכול להיות שתצטרכו לעדכן את הפונקציה __handle_resolve_<api> כדי להצהיר באופן ידני על סוג האובייקט. מידע נוסף מופיע במאמר בנושא סוג אובייקט לא נתמך.

דוגמה: השיטה name

הרבה סוגי אובייקטים ב-VBA מגדירים שיטת name. בדרך כלל, המקבילה ב-Apps Script היא getName, אבל לא לכל סוג אובייקט. יכולים להיות כמה מקרים חלופיים:

  • השיטה המקבילה של האובייקט נקראת בשם שונה מ-getName.
  • לאובייקט אין API של Apps Script שאפשר להשתמש בו כדי לקבל את השם שלו.
  • אין אובייקט מקביל ב-Apps Script.

אם סוג האובייקט לא נקבע, הכלי Macro Converter יוצר פונקציה חדשה בשם __handle_resolve_name בקובץ variant_resolutions.gs.

הנה דוגמה לקוד:

קוד VBA מקורי

a = Selection.name

במקרה הזה, ה-method‏ name נקראת בבחירה הנוכחית. הבחירה יכולה להיות אובייקט Sheet או אובייקט Shape. אם זה אובייקט Sheet, התרגום הוא getName, אבל אם זה אובייקט Shape, אין לו מקבילה ב-Apps Script.

קוד Apps Script שהומר, נוסף בתוך השורה

a = __handle_resolve_name({}, getActiveSelection(), {});

הפונקציה __handle_resolve_name הבאה מתווספת לקובץ variant_resolution.gs כדי לפתור בעיות שקשורות לסוגים שונים של אובייקטים. הפונקציה בודקת את סוג האובייקט, ואז משתמשת ב-getName אם הוא נתמך, או מחזירה שגיאה אם getName לא נתמך.

הגדרה של פונקציה נוספה לקובץ variant_resolution.gs

function __handle_resolve_name(ExecutionContext, CallingObject, params_map) {
  var found_api_variant = false;
  var return_value;
  if (String(CallingObject) == "Sheet") {
    if (!ExecutionContext.isLhs) {
      return_value = CallingObject.getName();
      found_api_variant = true;
    }
  }
  if (CallingObject instanceof ChartInSheet) {
    if (!ExecutionContext.isLhs) {
      return_value = CallingObject.getName();
      found_api_variant = true;
    }
  }
  if (!found_api_variant) {
    ThrowException("API .name not supported yet.");
  }
  return return_value;
}

איתור שגיאות

אם נתקלים בשגיאה בקוד Apps Script שהומר, ההודעה מציינת את סוג השגיאה ואת המיקום שלה. הפורמט של הודעת השגיאה תלוי בסביבת זמן הריצה של Apps Script שבה אתם משתמשים.

אם אתם משתמשים בסביבת זמן הריצה V8 שמוגדרת כברירת מחדל, תוצג שגיאה שדומה לזו שמופיעה בהמשך:

_api_windows_active (unimplemented_constructs:2:3)

המשמעות היא שהשגיאה נמצאת בקובץ unimplemented_constructs.gs בשורה 2, תו 3.

אם אתם משתמשים בסביבת זמן הריצה Rhino שיצאה משימוש, תוצג שגיאה שדומה לזו שמופיעה בהמשך:

unimplemented_constructs:2 (_api_windows_active)

המשמעות היא שהשגיאה נמצאת בקובץ unimplemented_constructs.gs בשורה 2.

סוגי שגיאות

לפתור את רוב השגיאות שנתקלים בהן בקבצים unimplemented_constructs.gs ו-variant_resolution.gs שמתוארים למעלה.

אלה סוגי השגיאות שאתם עלולים להיתקל בהן:

Unimplemented API

ממשק API שלא הוטמע הוא ממשק API שלא ניתן להמיר מ-VBA ל-Apps Script באמצעות Macro Converter, ואין פתרון עקיף ידוע לממשק ה-API הזה.

ממשקי API שלא הוטמעו מתווספים בדרך כלל כפונקציות ריקות – לפעמים עם חתימות ריקות – לקובץ unimplemented_constructs.gs. אם לא ניתן לקבוע את סוג האובייקט, יכול להיות שה-API שלא הוטמע יתווסף לקובץ variant_resolution.gs.

בדוח התאימות שיצרתם לפני ההמרה, ה-API הזה מסומן כנדרשת בדיקה נוספת.

אם לא מתקנים את סוג ה-API הזה בקוד VBA לפני שממירים את הקובץ, כך הוא יופיע בפרויקט Apps Script:

/**
* Could not convert . Please add relevant code in the following
* function to implement it.
* This API has been used at the following locations in the VBA script.
*      : 
* We couldn't find an equivalent API in Apps Script for this VBA API. Please
* reconsider if this function call is critical, otherwise consider implementing
* it in a different way.
* @param param1 {}
* @param param2 {}
* ...
* @return {}
*/
function _api_<API_name>(param1, param2, ....) {
  ThrowException("API  not supported yet.");
}

תיקון שגיאות API שלא יושמו

להגדיר את ה-API שלא הוטמע באמצעות ממשקי Apps Script API קיימים או ספריות JS. לשם כך, בצע את הצעדים הבאים:

  1. פותחים את קוד Apps Script שהומר במיקום של השגיאה. איך מוצאים שגיאות
  2. מעל הפונקציה, קוראים את התגובה שנוספה. במקרים מסוימים, ההערה מציעה איך להטמיע את ה-API ב-Apps Script.
  3. אם לא הצלחתם להטמיע את ה-API ב-Apps Script, כדאי להסיר אותו מהקוד.
  4. אם לא הצלחתם למצוא פתרון עקיף או להסיר את ה-API הזה מהקוד, והמאקרו מחזיר את השגיאה הזו, לא תוכלו להמיר את המאקרו.

דוגמאות לשגיאות API שלא יושמו

הנה דוגמאות לתרחישי API שלא הוטמעו ואיך לפתור אותם:

דוגמה 1: אין API מקביל ב-Apps Script או שמדובר ב-API לא מוכר

בדוגמה הזו, Chart.Protect לא הומר אוטומטית כי אין דרך להגן על תרשים ב-Google Sheets.

/**
* Could not convert chart.protect API. Please add relevant code in the following
* function to implement it.
*
* This API has been used at the following locations in the VBA script.
*     sheet1 : line 3
* You can use the following Apps Script APIs to convert it.
*
* Comments : Auto conversion of Chart.Protect is not supported yet. If the API is
* critical for the workflow the user can implement the unimplemented handler
* method in the generated code, else comment out the throw statement.
*
* @param {Object} CallingObject represents the parent object using which the API
* has been called.
* @param {string} Password
* @param {boolean} DrawingObjects
* @param {boolean} Contents
* @param {boolean} Scenarios
* @param {boolean} UserInterfaceOnly
*
*/
function _api_chart_protect(
   CallingObject, Password, DrawingObjects, Contents, Scenarios,
   UserInterfaceOnly) {
 ThrowException('API chart.protect not supported yet.');
}
למרות שאי אפשר להגן על תרשים, אפשר להגן על טווח הנתונים של התרשים כדי שלא יהיה אפשר לשנות את הנתונים.

הנה הטמעה לדוגמה של הגנה על הטווח:
/**
* Could not convert chart.protect API. Please add relevant code in the following
* function to implement it.
* This API has been used at the following locations in the VBA script.
*     sheet1 : line 3
*
* You can use the following Apps Script APIs to convert it.
* Comments : Auto conversion of Chart.Protect is not supported yet. If the API
* is critical for the workflow the user can implement the unimplemented handler
* method in the generated code, else comment out the throw statement.
*
* @param {Object} CallingObject represents the parent object using which the API
* has been called.
* @param {string} Password
* @param {boolean} DrawingObjects
* @param {boolean} Contents
* @param {boolean} Scenarios
* @param {boolean} UserInterfaceOnly
*/
function _api_chart_protect(
  CallingObject, Password, DrawingObjects, Contents, Scenarios, UserInterfaceOnly) {
var ranges = CallingObject.getChart().getRanges();
for (var i = 0; i < ranges.length; i++) {
  // Note that this does not lock the range for the document owner.
  ranges[i].protect();
}
}
דוגמה 2: סוג אובייקט לא נתמך

אם סוג האובייקט לא ידוע, השגיאה 'API לא מיושם' מתווספת לקובץ variant_resolution.gs. בדוגמה הבאה מוצגת הרחבה של הדוגמה לשיטת VBA‏ name. מידע נוסף מפורט בvariant_resolution.gs.

בדוגמה הזו נסביר:

  1. איך השיטה name מומרת לפונקציה חדשה בקובץ variant_resolution.gs.
  2. איך קוראים לפונקציה החדשה בקוד שהומר.
  3. איך יוצרים פתרון עקיף ל-CommandBar, סוג אובייקט שלא נתמך, ב-Apps Script

1. מכיוון שהקוד שהומר לא יכול לקבוע את סוג האובייקט המדויק שמופעל על name, הכלי להמרת פקודות מאקרו יוצר פונקציה חדשה בשם __handle_resolve_name:

function __handle_resolve_name(ExecutionContext, CallingObject, params_map) {
 var found_api_variant = false;
 var return_value;
  if (String(CallingObject) == "Sheet") {
    if (!ExecutionContext.isLhs) {
      return_value = CallingObject.getName();
      found_api_variant = true;
    }
  }
  if (CallingObject instanceof ChartInSheet) {
    if (!ExecutionContext.isLhs) {
      return_value = CallingObject.getName();
      found_api_variant = true;
    }
  }
  if (!found_api_variant) {
    ThrowException('API .name not supported yet.');
  }
  return return_value;
}

‫2. נניח שקוד ה-VBA מגדיר פונקציה PrintName() שקוראת ל-API‏ name:

‘Defining a function that prints the name of the object in parameter
Sub PrintName(obj as Variant)
  Debug.Print obj.Name
End Sub
מכיוון שהפונקציה name()‎ נקראת באובייקט שהוא משתנה, הקוד שהומר לא יודע את סוג האובייקט בזמן ההמרה. קוד ה-Apps Script שהומר יקרא לפונקציה ‎ `__handle_resolve_name`:
function PrintName(obj) {
  Logger.log(_handle_resolve_name(obj));
}

‫3. נניח שקוד ה-VBA שלכם קורא לפונקציה PrintName() בסוג האובייקט CommandBar:

PrintName Application.CommandBars.item("Standard")
CommandBar לא נתמך ב-Apps Script, ולכן גם שתי השיטות שבהן נעשה שימוש בקוד VBA שלמעלה לא נתמכות.
  • Application.CommandBars(): ב-VBA, הפונקציה הזו מחזירה רשימה של כל האובייקטים מסוג CommandBar.
  • CommandBars.item(): ב-VBA, הפונקציה הזו מחזירה אובייקט CommandBar ספציפי.
מכיוון שסוג האובייקט הזה לא נתמך ב-Apps Script, הקוד שהומר יוצר את הפונקציות הבאות בקובץ unimplemented_constructs.gs שצריך להגדיר.
  • _api_application_commandbars()
  • _api_commandbars_item()
הפונקציות נקראות בקוד שהומר כמו שמוצג בהמשך:
PrintName(_api_commandbars_item(_api_application_commandbars(), "Standard")))

Heres how the new functions are added to the unimplemented_construct.gs file:

function _api_application_commandbars(CallingObject) {
  ThrowException('API application.commandbars not supported yet.');
}
function _api_commandbars_item(CallingObject, index) {
  ThrowException('API commandbars.item not supported yet.');
}

כדי שהפונקציות החדשות יפעלו, צריך לבצע את השלבים הבאים:

‫3.1 מגדירים סוג אובייקט חדש שיוצר את הפונקציות של CommandBars ואוסף חדש של CommandBars בדומה למה שקיים ב-VBA.

‫3.2 מוסיפים getName method לסוג האובייקט החדש.

שלבים 3.1 ו-3.2 מוצגים בקוד הבא. אובייקטים של תפריטים נוצרים כסוג אובייקט חדש שמדמה את ההתנהגות של CommandBars.

// Our Implementation of CommandBar using Menu objects.

function CommandBar(name) {
  this.name = name;
  // Create a menu object to represent the commandbar.
  this.menu = SpreadsheetApp.getUi().createMenu(name);
  // Create methods for retrieving or updating the name of the object
  this.getName = function() {
    return this.name;
  };
  this.updateName = function(name) {
    this.name = name;
  };
  // ========================================================================
  // Implement other methods of CommandBar objects that are used in the script.
  // =====================================================================
  return this;
}
// Our implementation of the collection of CommandBars that exists in VBA
function CommandBars() {
  this.commandBars = [];
  this.getCommandBar = function(name) {
    for (var i = 0; i < this.commandBars.length; i++) {
      if (!this.commandBars[i].getName() == name) {
        return this.commandBars[i];
      }
    }
    // No commandBar with the name exists, create a new one and return.
    var commandBar = new CommandBar(name);
    this.commandBars.push(commandBar);
    return commandBar;
  };
  return this;
}
// Create a global object that represents CommandBars collection.
var GlobalCommandBars = new CommandBars();

‫3.3 משנים את הפונקציה __handle_resolve_name בקובץ variant_resolution.gs כדי לטפל בסוג האובייקט החדש. מוסיפים קטע לפונקציה:

function __handle_resolve_name(ExecutionContext, CallingObject, params_map) {
 var found_api_variant = false;
 var return_value;
 if (String(CallingObject) == "Sheet") {
   if (!ExecutionContext.isLhs) {
     return_value = CallingObject.getName();
     found_api_variant = true;
   }
 }
 if (CallingObject instanceof ChartInSheet) {
   if (!ExecutionContext.isLhs) {
     return_value = CallingObject.getName();
     found_api_variant = true;
   }
 }
 // New section added below
 // ========================================================================
 if (CallingObject instanceof CommandBar) {
   objectExtend(params_map, {VALUETOSET: params_map.param0});
   if (ExecutionContext.isLhs) {
     // Call the setter method.
     CallingObject.updateName(params_map.VALUETOSET);
     found_api_variant = true;
   } else {
     // Getter is called, return the commandbar name,
     return_value = CallingObject.getName();
     found_api_variant = true;
   }
 }
 // ========================================================================
 // New section added above
 if (!found_api_variant) {
   ThrowException('API .name not supported yet.');
 }
 return return_value;
}

‫3.4 מגדירים את שתי הפונקציות שנוצרו בקובץ unimplemented_constructs.gs (_api_application_commandbars, _api_commandbars_item). השלב הזה מבטיח שהקריאות המקוריות של הפונקציה יפעלו.

//This is straightforward based on the implementation of a CommandBar and the
// CommandBars collection above:
function _api_application_commandbars(CallingObject) {
 return GlobalCommandBars;
}
function _api_commandbars_item(CallingObject, index) {
 return CallingObject.getCommandBar(index);
}

מבני שפה שלא יושמו

מבנה הוא רכיב של שפת הקוד ששולט בזרימת הביצוע או בתצוגת הנתונים. לדוגמה, לולאות, תוויות, אירועים ופקודות מעבר. רשימה של כל מבני ה-VBA מופיעה במאמר Statements (הצהרות) (VBA).

מבנים שהכלי להמרת פקודות מאקרו לא יכול להמיר נחשבים מבני שפה שלא הוטמעו.

אם הכלי להמרת מאקרו מזהה מבנה שפה שלא הוטמע, הוא מוסיף הערה TODO.

אין תמיכה במבנים הבאים של VBA:

תיקון שגיאות של מבני שפה שלא יושמו

  1. צריך לעדכן את הקוד כך שהלוגיקה לא תסתמך על מבנה השפה שלא נתמך.
  2. פותחים את קוד Apps Script שהומר במיקום השגיאה. איך מוצאים שגיאות
  3. בהתאם ללוגיקה של הקוד, מעדכנים אותו כך שלא יידרש מבנה שפה לא נתמך.
  4. אם אין לך דרך לשכתב את הקוד בלי מבנה השפה שלא נתמך, לא תהיה לך אפשרות להמיר את פקודת המאקרו הזו.

דוגמאות לשגיאות לגבי מבני שפה שלא הוטמעו

אחת מהמבנים הנפוצים ביותר בשפה שלא מיושמים היא הצהרת GoTo. מחליפים חלק מההצהרות של VBA GoTo בלולאות. בדוגמאות הבאות משתמשים בלולאות במקום בהצהרות GoTo.

דוגמה 1: החלפת GoTo ב-While Loop

קוד VBA מקורי
Sub Test()
 a = 0
 start: Debug.Print a
 While a < 100
   a = a + 1
   If a Mod 3 == 0
     Goto start
   End If
 Wend
End Sub
קוד מקביל ב-Apps Script
function test() {
 var a = 0;
 start: do {
   console.log(a);
   while (a < 100) {
     a = a + 1;
     if (a % 3 == 0) {
       continue start;
     }
   }
   break start;
 } while (true);
}

דוגמה 2: החלפת GoTo ב-For Loop

קוד VBA מקורי
Sub Test()
 a = 0
 For i = 1 to 100
   For j = 1 to 10
     a =a a + 1
     If i + j > 50
       GoTo endLoop
     End If
   Next j
 Next i
 endLoop: MsgBox a
End Sub
קוד מקביל ב-Apps Script
function test() {
 var a = 0;
 endLoop: for (var i = 1; i <= 100; i++) {
    for  (var j = 0; j <=10; j++) {
      If (i + j > 50) {
        break endLoop;
      }
    }
 }
 Browser.msgBox(a);
}

   break start;
 } while (true);
}

‫API עם תמיכה חלקית

במקרה של ממשקי API עם תמיכה חלקית, חלק מפרמטרי הקלט נתמכים ב-Apps Script וחלק לא.

לדוגמה, משתמשים ב-VBA API‏ legend_position כדי להגדיר את המקרא בתרשים של Excel. הוא תומך בכמה סוגים של ערכי קלט, כולל:

  • xlLegendPositionBottom: הצבת המקרא בתחתית התרשים.
  • xlLegendPositionCorner: הצבת המקרא בפינת התרשים.
  • xlLegendPositionCustom: הצבת המקרא במיקומים מותאמים אישית בתרשים.

ב-Apps Script יש קוד מקביל שתומך רק בחלק מהערכים האלה. הערכים הבאים לא נתמכים:

  • xlLegendPositionCorner
  • xlLegendPositionCustom

כדי לסמן ערכים לא נתמכים של ממשקי API שנתמכים באופן חלקי בקוד שהומר, מתווסף תנאי אימות לקובץ library.gs שבודק את הערכים האלה. לדוגמה:

if (position == xlLegendPositionCorner ||
     position == xlLegendPositionCustom) {
   position = _handle_legend_position_error(position);
}

אם התנאי של התיקוף מוצא אחד מהערכים שלא נתמכים, נוצרת פונקציית טיפול בשגיאות, _handle_<API_name>_error, בקובץ unimplemented_constructs.gs.

הפונקציה מחזירה שגיאת משתמש ולא מחליפה את הערך בערך נתמך. לדוגמה:

/**
* Throw error message for unsupported legend position.
* The VBA API Legend.Position which can take values xlLegendPositionTop,
* xlLegendPositionLeft, xlLegendPositionBottom, xlLegendPositionRight,
* xlLegendPositionCorner, xlLegendPositionCustom. It is partially supported in
* Apps Scripts that supports only a subset of the values (does not support
* xlLegendPositionCorner and xlLegendPositionCustom).
* @param {string} position
*/
function _handle_legend_position_error(position) {
// Please comment the throw statement and return a supported position value
// instead.
// Values that are supported here are xlLegendPositionTop,
// xlLegendPositionLeft, xlLegendPositionBottom, xlLegendPositionRight.
throw new Error(
   'Google Sheets does not support legend position: ' + position);
}

תיקון שגיאות API שנתמכות באופן חלקי

מגדירים את הפונקציה _handle_<API_name>_error כדי להחליף את הערכים הלא נתמכים בפתרון עקיף מקובל שמתאים לצרכים שלכם.

  1. פותחים את קוד Apps Script שהומר במיקום השגיאה. אפשר לקרוא על איתור שגיאות.
  2. קוראים את התגובה שמעל הפונקציה כדי להבין אילו ערכים נתמכים ואילו לא.
  3. לגבי הערכים שלא נתמכים, צריך לקבוע אילו ערכים נתמכים יכולים לשמש כתחליף מתאים.
  4. במקום זאת, צריך לעדכן את הפונקציה _handle_<API_name>_error כך שתחזיר ערך נתמך.
  5. אם לא מוצאים דרך להחליף את הערך שלא נתמך, אי אפשר להמיר את פקודת המאקרו הזו.

דוגמה לשגיאה ב-API שנתמך באופן חלקי

בדוגמה הבאה מוסבר על VBA API legend_position שהוזכר קודם. מידע נוסף זמין במאמר בנושא API עם תמיכה חלקית.

בדוגמה הבאה מוצג קוד VBA מקורי שמשתמש בערך לא נתמך, xlLegendPositionCustom.

Charts(1).Legend.Position = xlLegendPositionCustom

הכלי להמרת פקודות מאקרו מוסיף את הפונקציה הבאה לקובץ unimplemented_constructs.gs:

/**
* Throw error message for unsupported legend position.
* The VBA API Legend.Position which can take values xlLegendPositionTop,
* xlLegendPositionLeft, xlLegendPositionBottom, xlLegendPositionRight,
* xlLegendPositionCorner, xlLegendPositionCustom. It is partially supported in
* Apps Scripts that supports only a subset of the values (does not support
* xlLegendPositionCorner and xlLegendPositionCustom).
* @param {string} position
*/
function _handle_legend_position_error(position) {
// Please comment the throw statement and return a supported position value
// instead.
// Values that are supported here are xlLegendPositionTop,
// xlLegendPositionLeft, xlLegendPositionBottom, xlLegendPositionRight.
throw new Error(
   'Google Sheets does not support legend position: ' + position);
}

נדרשת עבודה ידנית

נדרשת עבודה ידנית – אפשר להמיר את VBA API ל-Apps Script, אבל צריך פתרון עקיף.

בדוח התאימות שיצרתם לפני ההמרה, סוג ה-API הזה מסומן כנתמך עם פתרונות עקיפים.

אם לא מתקנים את סוג ה-API הזה בקוד VBA לפני שממירים את הקובץ, כך הוא יופיע בפרויקט Apps Script:

/**
* Could not convert  API. Please add relevant code in the following
* function to implement it.
* This API has been used at the following locations in the VBA script.
*      : 
*
* You can use the following Apps Script APIs to convert it.
* Apps Script APIs : 
* Apps Script documentation links : 
*
* @param param1 {}
* @param param2 {}
* ...
* @return {}
*/
function _api_<API_name>(param1, param2, ....) {
 ThrowException("API  not supported yet.");
}

תיקון שגיאות שדורשות עבודה ידנית

מיישמים פתרון עקיף ל-API כדי שהוא יפעל כמצופה. 1. פותחים את קוד Apps Script שהומר במיקום של השגיאה. איך מוצאים שגיאות 1. כדאי לקרוא את התגובה שמעל הפונקציה כדי להבין באילו ממשקי API אפשר להשתמש כדי למצוא פתרון עקיף. 1. אם לא מצאתם פתרון עקיף מתאים, כדאי להסיר את ה-API מהקוד. 1. אם לא הצלחתם למצוא פתרון עקיף או להסיר את ה-API הזה מהקוד, והמאקרו שלכם מחזיר שגיאה, לא תוכלו להמיר את המאקרו הזה.

דוגמאות לשגיאות שדורשות עבודה ידנית

ריכזנו כאן דוגמאות לממשקי API שמופיעות בהם שגיאות שדורשות עבודה ידנית, והסבר איך לפתור אותן:

דוגמה 1: Autocorrect.Addreplacement

בדוגמה הבאה, אפשר להמיר את VBA API Autocorrect.Addreplacement, אבל צריך לעקוף את הבעיה. הכלי להמרת פקודות מאקרו מציע איך להטמיע את הפונקציה בהערות הקוד.

/**
* Could not convert autocorrect.addreplacement API. Please add relevant code in
* the following function to implement it.
* This API has been used at the following locations in the VBA script.
*     sheet1 : line 3
* You can use the following Apps Script APIs to convert it.
* Apps Script APIs : FindReplaceRequest , onEdit
* Apps Script documentation links :
* https://developers.google.com/apps-script/reference/script/spreadsheet-trigger-builder#onedit
* https://developers.google.com/sheets/api/eap/reference/rest/v4/spreadsheets/request?hl=en#findreplacerequest

* Comments : AutoCorrect.AddReplacement was not converted, but there is an
* equivalent option you can implement manually. Use onEdit and FindReplaceRequest
* APIs instead, see https://developers.google.com/apps-script/reference/script/spreadsheet-trigger-builder#onedit
* and https://developers.google.com/sheets/api/eap/reference/rest/v4/spreadsheets/request?hl=en#findreplacerequest.
* For more information on API manual implementation, see
* https://developers.google.com/apps-script/guides/macro-converter/fix-conversion-errors.

* @param {Object} CallingObject represents the parent object using which the API
* has been called.
* @param {string} What
* @param {string} Replacement
* @return {string}
*/

function _api_autocorrect_addreplacement(CallingObject, What, Replacement) {
  ThrowException('API autocorrect.addreplacement not supported yet.');

}

בדוגמה הבאה מוצגת ההטמעה של Autocorrect.Addreplacement API:

var AUTO_CORRECTIONS = "AUTO_CORRECTIONS";
// Need to get the autocorrections set in previous sessions and use them.
var savedAutoCorrections = PropertiesService.getDocumentProperties().getProperty(AUTO_CORRECTIONS);
var autoCorrections = savedAutoCorrections ? JSON.parse(savedAutoCorrections) : {};
function onEdit(e) {
autoCorrect(e.range);
}
function autoCorrect(range) {
for (key in autoCorrections) {
// Replace each word that needs to be auto-corrected with their replacements.
range.createTextFinder(key)
.matchCase(true)
.matchEntireCell(false)
.matchFormulaText(false)
.useRegularExpression(false)
.replaceAllWith(autoCorrections[key]);
}
}
/**
* Could not convert autocorrect.addreplacement API. Please add relevant code in
* the following function to implement it.
* This API has been used at the following locations in the VBA script.
* sheet1 : line 3
*
* You can use the following Apps Script APIs to convert it.
* Apps Script APIs : createTextFinder , onEdit
* Apps Script documentation links : https://developers.google.com/apps-script/reference/script/spreadsheet-trigger-builder#onedit ,
createTextFinder
* Comments : AutoCorrect.AddReplacement was not converted, but there is an
* equivalent option you can implement manually. Use onEdit and FindReplaceRequest
* APIs instead, see https://developers.google.com/apps-script/reference/script/spreadsheet-trigger-builder#onedit
* and createTextFinder. For more information on API manual implementation, see
* https://developers.google.com/apps-script/guides/macro-converter/fix-conversion-errors.
*
* @param {Object} CallingObject represents the parent object using which the API has been called.
* @param {string} What
* @param {string} Replacement
*
* @return {string}
*/

function _api_autocorrect_addreplacement(CallingObject, What, Replacement) {
autoCorrections[What] = Replacement;
// Store the updated autoCorrections in the properties so that future executions use the correction.
PropertiesService.getDocumentProperties().setProperty(AUTO_CORRECTIONS, JSON.stringify(autoCorrections));
}

דוגמה 2: השיטה Workbook.open

‫VBA API‏ workbook.open פותח קובץ מקומי על סמך נתיב.

נניח שקוד ה-VBA פותח שני קבצים באמצעות workbook.open:

  • קובץ 1: C:\Data\abc.xlsx
  • קובץ 2: C:\Data\xyz.xlsx

בדוגמת הקוד הבאה אפשר לראות איך Macro Converter מחליף את Workbook.open ב-Apps Script בכל מקום שבו נעשה שימוש ב-Workbook.open כדי לפתוח את קובץ 1:

var spreadSheetId =
   _handle_mso_excel_get_google_spreadsheet_id("C:\Data\abc.xlsx");
var spreadSheet = SpreadsheetApp.openById(spreadSheetId);
השגיאה הבאה מתווספת לקובץ unimplemented_constructs.gs בפרויקט Apps Script:
/**
* Method to return the spreadsheet id manually.
*
* @param {string} FileName ID of the spreadsheet to be opened.
* @return {string} return the spreadsheet id.
*/
function _handle_mso_excel_get_google_spreadsheet_id(FileName) {
 // Upload the Excel files being opened by the API to Google Drive and convert
 // them to Google Sheets.
 // Determine the spreadsheet ID of the Google Sheets file created.
 // Implement this method to return the corresponding spreadsheet ID when given
 //the original file path as parameter.
 throw new Error('Please return the spreadsheet ID corresponding to filename: ' + FileName);
 return '';
}

כמו שמוסבר בהערות בדוגמה הקודמת, צריך להמיר את קובצי היעד לקובצי Sheets ב-Drive.

המזהים המתאימים של גיליונות אלקטרוניים של Google מודגשים ברשימה הבאה:

  • קובץ מספר 1: C:\Data\abc.xlsx הופך ל-https://docs.google.com/spreadsheets/d/abc123Abc123Abc123abc
  • קובץ מספר 2: C:\Data\xyz.xlsx הופך ל-https://docs.google.com/spreadsheets/d/xyz456Xyz456xYz456xyZ

לאחר מכן, משנים את הקוד בפונקציית Apps Script כדי לפתוח את הקבצים לפי מזהה:

/**
* Method to return the spreadsheet id manually.
*
* @param {string} FileName ID of the spreadsheet to be opened.
* @return {string} return the spreadsheet id.
*/
function _handle_mso_excel_get_google_spreadsheet_id(FileName) {
 // Upload the Excel files being opened by the API to Google Drive and convert
 //them to Google Sheets.
 // Determine the spreadsheet ID of the Google Sheets file created.
 // Implement this method to return the corresponding spreadsheet ID when given
 //the original file path as parameter
 if (Filename.indexOf("abc.xlsx") >= 0) {
   return "abc123Abc123Abc123abc";
 } else if (Filename.indexOf("xyz.xlsx") >= 0) {
   return "xyz456Xyz456xYz456xyZ";
 }

שגיאה מכוונת

שגיאות מכוונות מתווספות לקוד שהומר כדי לחקות את התנהגות השגיאה של קוד ה-VBA המקורי. אין צורך לשנות את השגיאות האלה.

דוגמה לשגיאה מכוונת

אם מנסים לגשת לרכיב שחורג מגבולות המערך ב-VBA, הקוד מייצר חריגה. ב-Apps Script, הקוד מחזיר undefined.

כדי למנוע תוצאות לא צפויות, הכלי להמרת פקודות מאקרו מוסיף קוד Apps Script שיוצר חריג אם מנסים לגשת לרכיבים שחורגים מגבולות המערך.

הדוגמה הזו מוצגת בקוד הבא:

קוד VBA מקורי
Dim arr
arr = Array("apple", "orange")
MsgBox arr(5)
Will throw the following error:
Subscript out of range
קוד Apps Script שהומר (לפני הוספת שגיאת חריגה)
var arr;
arr = ["apple", "orange"];
Browser.msgBox(arr[5]);
Will return this value and not throw an error:
undefined
קוד Apps Script שנוסף כדי להקפיץ את שגיאת החריגה
/**
* Extend the regular JS array to support VB style indexing with a get method.
* @returns{*} value at the index
*/
Array.prototype.get = function() {
 var curr_res = this;
 for (var i = 0; i < arguments.length; i++) {
   if (!Array.isArray(curr_res) || curr_res.length < arguments[i]) {
     throw new Error(Converted VBA Error (Intentional Error): Subscript out of range);
   }
   curr_res = curr_res[arguments[i]];
 }
 return curr_res;
};
var arr;
arr  = ["apple", "orange"];
Browser.msgBox(arr.get(5));