1 // =================================
   2 // Copyright (c) 2021 Seppo Laakko
   3 // Distributed under the MIT license
   4 // =================================
   5 
   6 using System;
   7 using System.Collections;
   8 using System.IO;
   9 
  10 namespace System.Windows.API
  11 {
  12     public const long ERROR_DUPLICATE_CODE_MAPPING = -2;
  13     public const long ERROR_CODE_NOT_FOUND = -1;
  14     public const long ERROR_OUT_OF_RESOURCES = 0;
  15     public const long ERROR_FILE_NOT_FOUND = 1;
  16     public const long ERROR_PATH_NOT_FOUND = 2;
  17     public const long ERROR_BAD_FORMAT = 3;
  18     public const long SE_ERR_ACCESSDENIED =  4;
  19     public const long SE_ERR_ASSOCINCOMPLETE = 5;
  20     public const long SE_ERR_DDEBUSY = 6;
  21     public const long SE_ERR_DDEFAIL = 7;
  22     public const long SE_ERR_DDETIMEOUT = 8;
  23     public const long SE_ERR_DLLNOTFOUND = 9;
  24     public const long SE_ERR_FNF = 10;
  25     public const long SE_ERR_NOASSOC = 11;
  26     public const long SE_ERR_OOM = 12;
  27     public const long SE_ERR_PNF = 13;
  28     public const long SE_ERR_SHARE = 14;
  29 
  30     public string GetWindowsAPIErrorMessage(ulong errorCode)
  31     {
  32         wchar[4096] buffer;
  33         WinFormatMessage(errorCode&buffer[0]);
  34         return ToUtf8(wstring(&buffer[0]));
  35     }
  36 
  37     public class WindowsAPIException : Exception
  38     {
  39         public WindowsAPIException(ulong errorCode_) : base(GetWindowsAPIErrorMessage(errorCode_))errorCode(errorCode_)
  40         {
  41         }
  42         public ulong ErrorCode() const
  43         {
  44             return errorCode;
  45         }
  46         private ulong errorCode;
  47     }
  48 
  49     public class CommonDialogError : Exception
  50     {
  51         public CommonDialogError(uint errorCodeconst string& errorStr) : base("Common dialog error " + ToString(errorCode) + ": " + errorStr)
  52         {
  53         }
  54         private uint errorCode;
  55     }
  56 
  57     public void* FindFirstChangeNotification(const string& directoryPath)
  58     {
  59         void* notificationHandle = WinFindFirstChangeNotification(directoryPath.Chars());
  60         if (notificationHandle == null)
  61         {
  62             throw WindowsAPIException(WinGetLastError());
  63         }
  64         return notificationHandle;
  65     }
  66 
  67     public void FindNextChangeNotification(void* notificationHandle)
  68     {
  69         bool result = WinFindNextChangeNotification(notificationHandle);
  70         if (!result)
  71         {
  72             throw WindowsAPIException(WinGetLastError());
  73         }
  74     }
  75 
  76     public void FindCloseChangeNotification(void* notificationHandle)
  77     {
  78         bool result = WinFindCloseChangeNotification(notificationHandle);
  79         if (!result)
  80         {
  81             throw WindowsAPIException(WinGetLastError());
  82         }
  83     }
  84 
  85     public void* CreateEvent()
  86     {
  87         void* eventHandle = WinCreateEvent();
  88         if (eventHandle == null)
  89         {
  90             throw WindowsAPIException(WinGetLastError());
  91         }
  92         return eventHandle;
  93     }
  94 
  95     public void SetEvent(void* eventHandle)
  96     {
  97         bool result = WinSetEvent(eventHandle);
  98         if (!result)
  99         {
 100             throw WindowsAPIException(WinGetLastError());
 101         }
 102     }
 103 
 104     public void ResetEvent(void* eventHandle)
 105     {
 106         bool result = WinResetEvent(eventHandle);
 107         if (!result)
 108         {
 109             throw WindowsAPIException(WinGetLastError());
 110         }
 111     }
 112 
 113     public void WaitEvent(void* eventHandle)
 114     {
 115         bool result = WinWaitEvent(eventHandle);
 116         if (!result)
 117         {
 118             throw WindowsAPIException(WinGetLastError());
 119         }
 120     }
 121 
 122     public void CloseHandle(void* handle)
 123     {
 124         bool result = WinCloseHandle(handle);
 125         if (!result)
 126         {
 127             throw WindowsAPIException(WinGetLastError());
 128         }
 129     }
 130 
 131     public void CloseEvent(void* eventHandle)
 132     {
 133         CloseHandle(eventHandle);
 134     }
 135 
 136     public int WaitForMultipleObjects(const List<void*>& handles)
 137     {
 138         int result = WinWaitForMultipleObjects(cast<uint>(handles.Count())handles.Begin().Ptr());
 139         if (result >= 0 && result <= handles.Count())
 140         {
 141             return result;
 142         }
 143         else
 144         {
 145             throw WindowsAPIException(WinGetLastError());
 146         }
 147     }
 148 
 149     public void ShellExecute(const string& filePath)
 150     {
 151         long errorCode = 0;
 152         bool succeeded = WinShellExecute(filePath.Chars()errorCode);
 153         if (!succeeded)
 154         {
 155             string errorMessage = "shell execution error for file '" + filePath + "': ";
 156             string error = "out of resources";
 157             switch (errorCode)
 158             {
 159                 case ERROR_DUPLICATE_CODE_MAPPING:
 160                 {
 161                     error = "duplicate code mapping detected"; break;
 162                 }
 163                 case ERROR_CODE_NOT_FOUND:
 164                 {
 165                     error = "code not found"; break;
 166                 }
 167                 case ERROR_OUT_OF_RESOURCES:
 168                 {
 169                     error = "out of resources"; break;
 170                 }
 171                 case ERROR_FILE_NOT_FOUND:
 172                 {
 173                     error = "file not found"; break;
 174                 }
 175                 case ERROR_PATH_NOT_FOUND:
 176                 {
 177                     error = "path not found"; break;
 178                 }
 179                 case ERROR_BAD_FORMAT:
 180                 {
 181                     error = "invalid executable"; break;
 182                 }
 183                 case SE_ERR_ACCESSDENIED:
 184                 {
 185                     error = "access denied"; break;
 186                 }
 187                 case SE_ERR_ASSOCINCOMPLETE:
 188                 {
 189                     error = "invalid file association"; break;
 190                 }
 191                 case SE_ERR_DDEBUSY:
 192                 {
 193                     error = "DDE busy"; break;
 194                 }
 195                 case SE_ERR_DDEFAIL:
 196                 {
 197                     error = "DDE failed"; break;
 198                 }
 199                 case SE_ERR_DDETIMEOUT:
 200                 {
 201                     error = "DDE timeout"; break;
 202                 }
 203                 case SE_ERR_DLLNOTFOUND:
 204                 {
 205                     error = "DLL not found"; break;
 206                 }
 207                 case SE_ERR_FNF:
 208                 {
 209                     error = "file not found"; break;
 210                 }
 211                 case SE_ERR_NOASSOC:
 212                 {
 213                     error = "no association for this type of file"; break;
 214                 }
 215                 case SE_ERR_OOM:
 216                 {
 217                     error = "not enough memory"; break;
 218                 }
 219                 case SE_ERR_PNF:
 220                 {
 221                     error = "path not found"; break;
 222                 }
 223                 case SE_ERR_SHARE:
 224                 {
 225                     error = "sharing violation"; break;
 226                 }
 227             }
 228             errorMessage.Append(error);
 229             throw Exception(errorMessage);
 230         }
 231     }
 232 
 233     public ushort RegisterWindowClass(const char* windowClassNameuint styleint backgroundColor)
 234     {
 235         ushort windowClass = WinRegisterWindowClass(windowClassNamestylebackgroundColor);
 236         if (windowClass == 0u)
 237         {
 238             throw WindowsAPIException(WinGetLastError());
 239         }
 240         return windowClass;
 241     }
 242 
 243     public void* CreateWindowByClassAtom(ushort windowClassconst char* windowNamelong stylelong exStylePoint locationSize sizeControl* parent)
 244     {
 245         void* parentHandle = null;
 246         if (parent != null)
 247         {
 248             parentHandle = parent->Handle();
 249         }
 250         void* result = WinCreateWindowByClassAtom(windowClasswindowNamestyleexStylelocation.xlocation.ysize.wsize.hparentHandle);
 251         if (result == null)
 252         {
 253             throw WindowsAPIException(WinGetLastError());
 254         }
 255         else
 256         {
 257             return result;
 258         }
 259     }
 260 
 261     public void* CreateWindowByClassName(const char* windowClassconst char* windowNamelong stylelong exStylePoint locationSize sizeControl* parent)
 262     {
 263         void* parentHandle = null;
 264         if (parent != null)
 265         {
 266             parentHandle = parent->Handle();
 267         }
 268         void* result = WinCreateWindowByClassName(windowClasswindowNamestyleexStylelocation.xlocation.ysize.wsize.hparentHandle);
 269         if (result == null)
 270         {
 271             throw WindowsAPIException(WinGetLastError());
 272         }
 273         else
 274         {
 275             return result;
 276         }
 277     }
 278 
 279     public void MoveWindow(void* windowHandleconst Point& locationconst Size& sizebool repaint)
 280     {
 281         bool succeeded = WinMoveWindow(windowHandlelocation.xlocation.ysize.wsize.hrepaint);
 282         if (!succeeded)
 283         {
 284             throw WindowsAPIException(WinGetLastError());
 285         }
 286     }
 287 
 288     public WinRect GetClientRect(void* windowHandle)
 289     {
 290         WinRect rect;
 291         bool succeeded = WinGetClientRect(windowHandle&rect);
 292         if (!succeeded)
 293         {
 294             throw WindowsAPIException(WinGetLastError());
 295         }
 296         return rect;
 297     }
 298 
 299     public WinRect GetWindowRect(void* windowHandle)
 300     {
 301         WinRect rect;
 302         bool succeeded = WinGetWindowRect(windowHandle&rect);
 303         if (!succeeded)
 304         {
 305             throw WindowsAPIException(WinGetLastError());
 306         }
 307         return rect;
 308     }
 309 
 310     public Point ClientToScreen(void* windowHandleconst Point& point)
 311     {
 312         Point pt(point);
 313         bool succeeded = WinClientToScreen(windowHandle&pt);
 314         if (!succeeded)
 315         {
 316             throw WindowsAPIException(WinGetLastError());
 317         }
 318         return pt;
 319     }
 320 
 321     public Point ScreenToClient(void* windowHandleconst Point& point)
 322     {
 323         Point pt(point);
 324         bool succeeded = WinScreenToClient(windowHandle&pt);
 325         if (!succeeded)
 326         {
 327             throw WindowsAPIException(WinGetLastError());
 328         }
 329         return pt;
 330     }
 331 
 332     public nothrow bool PtInRect(const Point& pointconst WinRect& rect)
 333     {
 334         return WinPtInRect(&rect&point);
 335     }
 336 
 337     public void SetWindowText(void* windowHandleconst string& text)
 338     {
 339         bool succeeded = WinSetWindowText(windowHandletext.Chars());
 340         if (!succeeded)
 341         {
 342             throw WindowsAPIException(WinGetLastError());
 343         }
 344     }
 345 
 346     public int GetWindowTextLength(void* windowHandle)
 347     {
 348         int result = WinGetWindowTextLength(windowHandle);
 349         if (result == 0)
 350         {
 351             ulong errorCode = WinGetLastError();
 352             if (errorCode != 0u)
 353             {
 354                 throw WindowsAPIException(errorCode);
 355             }
 356         }
 357         return result;
 358     }
 359 
 360     public string GetWindowText(void* windowHandle)
 361     {
 362         int length = GetWindowTextLength(windowHandle);
 363         UniquePtr<wchar> buffer(cast<wchar*>(MemAlloc(sizeof(wchar) * (length + 1))));
 364         int result = WinGetWindowText(windowHandlebuffer.Get()length + 1);
 365         if (result == 0)
 366         {
 367             ulong errorCode = WinGetLastError();
 368             if (errorCode != 0u)
 369             {
 370                 throw WindowsAPIException(errorCode);
 371             }
 372         }
 373         return ToUtf8(buffer.Get());
 374     }
 375 
 376     public void SetParentWindow(void* childWindowHandlevoid* parentWindowHandle)
 377     {
 378         void* result = WinSetParent(childWindowHandleparentWindowHandle);
 379         if (result == null)
 380         {
 381             throw WindowsAPIException(WinGetLastError());
 382         }
 383     }
 384 
 385     public void* BeginPaint(void* windowHandlevoid*& paintStruct)
 386     {
 387         void* result = WinBeginPaint(windowHandlepaintStruct);
 388         if (result == null)
 389         {
 390             throw WindowsAPIException(WinGetLastError());
 391         }
 392         return result;
 393     }
 394 
 395     public void EndPaint(void* windowHandlevoid* paintStruct)
 396     {
 397         WinEndPaint(windowHandlepaintStruct);
 398     }
 399 
 400     public void InvalidateRect(void* windowHandleWinRect* rectbool eraseBackground)
 401     {
 402         bool succeeded = WinInvalidateRect(windowHandlerecteraseBackground);
 403         if (!succeeded)
 404         {
 405             throw WindowsAPIException(WinGetLastError());
 406         }
 407     }
 408 
 409     public nothrow bool MessageBeep(uint beepType)
 410     {
 411         return WinMessageBeep(beepType);
 412     }
 413 
 414     public void MessageBeepChecked(uint beepType)
 415     {
 416         bool succeeded = WinMessageBeep(beepType);
 417         if (!succeeded)
 418         {
 419             throw WindowsAPIException(WinGetLastError());
 420         }
 421     }
 422 
 423     public void* GetDC(void* windowHandle)
 424     {
 425         return WinGetDC(windowHandle);
 426     }
 427 
 428     public void PostMessage(void* windowHandleuint msguint wparamlong lparam)
 429     {
 430         bool succeeded = WinPostMessage(windowHandlemsgwparamlparam);
 431         if (!succeeded)
 432         {
 433             throw WindowsAPIException(WinGetLastError());
 434         }
 435     }
 436 
 437     public void ThrowCommonDialogError(uint errorCode)
 438     {
 439         string errorStr;
 440         switch (errorCode)
 441         {
 442             case 0xFFFFu:
 443             {
 444                 errorStr = "CDERR_DIALOGFAILURE";
 445                 break;
 446             }
 447             case 0x0006u:
 448             {
 449                 errorStr = "CDERR_FINDRESFAILURE";
 450                 break;
 451             }
 452             case 0x0002u:
 453             {
 454                 errorStr = "CDERR_INITIALIZATION";
 455                 break;
 456             }
 457             case 0x0007u:
 458             {
 459                 errorStr = "CDERR_LOADRESFAILURE";
 460                 break;
 461             }
 462             case 0x0005u:
 463             {
 464                 errorStr = "CDERR_LOADSTRFAILURE";
 465                 break;
 466             }
 467             case 0x0008u:
 468             {
 469                 errorStr = "CDERR_LOCKRESFAILURE";
 470                 break;
 471             }
 472             case 0x0009u:
 473             {
 474                 errorStr = "CDERR_MEMALLOCFAILURE";
 475                 break;
 476             }
 477             case 0x000Au:
 478             {
 479                 errorStr = "CDERR_MEMLOCKFAILURE";
 480                 break;
 481             }
 482             case 0x0004u:
 483             {
 484                 errorStr = "CDERR_NOHINSTANCE";
 485                 break;
 486             }
 487             case 0x000Bu:
 488             {
 489                 errorStr = "CDERR_NOHOOK";
 490                 break;
 491             }
 492             case 0x0003u:
 493             {
 494                 errorStr = "CDERR_NOTEMPLATE";
 495                 break;
 496             }
 497             case 0x000Cu:
 498             {
 499                 errorStr = "CDERR_REGISTERMSGFAIL";
 500                 break;
 501             }
 502             case 0x0001u:
 503             {
 504                 errorStr = "CDERR_STRUCTSIZE";
 505                 break;
 506             }
 507             case 0x100Au:
 508             {
 509                 errorStr = "PDERR_CREATEICFAILURE";
 510                 break;
 511             }
 512             case 0x100Cu:
 513             {
 514                 errorStr = "PDERR_DEFAULTDIFFERENT";
 515                 break;
 516             }
 517             case 0x1009u:
 518             {
 519                 errorStr = "PDERR_DNDMMISMATCH";
 520                 break;
 521             }
 522             case 0x1005u:
 523             {
 524                 errorStr = "PDERR_GETDEVMODEFAIL";
 525                 break;
 526             }
 527             case 0x1006u:
 528             {
 529                 errorStr = "PDERR_INITFAILURE";
 530                 break;
 531             }
 532             case 0x1004u:
 533             {
 534                 errorStr = "PDERR_LOADDRVFAILURE";
 535                 break;
 536             }
 537             case 0x1008u:
 538             {
 539                 errorStr = "PDERR_NODEFAULTPRN";
 540                 break;
 541             }
 542             case 0x1007u:
 543             {
 544                 errorStr = "PDERR_NODEVICES";
 545                 break;
 546             }
 547             case 0x1002u:
 548             {
 549                 errorStr = "PDERR_PARSEFAILURE";
 550                 break;
 551             }
 552             case 0x100Bu:
 553             {
 554                 errorStr = "PDERR_PRINTERNOTFOUND";
 555                 break;
 556             }
 557             case 0x1003u:
 558             {
 559                 errorStr = "PDERR_RETDEFFAILURE";
 560                 break;
 561             }
 562             case 0x1001u:
 563             {
 564                 errorStr = "PDERR_SETUPFAILURE";
 565                 break;
 566             }
 567             case 0x2002u:
 568             {
 569                 errorStr = "CFERR_MAXLESSTHANMIN";
 570                 break;
 571             }
 572             case 0x2001u:
 573             {
 574                 errorStr = "CFERR_NOFONTS";
 575                 break;
 576             }
 577             case 0x3003u:
 578             {
 579                 errorStr = "FNERR_BUFFERTOOSMALL";
 580                 break;
 581             }
 582             case 0x3002u:
 583             {
 584                 errorStr = "FNERR_INVALIDFILENAME";
 585                 break;
 586             }
 587             case 0x3001u:
 588             {
 589                 errorStr = "FNERR_SUBCLASSFAILURE";
 590                 break;
 591             }
 592             case 0x4001u:
 593             {
 594                 errorStr = "FRERR_BUFFERLENGTHZERO";
 595                 break;
 596             }
 597             default:
 598             {
 599                 errorStr = "generic error";
 600                 break;
 601             }
 602         }
 603         MessageBox.Show(errorStr);
 604         //throw CommonDialogError(errorCode, errorStr);
 605     }
 606 
 607     public const uint OFN_READONLY =                0x00000001u;
 608     public const uint OFN_OVERWRITEPROMPT =         0x00000002u;
 609     public const uint OFN_HIDEREADONLY =            0x00000004u;
 610     public const uint OFN_NOCHANGEDIR =             0x00000008u;
 611     public const uint OFN_SHOWHELP =                0x00000010u;
 612     public const uint OFN_ENABLEHOOK =              0x00000020u;
 613     public const uint OFN_ENABLETEMPLATE =          0x00000040u;
 614     public const uint OFN_ENABLETEMPLATEHANDLE =    0x00000080u;
 615     public const uint OFN_NOVALIDATE =              0x00000100u;
 616     public const uint OFN_ALLOWMULTISELECT =        0x00000200u;
 617     public const uint OFN_EXTENSIONDIFFERENT =      0x00000400u;
 618     public const uint OFN_PATHMUSTEXIST =           0x00000800u;
 619     public const uint OFN_FILEMUSTEXIST =           0x00001000u;
 620     public const uint OFN_CREATEPROMPT =            0x00002000u;
 621     public const uint OFN_SHAREAWARE =              0x00004000u;
 622     public const uint OFN_NOREADONLYRETURN =        0x00008000u;
 623     public const uint OFN_NOTESTFILECREATE =        0x00010000u;
 624     public const uint OFN_NONETWORKBUTTON =         0x00020000u;
 625     public const uint OFN_NOLONGNAMES =             0x00040000u;
 626     public const uint OFN_EXPLORER =                0x00080000u;
 627     public const uint OFN_NODEREFERENCELINKS =      0x00100000u;
 628     public const uint OFN_LONGNAMES =               0x00200000u;
 629 
 630     public bool GetOpenFileName(void* windowHandleconst List<Pair<stringstring>>& descriptionFilterPairsconst string& initialDirectoryconst string& defaultFilePath
 631         const string& defaultExtensionuint flagsstring& currentDirectoryList<string>& fileNames)
 632     {
 633         long filterBufferSize = 0;
 634         for (const Pair<stringstring>& descriptionFilterPair : descriptionFilterPairs)
 635         {
 636             wstring description = ToUtf16(descriptionFilterPair.first);
 637             wstring filter = ToUtf16(descriptionFilterPair.second);
 638             filterBufferSize = filterBufferSize + description.Length() + 1 + filter.Length() + 1;
 639         }
 640         filterBufferSize = filterBufferSize + 1 + 1;
 641         UniquePtr<wchar> filterBuffer = cast<wchar*>(MemAlloc(sizeof(wchar) * filterBufferSize));
 642         wchar* filterBufferPtr = filterBuffer.Get();
 643         for (const Pair<stringstring>& descriptionFilterPair : descriptionFilterPairs)
 644         {
 645             wstring description = ToUtf16(descriptionFilterPair.first);
 646             long descLen = StrCopy(filterBufferPtrdescription.Chars()description.Length());
 647             filterBufferPtr = filterBufferPtr + descLen + 1;
 648             wstring filter = ToUtf16(descriptionFilterPair.second);
 649             long filterLen = StrCopy(filterBufferPtrfilter.Chars()filter.Length());
 650             filterBufferPtr = filterBufferPtr + filterLen + 1;
 651         }
 652         *filterBufferPtr++ = '\0';
 653         *filterBufferPtr++ = '\0';
 654         long fileNameBufferSize = 16 * 1024;
 655         UniquePtr<wchar> fileNameBuffer = cast<wchar*>(MemAlloc(sizeof(wchar) * fileNameBufferSize));
 656         wstring defFilePath = ToUtf16(defaultFilePath);
 657         if (defaultFilePath.IsEmpty())
 658         {
 659             *fileNameBuffer.Get() = '\0';
 660         }
 661         else
 662         {
 663             StrCopy(fileNameBuffer.Get()defFilePath.Chars()defFilePath.Length());
 664         }
 665         wstring initialDir = ToUtf16(initialDirectory);
 666         wstring defExt = ToUtf16(defaultExtension);
 667         bool success = WinGetOpenFileName(windowHandlefilterBuffer.Get()initialDir.Chars()fileNameBuffer.Get()cast<uint>(fileNameBufferSize)flagsdefExt.Chars());
 668         if (success)
 669         {
 670             long firstPathLength = StrLen(fileNameBuffer.Get());
 671             string firstPath = ToUtf8(wstring(fileNameBuffer.Get()firstPathLength));
 672             if ((flags & OFN_ALLOWMULTISELECT) == 0)
 673             {
 674                 currentDirectory = Path.GetDirectoryName(firstPath);
 675                 fileNames.Add(Path.GetFileName(firstPath));
 676             }
 677             else
 678             {
 679                 currentDirectory = firstPath;
 680                 long pathStart = firstPathLength + 1;
 681                 wchar* wpath = fileNameBuffer.Get() + pathStart;
 682                 while (*wpath != '\0')
 683                 {
 684                     long pathLength = StrLen(wpath);
 685                     string path = ToUtf8(wstring(wpathpathLength));
 686                     fileNames.Add(path);
 687                     wpath = wpath + pathLength + 1;
 688                 }
 689             }
 690             return true;
 691         }
 692         else
 693         {
 694             uint errorCode = WinCommDlgExtendedError();
 695             if (errorCode == 0u)
 696             {
 697                 return false;
 698             }
 699             else
 700             {
 701                 ThrowCommonDialogError(errorCode);
 702             }
 703         }
 704         return false;
 705     }
 706 
 707     public bool GetSaveFileName(void* windowHandleconst List<Pair<stringstring>>& descriptionFilterPairsconst string& initialDirectoryconst string& defaultFilePath
 708         const string& defaultExtensionuint flagsstring& currentDirectorystring& filePath)
 709     {
 710         long filterBufferSize = 0;
 711         for (const Pair<stringstring>& descriptionFilterPair : descriptionFilterPairs)
 712         {
 713             wstring description = ToUtf16(descriptionFilterPair.first);
 714             wstring filter = ToUtf16(descriptionFilterPair.second);
 715             filterBufferSize = filterBufferSize + description.Length() + 1 + filter.Length() + 1;
 716         }
 717         filterBufferSize = filterBufferSize + 1 + 1;
 718         UniquePtr<wchar> filterBuffer = cast<wchar*>(MemAlloc(sizeof(wchar) * filterBufferSize));
 719         wchar* filterBufferPtr = filterBuffer.Get();
 720         for (const Pair<stringstring>& descriptionFilterPair : descriptionFilterPairs)
 721         {
 722             wstring description = ToUtf16(descriptionFilterPair.first);
 723             long descLen = StrCopy(filterBufferPtrdescription.Chars()description.Length());
 724             filterBufferPtr = filterBufferPtr + descLen + 1;
 725             wstring filter = ToUtf16(descriptionFilterPair.second);
 726             long filterLen = StrCopy(filterBufferPtrfilter.Chars()filter.Length());
 727             filterBufferPtr = filterBufferPtr + filterLen + 1;
 728         }
 729         *filterBufferPtr++ = '\0';
 730         *filterBufferPtr++ = '\0';
 731         long fileNameBufferSize = 1024;
 732         UniquePtr<wchar> fileNameBuffer = cast<wchar*>(MemAlloc(sizeof(wchar) * fileNameBufferSize));
 733         wstring defFilePath = ToUtf16(defaultFilePath);
 734         if (defaultFilePath.IsEmpty())
 735         {
 736             *fileNameBuffer.Get() = '\0';
 737         }
 738         else
 739         {
 740             StrCopy(fileNameBuffer.Get()defFilePath.Chars()defFilePath.Length());
 741         }
 742         wstring initialDir = ToUtf16(initialDirectory);
 743         wstring defExt = ToUtf16(defaultExtension);
 744         bool success = WinGetSaveFileName(windowHandlefilterBuffer.Get()initialDir.Chars()fileNameBuffer.Get()cast<uint>(fileNameBufferSize)flagsdefExt.Chars());
 745         if (success)
 746         {
 747             long firstPathLength = StrLen(fileNameBuffer.Get());
 748             filePath = ToUtf8(wstring(fileNameBuffer.Get()firstPathLength));
 749             return true;
 750         }
 751         else
 752         {
 753             uint errorCode = WinCommDlgExtendedError();
 754             if (errorCode == 0u)
 755             {
 756                 return false;
 757             }
 758             else
 759             {
 760                 ThrowCommonDialogError(errorCode);
 761             }
 762         }
 763         return false;
 764     }
 765 
 766     public void* LoadImage(const char* nameuint typeint cxint cy)
 767     {
 768         void* result = WinLoadImage(nametypecxcy);
 769         if (result == null)
 770         {
 771             throw WindowsAPIException(WinGetLastError());
 772         }
 773         return result;
 774     }
 775 
 776     public void* LoadCursor(const char* name)
 777     {
 778         void* result = WinLoadCursor(name);
 779         if (result == null)
 780         {
 781             throw WindowsAPIException(WinGetLastError());
 782         }
 783         return result;
 784     }
 785 
 786     public void* LoadIcon(const char* name)
 787     {
 788         void* result = WinLoadIcon(name);
 789         if (result == null)
 790         {
 791             throw WindowsAPIException(WinGetLastError());
 792         }
 793         return result;
 794     }
 795 
 796     public void* LoadBitmap(const char* name)
 797     {
 798         void* result = WinLoadBitmap(name);
 799         if (result == null)
 800         {
 801             throw WindowsAPIException(WinGetLastError());
 802         }
 803         return result;
 804     }
 805 
 806     public int GetSystemMetrics(int index)
 807     {
 808         int result = WinGetSystemMetrics(index);
 809         if (result == 0)
 810         {
 811             throw WindowsAPIException(WinGetLastError());
 812         }
 813         return result;
 814     }
 815 
 816     public void* GetTopWindow(void* windowHandle)
 817     {
 818         return WinGetTopWindow(windowHandle);
 819     }
 820 
 821     public void BringWindowToTop(void* windowHandle)
 822     {
 823         bool succeeded = WinBringWindowToTop(windowHandle);
 824         if (!succeeded)
 825         {
 826             throw WindowsAPIException(WinGetLastError());
 827         }
 828     }
 829 
 830     public enum WindowPos : int
 831     {
 832         top = 0bottom = 1topmost = -1noTopmost = -2
 833     }
 834 
 835     public nothrow void* WindowPosHandle(WindowPos pos)
 836     {
 837         return cast<void*>(cast<ulong>(cast<int>(pos)));
 838     }
 839 
 840     public enum SetWindowPosFlags : uint
 841     {
 842         SWP_ASYNCWINDOWPOS = 0x4000u
 843         SWP_DEFERERASE = 0x2000u
 844         SWP_DRAWFRAME = 0x20u
 845         SWP_FRAMECHANGED = 0x20u
 846         SWP_HIDEWINDOW = 0x80u
 847         SWP_NOACTIVATE = 0x10u
 848         SWP_NOCOPYBITS = 0x100u
 849         SWP_NOMOVE = 0x2u
 850         SWP_NOOWNERZORDER = 0x200u
 851         SWP_NOREDRAW = 0x8u
 852         SWP_NOREPOSITION = 0x200u
 853         SWP_NOSENDCHANGING = 0x400u
 854         SWP_NOSIZE = 0x1u
 855         SWP_NOZORDER = 0x4u
 856         SWP_SHOWWINDOW = 0x40u
 857     }
 858 
 859     public void SetWindowPos(void* windowHandlevoid* insertAfterWindowHandleint xint yint cxint cySetWindowPosFlags flags)
 860     {
 861         bool succeeded = WinSetWindowPos(windowHandleinsertAfterWindowHandlexycxcycast<uint>(flags));
 862         if (!succeeded)
 863         {
 864             throw WindowsAPIException(WinGetLastError());
 865         }
 866     }
 867 
 868     public enum ClassLongIndex : int
 869     {
 870         GCL_CBCLSEXTRA = -20
 871         GCL_CBWNDEXTRA = -18
 872         GCL_HBRBACKGROUND = -10
 873         GCL_HCURSOR = -12
 874         GCL_HICON = -14
 875         GCL_HICONSM = -34
 876         GCL_HMODULE = -16
 877         GCL_MENUNAME = -8
 878         GCL_STYLE = -26
 879         GCL_WNDPROC = -24
 880     }
 881 
 882     public long GetClassLong(void* windowHandleClassLongIndex index)
 883     {
 884         long value = WinGetClassLong(windowHandlecast<int>(index));
 885         if (value == 0)
 886         {
 887             ulong errorCode = WinGetLastError();
 888             if (errorCode != 0u)
 889             {
 890                 throw WindowsAPIException(errorCode);
 891             }
 892         }
 893         return value;
 894     }
 895 
 896     public void SetClassLong(void* windowHandleClassLongIndex indexlong newValue)
 897     {
 898         long value = WinSetClassLong(windowHandlecast<int>(index)newValue);
 899         if (value == 0)
 900         {
 901             ulong errorCode = WinGetLastError();
 902             if (errorCode != 0u)
 903             {
 904                 throw WindowsAPIException(errorCode);
 905             }
 906         }
 907     }
 908 
 909     public enum WindowLongIndex : int
 910     {
 911         GWL_EXSTYLE = -20
 912         GWLP_HINSTANCE = -6
 913         GWLP_ID = -12
 914         GWL_STYLE = -16
 915         GWLP_USERDATA = -21
 916         GWLP_WNDPROC = -4
 917     }
 918 
 919     public long GetWindowLong(void* windowHandleWindowLongIndex index)
 920     {
 921         long value = WinGetWindowLong(windowHandlecast<int>(index));
 922         if (value == 0)
 923         {
 924             throw WindowsAPIException(WinGetLastError());
 925         }
 926         return value;
 927     }
 928 
 929     public void SetWindowLong(void* windowHandleWindowLongIndex indexlong newValue)
 930     {
 931         bool succeeded = WinSetWindowLong(windowHandlecast<int>(index)newValue);
 932         if (!succeeded)
 933         {
 934             throw WindowsAPIException(WinGetLastError());
 935         }
 936     }
 937 
 938     public void ScrollWindow(void* windowHandleint xAmountint yAmountconst Rect* clientRectconst Rect* clipRect)
 939     {
 940         int clientLocX = 0;
 941         int clientLocY = 0;
 942         int clientSizeW = 0;
 943         int clientSizeH = 0;
 944         if (clientRect != null)
 945         {
 946             clientLocX = clientRect->location.x;
 947             clientLocY = clientRect->location.y;
 948             clientSizeW = clientRect->size.w;
 949             clientSizeH = clientRect->size.h;
 950         }
 951         int clipLocX = 0;
 952         int clipLocY = 0;
 953         int clipSizeW = 0;
 954         int clipSizeH = 0;
 955         if (clipRect != null)
 956         {
 957             clipLocX = clipRect->location.x;
 958             clipLocY = clipRect->location.y;
 959             clipSizeW = clipRect->size.w;
 960             clipSizeH = clipRect->size.h;
 961         }
 962         bool succeeded = WinScrollWindow(windowHandlexAmountyAmountclientLocXclientLocYclientSizeWclientSizeHclipLocXclipLocYclipSizeWclipSizeH);
 963         if (!succeeded)
 964         {
 965             throw WindowsAPIException(WinGetLastError());
 966         }
 967     }
 968 
 969     public void ScrollWindowEx(void* windowHandleint dxint dyconst Rect* clientRectconst Rect* clipRectconst Rect& updateRect)
 970     {
 971         int clientLocX = 0;
 972         int clientLocY = 0;
 973         int clientSizeW = 0;
 974         int clientSizeH = 0;
 975         if (clientRect != null)
 976         {
 977             clientLocX = clientRect->location.x;
 978             clientLocY = clientRect->location.y;
 979             clientSizeW = clientRect->size.w;
 980             clientSizeH = clientRect->size.h;
 981         }
 982         int clipLocX = 0;
 983         int clipLocY = 0;
 984         int clipSizeW = 0;
 985         int clipSizeH = 0;
 986         if (clipRect != null)
 987         {
 988             clipLocX = clipRect->location.x;
 989             clipLocY = clipRect->location.y;
 990             clipSizeW = clipRect->size.w;
 991             clipSizeH = clipRect->size.h;
 992         }
 993         int x1 = updateRect.location.x;
 994         int y1 = updateRect.location.y;
 995         int x2 = updateRect.location.x + updateRect.size.w;
 996         int y2 = updateRect.location.y + updateRect.size.h;
 997         bool succeeded = WinScrollWindowEx(windowHandledxdyclientLocXclientLocYclientSizeWclientSizeHclipLocXclipLocYclipSizeWclipSizeHx1y1x2y2);
 998         if (!succeeded)
 999         {
1000             throw WindowsAPIException(WinGetLastError());
1001         }
1002     }
1003 
1004     public enum ScrollBar : int
1005     {
1006         SB_BOTH = 3
1007         SB_CTL = 2
1008         SB_HORZ = 0
1009         SB_VERT = 1
1010     }
1011 
1012     public void GetScrollInfo(void* windowHandleScrollBar scrollBaruint& nPageint& nPosint& nMinint& nMaxint& nTrackPos)
1013     {
1014         bool succeeded = WinGetScrollInfo(windowHandlecast<int>(scrollBar)nPagenPosnMinnMaxnTrackPos);
1015         if (!succeeded)
1016         {
1017             throw WindowsAPIException(WinGetLastError());
1018         }
1019     }
1020 
1021     public enum ScrollInfoMask : uint
1022     {
1023         SIF_DISABLENOSCROLL = 8u
1024         SIF_PAGE = 2u
1025         SIF_POS = 4u
1026         SIF_RANGE = 1u
1027     }
1028 
1029     public int SetScrollInfo(void* windowHandleScrollBar scrollBarScrollInfoMask maskbool redrawuint nPageint nPosint nMinint nMax)
1030     {
1031         return WinSetScrollInfo(windowHandlecast<int>(scrollBar)cast<uint>(mask)redrawnPagenPosnMinnMax);
1032     }
1033 
1034     public void ShowScrollBar(void* windowHandleScrollBar scrollBarbool show)
1035     {
1036         bool succeeded = WinShowScrollBar(windowHandlecast<int>(scrollBar)show);
1037         if (!succeeded)
1038         {
1039             throw WindowsAPIException(WinGetLastError());
1040         }
1041     }
1042 
1043     public void CreateCaret(void* windowHandlevoid* bitmapHandleint widthint height)
1044     {
1045         bool succeeded = WinCreateCaret(windowHandlebitmapHandlewidthheight);
1046         if (!succeeded)
1047         {
1048             throw WindowsAPIException(WinGetLastError());
1049         }
1050     }
1051 
1052     public void DestroyCaret()
1053     {
1054         bool succeeded = WinDestroyCaret();
1055         if (!succeeded)
1056         {
1057             throw WindowsAPIException(WinGetLastError());
1058         }
1059     }
1060 
1061     public void ShowCaret(void* windowHandle)
1062     {
1063         bool succeeded = WinShowCaret(windowHandle);
1064         if (!succeeded)
1065         {
1066             throw WindowsAPIException(WinGetLastError());
1067         }
1068     }
1069 
1070     public void HideCaret(void* windowHandle)
1071     {
1072         bool succeeded = WinHideCaret(windowHandle);
1073         if (!succeeded)
1074         {
1075             throw WindowsAPIException(WinGetLastError());
1076         }
1077     }
1078 
1079     public Point GetCaretPos()
1080     {
1081         Point caretPos;
1082         bool succeeded = WinGetCaretPos(caretPos.xcaretPos.y);
1083         if (!succeeded)
1084         {
1085             throw WindowsAPIException(WinGetLastError());
1086         }
1087         return caretPos;
1088     }
1089 
1090     public void SetCaretPos(const Point& caretPos)
1091     {
1092         bool succeeded = WinSetCaretPos(caretPos.xcaretPos.y);
1093         if (!succeeded)
1094         {
1095             throw WindowsAPIException(WinGetLastError());
1096         }
1097     }
1098 
1099     public void SetTimer(void* windowHandleuint timerIduint elapseMs)
1100     {
1101         bool succeeded = WinSetTimer(windowHandletimerIdelapseMs);
1102         if (!succeeded)
1103         {
1104             throw WindowsAPIException(WinGetLastError());
1105         }
1106     }
1107 
1108     public void KillTimer(void* windowHandleuint timerId)
1109     {
1110         bool succeeded = WinKillTimer(windowHandletimerId);
1111         if (!succeeded)
1112         {
1113             throw WindowsAPIException(WinGetLastError());
1114         }
1115     }
1116 
1117     public uint RegisterClipboardFormat(const char* formatName)
1118     {
1119         uint format = WinRegisterClipboardFormat(formatName);
1120         if (format == 0u)
1121         {
1122             throw WindowsAPIException(WinGetLastError());
1123         }
1124         return format;
1125     }
1126 
1127     public void OpenClipboard(void* windowHandle)
1128     {
1129         int maxWait = 100;
1130         for (int i = 0; i < 5; ++i;)
1131         {
1132             bool succeeded = WinOpenClipboard(windowHandle);
1133             if (succeeded)
1134             {
1135                 return;
1136             }
1137             else if (i < 4)
1138             {
1139                 Sleep(Duration.FromMilliseconds(Random() % maxWait));
1140                 maxWait = maxWait * 2;
1141             }
1142         }
1143         throw WindowsAPIException(WinGetLastError());
1144     }
1145 
1146     public void CloseClipboard()
1147     {
1148         bool succeeded = WinCloseClipboard();
1149         if (!succeeded)
1150         {
1151             throw WindowsAPIException(WinGetLastError());
1152         }
1153     }
1154 
1155     public void EmptyClipboard()
1156     {
1157         bool succeeded = WinEmptyClipboard();
1158         if (!succeeded)
1159         {
1160             throw WindowsAPIException(WinGetLastError());
1161         }
1162     }
1163 
1164     public void* SetClipboardData(uint formatvoid* mem)
1165     {
1166         void* retval = WinSetClipboardData(formatmem);
1167         if (retval == null)
1168         {
1169             throw WindowsAPIException(WinGetLastError());
1170         }
1171         return retval;
1172     }
1173 
1174     public void* GetClipboardData(uint format)
1175     {
1176         void* retval = WinGetClipboardData(format);
1177         if (retval == null)
1178         {
1179             throw WindowsAPIException(WinGetLastError());
1180         }
1181         return retval;
1182     }
1183 
1184     public bool IsClipboardFormatAvailable(uint format)
1185     {
1186         return WinIsClipboardFormatAvailable(format);
1187     }
1188 
1189     public void AddClipboardFormatListener(void* windowHandle)
1190     {
1191         bool succeeded = WinAddClipboardFormatListener(windowHandle);
1192         if (!succeeded)
1193         {
1194             throw WindowsAPIException(WinGetLastError());
1195         }
1196     }
1197 
1198     public void RemoveClipboardFormatListener(void* windowHandle)
1199     {
1200         bool succeeded = WinRemoveClipboardFormatListener(windowHandle);
1201         if (!succeeded)
1202         {
1203             throw WindowsAPIException(WinGetLastError());
1204         }
1205     }
1206 
1207     public enum GlobalAllocFlags : uint
1208     {
1209         GMEM_FIXED = 0x0u
1210         GHND = 0x0042u
1211         GMEM_MOVEABLE = 0x0002u
1212         GMEM_ZEROINIT = 0x0040u
1213         GPTR = 0x0040u
1214     }
1215 
1216     public void* GlobalAlloc(GlobalAllocFlags flagsulong size)
1217     {
1218         void* retval = WinGlobalAlloc(cast<uint>(flags)size);
1219         if (retval == null)
1220         {
1221             throw WindowsAPIException(WinGetLastError());
1222         }
1223         return retval;
1224     }
1225 
1226     public void* GlobalLock(void* memHandle)
1227     {
1228         void* retval = WinGlobalLock(memHandle);
1229         if (retval == null)
1230         {
1231             throw WindowsAPIException(WinGetLastError());
1232         }
1233         return retval;
1234     }
1235 
1236     public void GlobalUnlock(void* memHandle)
1237     {
1238         bool retval = WinGlobalUnlock(memHandle);
1239         if (!retval)
1240         {
1241             ulong error = WinGetLastError();
1242             if (error != 0u)
1243             {
1244                 throw WindowsAPIException(error);
1245             }
1246         }
1247     }
1248 
1249     public void GlobalFree(void* memHandle)
1250     {
1251         void* retval = WinGlobalFree(memHandle);
1252         if (retval != null)
1253         {
1254             throw WindowsAPIException(WinGetLastError());
1255         }
1256     }
1257 
1258     public ulong GlobalSize(void* memHandle)
1259     {
1260         ulong size = WinGlobalSize(memHandle);
1261         if (size == 0u)
1262         {
1263             throw WindowsAPIException(WinGetLastError());
1264         }
1265         return size;
1266     }
1267 
1268     public void GetCursorPos(int& xint& y)
1269     {
1270         if (!WinGetCursorPos(xy))
1271         {
1272             throw WindowsAPIException(WinGetLastError());
1273         }
1274     }
1275     public string GetFolder(void* windowHandleconst string& defaultDirectory)
1276     {
1277         wstring defaultDir = ToUtf16(defaultDirectory);
1278         long folderNameBufferSize = 1024;
1279         UniquePtr<wchar> folderNameBuffer = cast<wchar*>(MemAlloc(sizeof(wchar) * folderNameBufferSize));
1280         bool success = WinGetFolder(windowHandledefaultDir.Chars()folderNameBuffer.Get()cast<uint>(folderNameBufferSize));
1281         if (success)
1282         {
1283             wstring folderName(folderNameBuffer.Get());
1284             return ToUtf8(folderName);
1285         }
1286         else
1287         {
1288             return string();
1289         }
1290     }
1291 }