Process Hacker
cpysave.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * copy/save code for listviews and treelists
4  *
5  * Copyright (C) 2010-2012 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phgui.h>
24 #include <treenew.h>
25 #include <cpysave.h>
26 
27 #define TAB_SIZE 8
28 
30  _Inout_ PPH_STRING_BUILDER StringBuilder,
31  _In_ PPH_STRING String
32  )
33 {
34  SIZE_T i;
35  SIZE_T length;
36  PWCHAR runStart;
37  SIZE_T runLength;
38 
39  length = String->Length / sizeof(WCHAR);
40  runStart = NULL;
41 
42  for (i = 0; i < length; i++)
43  {
44  switch (String->Buffer[i])
45  {
46  case '\"':
47  if (runStart)
48  {
49  PhAppendStringBuilderEx(StringBuilder, runStart, runLength * sizeof(WCHAR));
50  runStart = NULL;
51  }
52 
53  PhAppendStringBuilder2(StringBuilder, L"\"\"");
54 
55  break;
56  default:
57  if (runStart)
58  {
59  runLength++;
60  }
61  else
62  {
63  runStart = &String->Buffer[i];
64  runLength = 1;
65  }
66 
67  break;
68  }
69  }
70 
71  if (runStart)
72  PhAppendStringBuilderEx(StringBuilder, runStart, runLength * sizeof(WCHAR));
73 }
74 
83  _Out_ PPH_STRING ***Table,
84  _In_ ULONG Rows,
85  _In_ ULONG Columns
86  )
87 {
88  PPH_STRING **table;
89  ULONG i;
90 
91  table = PhAutoDereferenceObject(PhCreateAlloc(sizeof(PPH_STRING *) * Rows));
92 
93  for (i = 0; i < Rows; i++)
94  {
95  table[i] = PhAutoDereferenceObject(PhCreateAlloc(sizeof(PPH_STRING) * Columns));
96  memset(table[i], 0, sizeof(PPH_STRING) * Columns);
97  }
98 
99  *Table = table;
100 }
101 
114  _In_ PPH_STRING **Table,
115  _In_ ULONG Rows,
116  _In_ ULONG Columns,
117  _In_ ULONG Mode
118  )
119 {
120  PPH_LIST lines;
121  // The tab count array contains the number of tabs need to fill the biggest
122  // row cell in each column.
123  PULONG tabCount;
124  ULONG i;
125  ULONG j;
126 
127  if (Mode == PH_EXPORT_MODE_TABS || Mode == PH_EXPORT_MODE_SPACES)
128  {
129  // Create the tab count array.
130 
131  tabCount = PhAutoDereferenceObject(PhCreateAlloc(sizeof(ULONG) * Columns));
132  memset(tabCount, 0, sizeof(ULONG) * Columns); // zero all values
133 
134  for (i = 0; i < Rows; i++)
135  {
136  for (j = 0; j < Columns; j++)
137  {
138  ULONG newCount;
139 
140  if (Table[i][j])
141  newCount = (ULONG)(Table[i][j]->Length / sizeof(WCHAR) / TAB_SIZE);
142  else
143  newCount = 0;
144 
145  // Replace the existing count if this tab count is bigger.
146  if (tabCount[j] < newCount)
147  tabCount[j] = newCount;
148  }
149  }
150  }
151 
152  // Create the final list of lines by going through each cell and appending
153  // the proper tab count (if we are using tabs). This will make sure each column
154  // is properly aligned.
155 
156  lines = PhCreateList(Rows);
157 
158  for (i = 0; i < Rows; i++)
159  {
160  PH_STRING_BUILDER stringBuilder;
161 
162  PhInitializeStringBuilder(&stringBuilder, 100);
163 
164  switch (Mode)
165  {
166  case PH_EXPORT_MODE_TABS:
167  {
168  for (j = 0; j < Columns; j++)
169  {
170  ULONG k;
171 
172  if (Table[i][j])
173  {
174  // Calculate the number of tabs needed.
175  k = (ULONG)(tabCount[j] + 1 - Table[i][j]->Length / sizeof(WCHAR) / TAB_SIZE);
176 
177  PhAppendStringBuilder(&stringBuilder, &Table[i][j]->sr);
178  }
179  else
180  {
181  k = tabCount[j] + 1;
182  }
183 
184  PhAppendCharStringBuilder2(&stringBuilder, '\t', k);
185  }
186  }
187  break;
189  {
190  for (j = 0; j < Columns; j++)
191  {
192  ULONG k;
193 
194  if (Table[i][j])
195  {
196  // Calculate the number of spaces needed.
197  k = (ULONG)((tabCount[j] + 1) * TAB_SIZE - Table[i][j]->Length / sizeof(WCHAR));
198 
199  PhAppendStringBuilder(&stringBuilder, &Table[i][j]->sr);
200  }
201  else
202  {
203  k = (tabCount[j] + 1) * TAB_SIZE;
204  }
205 
206  PhAppendCharStringBuilder2(&stringBuilder, ' ', k);
207  }
208  }
209  break;
210  case PH_EXPORT_MODE_CSV:
211  {
212  for (j = 0; j < Columns; j++)
213  {
214  PhAppendCharStringBuilder(&stringBuilder, '\"');
215 
216  if (Table[i][j])
217  {
218  PhpEscapeStringForCsv(&stringBuilder, Table[i][j]);
219  }
220 
221  PhAppendCharStringBuilder(&stringBuilder, '\"');
222 
223  if (j != Columns - 1)
224  PhAppendCharStringBuilder(&stringBuilder, ',');
225  }
226  }
227  break;
228  }
229 
230  PhAddItemList(lines, PhFinalStringBuilderString(&stringBuilder));
231  }
232 
233  return lines;
234 }
235 
237  _In_ HWND TreeNewHandle,
238  _Out_opt_ PULONG *DisplayToId,
239  _Out_opt_ PWSTR **DisplayToText,
240  _Out_ PULONG NumberOfColumns
241  )
242 {
243  PPH_TREENEW_COLUMN fixedColumn;
244  ULONG numberOfColumns;
245  PULONG displayToId;
246  PWSTR *displayToText;
247  ULONG i;
248  PH_TREENEW_COLUMN column;
249 
250  fixedColumn = TreeNew_GetFixedColumn(TreeNewHandle);
251  numberOfColumns = TreeNew_GetVisibleColumnCount(TreeNewHandle);
252 
253  displayToId = PhAllocate(numberOfColumns * sizeof(ULONG));
254 
255  if (fixedColumn)
256  {
257  TreeNew_GetColumnOrderArray(TreeNewHandle, numberOfColumns - 1, displayToId + 1);
258  displayToId[0] = fixedColumn->Id;
259  }
260  else
261  {
262  TreeNew_GetColumnOrderArray(TreeNewHandle, numberOfColumns, displayToId);
263  }
264 
265  if (DisplayToText)
266  {
267  displayToText = PhAllocate(numberOfColumns * sizeof(PWSTR));
268 
269  for (i = 0; i < numberOfColumns; i++)
270  {
271  if (TreeNew_GetColumn(TreeNewHandle, displayToId[i], &column))
272  {
273  displayToText[i] = column.Text;
274  }
275  }
276 
277  *DisplayToText = displayToText;
278  }
279 
280  if (DisplayToId)
281  *DisplayToId = displayToId;
282  else
283  PhFree(displayToId);
284 
285  *NumberOfColumns = numberOfColumns;
286 }
287 
289  _In_ HWND TreeNewHandle,
290  _Reserved_ ULONG Reserved
291  )
292 {
293  PH_STRING_BUILDER stringBuilder;
294  PULONG displayToId;
295  ULONG rows;
296  ULONG columns;
297  ULONG i;
298  ULONG j;
299 
300  PhMapDisplayIndexTreeNew(TreeNewHandle, &displayToId, NULL, &columns);
301  rows = TreeNew_GetFlatNodeCount(TreeNewHandle);
302 
303  PhInitializeStringBuilder(&stringBuilder, 0x100);
304 
305  for (i = 0; i < rows; i++)
306  {
307  PH_TREENEW_GET_CELL_TEXT getCellText;
308 
309  getCellText.Node = TreeNew_GetFlatNode(TreeNewHandle, i);
310  assert(getCellText.Node);
311 
312  if (!getCellText.Node->Selected)
313  continue;
314 
315  for (j = 0; j < columns; j++)
316  {
317  getCellText.Id = displayToId[j];
318  PhInitializeEmptyStringRef(&getCellText.Text);
319  TreeNew_GetCellText(TreeNewHandle, &getCellText);
320 
321  PhAppendStringBuilder(&stringBuilder, &getCellText.Text);
322  PhAppendStringBuilder2(&stringBuilder, L", ");
323  }
324 
325  // Remove the trailing comma and space.
326  if (stringBuilder.String->Length != 0)
327  PhRemoveEndStringBuilder(&stringBuilder, 2);
328 
329  PhAppendStringBuilder2(&stringBuilder, L"\r\n");
330  }
331 
332  PhFree(displayToId);
333 
334  return PhFinalStringBuilderString(&stringBuilder);
335 }
336 
338  _In_ HWND TreeNewHandle,
339  _In_ ULONG Mode
340  )
341 {
342  PH_AUTO_POOL autoPool;
343  PPH_LIST lines;
344  ULONG rows;
345  ULONG columns;
346  ULONG numberOfNodes;
347  PULONG displayToId;
348  PWSTR *displayToText;
349  PPH_STRING **table;
350  ULONG i;
351  ULONG j;
352 
353  PhInitializeAutoPool(&autoPool);
354 
355  numberOfNodes = TreeNew_GetFlatNodeCount(TreeNewHandle);
356 
357  rows = numberOfNodes + 1;
358  PhMapDisplayIndexTreeNew(TreeNewHandle, &displayToId, &displayToText, &columns);
359 
360  PhaCreateTextTable(&table, rows, columns);
361 
362  for (i = 0; i < columns; i++)
363  table[0][i] = PhaCreateString(displayToText[i]);
364 
365  for (i = 0; i < numberOfNodes; i++)
366  {
367  PPH_TREENEW_NODE node;
368 
369  node = TreeNew_GetFlatNode(TreeNewHandle, i);
370 
371  if (node)
372  {
373  for (j = 0; j < columns; j++)
374  {
375  PH_TREENEW_GET_CELL_TEXT getCellText;
376 
377  getCellText.Node = node;
378  getCellText.Id = displayToId[j];
379  PhInitializeEmptyStringRef(&getCellText.Text);
380  TreeNew_GetCellText(TreeNewHandle, &getCellText);
381 
382  table[i + 1][j] = PhaCreateStringEx(getCellText.Text.Buffer, getCellText.Text.Length);
383  }
384  }
385  else
386  {
387  for (j = 0; j < columns; j++)
388  {
389  table[i + 1][j] = PhAutoDereferenceObject(PhReferenceEmptyString());
390  }
391  }
392  }
393 
394  PhFree(displayToText);
395  PhFree(displayToId);
396 
397  lines = PhaFormatTextTable(table, rows, columns, Mode);
398 
399  PhDeleteAutoPool(&autoPool);
400 
401  return lines;
402 }
403 
405  _In_ HWND ListViewHandle,
406  _Out_writes_(Count) PULONG DisplayToId,
407  _Out_writes_opt_(Count) PPH_STRING *DisplayToText,
408  _In_ ULONG Count,
409  _Out_ PULONG NumberOfColumns
410  )
411 {
412  LVCOLUMN lvColumn;
413  ULONG i;
414  ULONG count;
415  WCHAR buffer[128];
416 
417  count = 0;
418  lvColumn.mask = LVCF_ORDER | LVCF_TEXT;
419  lvColumn.pszText = buffer;
420  lvColumn.cchTextMax = sizeof(buffer) / sizeof(WCHAR);
421 
422  for (i = 0; i < Count; i++)
423  {
424  if (ListView_GetColumn(ListViewHandle, i, &lvColumn))
425  {
426  ULONG displayIndex;
427 
428  displayIndex = (ULONG)lvColumn.iOrder;
429  assert(displayIndex < Count);
430  DisplayToId[displayIndex] = i;
431 
432  if (DisplayToText)
433  DisplayToText[displayIndex] = PhaCreateString(buffer);
434 
435  count++;
436  }
437  else
438  {
439  break;
440  }
441  }
442 
443  *NumberOfColumns = count;
444 }
445 
447  _In_ HWND ListViewHandle,
448  _In_ INT Index,
449  _In_ INT SubItemIndex
450  )
451 {
452  PPH_STRING buffer;
453  SIZE_T allocatedCount;
454  SIZE_T count;
455  LVITEM lvItem;
456 
457  // Unfortunately LVM_GETITEMTEXT doesn't want to return the actual length of the text.
458  // Keep doubling the buffer size until we get a return count that is strictly less than
459  // the amount we allocated.
460 
461  buffer = NULL;
462  allocatedCount = 256;
463  count = allocatedCount;
464 
465  while (count >= allocatedCount)
466  {
467  if (buffer)
468  PhDereferenceObject(buffer);
469 
470  allocatedCount *= 2;
471  buffer = PhCreateStringEx(NULL, allocatedCount * sizeof(WCHAR));
472  buffer->Buffer[0] = 0;
473 
474  lvItem.iSubItem = SubItemIndex;
475  lvItem.cchTextMax = (INT)allocatedCount + 1;
476  lvItem.pszText = buffer->Buffer;
477  count = SendMessage(ListViewHandle, LVM_GETITEMTEXT, Index, (LPARAM)&lvItem);
478  }
479 
481  PhAutoDereferenceObject(buffer);
482 
483  return buffer;
484 }
485 
487  _In_ HWND ListViewHandle
488  )
489 {
490  PH_AUTO_POOL autoPool;
491  PH_STRING_BUILDER stringBuilder;
492  ULONG displayToId[100];
493  ULONG rows;
494  ULONG columns;
495  ULONG i;
496  ULONG j;
497 
498  PhInitializeAutoPool(&autoPool);
499 
500  PhaMapDisplayIndexListView(ListViewHandle, displayToId, NULL, 100, &columns);
501  rows = ListView_GetItemCount(ListViewHandle);
502 
503  PhInitializeStringBuilder(&stringBuilder, 0x100);
504 
505  for (i = 0; i < rows; i++)
506  {
507  if (!(ListView_GetItemState(ListViewHandle, i, LVIS_SELECTED) & LVIS_SELECTED))
508  continue;
509 
510  for (j = 0; j < columns; j++)
511  {
512  PhAppendStringBuilder(&stringBuilder, &PhaGetListViewItemText(ListViewHandle, i, j)->sr);
513  PhAppendStringBuilder2(&stringBuilder, L", ");
514  }
515 
516  // Remove the trailing comma and space.
517  if (stringBuilder.String->Length != 0)
518  PhRemoveEndStringBuilder(&stringBuilder, 2);
519 
520  PhAppendStringBuilder2(&stringBuilder, L"\r\n");
521  }
522 
523  PhDeleteAutoPool(&autoPool);
524 
525  return PhFinalStringBuilderString(&stringBuilder);
526 }
527 
529  _In_ HWND ListViewHandle,
530  _In_ ULONG Mode
531  )
532 {
533  PH_AUTO_POOL autoPool;
534  PPH_LIST lines;
535  ULONG rows;
536  ULONG columns;
537  ULONG displayToId[100];
538  PPH_STRING displayToText[100];
539  PPH_STRING **table;
540  ULONG i;
541  ULONG j;
542 
543  PhInitializeAutoPool(&autoPool);
544 
545  rows = ListView_GetItemCount(ListViewHandle) + 1; // +1 for column headers
546 
547  // Create the display index/text to ID map.
548  PhaMapDisplayIndexListView(ListViewHandle, displayToId, displayToText, 100, &columns);
549 
550  PhaCreateTextTable(&table, rows, columns);
551 
552  // Populate the first row with the column headers.
553  for (i = 0; i < columns; i++)
554  table[0][i] = displayToText[i];
555 
556  // Populate the other rows with text.
557  for (i = 1; i < rows; i++)
558  {
559  for (j = 0; j < columns; j++)
560  {
561  // Important: use this to bypass extlv's hooking.
562  // extlv only hooks LVM_GETITEM, not LVM_GETITEMTEXT.
563  table[i][j] = PhaGetListViewItemText(ListViewHandle, i - 1, j);
564  }
565  }
566 
567  lines = PhaFormatTextTable(table, rows, columns, Mode);
568 
569  PhDeleteAutoPool(&autoPool);
570 
571  return lines;
572 }