פקדים לא מוכרים
בשבוע האחרון קיבלתי שאלה מ-Hyderkhan (באתר באנגלית) בנוגע לאוטומציה של פקדי Sandbar-Ribbon. הגרסה הישנה של הפקדים מתוארת בקבצי העזרה ב-.Net add-in extensibility, אבל בכל זאת החלטתי לטפל בנושא משתי סיבות:
א. זאת נראית הזדמנות מצוינת להסביר איך חוקרים פקדים לא מוכרים, וכותבים להם תסריטים.
ב. אני שונא להשתמש בהרחבות .Net (כלומר extensibilities). לדעתי מורכב מדי לכתוב אותם במסגרת הפרויקט, וההרחבות שנכתבו על-ידי בית התוכנה של הפקד אף פעם לא עמוקות וטובות מספיק.
המאמר אמנם יעסוק בפקדי Sandbar-Ribbon, אבל הטכניקות והתהליכים שאתר יעבדו עם כל פקד לא מוכר. לצורך ההדגמה כתבתי אפליקציית דוגמה, אבל עשיתי זאת רק על מנת שיהיו Tooltips לכפתורים השונים – הקוד יעבוד גם עם הדוגמה המגיעה Out-Of-The-Box עם חבילת הפקדים. אז, הורידו את אפליקציית הדוגמה, את התקנת הדוגמה מאתר החברה (אפליקציית הדוגמה לא תעבוד בלי ההתקנה), ונתחיל.
הדבר הראשון שעלינו לעשות הוא להכין רשימת מטרות למימוש (כלומר, פיצ’רים שנרצה למכן). הרשימה לא אמורה להיות שלמה או מלאה – פיצ’ר מרכזי אחד או שניים יהיו נקודת התחלה טובה בהחלט. כשרשימת המטרות בידנו, נוכל לנהל חקירת אובייקטים מאוד ממוקדת ויעילה, ולהגיע לתוצאות מהירות ופשוטות.
במקרה שלנו, שתי המשימות הטבעיות הן ניווט לטאב ספציפי, והקלקה על כפתור מסויים.
השלב הבא אחרי גיבוש רשימת המטרות הוא לחקור בפועל את הפקד והאובייקטים שלו. גם במקרים בהם תיעוד קוד המקור של הפקד זמין ומפורט (ועוד על כך בהמשך), אני עדיין מעדיף ללמוד "בידיים". על מנת לעשות זאת, נתחיל בסריקה ראשונית עם חברנו משכבר הימים – Object Spy.
היות ומדובר בפקד לא מוכר, טאב ה-TO לא יגלה לנו הרבה (או ליתר דיוק, כלום), אז נעבור מיידית לטאב ה-RO. שם נחפש תכונות באופן שעשוי לנגוד את האינטואיציה הראשונית – תכונות ריקות. אכן, קראתם נכון, התכונות השימושיות ביותר בפקדים לא מוכרים הן אלו המופיעות כריקות ב-Object Spy. הסיבה לכך היא שתכונות המופיעות כריקות הן לרוב אובייקטים מורכבים (לפעמים הן פשוט תכונות ריקות) – וכפי שתראו בהמשך, אובייקטים מורכבים הם החלון הטוב ביותר לליבו של הפקד והמידע המוכל בו.

לפקד Sandbar-Ribbon יש עשרות תכונות מבטיחות המופיעות כריקות. הצצה מהירה ברשימת המשימות שלנו תזכיר לנו להתמקד בתכונות הקשורות לניווט בין הטאבים השונים, וספציפית, אנחנו בשיטה כלשהי לאיתור הטאב הדרוש. תכונה שמייד קופצת לעין היא Tabs, והיות והפקד מאכסן בתוכו מספר טאבים, Tabs הוא ככל הנראה אוסף אובייקטים מסוג כלשהו (Collection). לצערנו, ה-Object Spy מוגבל לרמה העליונה ביותר של תכונות, ולכן לא ניתן לגלות באמצעותו מה המבנה הפנימי של האוסף, איך ניתן לעבור סדרתית על האובייקטים המוכלים בו, לגשת אליהם, ולהשתמש בהם. על מנת "לקדוח" לרמות הבאות, נפנה ל-Debug View. על ידי עצירת הריצה של QTP ב-Debug View, נוכל לגשת בקלות לאובייקטים הפנימיים, אם כי נאלץ לנחש את התכונות שלהם, היות ול-Debug View אין השלמה אוטומטית לתכונות אובייקטים.

אנו מעוניינים לגשת לאובייקטים המוכלים באוסף הטאבים בתכונה Tabs. ברוב האוספים, ניתן לגשת לאובייקטים הפנימיים על ידי oCollection.Item(iIndex) / oCollection.Items.Item(iIndex) או פקודה במבנה דומה. בדיקה מהירה ב-Debug View תגלה שהדרך הנכונה לגשת לטאבים היא באמצעות oCollection.Item(iIndex). כעת אנחנו רק צריכים לגלות כמה אובייקטים יש באוסף, ונוכל להתחיל לכתוב את לולאת החיפוש אחר הטאב הרלוונטי.
לרוב האוספים המחזיקים טאבים, ערכים ב-ComboBox, כפתורים וכו’, יש תכונה בשם .Count או .Length המחזירה את מספר האובייקטים במערך. לצערנו, ה-Debug View יגלה שהאוסף הספציפי שלנו לא תומך באף פקודה כזו. המשמעות היא שהלולאה הרצה על הטאבים לא תהיה לולאת For אלגנטית, אלא לולאת While מכוערת למדי:
bFound = False ‘ Did we find the needed tab?
i = 0
‘If we overshot the number of tabs, the next one won’t be an object
While (IsObject(oRibbon.Object.Tabs.Item(i))) And (bFound = False)
‘Search Code Here
i = i+1
Wend
i = 0
‘If we overshot the number of tabs, the next one won’t be an object
While (IsObject(oRibbon.Object.Tabs.Item(i))) And (bFound = False)
‘Search Code Here
i = i+1
Wend
הלולאה קצת מכוערת, אבל נוכל לחיות עם זה. השאלה האמיתית היא איך נוכל לדעת שהגענו לטאב המבוקש. היות וקריטריון החיפוש הנפוץ ביותר במקרים אלו הוא על-ידי שם הטאב, או הכותרת שלו, נחפש דרך כלשהי להוציא את המידע הזה מהאובייקט. היות ואין לנו השלמה אוטומטית, נאלץ לנחש את שם התכונה המחזיקה את המידע, או לפתוח את תיעוד קוד המקור, ולמצוא אותה שם. חבילת ההדגמה של Sand-Ribbon מגיעה עם תיעוד יעיל למדי, וניתן לגלות תוך שניות שהתכונה הדרושה היא .Text; במקרים שתיעוד הקוד לא זמין, התכונה תהיה לרוב .Text, .Title, .Name וכו’.
הכל טוב ויפה, אבל בדיקה מהירה ב-Debug View תגלה בעיה. הטאב הראשון מופיע באפליקציה כ-"Tab 1", אבל הערך בתכונה .Text הוא "&Tab 1". מדובר בבעיה נפוצה למדי בטאבים, כפתורים ותפריטים – השם שלהם עלול לכלול מידע קיצורי מקלדת ורעשים אחרים. ברגע שאנחנו מודעים לבעיה, קל מאוד להתגבר עליה, וכעת אנחנו כבר יכולים לכתוב את לולאת החיפוש:
i = 0
Set oTab = oRibbon.Object.Tabs.Item(i) ‘Just to make things clearer
‘Start searching for tab with name = sTabName
bFound = False
While (IsObject(oRibbon.Object.Tabs.Item(i))) And (bFound = False)
If Replace(oTab.Text, "&","") = sTabName Then bFound = True
‘The replace is there because the headers have short-keys information
i = i+1
If IsObject(oRibbon.Object.Tabs.Item(i)) Then _
Set oTab = oRibbon.Object.Tabs.Item(i)
Wend
Set oTab = oRibbon.Object.Tabs.Item(i) ‘Just to make things clearer
‘Start searching for tab with name = sTabName
bFound = False
While (IsObject(oRibbon.Object.Tabs.Item(i))) And (bFound = False)
If Replace(oTab.Text, "&","") = sTabName Then bFound = True
‘The replace is there because the headers have short-keys information
i = i+1
If IsObject(oRibbon.Object.Tabs.Item(i)) Then _
Set oTab = oRibbon.Object.Tabs.Item(i)
Wend
באמצעות לולאת החיפוש הנ"ל נוכל להשיג את אינדקס הטאב הרלוונטי לנו. עכשיו עולה השאלה, איך נממש את בחירת הטאב בפועל? במקרים כאלו עומדות בפנינו מספר אפשרויות – חיפוש אורדינאטות כותרת הטאב והקלקה עליה, שימוש בקיצורי מקלדת, או שינוי ערכי RO בפקד. במקרה הספציפי שלנו (בעיקר למטרות ההדגמה), נשתמש בשינוי ערכי RO.
ברוב הפקדים יש תכונה מסוג SelectedIndex, SelectedItem או SelectedTab, שקביעת הערך בה עושה את העבודה. בפקד Sandbar-Ribbon יש אכן תכונה בשם SelectedTab, אבל מסתבר שהיא מכילה את אובייקט הטאב עצמו, ולא אינדקס המצביע אליו. באופן דומה, התכונה המבטיחה TabIndex לא משפיעה בפועל על מצב האפליקציה.

בשלב זה, רצוי לעבור מטאב תכונות ה-RO ב-Object Spy לטאב המתודות (Methods). שם נוכל לגלות מתודה בשם AdvanceSelectedTab, שנראית מבטיחה למדי. חקירה נוספת ומספר ניסויים מפיקים בסופו של דבר פונקציית קופסה שחורה מלאה לניווט בין טאבים:
Function SelectRibbonTab(oRibbon, sTabName)
Dim iCurrentTab
Dim i
Dim oTab
Dim bFound
Dim iSteps
iCurrentTab = oRibbon.Object.Tabs.IndexOf(oRibbon.Object.SelectedTab)
‘What tab are we in?
i = 0
Set oTab = oRibbon.Object.Tabs.Item(i)
‘Start searching for tab with name = sTabName
bFound = False
While (IsObject(oRibbon.Object.Tabs.Item(i))) And (bFound = False)
If Replace(oTab.Text, "&","") = sTabName Then bFound = True
‘The replace is there because the headers have short-keys information
i = i+1
If IsObject(oRibbon.Object.Tabs.Item(i)) Then _
Set oTab = oRibbon.Object.Tabs.Item(i)
Wend
i=i-1 ‘We overpromoted i
If bFound = True Then
iSteps = (i - iCurrentTab) ‘Where do we need to go?
For i = 1 to abs(iSteps) ‘iSteps might be negative
‘AdvanceSelectedTab promotes by 1/-1 only
oRibbon.Object.AdvanceSelectedTab iSteps
Next
End If
Set oTab = Nothing ‘Release pointers
SelectRibbonTab = bFound ‘Return pass/Fail
End Function
Dim iCurrentTab
Dim i
Dim oTab
Dim bFound
Dim iSteps
iCurrentTab = oRibbon.Object.Tabs.IndexOf(oRibbon.Object.SelectedTab)
‘What tab are we in?
i = 0
Set oTab = oRibbon.Object.Tabs.Item(i)
‘Start searching for tab with name = sTabName
bFound = False
While (IsObject(oRibbon.Object.Tabs.Item(i))) And (bFound = False)
If Replace(oTab.Text, "&","") = sTabName Then bFound = True
‘The replace is there because the headers have short-keys information
i = i+1
If IsObject(oRibbon.Object.Tabs.Item(i)) Then _
Set oTab = oRibbon.Object.Tabs.Item(i)
Wend
i=i-1 ‘We overpromoted i
If bFound = True Then
iSteps = (i - iCurrentTab) ‘Where do we need to go?
For i = 1 to abs(iSteps) ‘iSteps might be negative
‘AdvanceSelectedTab promotes by 1/-1 only
oRibbon.Object.AdvanceSelectedTab iSteps
Next
End If
Set oTab = Nothing ‘Release pointers
SelectRibbonTab = bFound ‘Return pass/Fail
End Function
בפעם הבאה נראה כיצד ניתן לממש את המטרה השנייה שלנו: לחיצה על כפתור ספציפי בטאב.
עדכון : החלק השני זמין כאן!
עדכון - למאמר זה קיימת הרחבה כאן.
שמור את המאמר לשימוש עתידי
הדפס את המאמר
A PDF Version Of This Post
ניווט במאגר הידע



July 14th, 2007 at 3:20 pm
[…] מאמר חדש זמין במאגר הידע - עבודה עם פקדי .Net לא מוכרים! […]
September 18th, 2009 at 8:50 pm
They will feel like real men if they can bench press a few more pounds each visit to the gym. buy viagra >:((( auto insurance %-DDD cheap auto insurance siivpl auto insurance lkdwg blackjack 46606 buy levitra lzca
October 2nd, 2009 at 1:00 pm
Make the switch and order tramadol from our online pharmacy. propecia =-[[[ tramadol 8P
November 5th, 2009 at 6:05 am
Your energy will increase and you will be amazed at your new found sexual virility. ambien =( accutane %-DDD acomplia ayyg
November 23rd, 2009 at 10:15 pm
zyrtec product %PPP alliance atorvastatin trial usuvw claritin alavert uccl taking lipitor feeling lightheaded ziz
January 6th, 2010 at 10:02 pm
neurontin weight gain hhyu zolpidem hemitartrate 04946 pneumonia zithromax adults 2035 augmentin hib 0488
January 26th, 2010 at 2:02 am
wellbutrin make you retain water :-((( lodoxamide clearance wbvwc diflucan mylan >:-] tazorac active ingredient esrzv
January 27th, 2010 at 5:32 am
what dosage of viagra =-)) best hoodia product review %-[ valerian root and lexapro 852103 phentermine get prescription on line 9393 metamfetamine available online 71283
February 9th, 2010 at 5:17 pm
buy hexamethonium bromide cheap hexamethonium bromide evt cialis in pulmonary hypertension 114808 soma and excedrin >:))) buy etynodiol no otaiez natural dexmedetomidine >:((
February 13th, 2010 at 4:51 am
aspirin or plavix for heart disease nfmij lawsuits against effexor 959118 droperidol no prescription cheapest eua claritin otc switch clxkwh ranimustine discount online iqug
June 20th, 2010 at 7:33 pm
valium 833 cheap xanax 221 tramadol =[[ prednisone >:-((
June 23rd, 2010 at 1:39 am
health insurance 136 home insurance ggsw no exam life insurance 0822 state auto insurance 8-]]
June 30th, 2010 at 3:29 pm
where to buy viagra 836 tramadol hcl :[ credible online cialis 346
July 5th, 2010 at 4:44 pm
slots :O low car insurance ihd car insurance quotes xmxbtp affordable health insurance zwg
July 10th, 2010 at 9:44 am
affordable car insurance 679439 can flexeril cause erectile dysfunction 918 health insurance quotes uruox ambien sleep eating hpwua
July 14th, 2010 at 6:38 pm
home insurance 6774 cheap health insurance pmq life insurance 447 new york health insurance tcejrx
July 18th, 2010 at 12:28 am
health insurance juwvnl business insurance 419 buy car insurance online 697 life insurance 116041 florida auto insurance 924631
July 19th, 2010 at 1:56 am
accutane jgtnzd ambien dangers 4778 tramadol :-))) tramadol >:-DD
July 24th, 2010 at 1:50 am
military star credit card mesz new jersey manufactures insurance ddnx risk insurance management society llgrf promise mp3 ringtone for phone :-OOO
July 28th, 2010 at 10:10 pm
retail services gateway computer credit card nteya meridia diet pills usa abd nexium ostoperosis =-P samsung blackjack free accessories zhm turning stone casino black jack tournements tookky poker and black jack set 932