|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 // |
|
18 // MbrUtil.cpp |
|
19 // |
|
20 |
|
21 |
|
22 #define _WIN32_WINNT 0x0400 |
|
23 #include <windows.h> |
|
24 |
|
25 #pragma warning (disable:4201) // warning C4201: nonstandard extension used : nameless struct/union |
|
26 #include <winioctl.h> |
|
27 |
|
28 #include <stdio.h> |
|
29 |
|
30 |
|
31 const int KSectorSize = 512; |
|
32 BYTE TheBuffer[KSectorSize]; |
|
33 |
|
34 template <class T> |
|
35 T Min(T aLeft,T aRight) |
|
36 {return(aLeft<aRight ? aLeft : aRight);} |
|
37 |
|
38 |
|
39 |
|
40 const unsigned char KPartitionTypeEmpty=0x00; |
|
41 const unsigned char KPartitionTypeFAT12=0x01; |
|
42 const unsigned char KPartitionTypeFAT16small=0x04; |
|
43 const unsigned char KPartitionTypeFAT16=0x06; |
|
44 const unsigned char KPartitionTypeNTFS=0x07; |
|
45 const unsigned char KPartitionTypeWin95FAT32=0x0b; |
|
46 const unsigned char KPartitionTypeWin95FAT32LBA=0x0c; |
|
47 const unsigned char KPartitionTypeWin95FAT16LBA=0x0e; |
|
48 const unsigned char KPartitionTypeWin95ExtdLBA=0x0f; |
|
49 const unsigned char KPartitionTypeHiddenFAT12=0x11; |
|
50 const unsigned char KPartitionTypeHiddenFAT16small=0x14; |
|
51 const unsigned char KPartitionTypeHiddenFAT16=0x16; |
|
52 const unsigned char KPartitionTypeHiddenNTFS=0x17; |
|
53 const unsigned char KPartitionTypeHiddenWin95FAT32=0x1b; |
|
54 const unsigned char KPartitionTypeHiddenWin95FAT32LBA=0x1c; |
|
55 const unsigned char KPartitionTypeHiddenWin95FAT16LBA=0x1e; |
|
56 |
|
57 const unsigned char KBootIndicatorBootable=0x80; |
|
58 |
|
59 const int KDiskSectorSize = 512; |
|
60 const int KDiskSectorSizeLog2 = 9; |
|
61 |
|
62 const int KMegaByte = 0x100000; |
|
63 const int KMegaByteLog2 = 20; |
|
64 const int KMegaByteToSectorSizeShift = KMegaByteLog2 - KDiskSectorSizeLog2; |
|
65 const int KMegaByteInSectors = KMegaByte >> KMegaByteToSectorSizeShift; |
|
66 |
|
67 const int KMaxPartitionEntries=0x4; |
|
68 const int KMBRFirstPartitionOffset=0x1BE; |
|
69 const int KMBRSignatureOffset=0x1FE; |
|
70 |
|
71 inline BOOL PartitionIsFAT(unsigned char a) |
|
72 { |
|
73 return ( |
|
74 a==KPartitionTypeFAT12 || |
|
75 a==KPartitionTypeFAT16small || |
|
76 a==KPartitionTypeFAT16 || |
|
77 a==KPartitionTypeFAT16 || |
|
78 a==KPartitionTypeWin95FAT16LBA || |
|
79 a==KPartitionTypeHiddenFAT12 || |
|
80 a==KPartitionTypeHiddenFAT16small || |
|
81 a==KPartitionTypeHiddenFAT16 || |
|
82 a==KPartitionTypeHiddenWin95FAT16LBA |
|
83 ); |
|
84 } |
|
85 |
|
86 inline BOOL PartitionIsFAT32(unsigned char a) |
|
87 { |
|
88 return ( |
|
89 a==KPartitionTypeWin95FAT32 || |
|
90 a==KPartitionTypeWin95FAT32LBA || |
|
91 a==KPartitionTypeHiddenWin95FAT32 || |
|
92 a==KPartitionTypeHiddenWin95FAT32LBA |
|
93 ); |
|
94 } |
|
95 |
|
96 inline BOOL PartitionIsNTFS(unsigned char a) |
|
97 { |
|
98 return ( |
|
99 a==KPartitionTypeNTFS || |
|
100 a==KPartitionTypeHiddenNTFS |
|
101 ); |
|
102 } |
|
103 |
|
104 class TMBRPartitionEntry |
|
105 { |
|
106 public: |
|
107 BOOL IsValidPartition() |
|
108 { return (iNumSectors>0 && iPartitionType!=KPartitionTypeEmpty); } |
|
109 BOOL IsValidDosPartition() |
|
110 { return (iNumSectors>0 && PartitionIsFAT(iPartitionType)); } |
|
111 BOOL IsDefaultBootPartition() |
|
112 { return(iX86BootIndicator==KBootIndicatorBootable && (IsValidDosPartition() || IsValidFAT32Partition())); } |
|
113 BOOL IsValidFAT32Partition() |
|
114 { return (iNumSectors>0 && PartitionIsFAT32(iPartitionType)); } |
|
115 public: |
|
116 unsigned char iX86BootIndicator; |
|
117 unsigned char iStartHead; |
|
118 unsigned char iStartSector; |
|
119 unsigned char iStartCylinder; |
|
120 unsigned char iPartitionType; |
|
121 unsigned char iEndHead; |
|
122 unsigned char iEndSector; |
|
123 unsigned char iEndCylinder; |
|
124 DWORD iFirstSector; |
|
125 DWORD iNumSectors; |
|
126 }; |
|
127 |
|
128 |
|
129 char* GetPartitionType(unsigned char aType) |
|
130 { |
|
131 switch (aType) |
|
132 { |
|
133 case KPartitionTypeFAT12: return "FAT12"; |
|
134 case KPartitionTypeFAT16: return "FAT16"; |
|
135 case KPartitionTypeFAT16small: return "FAT16"; |
|
136 case KPartitionTypeWin95FAT32: return "FAT32"; |
|
137 default: |
|
138 return "????"; |
|
139 |
|
140 } |
|
141 } |
|
142 |
|
143 void ReadMbr(unsigned char* aMbrBuf, int aDiskSizeInSectors, BOOL aHexDump) |
|
144 { |
|
145 // printf("Reading MBR...\n"); |
|
146 if (aHexDump) |
|
147 { |
|
148 int n; |
|
149 for (n=0; n<KDiskSectorSize; n+=16) |
|
150 { |
|
151 printf("%08X : %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", |
|
152 n, |
|
153 aMbrBuf[n+0x00],aMbrBuf[n+0x01],aMbrBuf[n+0x02],aMbrBuf[n+0x03],aMbrBuf[n+0x04],aMbrBuf[n+0x05],aMbrBuf[n+0x06],aMbrBuf[n+0x07], |
|
154 aMbrBuf[n+0x08],aMbrBuf[n+0x09],aMbrBuf[n+0x0A],aMbrBuf[n+0x0B],aMbrBuf[n+0x0C],aMbrBuf[n+0x0D],aMbrBuf[n+0x0E],aMbrBuf[n+0x0F]); |
|
155 } |
|
156 } |
|
157 |
|
158 |
|
159 |
|
160 if ((aMbrBuf[KMBRSignatureOffset+0] != 0x55) || |
|
161 (aMbrBuf[KMBRSignatureOffset+1] != 0xAA)) |
|
162 { |
|
163 printf("No valid MBR\n"); |
|
164 return; |
|
165 } |
|
166 |
|
167 int i; |
|
168 TMBRPartitionEntry *pe; |
|
169 for (i=0, pe = (TMBRPartitionEntry*)(&aMbrBuf[KMBRFirstPartitionOffset]); i < KMaxPartitionEntries && pe->iPartitionType != 0; i++, pe++) |
|
170 { |
|
171 if (pe->iFirstSector + pe->iNumSectors > (DWORD) aDiskSizeInSectors) |
|
172 { |
|
173 printf("No valid MBR\n"); |
|
174 return; |
|
175 } |
|
176 } |
|
177 |
|
178 printf("Partitions: \n"); |
|
179 printf(" # Name Type Fat BtI Hed Sct Cyl Hed Sct Cyl FirstSect NumSectors\n"); |
|
180 |
|
181 for (i=0, pe = (TMBRPartitionEntry*)(&aMbrBuf[KMBRFirstPartitionOffset]); i < KMaxPartitionEntries && pe->iPartitionType != 0; i++, pe++) |
|
182 { |
|
183 char* partitionName = GetPartitionType(pe->iPartitionType); |
|
184 printf("%3d: %-6s %3u %3u %3u %3u %3u %3u %3u %3u %3u %10u %10u (%u MB)\n", |
|
185 i, |
|
186 partitionName, |
|
187 pe->iPartitionType, |
|
188 pe->IsValidDosPartition() || pe->IsValidFAT32Partition() ? 1 : 0, |
|
189 pe->iX86BootIndicator, |
|
190 pe->iStartHead, |
|
191 pe->iStartSector, |
|
192 pe->iStartCylinder, |
|
193 pe->iEndHead, |
|
194 pe->iEndSector, |
|
195 pe->iEndCylinder, |
|
196 pe->iFirstSector, |
|
197 pe->iNumSectors, |
|
198 pe->iNumSectors >> KMegaByteToSectorSizeShift); |
|
199 |
|
200 if (pe->iPartitionType == KPartitionTypeHiddenNTFS) |
|
201 { |
|
202 printf("Drive contains an NTFS partition, aborting for safety\n"); |
|
203 return; |
|
204 } |
|
205 } |
|
206 } |
|
207 |
|
208 |
|
209 BOOL WriteMbr(unsigned char* aMbrBuf, int aFatSectorCount) |
|
210 { |
|
211 TMBRPartitionEntry *pe=(TMBRPartitionEntry*)(&aMbrBuf[KMBRFirstPartitionOffset]); |
|
212 |
|
213 // first partition starts at one MB |
|
214 int sectorStart = KMegaByteInSectors; |
|
215 |
|
216 // Create FAT partition |
|
217 pe->iFirstSector = sectorStart; |
|
218 |
|
219 |
|
220 pe->iNumSectors = aFatSectorCount; |
|
221 pe->iX86BootIndicator = 0x00; |
|
222 if (pe->iNumSectors < 32680) |
|
223 pe->iPartitionType = KPartitionTypeFAT12; |
|
224 else if(pe->iNumSectors < 65536) |
|
225 pe->iPartitionType = KPartitionTypeFAT16small; |
|
226 else if (pe->iNumSectors < 1048576) |
|
227 pe->iPartitionType = KPartitionTypeFAT16; |
|
228 else |
|
229 pe->iPartitionType = KPartitionTypeWin95FAT32; |
|
230 sectorStart+= pe->iNumSectors; |
|
231 |
|
232 aMbrBuf[KMBRSignatureOffset+0] = 0x55; |
|
233 aMbrBuf[KMBRSignatureOffset+1] = 0xAA; |
|
234 |
|
235 return true; |
|
236 } |
|
237 |
|
238 |
|
239 int main(int argc,char *argv[]) |
|
240 { |
|
241 |
|
242 if (argc < 2) |
|
243 { |
|
244 printf("MbrUtil - Decodes and optionally writes or erases the Master Boot Record on a removable drive\n"); |
|
245 printf("Syntax : MbrUtil <drive letter> [-e] [-w <FatSizeInMegabytes>] [FAT] [FAT32]\n"); |
|
246 printf("Where :\n"); |
|
247 printf("-e : erase Master Boot Record:\n"); |
|
248 printf("-w : create FAT partition of size <FatSizeInMegabytes>:\n"); |
|
249 printf("E.g. : MbrUtil f:\n"); |
|
250 printf(" : MbrUtil f: -w 16 FAT\n"); |
|
251 exit(0); |
|
252 } |
|
253 |
|
254 bool writeMbr = false; |
|
255 bool eraseMbr = false; |
|
256 int fatSectorCount = 0; |
|
257 char* fatType = "FAT"; |
|
258 |
|
259 char driveLetter = (char) toupper(argv[1][0]); |
|
260 if ((strlen(argv[1]) > 2) || |
|
261 (strlen(argv[1]) == 2 && argv[1][1] != ':') || |
|
262 (driveLetter < 'A' || driveLetter > 'Z')) |
|
263 { |
|
264 printf("invalid drive letter"); |
|
265 exit(4); |
|
266 } |
|
267 |
|
268 for (int i=2; i<argc; i++) |
|
269 { |
|
270 if (strcmpi(argv[i], "-e") == 0) |
|
271 { |
|
272 eraseMbr = true; |
|
273 } |
|
274 else if (strcmpi(argv[i], "-w") == 0) |
|
275 { |
|
276 writeMbr = true; |
|
277 i++; |
|
278 if (i >= argc) |
|
279 { |
|
280 printf("no drive size specified"); |
|
281 exit(4); |
|
282 } |
|
283 int fatSizeInMegabytes = atoi(argv[i]); |
|
284 fatSectorCount = fatSizeInMegabytes << KMegaByteToSectorSizeShift; |
|
285 // printf("fatSizeInMegabytes %d, fatSectorCount %d\n", fatSizeInMegabytes, fatSectorCount); |
|
286 } |
|
287 else if (strcmpi(argv[i], "FAT") == 0) |
|
288 { |
|
289 } |
|
290 else if (strcmpi(argv[i], "FAT32") == 0) |
|
291 { |
|
292 fatType = argv[i]; |
|
293 } |
|
294 else |
|
295 { |
|
296 printf("invalid option"); |
|
297 exit(4); |
|
298 } |
|
299 } |
|
300 |
|
301 |
|
302 char diskName[10] = "\\\\.\\?:"; |
|
303 diskName[4] = driveLetter; |
|
304 char physDiskName[20] = "\\\\.\\PHYSICALDRIVE?"; |
|
305 |
|
306 |
|
307 |
|
308 DISK_GEOMETRY geometry; |
|
309 DWORD dummy; |
|
310 HANDLE logDeviceHandle; |
|
311 HANDLE physDeviceHandle; |
|
312 BOOL b; |
|
313 DWORD bytesRead; |
|
314 DWORD bytesWritten; |
|
315 DWORD dwRet; |
|
316 |
|
317 //***************************************************************************************** |
|
318 // open logical drive... |
|
319 //***************************************************************************************** |
|
320 printf("Opening %s...\n", diskName); |
|
321 logDeviceHandle = CreateFileA( |
|
322 diskName, |
|
323 GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE , |
|
324 NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL); |
|
325 if (logDeviceHandle == INVALID_HANDLE_VALUE) |
|
326 { |
|
327 printf("CreateFileA() returned INVALID_HANDLE_VALUE\n"); |
|
328 exit(4); |
|
329 } |
|
330 |
|
331 // query geometry |
|
332 b = DeviceIoControl( |
|
333 logDeviceHandle, |
|
334 IOCTL_DISK_GET_DRIVE_GEOMETRY, |
|
335 NULL, |
|
336 0, |
|
337 &geometry, |
|
338 sizeof(geometry), |
|
339 &dummy, |
|
340 (LPOVERLAPPED)NULL); |
|
341 if (!b) |
|
342 { |
|
343 printf("IOCTL_DISK_GET_DRIVE_GEOMETRY failed error %x", GetLastError()); |
|
344 CloseHandle(logDeviceHandle); |
|
345 exit(4); |
|
346 } |
|
347 |
|
348 LONGLONG diskSizeInBytes = geometry.Cylinders.QuadPart * geometry.TracksPerCylinder * geometry.SectorsPerTrack * KDiskSectorSize; |
|
349 int diskSizeInMegaBytes = (int) (diskSizeInBytes / KMegaByte); |
|
350 int diskSizeInSectors = (int) (diskSizeInBytes / KDiskSectorSize); |
|
351 |
|
352 |
|
353 printf("Drive %c MediaType: %d (%s). Size: %ld MBytes, %lu sectors\n", |
|
354 driveLetter, |
|
355 geometry.MediaType, |
|
356 geometry.MediaType==RemovableMedia ? "RemovableMedia" : |
|
357 geometry.MediaType==FixedMedia ? "FixedMedia" : "?", |
|
358 diskSizeInMegaBytes, |
|
359 diskSizeInSectors |
|
360 ); |
|
361 // printf("Size: %llu sectors\n", diskSizeInSectors); |
|
362 |
|
363 if (geometry.MediaType != RemovableMedia) |
|
364 { |
|
365 printf("Drive is not removable, exiting"); |
|
366 CloseHandle(logDeviceHandle); |
|
367 exit(4); |
|
368 } |
|
369 |
|
370 if (fatSectorCount + KMegaByteInSectors > diskSizeInSectors) |
|
371 { |
|
372 printf("Specified size is too big"); |
|
373 CloseHandle(logDeviceHandle); |
|
374 exit(4); |
|
375 } |
|
376 |
|
377 //***************************************************************************************** |
|
378 // Get physical device number |
|
379 //***************************************************************************************** |
|
380 STORAGE_DEVICE_NUMBER storageDeviceNumber; |
|
381 b = IOCTL_STORAGE_GET_DEVICE_NUMBER; |
|
382 b = DeviceIoControl( |
|
383 logDeviceHandle, |
|
384 IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &storageDeviceNumber, sizeof(storageDeviceNumber), &bytesRead, 0); |
|
385 // printf("IOCTL_STORAGE_GET_DEVICE_NUMBER b %d DeviceNumber %d\n", b, storageDeviceNumber.DeviceNumber); |
|
386 if (!b) |
|
387 { |
|
388 printf("IOCTL_STORAGE_GET_DEVICE_NUMBER failed error %x", GetLastError()); |
|
389 CloseHandle(logDeviceHandle); |
|
390 exit(4); |
|
391 } |
|
392 |
|
393 |
|
394 |
|
395 //***************************************************************************************** |
|
396 // open physical drive... |
|
397 //***************************************************************************************** |
|
398 physDiskName[strlen(physDiskName)-1] = (char) ('0' + storageDeviceNumber.DeviceNumber); |
|
399 |
|
400 printf("Opening %s...\n", physDiskName); |
|
401 physDeviceHandle = CreateFileA( |
|
402 physDiskName, |
|
403 GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE , |
|
404 NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL); |
|
405 if (physDeviceHandle == INVALID_HANDLE_VALUE) |
|
406 { |
|
407 printf("CreateFileA() returned INVALID_HANDLE_VALUE\n"); |
|
408 exit(4); |
|
409 } |
|
410 |
|
411 |
|
412 |
|
413 |
|
414 //******************************************************* |
|
415 // Read first sector |
|
416 //******************************************************* |
|
417 dwRet = SetFilePointer( |
|
418 physDeviceHandle, 0, 0, FILE_BEGIN); |
|
419 if (dwRet != 0) |
|
420 { |
|
421 printf("Unable to set file pointer, exiting"); |
|
422 CloseHandle(physDeviceHandle); |
|
423 CloseHandle(logDeviceHandle); |
|
424 exit(4); |
|
425 } |
|
426 if (!ReadFile (physDeviceHandle, TheBuffer, KSectorSize, &bytesRead, NULL) ) |
|
427 { |
|
428 printf("ReadFile failed with %d", GetLastError()); |
|
429 CloseHandle(physDeviceHandle); |
|
430 CloseHandle(logDeviceHandle); |
|
431 exit(4); |
|
432 } |
|
433 if(bytesRead != KSectorSize) |
|
434 { |
|
435 printf("ReadFile length too small: %d", bytesRead); |
|
436 CloseHandle(physDeviceHandle); |
|
437 CloseHandle(logDeviceHandle); |
|
438 exit(4); |
|
439 } |
|
440 |
|
441 //******************************************************* |
|
442 // Interpret MBR |
|
443 //******************************************************* |
|
444 ReadMbr(TheBuffer, diskSizeInSectors, false); |
|
445 |
|
446 //******************************************************* |
|
447 // Write new MBR |
|
448 //******************************************************* |
|
449 if (writeMbr) |
|
450 { |
|
451 printf("Writing MBR...\n"); |
|
452 memset(TheBuffer, 0, sizeof(TheBuffer)); |
|
453 b = WriteMbr(TheBuffer, fatSectorCount); |
|
454 if (!b) |
|
455 writeMbr = eraseMbr = false; |
|
456 } |
|
457 if (eraseMbr) |
|
458 { |
|
459 printf("Erasing MBR...\n"); |
|
460 memset(TheBuffer, 0, sizeof(TheBuffer)); |
|
461 } |
|
462 if (writeMbr || eraseMbr) |
|
463 { |
|
464 // Write first sector |
|
465 dwRet = SetFilePointer( |
|
466 physDeviceHandle, 0, 0, FILE_BEGIN); |
|
467 if (dwRet != 0) |
|
468 { |
|
469 printf("Unable to set file pointer, exiting"); |
|
470 CloseHandle(physDeviceHandle); |
|
471 CloseHandle(logDeviceHandle); |
|
472 exit(4); |
|
473 } |
|
474 if (!WriteFile (physDeviceHandle, TheBuffer, KSectorSize, &bytesWritten, NULL) ) |
|
475 { |
|
476 printf("WriteFile failed with %d", GetLastError()); |
|
477 CloseHandle(physDeviceHandle); |
|
478 CloseHandle(logDeviceHandle); |
|
479 exit(4); |
|
480 } |
|
481 if(bytesWritten != KSectorSize) |
|
482 { |
|
483 printf("WriteFile length too small: %d", bytesWritten); |
|
484 CloseHandle(physDeviceHandle); |
|
485 CloseHandle(logDeviceHandle); |
|
486 exit(4); |
|
487 } |
|
488 if (writeMbr) |
|
489 ReadMbr(TheBuffer, diskSizeInSectors, false); |
|
490 } |
|
491 |
|
492 |
|
493 CloseHandle(physDeviceHandle); |
|
494 |
|
495 |
|
496 if (writeMbr || eraseMbr) |
|
497 { |
|
498 printf("Dismounting %s...\n", diskName); |
|
499 b = DeviceIoControl( |
|
500 logDeviceHandle, |
|
501 FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bytesRead, 0); |
|
502 if (!b) |
|
503 printf("FSCTL_DISMOUNT_VOLUME b %d err %d\n", b, GetLastError()); |
|
504 } |
|
505 |
|
506 CloseHandle(logDeviceHandle); |
|
507 |
|
508 //******************************************************* |
|
509 // Format the disk |
|
510 //******************************************************* |
|
511 if (writeMbr || eraseMbr) |
|
512 { |
|
513 printf("\n"); |
|
514 char prompt[80]; |
|
515 sprintf(prompt, "Eject card from drive %C: and press ENTER when ready...\n", driveLetter); |
|
516 printf(prompt); |
|
517 getchar(); |
|
518 |
|
519 char winCmd[1024]; |
|
520 char cmdArgs[1024]; |
|
521 STARTUPINFO si = {0,}; |
|
522 PROCESS_INFORMATION pi; |
|
523 |
|
524 GetWindowsDirectory(winCmd, sizeof(winCmd)); |
|
525 strncat(winCmd, "\\system32\\cmd.exe", sizeof(winCmd)); |
|
526 sprintf(cmdArgs, "/C format %c: /FS:%s /Q /X", driveLetter, fatType); |
|
527 printf("Executing : %s %s\n", winCmd, cmdArgs); |
|
528 b = CreateProcessA( |
|
529 winCmd, //__in_opt LPCTSTR lpApplicationName, |
|
530 cmdArgs, //__inout_opt LPTSTR lpCommandLine, |
|
531 NULL, //__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, |
|
532 NULL, //__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, |
|
533 FALSE, //__in BOOL bInheritHandles, |
|
534 0, //__in DWORD dwCreationFlags, |
|
535 NULL, //__in_opt LPVOID lpEnvironment, |
|
536 NULL, //__in_opt LPCTSTR lpCurrentDirectory, |
|
537 &si, //__in LPSTARTUPINFO lpStartupInfo, |
|
538 &pi); //__out LPPROCESS_INFORMATION lpProcessInformation |
|
539 if (!b) |
|
540 printf("CreateProcess failed with %d", GetLastError()); |
|
541 |
|
542 dwRet = WaitForSingleObject(pi.hProcess, INFINITE); |
|
543 CloseHandle(pi.hThread); |
|
544 CloseHandle(pi.hProcess); |
|
545 |
|
546 } |
|
547 |
|
548 return 0; |
|
549 } |