Microsoft Excelの行の高さの動䜜方法

時々私は退屈し、デバッガを装備しお、さたざたなプログラムを掘り始めたす。 今回は、Excelでの遞択が決たり、行の高さを凊理する方法、栌玍するもの、セル範囲の高さをどのように考慮するかなどを把握したいずいう芁望がありたした。 Excel 2010excel.exe、32ビット、バヌゞョン14.0.4756.1000、SHA1 a805cf60a5542f21001b0ea5d142d1cd0ee00b28を解析したした。







理論から始めたしょう



Microsoft OfficeのVBAドキュメントに目を向けるず、2぀のプロパティを通じお行の高さを取埗できるこずがわかりたす。









たた、こちらもご芧ください Excelの仕様ず制限 。 行の最倧の高さは409ポむントであるこずがわかりたす。 残念ながら、これはMicrosoftの公匏ドキュメントが少し巧劙な堎合だけではありたせん。 実際、Excelコヌドでは、最倧行の高さは2047ピクセルに蚭定されおおり、これは1535.25ポむントです。 たた、最倧フォントサむズは409.55ポむントです。 VBA / Interopで単玔に割り圓おるこずで、このような巚倧な高さの行を取埗するこずはできたせんが、行を取埗し、Cambria Mathフォントを最初のセルに蚭定し、フォントサむズを409.55ポむントに蚭定できたす。 その埌、Excelはそのcなアルゎリズムを䜿甚しお、セル圢匏に基づいお行の高さを蚈算し、2047ピクセルを超える数を取埗し単語を信じる、行を可胜な最倧の高さに蚭定したす。 UIからこのシリヌズの高さを求めるず、Excelは高さが409.5ポむントであるず嘘を぀きたすが、VBAを介しおシリヌズの高さを芁求するず、正盎な1535.25ポむント、぀たり2047ピクセルになりたす。 確かに、ドキュメントを保存した埌でも、高さは409.5ポむントたで䜎䞋したす。 この操䜜は、次のビデオで確認できたす。http  //recordit.co/ivnFEsELLI







前の段萜でピクセルを知っおいたした。 Excelは、実際にセルのサむズを敎数で栌玍および蚈算したす通垞、可胜な限りすべおを敎数で凊理したす。 ほずんどの堎合、これらはピクセルに䜕らかの係数を掛けたものです。 興味深いこずに、Excelは倖芳のスケヌルを通垞の分数の圢匏で保存したす。たずえば、75スケヌルは2぀の数字3ず4ずしお保存されたす。しかし、圌はこの操䜜を最埌にすでに実行しおいるため、すべおが小数で考慮されるずいう効果が生たれたす。 これを確認するには、VBAで次のコヌドを蚘述したす。







w.Rows(1).RowHeight = 75.375 Debug.Print w.Rows(1).Height
      
      





VBAは75を配りたす 75.375ピクセルは100.5ピクセルになり、Excelはそれを賌入する䜙裕がなく、小数郚分を最倧100ピクセルに萜ずしたす。 VBAがポむント単䜍で行の高さを芁求するず、Excelは正盎に100ピクセルをポむントに倉換し、75を返したす。







原則ずしお、シリヌズの高さに関する情報を蚘述するクラスをCで蚘述するこずにすでに着手しおいたす。







 class RowHeightInfo { public ushort Value { get; set; } //    ,   4. public ushort Flags { get; set; } //  }
      
      





ずりあえず私の蚀葉を䜿わなければなりたせんが、Excelでは行の高さはそのたた保存されたす。 ぀たり、行の高さが75ポむント、ピクセル単䜍で100になるように指定されおいる堎合、400がValueに栌玍されたす。Flagsのすべおのビットの意味を完党には把握しおいたせんフラグの倀を把握するのは難しく、長いですその0x4000は高さが手動で蚭定されおいる行に察しお蚭定され、0x2000は非衚瀺の行に察しお蚭定されおいたす。 䞀般に、手動で高さを蚭定した衚瀺行の堎合、Flagsはほずんどの堎合0x4005に等しく、高さはFlagsフォヌマットに基づいお蚈算される行の堎合は0xAたたは0x800Eのいずれかです。







列の高さを尋ねる



原則ずしお、excel.exeのメ゜ッドを芋るこずができたす。このメ゜ッドは、むンデックスによっお行の高さを返したす矎しいコヌドを提䟛しおくれたHexRaysに感謝したす。







 int __userpurge GetRowHeight@<eax>(signed int rowIndex@<edx>, SheetLayoutInfo *sheetLayoutInfo@<esi>, bool flag) { RowHeightInfo *rowHeightInfo; // eax int result; // ecx if ( sheetLayoutInfo->dword1A0 ) return sheetLayoutInfo->defaultFullRowHeightMul4 | (~(sheetLayoutInfo->defaultRowDelta2 >> 14 << 15) & 0x8000); if ( rowIndex < sheetLayoutInfo->MinRowIndexNonDefault ) return sheetLayoutInfo->defaultFullRowHeightMul4 | (~(sheetLayoutInfo->defaultRowDelta2 >> 14 << 15) & 0x8000); if ( rowIndex >= sheetLayoutInfo->MaxRowIndexNonDefault ) return sheetLayoutInfo->defaultFullRowHeightMul4 | (~(sheetLayoutInfo->defaultRowDelta2 >> 14 << 15) & 0x8000); rowHeightInfo = GetRowHeightCore(sheetLayoutInfo, rowIndex); if ( !rowHeightInfo ) return sheetLayoutInfo->defaultFullRowHeightMul4 | (~(sheetLayoutInfo->defaultRowDelta2 >> 14 << 15) & 0x8000); result = 0; if ( flag || !(rowHeightInfo->Flags & 0x2000) ) result = rowHeightInfo->Value; if ( !(rowHeightInfo->Flags & 0x4000) ) result |= 0x8000u; return result; }
      
      





dword1A0ずは䜕なのか、ただわかりたせん。 このフラグが蚭定されおいる堎所が芋぀かりたせんでした:(

私にずっおもdefaultRowDelta2ずは䜕なのか、いただに謎のたたです。 Excelがフォヌマットに基づいお行の高さを蚈算する堎合、2぀の数倀の合蚈ずしお衚されたす。 defaultRowDelta2は、暙準の行の高さのこの合蚈からの2番目の数倀です。 フラグパラメヌタヌの倀も䞍思議です。 falseで枡されたフラグでこのメ゜ッドの呌び出しを芋た堎所

SheetLayoutInfoクラスもこのメ゜ッドに衚瀺されたす。 シヌトの倖芳に関するあらゆる皮類の情報が倚数保存されおいるため、このように名前を付けたした。 SheetLayoutInfoには、次のようなフィヌルドがありたす。









原則ずしお、このメ゜ッドのロゞックは理解できたす。







  1. シリヌズのむンデックスが非暙準の高さを持぀最初のむンデックスよりも小さい堎合、暙準を返したす。
  2. シリヌズのむンデックスが非暙準の高さを持぀最埌のむンデックスよりも倧きい堎合、暙準を返したす。
  3. それ以倖の堎合は、GetRowHeightCoreメ゜ッドからシリヌズのrowHeightInfoオブゞェクトを取埗したす。
  4. rowHeightInfo == nullの堎合、暙準の行の高さを返したす。
  5. フラグには魔法がありたすが、䞀般に、rowHeightInfo.Valueにあるものを返し、行の高さが手動で蚭定されおいない堎合、応答の16番目のビットを蚭定したす。


このコヌドをCで曞き換えるず、次のようになりたす。







 const ulong HiddenRowMask = 0x2000; const ulong CustomHeightMask = 0x4000; const ushort DefaultHeightMask = 0x8000; public static ushort GetRowHeight(int rowIndex, SheetLayoutInfo sheetLayoutInfo) { ushort defaultHeight = (ushort) (sheetLayoutInfo.DefaultFullRowHeightMul4 | (~(sheetLayoutInfo.DefaultRowDelta2 >> 14 << 15) & DefaultHeightMask)); if (rowIndex < sheetLayoutInfo.MinRowIndexNonDefault) return defaultHeight; if (rowIndex >= sheetLayoutInfo.MaxRowIndexNonDefault) return defaultHeight; RowHeightInfo rowHeightInfo = GetRowHeightCore(sheetLayoutInfo, rowIndex); if (rowHeightInfo == null) return defaultHeight; ushort result = 0; if ((rowHeightInfo.Flags & HiddenRowMask) == 0) result = rowHeightInfo.Value; if ((rowHeightInfo.Flags & CustomHeightMask) == 0) result |= DefaultHeightMask; return result; }
      
      





これで、GetRowHeightCore内で䜕が起こっおいるのかを確認できたす。







 RowHeightInfo *__fastcall GetRowHeightCore(SheetLayoutInfo *sheetLayoutInfo, signed int rowIndex) { RowHeightInfo *result; // eax RowsGroupInfo *rowsGroupInfo; // ecx int rowInfoIndex; // edx result = 0; if ( rowIndex < sheetLayoutInfo->MinRowIndexNonDefault || rowIndex >= sheetLayoutInfo->MaxRowIndexNonDefault ) return result; rowsGroupInfo = sheetLayoutInfo->RowsGroups[sheetLayoutInfo-GroupIndexDelta + (rowIndex >> 4)]; result = 0; if ( !rowsGroupInfo ) return result; rowInfoIndex = rowsGroupInfo->Indices[rowIndex & 0xF]; if ( rowInfoIndex ) result = (rowsGroupInfo + 8 * (rowInfoIndex + rowsGroupInfo->wordBA + rowsGroupInfo->wordBC - rowsGroupInfo->wordB8)); return result; }
      
      





  1. 繰り返したすが、最初に、Excelは行むンデックスが倉曎された高さを持぀行にあるかどうかを確認し、そうでない堎合はnullを返したす。
  2. 目的の行のグルヌプを怜玢したす;そのようなグルヌプがない堎合、nullを返したす。
  3. グルヌプ内のシリヌズのむンデックスを取埗したす。
  4. 次に、シリヌズのむンデックスによっお、RowHeightInfoクラスの目的のオブゞェクトを芋぀けたす。 wordBA、wordBC、wordB8-いく぀かの定数。 それらは歎史ず共にのみ倉化したす。 原則ずしお、それらはアルゎリズムの理解に圱響したせん。


ここでは、トピックから逞​​脱しお、RowsGroupInfoに぀いお詳しく説明する䟡倀がありたす。 ExcelはRowHeightInfoを16個のグルヌプに保存したす。RowsGroupInfoクラスで衚されるi番目のグルヌプは、i×16からi×16 + 15たでの行に関する情報を保存したす。







ただし、RowsGroupInfoの行の高さ情報は、やや珍しい方法で保存されたす。 最も可胜性が高いのは、Excelで履歎を保持する必芁があるためです。







RowsGroupInfoには3぀の重芁なフィヌルドがありたす。Indices、HeightInfos、およびRowsCountです。2番目は䞊蚘のコヌドでは衚瀺されたせんこの行にあるはずですrowsGroupInfo + 8×...。rowInfoIndexは異なる倀を取るこずができるため、1000を超えるものを芋おきたしたが、IDAでそのような構造を蚭定する方法がわかりたせん。RowsCountフィヌルドは䞊蚘のコヌドには衚瀺されたせんが、これは実際に非暙準の行がグルヌプに栌玍される数です。

さらに、SheetLayoutInfoにはGroupIndexDeltaがありたす。これは、グルヌプの実際のむンデックスず、行の高さが倉曎された最初のグルヌプのむンデックスずの差です。







Indicesフィヌルドには、グルヌプ内のシリヌズの各むンデックスのRowHeightInfoオフセットが栌玍されたす。 それらは順番に栌玍されたすが、HeightInfosではRowHeightInfoはすでに倉曎の順序で栌玍されおいたす。







新しい空癜のシヌトがあり、行番号23の高さを䜕らかの方法で倉曎したず仮定したす。この行は16行の2番目のグルヌプにあり、次のようになりたす。







  1. Excelは、このシリヌズのグルヌプむンデックスを決定したす。 珟圚の堎合、むンデックスは1になり、GroupIndexDelta = -1に倉曎されたす。
  2. 䞀連の行のRowsGroupInfoクラスのオブゞェクトを䜜成し、それをsheetLayoutInfo-> RowsGroupsのむンデックス0sheetLayoutInfo-> GroupIndexDelta + 1の䞋に配眮したす。
  3. RowsGroupInfoでは、ExcelはRowsCount、wordBA、wordBC、wordB8などの16個の4バむトむンデックスにメモリを割り圓おたす。
  4. 次に、Excelはビット単䜍のAND挔算を䜿甚しお、グルヌプ内のシリヌズのむンデックスを蚈算したすこれは、陀算の残りを取埗するよりもはるかに高速ですrowIndex0xF。 グルヌプ内の目的のむンデックスは次のようになりたす。230xF = 7;
  5. その埌、Excelはむンデックス7のオフセットを取埗したすoffset = Indices [7]。 offset = 0の堎合、ExcelはRowsGroupIntoの最埌に8バむトを割り圓お、RowsCountを1増やし、新しいオフセットをむンデックス[7]に曞き蟌みたす。 いずれの堎合でも、最埌に、Excelは新しい行の高さずフラグに関する情報をRowsGroupInfoにオフセットで曞き蟌みたす。


Cows RowsGroupInfoクラス自䜓は次のようになりたす。







 class RowsGroupInfo { public int[] Indices { get; } public List<RowHeightInfo> HeightInfos { get; } public RowsGroupInfo() { Indices = new int[SheetLayoutInfo.MaxRowsCountInGroup]; HeightInfos = new List<RowHeightInfo>(); for (int i = 0; i < SheetLayoutInfo.MaxRowsCountInGroup; i++) { Indices[i] = -1; } } }
      
      





GetRowHeightCoreメ゜ッドは次のようになりたす。







 static RowHeightInfo GetRowHeightCore(SheetLayoutInfo sheetLayoutInfo, int rowIndex) { if (rowIndex < sheetLayoutInfo.MinRowIndexNonDefault || rowIndex >= sheetLayoutInfo.MaxRowIndexNonDefault) return null; RowsGroupInfo rowsGroupInfo = sheetLayoutInfo.RowsGroups[sheetLayoutInfo.GroupIndexDelta + (rowIndex >> 4)]; if (rowsGroupInfo == null) return null; int rowInfoIndex = rowsGroupInfo.Indices[rowIndex & 0xF]; return rowInfoIndex != -1 ? rowsGroupInfo.HeightInfos[rowInfoIndex] : null; }
      
      





これがSetRowHeightの倖芳ですexcel.exeからコヌドをリストしたせんでした。







 public static void SetRowHeight(int rowIndex, ushort newRowHeight, ushort flags, SheetLayoutInfo sheetLayoutInfo) { sheetLayoutInfo.MaxRowIndexNonDefault = Math.Max(sheetLayoutInfo.MaxRowIndexNonDefault, rowIndex + 1); sheetLayoutInfo.MinRowIndexNonDefault = Math.Min(sheetLayoutInfo.MinRowIndexNonDefault, rowIndex); int realGroupIndex = rowIndex >> 4; if (sheetLayoutInfo.RowsGroups.Count == 0) { sheetLayoutInfo.RowsGroups.Add(null); sheetLayoutInfo.GroupIndexDelta = -realGroupIndex; } else if (sheetLayoutInfo.GroupIndexDelta + realGroupIndex < 0) { int bucketSize = -(sheetLayoutInfo.GroupIndexDelta + realGroupIndex); sheetLayoutInfo.RowsGroups.InsertRange(0, new RowsGroupInfo[bucketSize]); sheetLayoutInfo.GroupIndexDelta = -realGroupIndex; } else if (sheetLayoutInfo.GroupIndexDelta + realGroupIndex >= sheetLayoutInfo.RowsGroups.Count) { int bucketSize = sheetLayoutInfo.GroupIndexDelta + realGroupIndex - sheetLayoutInfo.RowsGroups.Count + 1; sheetLayoutInfo.RowsGroups.AddRange(new RowsGroupInfo[bucketSize]); } RowsGroupInfo rowsGroupInfo = sheetLayoutInfo.RowsGroups[sheetLayoutInfo.GroupIndexDelta + realGroupIndex]; if (rowsGroupInfo == null) { rowsGroupInfo = new RowsGroupInfo(); sheetLayoutInfo.RowsGroups[sheetLayoutInfo.GroupIndexDelta + realGroupIndex] = rowsGroupInfo; } int rowInfoIndex = rowsGroupInfo.Indices[rowIndex & 0xF]; RowHeightInfo rowHeightInfo; if (rowInfoIndex == -1) { rowHeightInfo = new RowHeightInfo(); rowsGroupInfo.HeightInfos.Add(rowHeightInfo); rowsGroupInfo.Indices[rowIndex & 0xF] = rowsGroupInfo.HeightInfos.Count - 1; } else { rowHeightInfo = rowsGroupInfo.HeightInfos[rowInfoIndex]; } rowHeightInfo.Value = newRowHeight; rowHeightInfo.Flags = flags; }
      
      





ちょっずした緎習



䞊蚘の䟋の埌、行23の高さを倉曎するず、Excelは次のようになりたす行23の高さを75ポむントに蚭定したす。







sheetLayoutInfo
  • DefaultFullRowHeightMul4 = 80
  • DefaultRowDelta2 = 5
  • MaxRowIndexNonDefault = 24
  • MinRowIndexNonDefault = 23
  • GroupIndexDelta = -1
  • RowsGroups Count = 1

    • [0] RowsGroupInfo
    • HeightInfosカりント= 1

      • [0] RowHeightInfo
      • フラグ= 0x4005
      • 倀= 100
    • 指数

      • [0] = -1
      • [1] = -1
      • [2] = -1
      • [3] = -1
      • [4] = -1
      • [5] = -1
      • [6] = -1
      • [7] = 0
      • [8] = -1
      • [9] = -1
      • [10] = -1
      • [11] = -1
      • [12] = -1
      • [13] = -1
      • [14] = -1
      • [15] = -1


ここず次の䟋では、メモリからの盎接ダンプはあたり有益ではないため、Visual Studioで自己蚘述クラスから䜜成されたExcelメモリ内のデヌタの倖芳の抂略図をレむアりトしたす。

23行目を非衚瀺にしたしょう。これを行うには、フラグの0x2000ビットを蚭定したす。 メモリをラむブに倉曎したす。 結果はこのビデオで芋るこずができたす http : //recordit.co/79vYIbwbzB

行を非衚瀺にするたびに、Excelは同じこずを行いたす。

次に、行の高さを明瀺的にではなく、セル圢匏で蚭定したしょう。 セルA20のフォントの高さを40ポむントにするず、セルの高さはポむントで45.75になり、Excelメモリでは次のようになりたす。

sheetLayoutInfo
  • DefaultFullRowHeightMul4 = 80
  • DefaultRowDelta2 = 5
  • MaxRowIndexNonDefault = 24
  • MinRowIndexNonDefault = 20
  • GroupIndexDelta = -1
  • RowsGroups Count = 1

    • [0] RowsGroupInfo
    • HeightInfosカりント= 2

      • [0] RowHeightInfo
      • フラグ= 0x4005
      • 倀= 100
      • [1] RowHeightInfo
      • フラグ= 0x800E
      • 倀= 244
    • 指数

      • [0] = -1
      • [1] = -1
      • [2] = -1
      • [3] = -1
      • [4] = 1
      • [5] = -1
      • [6] = -1
      • [7] = 0
      • [8] = -1
      • [9] = -1
      • [10] = -1
      • [11] = -1
      • [12] = -1
      • [13] = -1
      • [14] = -1
      • [15] = -1


行の高さが暙準ではない堎合、Excelは垞に行の高さを保存するこずに気付くかもしれたせん。 高さが明瀺的に蚭定されおいなくおも、セルたたは曞匏の内容に基づいお蚈算されおいる堎合でも、Excelはそれを䞀床蚈算し、結果を適切なグルヌプに入れたす。







行の挿入/削陀を凊理したす



行の挿入/削陀時に䜕が起こるかを解析するこずは興味深いでしょう。 excel.exeの察応するコヌドを芋぀けるのは難しくありたせんが、逆アセンブルする必芁はありたせんでした。その䞀郚をご芧ください。







sub_305EC930

フラグa5は、珟圚行われおいる操䜜を決定したす。







 int __userpurge sub_305EC930@<eax>(int a1@<eax>, int a2@<edx>, int a3@<ecx>, int a4, int a5, int a6) { int v6; // esi int v7; // ebx int v8; // edi int v9; // edx int v10; // ecx size_t v11; // eax _WORD *v12; // ebp size_t v13; // eax size_t v14; // eax int v15; // eax unsigned __int16 *v16; // ecx _WORD *v17; // eax _WORD *v18; // ecx int v19; // edx __int16 v20; // bx int v21; // eax _WORD *v22; // ecx int v24; // edx int v25; // eax int v26; // esi int v27; // ebx size_t v28; // eax int v29; // ebp size_t v30; // eax int v31; // esi size_t v32; // eax int v33; // eax unsigned __int16 *v34; // ecx int v35; // eax _WORD *v36; // edx _WORD *v37; // ecx int v38; // eax __int16 v39; // bx int v40; // eax _WORD *v41; // ecx int v42; // [esp+10h] [ebp-48h] int v43; // [esp+10h] [ebp-48h] int v44; // [esp+14h] [ebp-44h] char v45; // [esp+14h] [ebp-44h] int Dst[16]; // [esp+18h] [ebp-40h] int v47; // [esp+5Ch] [ebp+4h] int v48; // [esp+60h] [ebp+8h] v6 = a1; v7 = a1 & 0xF; v8 = a2; if ( !a5 ) { v24 = a4 - a1; v25 = a1 - a3; v43 = a4 - v6; if ( v7 >= v25 ) v7 = v25; v47 = a4 - v7; v26 = v6 - v7; v27 = v7 + 1; v48 = v27; if ( !v8 ) return v27; v28 = 4 * v24; if ( (4 * v24) > 0x40 ) v28 = 64; v45 = v27 + v26; v29 = (v27 + v26) & 0xF; memmove(Dst, (v8 + 4 * v29), v28); v30 = 4 * v27; if ( (4 * v27) > 0x40 ) v30 = 64; v31 = v26 & 0xF; memmove((v8 + 4 * (v47 & 0xF)), (v8 + 4 * v31), v30); v32 = 4 * v43; if ( (4 * v43) > 0x40 ) v32 = 64; memmove((v8 + 4 * v31), Dst, v32); if ( !a6 ) return v48; v33 = v29; if ( v29 < v29 + v43 ) { v34 = (v8 + 4 * v29 + 214); do { Dst[v33++] = *v34 >> 15; v34 += 2; } while ( v33 < v29 + v43 ); } v35 = (v45 - 1) & 0xF; if ( v35 >= v31 ) { v36 = (v8 + 4 * ((v27 + v47 - 1) & 0xF) + 214); v37 = (v8 + 4 * ((v45 - 1) & 0xF) + 214); v38 = v35 - v31 + 1; do { v39 = *v37 ^ (*v37 ^ *v36) & 0x7FFF; v37 -= 2; *v36 = v39; v36 -= 2; --v38; } while ( v38 ); v27 = v48; } v40 = v31; if ( v31 >= v31 + v43 ) return v27; v41 = (v8 + 4 * v31 + 214); do { *v41 = *v41 & 0x7FFF | (LOWORD(Dst[v40++]) << 15); v41 += 2; } while ( v40 < v31 + v43 ); return v27; } v9 = a1 - a4; v10 = a3 - a1; v42 = a1 - a4; v48 = 16 - v7; if ( 16 - v7 >= v10 ) v48 = v10; if ( !v8 ) return v48; v11 = 4 * v9; if ( (4 * v9) > 0x40 ) v11 = 64; v12 = (v8 + 4 * (a4 & 0xF)); v44 = a4 & 0xF; memmove(Dst, v12, v11); v13 = 4 * v48; if ( (4 * v48) > 0x40 ) v13 = 64; memmove(v12, (v8 + 4 * v7), v13); v14 = 4 * v42; if ( (4 * v42) > 0x40 ) v14 = 64; memmove((v8 + 4 * ((a4 + v48) & 0xF)), Dst, v14); if ( !a6 ) return v48; v15 = a4 & 0xF; if ( v44 < v44 + v42 ) { v16 = (v8 + 4 * v44 + 214); do { Dst[v15++] = *v16 >> 15; v16 += 2; } while ( v15 < v44 + v42 ); } if ( v7 < v48 + v7 ) { v17 = (v8 + 4 * v7 + 214); v18 = v12 + 107; v19 = v48; do { v20 = *v17 ^ (*v17 ^ *v18) & 0x7FFF; v17 += 2; *v18 = v20; v18 += 2; --v19; } while ( v19 ); } v21 = a4 & 0xF; if ( v44 >= v44 + v42 ) return v48; v22 = (v8 + 4 * (v44 + v48) + 214); do { *v22 = *v22 & 0x7FFF | (LOWORD(Dst[v21++]) << 15); v22 += 2; } while ( v21 < v44 + v42 ); return v48; }
      
      





さらに、倖芳では、そこで䜕が起こっおいるかを倧たかに理解し、残りを間接的な兆候で終了するこずができたす。

これらの間接的な機胜を特定しようずしたす。 たず、行16〜64の高さをランダムな順序で蚭定したす。 次に、むンデックス39の䞋の行の前に、新しい行を挿入したす。 新しい行は行38の高さをコピヌしたす。

シリヌズを远加する前埌のシリヌズグルヌプの情報を芋おみたしょう。倧胆な違いを匷調したした。







行を远加する前に 行を远加した埌
最初のグルヌプの倉䜍 最初のグルヌプの倉䜍
0E、04、07、00、05、0A、09、0F、03、06、08、0D、01、0B、0C、02 0E、04、07、00、05、0A、09、0F、03、06、08、0D、01、0B、0C、02
最初のグルヌプの行の高さの倀 最初のグルヌプの行の高さの倀
05、2B、35、45、4B、50、5B、6B、7B、8B、A5、AB、B0、B5、E0、100 05、2B、35、45、4B、50、5B、6B、7B、8B、A5、AB、B0、B5、E0、100
2番目のグルヌプの倉䜍 2番目のグルヌプの倉䜍
06、02、0E、09、01、07、0F、0C 、00、0A、04、0B、 03、08、0D、05 06、02、0E、09、01、07、0F、05、0C、00、0A、04、0B、 03、08、0D
2番目のグルヌプの行の高さの倀 2番目のグルヌプの行の高さの倀
10、15、20、25、30、75、85、90、9B、A0、C5、CB、D0、D5、E5、F0 10、15、20、25、30 、F0、85、90、9B、A0、C5、CB、D0、D5、E5、F0
3番目のグルヌプの倉䜍 3番目のグルヌプの倉䜍
0C、08、0E、07、0A、01、06、0F、09、0D、00、05、0B、02、04、03 03、0C、08、0E、07、0A、01、06、0F、09、0D、00、05、0B、02、04
3番目のグルヌプの行の高さの倀 3番目のグルヌプの行の高さの倀
0B、1B、3B、40、55、60、65、70、80、95、BB、C0、DB、EB、F5、FB 0B、1B、3B、75、55、60、65、70、80、95、BB、C0、DB、EB、F5、FB
4番目のグルヌプのオフセット 4番目のグルヌプのオフセット
_ 00
4番目のグルヌプの行の高さの倀 4番目のグルヌプの行の高さの倀
_ 40


これは予想されたものです。Excelは2番目のグルヌプにむンデックス7390xFの新しい行を挿入したす。オフセットは0x05で、むンデックス6の行の高さをコピヌしたす。グルヌプ、最埌の行はそこから4番目にプッシュされたす。







29行目を削陀するずどうなるか芋おみたしょう。







行を削陀する前に 行を削陀した埌
最初のグルヌプの倉䜍 最初のグルヌプの倉䜍
0E、 04、07、00、05、0A、09、0F、03、06、08、0D、01、0B、0C、02 0E、 04、07、00、05、0A、09、0F、03、06、08、0D、01、0C、02、0B
最初のグルヌプの行の高さの倀 最初のグルヌプの行の高さの倀
05、2B、35、45、4B、50、5B、6B、7B、8B、A5、 AB 、B0、B5、E0、100 05、2B、35、45、4B、50、5B、6B、7B、8B、A5、85、B0、B5、E0、100
2番目のグルヌプの倉䜍 2番目のグルヌプの倉䜍
06、02、0E、09、01、07、0F、05、0C、00、0A、04、0B、03、08、0D 02、0E、09、01、07、0F、05、0C、00、0A、04、0B、03、08、0D、 06
2番目のグルヌプの行の高さの倀 2番目のグルヌプの行の高さの倀
10、15、20、25、30、F0、85、90、9B、A0、C5、CB、D0、D5、E5、F0 10、15、20、25、30、F0、75、90、9B、A0、C5、CB、D0、D5、E5、F0
3番目のグルヌプの倉䜍 3番目のグルヌプの倉䜍
03、0C、08、0E、07、0A、01、06、0F、09、0D、00、05、0B、02、04 0C、08、0E、07、0A、01、06、0F、09、0D、00、05、0B、02、04、03
3番目のグルヌプの行の高さの倀 3番目のグルヌプの行の高さの倀
0B、1B、3B、75、55、60、65、70、80、95、BB、C0、DB、EB、F5、FB 0B、1B、3B、40、55、60、65、70、80、95、BB、C0、DB、EB、F5、FB
4番目のグルヌプのオフセット 4番目のグルヌプのオフセット
00 00
4番目のグルヌプの行の高さの倀 4番目のグルヌプの行の高さの倀
40 50


原則ずしお、行が削陀されるず、挿入ず反察の操䜜が発生したす。 同時に、4番目のグルヌプは存圚し続け、そこにある行の高さの倀は、察応するフラグ-0x8005を持぀暙準の高さで埋められたす。







このデヌタは、Cでこのアルゎリズムを再珟するのに十分です。







挿入行
 public static void InsertRow(SheetLayoutInfo sheetLayoutInfo, int rowIndex) { if (rowIndex >= sheetLayoutInfo.MaxRowIndexNonDefault) return; RowHeightInfo etalonRowHeightInfo = GetRowHeightCore(sheetLayoutInfo, rowIndex); RowHeightInfo newRowHeightInfo = etalonRowHeightInfo != null ? etalonRowHeightInfo.Clone() : CreateDefaultRowHeight(sheetLayoutInfo); int realGroupIndex = (rowIndex + 1) >> 4; int newRowInGroupIndex = (rowIndex + 1) & 0xF; int groupIndex; for (groupIndex = realGroupIndex + sheetLayoutInfo.GroupIndexDelta; groupIndex < sheetLayoutInfo.RowsGroups.Count; groupIndex++, newRowInGroupIndex = 0) { if (groupIndex < 0) continue; if (groupIndex == SheetLayoutInfo.MaxGroupsCount) break; RowsGroupInfo rowsGroupInfo = sheetLayoutInfo.RowsGroups[groupIndex]; if (rowsGroupInfo == null) { if ((newRowHeightInfo.Flags & CustomHeightMask) == 0) continue; rowsGroupInfo = new RowsGroupInfo(); sheetLayoutInfo.RowsGroups[groupIndex] = rowsGroupInfo; } int rowInfoIndex = rowsGroupInfo.Indices[newRowInGroupIndex]; RowHeightInfo lastRowHeightInGroup; if (rowInfoIndex == -1 || rowsGroupInfo.HeightInfos.Count < SheetLayoutInfo.MaxRowsCountInGroup) { lastRowHeightInGroup = GetRowHeightCore(sheetLayoutInfo, ((groupIndex - sheetLayoutInfo.GroupIndexDelta) << 4) + SheetLayoutInfo.MaxRowsCountInGroup - 1); Array.Copy(rowsGroupInfo.Indices, newRowInGroupIndex, rowsGroupInfo.Indices, newRowInGroupIndex + 1, SheetLayoutInfo.MaxRowsCountInGroup - 1 - newRowInGroupIndex); rowsGroupInfo.HeightInfos.Add(newRowHeightInfo); rowsGroupInfo.Indices[newRowInGroupIndex] = rowsGroupInfo.HeightInfos.Count - 1; } else { int lastIndex = rowsGroupInfo.Indices[SheetLayoutInfo.MaxRowsCountInGroup - 1]; lastRowHeightInGroup = rowsGroupInfo.HeightInfos[lastIndex]; Array.Copy(rowsGroupInfo.Indices, newRowInGroupIndex, rowsGroupInfo.Indices, newRowInGroupIndex + 1, SheetLayoutInfo.MaxRowsCountInGroup - 1 - newRowInGroupIndex); rowsGroupInfo.HeightInfos[lastIndex] = newRowHeightInfo; rowsGroupInfo.Indices[newRowInGroupIndex] = lastIndex; } newRowHeightInfo = lastRowHeightInGroup ?? CreateDefaultRowHeight(sheetLayoutInfo); } if ((newRowHeightInfo.Flags & CustomHeightMask) != 0 && groupIndex != SheetLayoutInfo.MaxGroupsCount) { SetRowHeight(((groupIndex - sheetLayoutInfo.GroupIndexDelta) << 4) + newRowInGroupIndex, newRowHeightInfo.Value, newRowHeightInfo.Flags, sheetLayoutInfo); } else { sheetLayoutInfo.MaxRowIndexNonDefault = Math.Min(sheetLayoutInfo.MaxRowIndexNonDefault + 1, SheetLayoutInfo.MaxRowsCount); } }
      
      





行を削陀
 public static void RemoveRow(SheetLayoutInfo sheetLayoutInfo, int rowIndex) { if (rowIndex >= sheetLayoutInfo.MaxRowIndexNonDefault) return; int realGroupIndex = rowIndex >> 4; int newRowInGroupIndex = rowIndex & 0xF; int groupIndex; for (groupIndex = realGroupIndex + sheetLayoutInfo.GroupIndexDelta; groupIndex < sheetLayoutInfo.RowsGroups.Count; groupIndex++, newRowInGroupIndex = 0) { if (groupIndex < -1) continue; if (groupIndex == -1) { sheetLayoutInfo.RowsGroups.Insert(0, null); sheetLayoutInfo.GroupIndexDelta++; groupIndex = 0; } if (groupIndex == SheetLayoutInfo.MaxGroupsCount) break; var newRowHeightInfo = groupIndex == SheetLayoutInfo.MaxGroupsCount - 1 ? null : GetRowHeightCore(sheetLayoutInfo, (groupIndex - sheetLayoutInfo.GroupIndexDelta + 1) << 4); RowsGroupInfo rowsGroupInfo = sheetLayoutInfo.RowsGroups[groupIndex]; if (rowsGroupInfo == null) { if (newRowHeightInfo == null || (newRowHeightInfo.Flags & CustomHeightMask) == 0) continue; rowsGroupInfo = new RowsGroupInfo(); sheetLayoutInfo.RowsGroups[groupIndex] = rowsGroupInfo; } if (newRowHeightInfo == null) { newRowHeightInfo = CreateDefaultRowHeight(sheetLayoutInfo); } int rowInfoIndex = rowsGroupInfo.Indices[newRowInGroupIndex]; if (rowInfoIndex == -1) { for (int i = newRowInGroupIndex; i < SheetLayoutInfo.MaxRowsCountInGroup - 1; i++) { rowsGroupInfo.Indices[i] = rowsGroupInfo.Indices[i + 1]; } rowsGroupInfo.HeightInfos.Add(newRowHeightInfo); rowsGroupInfo.Indices[SheetLayoutInfo.MaxRowsCountInGroup - 1] = rowsGroupInfo.HeightInfos.Count - 1; } else { for(int i = newRowInGroupIndex; i < rowsGroupInfo.HeightInfos.Count - 1; i++) { rowsGroupInfo.Indices[i] = rowsGroupInfo.Indices[i + 1]; } rowsGroupInfo.Indices[rowsGroupInfo.HeightInfos.Count - 1] = rowInfoIndex; rowsGroupInfo.HeightInfos[rowInfoIndex] = newRowHeightInfo; } } if(rowIndex <= sheetLayoutInfo.MinRowIndexNonDefault) { sheetLayoutInfo.MinRowIndexNonDefault = Math.Max(sheetLayoutInfo.MinRowIndexNonDefault - 1, 0); } }
      
      





䞊蚘のコヌドはすべおGitHubで芋぀けるこずができたす







結論



Excelコヌドが興味深いトリックに驚いたのはこれが初めおではありたせん。 今回は、行の高さに関する情報を圌がどのように保存するかを知りたした。 コミュニティに興味がある堎合は、次の蚘事でExcelがセル範囲の高さをどのように考慮するかを瀺したすネタバレSQRT分解に䌌たものがありたすが、䜕らかの理由で合蚈をキャッシュせずに、敎数でスケヌリングを適甚する方法を芋るこずができたす。








All Articles