installer: Fixed extra bin/bin path
[sdk] / installer / src / installer.ec
1 static define versionString = "Ecere SDK v0.44.15";
2 static define dateString = $"August 4, 2016";
3 static define builtOnString = $"built on ";
4 static define withoutMinGW = $" (Without MinGW)";
5
6 #define WIN32_LEAN_AND_MEAN
7 #define GetFreeSpace _GetFreeSpace
8 #include <windows.h>
9 #undef GetFreeSpace
10
11 #ifdef ECERE_STATIC
12 import static "ecere"
13 #else
14 import "ecere"
15 #endif
16 import "IDESettings"
17 import "createLink"
18 import "licensing"
19 import "CheckListBox"
20
21 static void SetBuildString(Label label)
22 {
23    static const String addMinGW = "";
24    String s;
25
26 #ifdef NOMINGW
27    addMinGW = withoutMinGW;
28 #endif
29
30    s = PrintString(versionString, addMinGW, " -- ", builtOnString, dateString);
31    label.caption = s;
32    delete s;
33 }
34
35 static bool IsAdministrator()
36 {
37    BOOL b;
38    SID_IDENTIFIER_AUTHORITY NtAuthority = { SECURITY_NT_AUTHORITY };
39    PSID AdministratorsGroup;
40    b = AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup);
41    if(b)
42    {
43       if(!CheckTokenMembership(NULL, AdministratorsGroup, &b))
44          b = FALSE;
45        FreeSid(AdministratorsGroup);
46    }
47    return b == TRUE;
48 }
49
50 struct CheckItem
51 {
52    const char * name;
53    void * data;
54    bool add32Bit;
55    const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
56    {
57       if(add32Bit)
58       {
59          strcpy(tempString, name);
60          strcat(tempString, " (32)");
61          return tempString;
62       }
63       return name;
64    }
65
66    void OnDisplay(Surface surface, int x, int y, int width, void * fieldData, Alignment alignment, DataDisplayFlags displayFlags)
67    {
68       if(!displayFlags.active) { displayFlags.current = false; displayFlags.selected = false; }
69       class::OnDisplay(surface, x + 22, y, width - 22, fieldData, alignment, displayFlags);
70    }
71 };
72
73 //#define BUFFERSIZE 0x10000
74 #define BUFFERSIZE 0x1000
75 bool abortInstall = false;
76
77 void ExtractFileFromArchive(ProgressBar progressBar, const char * path, const char * outputFile)
78 {
79    char fileName[MAX_LOCATION];
80    FileAttribs exists = FileExists(path);
81    bool setTime = false;
82    FileStats stats;
83
84    if(exists.isDirectory)
85    {
86       FileListing listing { path };
87
88       if(outputFile[0])
89       {
90          if(MakeDir(outputFile))
91          {
92             setTime = true;
93             FileGetStats(path, &stats);
94          }
95       }
96
97       while(listing.Find() && !abortInstall)
98       {
99          strcpy(fileName, outputFile);
100
101          // Tweak file name if out
102          if(outputFile[0])
103          {
104             if(!strcmp(fileName, ".")) fileName[0] = '\0';
105             if(listing.name[0] == '/' || listing.name[0] == '\\')
106             {
107                char * afterSlash, rest[MAX_LOCATION];
108                for(afterSlash = fileName; *afterSlash == '/' || *afterSlash == '\\'; afterSlash++);
109                strcpy(rest, afterSlash);
110                PathCat(fileName, "_root");
111                PathCat(fileName, rest);
112             }
113             else if(listing.name[1] == ':')
114             {
115                char letter[10];
116                sprintf(letter, "_%cdrive", toupper(listing.name[0]));
117                PathCat(fileName, letter);
118                PathCat(fileName, listing.name[2] ? (listing.name + 3) : (listing.name + 2));
119             }
120             else
121                PathCat(fileName, listing.name);
122          }
123          else
124             PathCat(fileName, listing.name);
125          if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
126             strcpy(fileName, DIR_SEPS);
127          ExtractFileFromArchive(progressBar, listing.path, fileName);
128       }
129       listing.Stop();
130    }
131    else if(exists)
132    {
133       File input = FileOpen(path, read);
134       if(input)
135       {
136          File output = FileOpen(outputFile, write);
137          if(output)
138          {
139             char fileName[MAX_FILENAME];
140             uint position = progressBar.progress;
141             FileSize dataSize;
142             int c;
143             byte buffer[BUFFERSIZE];
144
145             FileGetSize(path, &dataSize);
146             GetLastDirectory(outputFile, fileName);
147
148             ((GuiApplication)__thisModule).SignalEvent();
149             //((GuiApplication)__thisModule).ProcessInput();
150             //((GuiApplication)__thisModule).UpdateDisplay();
151
152             for(c = 0; c<dataSize && !abortInstall; c += BUFFERSIZE)
153             {
154                uint size = (dataSize > c + BUFFERSIZE) ? BUFFERSIZE : (dataSize - c);
155
156                ((GuiApplication)__thisModule).Unlock();
157
158                input.Read(buffer, 1, size);
159                output.Write(buffer, 1, size);
160
161                ((GuiApplication)__thisModule).Lock();
162
163                progressBar.progress = position + c + size;
164                ((GuiApplication)__thisModule).SignalEvent();
165
166                //((GuiApplication)__thisModule).ProcessInput();
167                //((GuiApplication)__thisModule).UpdateDisplay();
168             }
169
170             delete output;
171             setTime = true;
172             FileGetStats(path, &stats);
173          }
174          delete input;
175       }
176    }
177    if(setTime)
178       FileSetTime(outputFile, stats.created, 0, stats.modified);
179 }
180
181 public enum BitArch { none, bits32, bits64 };
182
183 struct Component
184 {
185    const char * name;
186    const char * dataPath;
187    const char * defInstallPath;
188    Component * subComponents;
189    bool mandatory;
190    bool selected;
191    bool available;
192    BitArch arch;
193
194    Component * parent;
195
196    uint requiredSize;
197    uint size;
198    char installPath[MAX_LOCATION];
199    DataRow row;
200
201    void GetFullPath(char * path, bool is32bit)
202    {
203       if(this != null && parent)
204          parent->GetFullPath(path, is32bit || (arch == bits32 && osIS64bit));
205       else
206          strcpy(path, (this && (is32bit || (arch == bits32 && osIS64bit))) ? installDir32 : installDir);
207
208       if(this != null)
209          PathCat(path, installPath);
210    }
211
212    void Install(char * parentPath, char * parentPath32)
213    {
214       int c;
215       if(selected && (arch == none || arch == bits32 || osIS64bit))
216       {
217          char path64[MAX_LOCATION];
218          char path32[MAX_LOCATION];
219          strcpy(path64, parentPath);
220          PathCat(path64, installPath);
221
222          strcpy(path32, parentPath32);
223          PathCat(path32, installPath);
224
225          installProgress.installing.SetText($"Installing %s...", name);
226          ((GuiApplication)__thisModule).UpdateDisplay();
227
228          if(dataPath)
229          {
230             char * path = path64;
231             char source[MAX_LOCATION];
232             strcpy(source, ":");
233             strcat(source, dataPath);
234
235             if(arch == bits32)
236                path = path32;
237
238             MakeDir(path);
239
240             if(FileExists(source).isFile)
241             {
242                char name[MAX_FILENAME];
243                GetLastDirectory(source, name);
244                PathCat(path, name);
245             }
246             if(requiredSize)
247                ExtractFileFromArchive(installProgress.progressBar, source, path);
248          }
249          if(subComponents)
250          {
251             for(c = 0; subComponents[c].name; c++)
252                subComponents[c].Install(path64, path32);
253          }
254       }
255    }
256 };
257
258 #ifndef NOMINGW
259 define minGWIncluded = true;
260 #else
261 define minGWIncluded = false;
262 #endif
263
264 Array<Component> samples
265 { [
266    { "3D",              "samples/3D",        "3D",          null, false, true, true, none },
267    { "Android",         "samples/android",   "android",          null, false, true, true, none },
268    { $"Audio",           "samples/audio",     "audio",       null, false, true, true, none },
269    { $"Database",        "samples/db",        "db",          null, false, true, true, none },
270    { "eC",              "samples/eC",        "eC",          null, false, true, true, none },
271    { $"Games",           "samples/games",     "games",       null, false, true, true, none },
272    { $"GUI & Graphics",  "samples/guiAndGfx", "guiAndGfx",   null, false, true, true, none },
273    { $"Miscellaneous",   "samples/misc",      "misc",        null, false, true, true, none },
274    { $"Networking",      "samples/net",       "net",         null, false, true, true, none },
275    { $"WIA Scanning",    "samples/scanning",  "scanning",    null, false, true, true, none },
276    { $"Threading",       "samples/threads",   "threads",     null, false, true, true, none },
277    { null }
278 ] };
279
280 public enum CoreSDKID
281 {
282    ide, ide32, runtime, runtime32, ec, ec32,
283    gcc, gdb, mingw, binutils, make,
284    none
285 };
286
287 Array<Component> coreSDK
288 { [
289    { "Ecere IDE",       "ecere-sdk/ide",                    "bin",      null, true,  true, true, bits64 },
290    { "Ecere IDE",       "ecere-sdk32/ide",                  "bin",      null, true,  true, true, bits32 },
291    { $"Runtime Library", "ecere-sdk/ecere.dll",             "bin",      null, true,  true, true, bits64 },
292    { $"Runtime Library", "ecere-sdk32/ecere.dll",           "bin",      null, true,  true, true, bits32 },
293    { $"eC Compiler",     "ecere-sdk/compiler",              "bin",      null, true,  true, true, bits64 },
294    { $"eC Compiler",     "ecere-sdk32/compiler",            "bin",      null, true,  true, true, bits32 },
295    { $"GNU C Compiler",  "tdm/gcc/core",   "tdm", null, true, true, minGWIncluded, none },
296    { $"GNU Debugger",    "tdm/gdb",        "tdm", null, true, true, minGWIncluded, none },
297    { $"MinGW-w64 Runtime",   "tdm/mingwrt",    "tdm", null, true, true, minGWIncluded, none },
298    { $"Binary Utils",    "tdm/binutils",   "tdm", null, true, true, minGWIncluded, none },
299    { $"GNU Make",        "tdm/make",       "tdm", null, true, true, minGWIncluded, none },
300    { null }
301 ] };
302
303 public enum AdditionalID
304 {
305    eda, eda32, audio, audio32, vanilla, vanilla32, extras, upx, gnurx, gnurx32, /*pthreads, */cpp, /*w32api, gcci18n, gdbi18n, makei18n, binutilsi18n, */none
306 };
307
308 Array<Component> additional
309 { [
310    { $"Data Access",     "ecere-sdk/eda",                 "bin",      null, false, true, true, bits64 },
311    { $"Data Access",     "ecere-sdk32/eda",               "bin",      null, false, true, true, bits32 },
312    { $"Ecere Audio",     "ecere-sdk/EcereAudio.dll",      "bin",      null, false, true, true, bits64 },
313    { $"Ecere Audio",     "ecere-sdk32/EcereAudio.dll",    "bin",      null, false, true, true, bits32 },
314    { $"Ecere Vanilla",   "ecere-sdk/libecereVanilla.a",   "lib",      null, false, true, true, bits64 },
315    { $"Ecere Vanilla",   "ecere-sdk32/libecereVanilla.a", "lib",      null, false, true, true, bits32 },
316    { $"Ecere Extras",    "extras",                        "extras",   null, false, true, true, none },
317    { "UPX",             "upx/bin",                       "upx/bin",      null, false, true, true, none },
318    { $"GNU Regexp",      "tdm/gnurx",                     "tdm",    null, false, true, true, bits64 },
319    { $"GNU Regexp",      "tdm/gnurx32",                   "tdm",    null, false, true, true, bits32 },
320 //   { "pthreads",        "tdm/pthreads",                   "mingw",    null, false, true, minGWIncluded, none },
321    { $"C++ Compiler",    "tdm/gcc/c++",                   "tdm",    null, false, true, minGWIncluded, none },
322 //   { "Win32 APIs",      "mingw/w32api",                  "mingw",    null, false, true, minGWIncluded, none },
323 /*   { "GCC I18n",        "mingw/locale/gcc",              "tdm",    null, false, false, minGWIncluded, none },
324    { "GDB I18n",        "mingw/locale/gdb",              "tdm",    null, false, false, minGWIncluded, none },
325    { "Make I18n",       "mingw/locale/make",             "tdm",    null, false, false, minGWIncluded, none },
326    { "Binutils I18n",   "mingw/locale/binutils",         "tdm",    null, false, false, minGWIncluded, none },
327 */
328    { null }
329 ] };
330
331 public enum DocumentationID
332 {
333    ecereBook, apiRef, coursework,
334    gccDoc, gppDocs, gdbDocs, makeDocs, binDocs, mingwDocs, gnurxDocs, upxDocs,
335    none
336 };
337
338 Array<Component> documentation
339 { [
340    { $"Ecere Book",         "ecere-sdk/book",       "doc",            null, false, true, true, none },
341    { $"API Reference",      "ecere-sdk/doc",        "doc",            null, false, true, true, none },
342    { $"Ecere Coursework",   "ecere-sdk/coursework", "doc",            null, false, true, true, none },
343    { $"GCC Docs",           "tdm/doc/gcc",          "tdm",          null, false, false, minGWIncluded, none },
344    { $"G++ Docs",           "tdm/doc/g++",          "tdm",          null, false, false, minGWIncluded, none },
345    { $"GDB Docs",           "tdm/doc/gdb",          "tdm",          null, false, false, minGWIncluded, none },
346    { $"Make Docs",          "tdm/doc/make",         "tdm",          null, false, false, minGWIncluded, none },
347    { $"Binutils Docs",      "tdm/doc/binutils",     "tdm",          null, false, false, minGWIncluded, none },
348    { $"gnurx Docs",         "tdm/doc/gnurx",        "tdm",          null, false, false, minGWIncluded, none },
349    { $"UPX Docs",           "upx/doc",              "upx/doc",  null, false, false, minGWIncluded, none },
350    { null }
351 ] };
352
353 public enum ComponentID
354 {
355    coreSDK,
356    additional,
357    documentation,
358    samples,
359    none
360 };
361
362 Array<Component> components
363 { [
364    { $"Core SDK Files", null, null, coreSDK.array, true, true, true, none },
365    { $"Additional Support", null, null, additional.array, false, true, true, none },
366    { $"Documentation", null /*"doc"*/, null /*"doc"*/, documentation.array, false, true, true, none },
367    { $"Samples", null, "samples", samples.array, false, true, true, none },
368    { null }
369 ] };
370 FileSize totalSize;
371 FileSize totalInstalled;
372
373 struct InstallOption
374 {
375    const char * name;
376    InstallOption * subOptions;
377    bool selected;
378    bool available;
379    DataRow row;
380 };
381
382 enum AssociateOptions
383 {
384    AssociateEPJ,
385    AssociateEC,
386    AssociateC,
387    AssociateCPP,
388    AssociateTXT,
389    Associate3DS,
390    AssociateIMG
391 };
392
393 Array<InstallOption> associateOptions
394 { [
395    { $"Associate with Ecere Project Files (*.epj)", null, true },
396    { $"Associate with eC Files (*.ec, *.eh)", null, true },
397    { $"Associate with C files (*.c, *.h)", null, false },
398    { $"Associate with C++ Files (*.cpp, *.hpp, *.cc, *.hh, *.cxx, *.hxx)", null, false },
399    { $"Associate with text files (*.txt)", null, false },
400    { $"Associate with 3D Studio Model Files (*.3ds)", null, true },
401    { $"Associate with Image Files (*.png, *.jpg, *.pcx, *.bmp, *.gif)", null, false },
402    { null }
403 ] };
404
405 enum PathOptions
406 {
407    AddECEREPaths, AddMinGWPaths
408 };
409
410 Array<InstallOption> pathOptions
411 { [
412    { $"Add Ecere binaries location to the user environment path", null, true, true },
413    { $"Add MinGW to the user environment path", null, true, minGWIncluded },
414    { null }
415 ] };
416
417 enum IconOptions
418 {
419    StartMenuIcon = 1,
420    DesktopIcon = 2,
421    QuickLaunchIcon = 3
422 };
423
424 Array<InstallOption> options
425 { [
426    { $"Install for All Users", null, true, true },
427    { $"Start Menu Group", null, true, true },
428    { $"Desktop Icon", null, true, true },
429    { $"Quicklaunch Icon", null, true, true },
430    { $"Associate the Ecere IDE with Supported File Types", associateOptions.array, true, true },
431    { $"Add binaries location to the user environment paths", pathOptions.array, true, minGWIncluded },
432    { null }
433 ] };
434
435 char sourceDir[MAX_LOCATION] = ":";
436 char installDir[MAX_LOCATION];
437 char installDir32[MAX_LOCATION];
438 bool osIS64bit;
439
440 class Installer : Window
441 {
442    background = formColor;
443    borderStyle = fixed;
444    hasMinimize = true;
445    hasClose = true;
446    tabCycle = true;
447    clientSize = { 636, 476 };
448    icon = { ":icon.png" };
449    caption = $"Ecere Software Development Kit Setup - v0.44.15 \"Ryōan-ji\" Web Edition";
450
451    // clientSize = { 796, 576 };
452    bool loaded;
453
454    Picture back { image = BitmapResource { ":ryoanji.png" }, parent = this, position = { 0, 0 } };
455    FileDialog fileDialog
456    {
457       master = this, type = selectDir,
458       caption = $"Select a new location"
459    };
460    Button browse
461    {
462       master = this, autoCreate = false, inactive = true, caption = "...";
463
464       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
465       {
466          DataRow row = componentsBox.currentRow;
467          Component * component = ((CheckItem *)row.GetData(componentField))->data;
468          char filePath[MAX_LOCATION];
469          component->GetFullPath(filePath, false);
470          fileDialog.filePath = filePath;
471          StripLastDirectory(filePath, filePath);
472          fileDialog.currentDirectory = filePath;
473
474          if(fileDialog.Modal() == ok)
475          {
476             componentsBox.StopEditing(false);
477             row.SetData(locationField, fileDialog.filePath);
478             componentsBox.NotifyChanged(this, componentsBox, row);
479             componentsBox.currentField = componentsBox.currentField;
480          }
481          return true;
482       }
483    };
484    CheckListBox componentsBox
485    {
486       this, size = { 460, 112 }, position = { 160, 160 }, hasHeader = true;
487       fullRowSelect = false, collapseControl = true, treeBranches = true, rootCollapseButton = true,
488       hasHorzScroll = true;
489       resizable = true,
490       noDragging = true;
491       rowHeight = 18;
492       selectionColor = { 145, 150, 140 };
493
494       alwaysEdit = true;
495       opacity = 0;
496
497       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
498       {
499          if(key == f2 && browse.visible)
500             browse.NotifyClicked(this, browse, 0, 0, 0);
501          return true;
502       }
503
504       bool NotifyChanged(ListBox listBox, DataRow row)
505       {
506          Component * component = ((CheckItem *)listBox.GetData(componentField))->data;
507          char path[MAX_LOCATION], relative[MAX_LOCATION] = "", * newPath;
508          char fullPath[MAX_LOCATION];
509
510          component->parent->GetFullPath(path, false);
511          strcpy(fullPath, path);
512
513          newPath = row.GetData(locationField);
514          if(newPath)
515          {
516             PathCat(fullPath, newPath);
517             if(IsPathInsideOf(fullPath, path))
518                MakePathRelative(fullPath, path, relative);
519             else
520                strcpy(relative, fullPath);
521          }
522          listBox.SetData(locationField, relative);
523          strcpy(component->installPath, relative);
524
525          //SetAvailableSpace(component, path);
526          {
527             ComponentID c;
528             install.disabled = false;
529             for(c = 0; components[c].name; c++)
530             {
531                SetAvailableSpace(components[c], installDir);
532             }
533          }
534          return true;
535       }
536
537       bool NotifyEditDone(ListBox listBox, DataRow row)
538       {
539          browse.Destroy(0);
540          return true;
541       }
542
543       bool NotifyEdited(ListBox listBox, DataRow row)
544       {
545          Window e;
546          browse.parent = listBox;
547          browse.position = { componentField.width + locationField.width + 18, (listBox.currentIndex+1) * listBox.rowHeight - 2 };
548          browse.size = { 30, listBox.rowHeight + 3 };
549          for(e = listBox.firstChild; e; e = e.next)
550          {
551             if(e._class == class(DataBox))
552                break;
553          }
554          if(e)
555             e.Activate();
556          browse.Create();
557          return true;
558       }
559
560       void NotifyChecked(CheckListBox listBox, DataRow row)
561       {
562          Component * component = ((CheckItem *)row.GetData(componentField))->data;
563          int c;
564          component->selected = listBox.IsChecked(row);
565
566          if(!component->parent) totalSize -= component->requiredSize;
567          component->requiredSize = 0;
568          if(component->selected)
569          {
570             component->requiredSize += component->size;
571             if(component->subComponents)
572                for(c = 0; component->subComponents[c].name; c++)
573                   component->requiredSize += component->subComponents[c].requiredSize;
574          }
575          if(component->requiredSize)
576          {
577             uint requiredSize = component->requiredSize;
578             row.SetData(reqField, requiredSize);
579          }
580          else
581             row.UnsetData(reqField);
582          if(!component->parent)
583          {
584             totalSize += component->requiredSize;
585             {
586                char sizeString[100];
587                PrintSize(sizeString, totalSize, 2);
588                totalSpaceValue.caption = sizeString;
589             }
590          }
591       }
592    };
593    Label agreementLbl { parent = this, caption = $"By installing the Ecere SDK, you agree to the", font = { "Tahoma", 8.25f }, anchor = Anchor { right = 399, top = 448 } };
594    Button licenseButton
595    {
596       this, inactive = true, offset = false, bevel = false, foreground = blue, font = { "Tahoma", 8.25f, underline = true, bold = true },
597       // caption = $"terms and conditions", anchor = Anchor { left = 241, top = 421 };
598       caption = $"terms and conditions", anchor = Anchor { left = 235, top = 445 };
599       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
600
601       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
602       {
603          // LicenseBox { master = this, sourceFile = ":ecere-sdk/doc/LICENSE" }.Modal();
604          LicensesForm { master = this }.Modal();
605          return true;
606       }
607    };
608    Label dotLbl { parent = this, caption = ".", font = { "Tahoma", 8.25f }, anchor = Anchor { left = 372, top = 448 } };
609    CheckListBox optionsBox
610    {
611       this, size = { 460, 114 }, position = { 160, 284 };
612       fullRowSelect = false, collapseControl = true, treeBranches = true, rootCollapseButton = true,
613       noDragging = true;
614       rowHeight = 18;
615       opacity = 0;
616
617       void NotifyChecked(CheckListBox listBox, DataRow row)
618       {
619          CheckItem * item = row.GetData(optionField);
620          InstallOption * option = item->data;
621          option->selected = listBox.IsChecked(row);
622          // Update default samples/extras path whether we're installing for All Users or not
623          if(option == &options[0])
624          {
625             char appData[MAX_LOCATION];
626
627             options[5].name = options[0].selected ? $"Add binaries location to the system environment paths" : $"Add binaries location to the user environment paths";
628             if(options[5].row)
629                ((CheckItem *)options[5].row.GetData(optionField))->name = options[5].name;
630
631             pathOptions[PathOptions::AddECEREPaths].name = options[0].selected ? $"Add Ecere binaries location to the system environment path" : $"Add Ecere binaries location to the user environment path";
632             if(pathOptions[PathOptions::AddECEREPaths].row)
633                ((CheckItem *)pathOptions[PathOptions::AddECEREPaths].row.GetData(optionField))->name = pathOptions[PathOptions::AddECEREPaths].name;
634
635             pathOptions[PathOptions::AddMinGWPaths].name = options[0].selected ? $"Add TDM-GCC/MinGW-w64 to the system environment path" : $"Add TDM-GCC/MinGW-w64 to the user environment path";
636             if(pathOptions[PathOptions::AddMinGWPaths].row)
637                ((CheckItem *)pathOptions[PathOptions::AddMinGWPaths].row.GetData(optionField))->name = pathOptions[PathOptions::AddMinGWPaths].name;
638
639             GetEnvironment(options[0].selected ? "ALLUSERSPROFILE" : "APPDATA", appData, sizeof(appData));
640             if(appData[0])
641             {
642                char defPath[MAX_LOCATION];
643
644                strcpy(defPath, installDir);
645                PathCat(defPath, components[ComponentID::samples].defInstallPath);
646                ChangeCh(defPath, '/', DIR_SEP);
647                if(!strcmp(defPath, components[ComponentID::samples].installPath))
648                {
649                   static char defSamplesPath[MAX_LOCATION];
650                   strcpy(defSamplesPath, appData);
651                   PathCat(defSamplesPath, "Ecere SDK\\Samples");
652                   components[ComponentID::samples].defInstallPath = defSamplesPath;
653
654                   strcpy(components[ComponentID::samples].installPath, components[ComponentID::samples].defInstallPath);
655                   ChangeCh(components[ComponentID::samples].installPath, '/', DIR_SEP);
656                   components[ComponentID::samples].row.SetData(locationField, components[ComponentID::samples].installPath);
657                }
658
659                strcpy(defPath, installDir);
660                PathCat(defPath, additional[AdditionalID::extras].defInstallPath);
661                ChangeCh(defPath, '/', DIR_SEP);
662                if(!strcmp(additional[AdditionalID::extras].installPath, additional[AdditionalID::extras].installPath))
663                {
664                   static char defExtrasPath[MAX_LOCATION];
665                   strcpy(defExtrasPath, appData);
666                   PathCat(defExtrasPath, "Ecere SDK\\extras");
667                   additional[AdditionalID::extras].defInstallPath = defExtrasPath;
668
669                   strcpy(additional[AdditionalID::extras].installPath, additional[AdditionalID::extras].defInstallPath);
670                   ChangeCh(additional[AdditionalID::extras].installPath, '/', DIR_SEP);
671                   additional[AdditionalID::extras].row.SetData(locationField, additional[AdditionalID::extras].installPath);
672                }
673             }
674             listBox.Update(null);
675          }
676       }
677    };
678    Button install
679    {
680       parent = this, caption = $"Install", isDefault = true, size = { 75, 23 }, position = { 432, 440 };
681
682       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
683       {
684          installProgress.Create();
685          Destroy(0);
686          // installProgress.thread.Main();
687          installProgress.thread.Create();
688          return true;
689       }
690    };
691    Button button3 { parent = this, caption = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 440 }, NotifyClicked = ButtonCloseDialog };
692    DropBox languageBox
693    {
694       this, position = { 14, 374 }, size = { 142, 0 }, caption = "Language:";
695
696       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
697       {
698          LanguageOption * option = row.GetData(null);
699          // If the language is already set, we need to override it
700          {
701             IDESettings settings = null;
702             IDESettingsContainer settingsContainer
703             {
704                driver = "JSON";
705                dataOwner = &settings;
706                dataClass = class(IDESettings);
707                allUsers = options[0].selected;
708             };
709             settingsContainer.Load();
710             if(settings.language)
711             {
712                settings.language = option->code;
713                settingsContainer.Save();
714             }
715             delete settingsContainer;
716             delete settings;
717          }
718          ((GuiApplication)__thisModule.application).desktop.Destroy(0);
719          LanguageRestart(option->code, __thisModule.application, null, null, null, null, false);
720          return true;
721       }
722    };
723    Label lblLanguageBox { this, position = { 14, 354 }, labeledWindow = languageBox };
724    Label label1 { labeledWindow = destBox, tabCycle = true, isGroupBox = true, parent = this, inactive = false, size = Size { 458, 50 }, anchor = Anchor { left = 160, top = 96 } };
725    PathBox destBox
726    {
727       parent = label1, master = this, caption = $" Destination Folder", size = Size { 336, 22 }, anchor = Anchor { left = 12, top = 20, right = 12 };
728       typeExpected = directory;
729       browseDialog = fileDialog;
730       opacity = 0;
731
732       bool NotifyModified(PathBox pathBox)
733       {
734          ComponentID c;
735          strcpy(installDir, destBox.path);
736          install.disabled = false;
737          for(c = 0; components[c].name; c++)
738          {
739             SetAvailableSpace(components[c], installDir);
740          }
741          return true;
742       }
743    };
744    /*Button button1
745    {
746       label1, this, $"Browse", altB, size = { 83, 24 }, position = { 360, 16 };
747
748       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
749       {
750          strcpy(fileDialog.filePath, installDir);
751          StripLastDirectory(installDir, fileDialog.currentDirectory);
752          if(fileDialog.Modal())
753          {
754             strcpy(installDir, fileDialog.filePath);
755             destBox.contents = installDir;
756             // TOCHECK: Should setting contents call NotifyModified?
757             destBox.NotifyModified(destBox.master, destBox);
758          }
759          return true;
760       }
761    };*/
762    EditBox label5
763    {
764       this, multiLine = true,
765       opacity = 0, borderStyle = none, inactive = true, size = { 136, 53 }, position = { 14, 96 }, noSelect = true,
766       contents = $"Select the default root\n"
767          "folder where to install\n"
768          "all components:"
769    };
770    EditBox label6
771    {
772       this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 132 }, position = { 14, 152 }, noSelect = true,
773       multiLine = true,
774       contents = $"Select the optional\n"
775          "components you wish\n"
776          "to install:\n\n"
777          "You may customize the\n"
778          "install location for each\n"
779          "of them, or use the default\n"
780          "based on the main\n"
781          "destination folder."
782    };
783    EditBox label7
784    {
785       this, opacity = 0, borderStyle = none, inactive = true, size = { 136, 83 }, position = { 14, 280 }, noSelect = true,
786       multiLine = true,
787       contents = $"Select icons to install, file\n"
788       "associations, and system\n"
789       "environment modifications:"
790    };
791    Label totalSpaceLabel
792    {
793       this, anchor = { right = 72, top = 404 }, caption = $"Space Required: "
794    };
795    Label totalSpaceValue
796    {
797       this, anchor = { right = 14, top = 404 }, caption = "0 mb"
798    };
799    EditBox editBox1
800    {
801       label3, caption = "editBox1", opacity = 0, borderStyle = none, inactive = true, size = { 350, 35 }, position = { 256, 40 }, multiLine = true, noSelect = true, contents = $"Choose in which folder to install the Ecere SDK, which features\n"
802          "of the SDK to install, as well as where to install program icons."
803    };
804    Label label2 { parent = this, position = { 16, 422 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
805    Picture picture1
806    {
807       image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, caption = "picture1", anchor = Anchor { left = 16, top = 4 };
808       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
809
810       bool OnLeftButtonDown(int x, int y, Modifiers mods)
811       {
812          ShellOpen("http://www.ecere.com/");
813          return true;
814       }
815    };
816    Label label4 { label3, font = { "Tahoma", 8.25f, bold = true }, /*size = { 326, 16 }, */position = { 248, 24 }, caption = $"Choose Components, Locations and Install Options" };
817    DataField componentField { "CheckItem", width = 160, header = $"Component" };
818    DataField locationField { "char *", width = 108, header = $"Destination Folder", editable = true };
819    DataField reqField { "FileSize", width = 70, header = $"Req. Space", alignment = right };
820    DataField avField { "FileSize64", width = 70, header = $"Avail. Space", alignment = right };
821    DataField optionField { "CheckItem" };
822
823    DataField languageField { class(LanguageOption) };
824
825    void SetAvailableSpace(Component component, const char * parentPath)
826    {
827       char path[MAX_LOCATION];
828       int c;
829       FileSize64 size = 0;
830
831       strcpy(path, parentPath);
832       PathCat(path, component.installPath);
833
834       if(component.subComponents)
835          for(c = 0; component.subComponents[c].name; c++)
836             SetAvailableSpace(component.subComponents[c], path);
837
838       while(!FileExists(path) && path[0])
839          StripLastDirectory(path, path);
840
841       if(path[0])
842          GetFreeSpace(path, &size);
843       component.row.SetData(avField, size);
844       if(!size) install.disabled = true;
845    }
846
847    FileSize ComputeSize(const char * path)
848    {
849       FileSize size = 0;
850       FileAttribs attribs = FileExists(path);
851       if(attribs.isDirectory)
852       {
853          FileListing listing { path };
854          while(listing.Find())
855          {
856             if(listing.stats.attribs.isDirectory)
857                size += ComputeSize(listing.path);
858             else
859                size += listing.stats.size;
860          }
861       }
862       else
863          FileGetSize(path, &size);
864       return size;
865    }
866
867    void AddComponent(Component component, Component parent, const char * parentPath)
868    {
869       DataRow row = (parent != null) ? parent.row.AddRow() : componentsBox.AddRow();
870       FileSize size = 0;
871       FileSize64 avSize = 0;
872       char path[MAX_LOCATION];
873
874       component.row = row;
875       strcpy(path, parentPath);
876       if(component.defInstallPath)
877          PathCat(path, component.defInstallPath);
878       component.parent = parent;
879
880       row.SetData(null, CheckItem { component.name, component, (component.arch == bits32 && osIS64bit) } );
881
882       if(component.defInstallPath)
883       {
884          strcpy(component.installPath, component.defInstallPath);
885          ChangeCh(component.installPath, '/', DIR_SEP);
886          row.SetData(locationField, component.installPath);
887       }
888
889       if(component.mandatory)
890       {
891          if(component.arch != bits32 || !osIS64bit) // || component == &coreSDK[gdb32])
892             componentsBox.SetDisabled(row, true);
893          /*else
894             component.selected = false;*/
895       }
896       else if(component.arch == bits32 && osIS64bit)
897          ; //component.selected = false;
898
899       componentsBox.SetCheck(row, component.selected);
900
901       if(component.dataPath)
902       {
903          char path[MAX_LOCATION];
904          strcpy(path, ":");
905          PathCat(path, component.dataPath);
906          component.size = ComputeSize(path);
907       }
908       if(component.subComponents)
909       {
910          int c;
911          for(c = 0; component.subComponents[c].name; c++)
912          {
913             Component * sub = &component.subComponents[c];
914             if(sub->available && (osIS64bit || sub->arch == bits32 || sub->arch == none))
915             {
916                AddComponent(sub, component, path);
917                size += sub->requiredSize;
918             }
919             else
920                sub->selected = false;
921          }
922       }
923
924       component.requiredSize = component.selected ? (size + component.size) : 0;
925       if(component.requiredSize)
926          row.SetData(reqField, component.requiredSize);
927
928       while(!FileExists(path) && path[0])
929          StripLastDirectory(path, path);
930
931       if(path[0])
932          GetFreeSpace(path, &avSize);
933       else
934          avSize = 0;
935       row.SetData(avField, avSize);
936       row.collapsed = true;
937    }
938
939    void AddOption(InstallOption option, InstallOption parent)
940    {
941       DataRow row = option.row = (parent != null) ? parent.row.AddRow() : optionsBox.AddRow();
942       row.SetData(null, CheckItem { option.name, option } );
943       optionsBox.SetCheck(row, option.selected);
944       if(option.subOptions)
945       {
946          int c;
947          for(c = 0; option.subOptions[c].name; c++)
948          {
949             AddOption(option.subOptions[c], option);
950          }
951       }
952       row.collapsed = true;
953    }
954
955    Installer()
956    {
957       int c;
958       char programFilesDir[MAX_LOCATION];
959       char appData[MAX_LOCATION];
960       char homeDrive[MAX_LOCATION];
961       char winDir[MAX_LOCATION];
962       char * x86 = null;
963
964       bool isAdministrator = IsAdministrator();
965
966       SetBuildString(label2);
967
968       if(!isAdministrator)
969       {
970          options[0].available = false;
971          options[0].selected = false;
972       }
973
974       // If the SDK is already installed, use currently selected language
975       {
976          IDESettings settings = null;
977          IDESettingsContainer settingsContainer
978          {
979             driver = "JSON";
980             dataOwner = &settings;
981             dataClass = class(IDESettings);
982             allUsers = options[0].selected;
983          };
984
985          settingsContainer.Load();
986
987          if(settings.language)
988          {
989             const String language = GetLanguageString();
990             if(settings.language.OnCompare(language))
991             {
992                // Relaunch the installer with previously selected language
993                LanguageRestart(settings.language, __thisModule.application, null, null, null, null, false);
994                return false;
995             }
996          }
997          delete settingsContainer;
998          delete settings;
999       }
1000
1001       GetEnvironment("HOMEDRIVE", homeDrive, sizeof(homeDrive));
1002       GetEnvironment("windir", winDir, sizeof(winDir));
1003
1004       GetEnvironment(options[0].selected ? "ALLUSERSPROFILE" : "APPDATA", appData, sizeof(appData));
1005
1006       componentsBox.AddField(componentField);
1007       componentsBox.AddField(locationField);
1008       componentsBox.AddField(reqField);
1009       componentsBox.AddField(avField);
1010
1011       optionsBox.AddField(optionField);
1012
1013       languageBox.AddField(languageField);
1014
1015       programFilesDir[0] = 0;
1016       if(GetEnvironment("ProgramFiles", programFilesDir, MAX_LOCATION))
1017       {
1018          x86 = strstr(programFilesDir, " (x86)");
1019          if(x86)
1020             osIS64bit = true;
1021       }
1022
1023       if(isAdministrator && programFilesDir[0])
1024       {
1025          if(x86)
1026          {
1027             strcpy(installDir32, programFilesDir);
1028             PathCat(installDir32, "Ecere SDK");
1029             *x86 = 0;
1030             strcpy(installDir, programFilesDir);
1031             PathCat(installDir, "Ecere SDK");
1032          }
1033          else
1034          {
1035             strcpy(installDir, programFilesDir);
1036             PathCat(installDir, "Ecere SDK");
1037             strcpy(installDir32, installDir);
1038          }
1039       }
1040       else if(homeDrive[0])
1041       {
1042          strcpy(installDir, homeDrive);
1043          PathCat(installDir, "Ecere SDK");
1044          strcpy(installDir32, installDir);
1045          strcat(installDir32, " (32)");
1046       }
1047       else if(winDir[0])
1048       {
1049          strcpy(installDir, winDir);
1050          PathCat(installDir, "..\\Ecere SDK");
1051          strcpy(installDir32, installDir);
1052          strcat(installDir32, " (32)");
1053       }
1054       else
1055       {
1056          strcpy(installDir, "C:\\Ecere SDK");
1057          strcpy(installDir32, installDir);
1058          strcat(installDir32, " (32)");
1059       }
1060
1061       if(appData[0])
1062       {
1063          static char defSamplesPath[MAX_LOCATION];
1064          static char defExtrasPath[MAX_LOCATION];
1065          strcpy(defSamplesPath, appData);
1066          PathCat(defSamplesPath, "Ecere SDK\\Samples");
1067          components[ComponentID::samples].defInstallPath = defSamplesPath;
1068
1069          strcpy(defExtrasPath, appData);
1070          PathCat(defExtrasPath, "Ecere SDK\\extras");
1071          additional[AdditionalID::extras].defInstallPath = defExtrasPath;
1072       }
1073
1074       destBox.path = installDir;
1075
1076       {
1077          ComponentID c;
1078          for(c = 0; components[c].name; c++)
1079             AddComponent(components[c], null, installDir);
1080       }
1081
1082       // Compute size apart because ToggleCheck will change it
1083       totalSize = 0;
1084       {
1085          ComponentID c;
1086          for(c = 0; components[c].name; c++)
1087             totalSize += components[c].requiredSize;
1088       }
1089       {
1090          char sizeString[100];
1091          PrintSize(sizeString, totalSize, 2);
1092          totalSpaceValue.caption = sizeString;
1093       }
1094       for(c = 0; options[c].name; c++)
1095       {
1096          if(options[c].available)
1097             AddOption(options[c], null);
1098       }
1099    }
1100
1101    void OnDestroy()
1102    {
1103       for(l : languages)
1104          delete l.res;
1105    }
1106
1107    bool OnPostCreate()
1108    {
1109       dotLbl.position.x = licenseButton.position.x + licenseButton.size.w - 4;
1110       return true;
1111    }
1112
1113    bool OnCreate()
1114    {
1115       // Constructor happens before Languages is instantiated...
1116       for(l : languages)
1117       {
1118          l.res = { l.bitmap, window = this };
1119          incref l.res;
1120       }
1121
1122       if(!loaded)
1123       {
1124          const String language = GetLanguageString();
1125          bool found = false;
1126          DataRow row;
1127
1128          // Try to find country-specific language first
1129          for(l : languages)
1130          {
1131             LanguageOption option = l;
1132             row = languageBox.AddRow();
1133             row.SetData(null, option); // TOFIX: l used directly here
1134
1135             if(!found && (!strcmpi(l.code, language) || (!row.GetPrevRow() && !strcmpi("en", language))))
1136             {
1137                languageBox.currentRow = row;
1138                found = true;
1139             }
1140          }
1141
1142          // Try generalizing locale
1143          if(!found)
1144          {
1145             char * under;
1146             char genericLocale[256];
1147             strncpy(genericLocale, language, sizeof(genericLocale));
1148             genericLocale[sizeof(genericLocale)-1] = 0;
1149
1150             under = strchr(genericLocale, '_');
1151             if(under)
1152                *under = 0;
1153             if(!strcmpi(genericLocale, "zh"))
1154                strcpy(genericLocale, "zh_CN");
1155             if(strcmp(genericLocale, language))
1156             {
1157                row = languageBox.firstRow;
1158                for(l : languages)
1159                {
1160                   if(!strcmpi(l.code, genericLocale) || (!row.GetPrevRow() && !strcmpi("en", genericLocale)))
1161                   {
1162                      languageBox.currentRow = row;
1163                      found = true;
1164                      break;
1165                   }
1166                   row = row.GetNextRow();
1167                }
1168             }
1169          }
1170
1171          if(!found)
1172             languageBox.currentRow = languageBox.firstRow;
1173          loaded = true;
1174       }
1175
1176       destBox.Activate();
1177       return true;
1178    }
1179
1180    void OnDrawOverChildren(Surface surface)
1181    {
1182       int tw = label2.size.w;
1183       surface.SetForeground(Color { 128, 128, 128 });
1184       surface.HLine(label2.position.x + tw + 6, 620, 430);
1185       surface.SetForeground(white);
1186       surface.HLine(label2.position.x + tw + 6, 621, 431);
1187       surface.PutPixel(621, 400);
1188    }
1189    Label label3
1190    {
1191       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
1192    };
1193 }
1194
1195 class InstallProgress : Window
1196 {
1197    caption = $"Ecere Software Development Kit Setup - v0.44.15 \"Ryōan-ji\" Web Edition";
1198    background = activeBorder;
1199    borderStyle = fixed;
1200    hasMinimize = true;
1201    hasClose = true;
1202    tabCycle = true;
1203    // size = Size { 640, 480 };
1204    clientSize = { 636, 476 };
1205    //clientSize = { 796, 576 };
1206    icon = { ":icon.png" };
1207
1208    InstallProgress()
1209    {
1210       SetBuildString(label2);
1211    }
1212
1213    Picture back { image = BitmapResource { ":ryoanji-progress.png" }, parent = this, position = { 0, 0 } };
1214    Label installing { this, position = { 32, 160 } };
1215    ProgressBar progressBar { parent = this, size = Size { 588, 24 }, anchor = Anchor { left = 24, top = 184 } };
1216    Button finish
1217    {
1218       parent = this, caption = $"Install", disabled = true, isDefault = true, size = Size { 75, 23 }, anchor = Anchor { left = 432, top = 440 };
1219
1220       NotifyClicked = ButtonCloseDialog
1221    };
1222    Button cancel
1223    {
1224       this, caption = $"Cancel", hotKey = altX, size = Size { 75, 23 }, anchor = Anchor { left = 544, top = 440 };
1225
1226       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1227       {
1228          abortInstall = true;
1229          return true;
1230       }
1231    };
1232    EditBox titleInfo
1233    {
1234       inactive = true, noSelect = true,
1235       multiLine = true, parent = label3, opacity = 0, borderStyle = none, size = Size { 350, 35 }, anchor = Anchor { horz = 111, vert = 13 },
1236       contents = $"Please wait while the Ecere Software Development Kit is being installed."
1237    };
1238    Label label2 { parent = this, position = { 16, 422 }, font = { "Tahoma", 10, true }, disabled = true, opacity = 0, background = activeBorder };
1239    Picture picture1
1240    {
1241       image = BitmapResource { ":ecere.png", alphaBlend = true }, filter = true, parent = label3, anchor = Anchor { left = 16, top = 4 };
1242       cursor = ((GuiApplication)__thisModule).GetCursor(hand);
1243
1244       bool OnLeftButtonDown(int x, int y, Modifiers mods)
1245       {
1246          ShellOpen("http://www.ecere.com/");
1247          return true;
1248       }
1249    };
1250    Label title { parent = label3, caption = $"Installing the Ecere SDK", font = FontResource { "Tahoma", 8.25f, bold = true }, size = Size { 326, 16 }, anchor = Anchor { horz = 91, vert = -12 } };
1251
1252    void OnDrawOverChildren(Surface surface)
1253    {
1254       int tw = label2.size.w;
1255       surface.SetForeground(Color { 128, 128, 128 });
1256       surface.HLine(label2.position.x + tw + 6, 620, 430);
1257       surface.SetForeground(white);
1258       surface.HLine(label2.position.x + tw + 6, 621, 431);
1259       surface.PutPixel(621, 400);
1260    }
1261
1262    Label label3
1263    {
1264       parent = this, opacity = 0, borderStyle = deep, size = Size { 644, 93 }, anchor = Anchor { left = -8, top = -8 };
1265    };
1266    InstallThread thread
1267    {
1268    };
1269 }
1270
1271 Installer installer {};
1272 InstallProgress installProgress { autoCreate = false };
1273
1274 static void AddPath(char * sysPaths[200], int sysCount, char * paths[200], int * count, char * oldPath, char * userPath, char * path)
1275 {
1276    int p;
1277    bool found = false;
1278    for(p = 0; p<sysCount; p++)
1279       if(!fstrcmp(sysPaths[p], path))
1280       {
1281          found = true;
1282          break;
1283       }
1284    for(p = 0; !found && p<*count; p++)
1285       if(!fstrcmp(paths[p], path))
1286       {
1287          found = true;
1288          break;
1289       }
1290    if(!found)
1291    {
1292       char * start;
1293       if(*count)
1294       {
1295          strcat(userPath, ";");
1296          start = paths[(*count)-1] + strlen(paths[(*count)-1])+1;
1297       }
1298       else
1299          start = oldPath;
1300
1301       strcpy(start, path);
1302       *(start + strlen(path)) = '\0';
1303       paths[(*count)++] = start;
1304
1305       strcat(userPath, path);
1306    }
1307 }
1308
1309
1310 void ModifyPath(char * systemPath, char * userPath)
1311 {
1312    char oldPath[8192], * paths[200], * sysPaths[200];
1313    int count, sysCount = 0;
1314
1315    if(userPath)
1316    {
1317       strcpy(oldPath, userPath);
1318       count = TokenizeWith(oldPath, sizeof(paths) / sizeof(char *), paths, ";", false);
1319       sysCount = TokenizeWith(systemPath, sizeof(sysPaths) / sizeof(char *), sysPaths, ";", false);
1320    }
1321    else
1322    {
1323       strcpy(oldPath, systemPath);
1324       count = TokenizeWith(oldPath, sizeof(paths) / sizeof(char *), paths, ";", false);
1325    }
1326
1327    {
1328       CoreSDKID c;
1329       for(c = 0; coreSDK[c].name; c++)
1330       {
1331          char path[MAX_LOCATION];
1332          if(!coreSDK[c].selected) continue;
1333          coreSDK[c].GetFullPath(path, false);
1334          if(c != ide && c != runtime && c != ec &&
1335             c != ide32 && c != runtime32 && c != ec32)
1336          {
1337             if(!pathOptions[PathOptions::AddMinGWPaths].available || !pathOptions[PathOptions::AddMinGWPaths].selected)
1338                continue;
1339             PathCat(path, "bin");
1340          }
1341          else if(!pathOptions[PathOptions::AddECEREPaths].selected) continue;
1342
1343          AddPath(sysPaths, sysCount, paths, &count, oldPath, userPath ? userPath : systemPath, path);
1344       }
1345    }
1346    {
1347       AdditionalID c;
1348       // Up to C++
1349       for(c = 0; c <= cpp; c++)
1350       {
1351          char path[MAX_LOCATION];
1352          if(!additional[c].selected || c == vanilla || c == vanilla32 || c == extras) continue;
1353          if((c != eda && c != eda32 && c != upx) && (!pathOptions[PathOptions::AddMinGWPaths].available || !pathOptions[PathOptions::AddMinGWPaths].selected))
1354             continue;
1355          additional[c].GetFullPath(path, false);
1356          if(c != eda && c != eda32 && c != upx && c != audio && c != audio32)
1357             PathCat(path, "bin");
1358          AddPath(sysPaths, sysCount, paths, &count, oldPath, userPath ? userPath : systemPath, path);
1359       }
1360    }
1361 }
1362
1363 void AssociateExtension(const char * extension, const char * description, const char *name, const char * action, const char * path)
1364 {
1365    HKEY key;
1366    DWORD status;
1367    char keyName[1024];
1368
1369    RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1370    RegSetValueEx(key, null, 0, REG_SZ, (byte *)name, (uint)strlen(name)+1);
1371    RegCloseKey(key);
1372
1373    RegCreateKeyEx(HKEY_CLASSES_ROOT, name, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1374    RegSetValueEx(key, null, 0, REG_SZ, (byte *)description, (uint)strlen(description)+1);
1375    RegCloseKey(key);
1376
1377    sprintf(keyName, "%s\\shell", extension);
1378    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1379    RegSetValueEx(key, null, 0, REG_SZ, (byte *)action, (uint)strlen(action)+1);
1380    RegCloseKey(key);
1381
1382    sprintf(keyName, "%s\\shell\\%s", name, action);
1383    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1384    RegCloseKey(key);
1385
1386    sprintf(keyName, "%s\\shell\\%s\\command", name, action);
1387    RegCreateKeyEx(HKEY_CLASSES_ROOT, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1388
1389    sprintf(keyName, path);
1390    strcat(keyName, " \"%L\"");
1391    {
1392       uint16 wKeyName[2048];
1393       UTF8toUTF16Buffer(keyName, wKeyName, sizeof(wKeyName) / sizeof(uint16));
1394       RegSetValueExW(key, null, 0, REG_SZ, (byte *)wKeyName, (uint)(wcslen(wKeyName) + 1)*sizeof(uint16));
1395    }
1396    RegCloseKey(key);
1397 }
1398
1399 class InstallThread : Thread
1400 {
1401    unsigned int Main()
1402    {
1403       ComponentID c;
1404       ((GuiApplication)__thisModule).Lock();
1405       installProgress.progressBar.range = totalSize;
1406
1407       if(!osIS64bit)
1408          strcpy(installDir32, installDir);
1409
1410       for(c = 0; components[c].name && !abortInstall; c++)
1411          components[c].Install(installDir, installDir32);
1412       if(abortInstall)
1413       {
1414          installProgress.progressBar.range = 0;
1415          installProgress.finish.Destroy(0);
1416          installProgress.cancel.caption = $"Close";
1417          installProgress.cancel.isDefault = true;
1418          installProgress.cancel.disabled = false;
1419          installProgress.cancel.NotifyClicked = Window::ButtonCloseDialog;
1420          installProgress.installing.caption = $"Installation Cancelled.";
1421          installProgress.title.caption = $"Installation Cancelled";
1422          installProgress.titleInfo.contents = $"The installation was not completed.";
1423       }
1424       else
1425       {
1426          char idePath[MAX_LOCATION];
1427          char userProfile[MAX_LOCATION];
1428
1429          // Configure IDE
1430          IDESettings settings = null; // Don't instantiate yet so we can pick up old settings
1431          CompilerConfigs configs { };
1432
1433          IDESettingsContainer settingsContainer
1434          {
1435             driver = "JSON";
1436             dataOwner = &settings;
1437             dataClass = class(IDESettings);
1438             allUsers = options[0].selected;
1439          };
1440          CompilerConfig compiler;
1441          installProgress.installing.caption = $"Configuring Ecere IDE...";
1442          ((GuiApplication)__thisModule).Unlock();
1443          ((GuiApplication)__thisModule).SignalEvent();
1444
1445          settingsContainer.Load();
1446          configs.read(settingsContainer);
1447
1448          compiler = configs.GetCompilerConfig(defaultCompilerName);
1449          if(compiler)
1450          {
1451             {
1452                CoreSDKID c;
1453                for(c = 0; coreSDK[c].name; c++)
1454                {
1455                   char path[MAX_LOCATION];
1456                   if(!coreSDK[c].selected || !coreSDK[c].available) continue;
1457
1458                   coreSDK[c].GetFullPath(path, false);
1459                   if(c != ide && c != runtime && c != ec &&
1460                      c != ide32 && c != runtime32 && c != ec32)
1461                      PathCat(path, "bin");
1462                   MakeSlashPath(path);
1463                   if((c == ide && osIS64bit) || (c == ide32 && !osIS64bit))
1464                   {
1465                      coreSDK[c].GetFullPath(idePath, false);
1466                      PathCat(idePath, "ecere-ide.exe");
1467                   }
1468
1469                   if(c == runtime || c == runtime32)
1470                   {
1471                      if(!compiler.libraryDirs.Find(path))
1472                         compiler.libraryDirs.Add(CopyString(path));
1473                   }
1474                }
1475             }
1476             {
1477                AdditionalID c;
1478                // Up to C++
1479                for(c = 0; c <= cpp; c++)
1480                {
1481                   char path[MAX_LOCATION];
1482                   if(c == extras || !additional[c].selected || !additional[c].available) continue;
1483                   additional[c].GetFullPath(path, false);
1484                   if(c != upx && c != eda && c != vanilla && c != eda32 && c != vanilla32 && c != audio && c != audio32)
1485                      PathCat(path, "bin");
1486                   MakeSlashPath(path);
1487                   if(c == vanilla || c == vanilla32)
1488                   {
1489                      if(!compiler.libraryDirs.Find(path))
1490                         compiler.libraryDirs.Add(CopyString(path));
1491                   }
1492                   else
1493                   {
1494                      if(!compiler.executableDirs.Find(path))
1495                         compiler.executableDirs.Add(CopyString(path));
1496                   }
1497                }
1498             }
1499          }
1500
1501          {
1502             char path[MAX_LOCATION] = "";
1503
1504             if(components[ComponentID::samples].selected)
1505                components[ComponentID::samples].GetFullPath(path, false);
1506             // IDE will now default to HOME for the default project/files locations
1507
1508             if(!settings.ideProjectFileDialogLocation[0])
1509                settings.ideProjectFileDialogLocation = path;
1510             if(!settings.ideFileDialogLocation[0])
1511                settings.ideFileDialogLocation = path;
1512
1513             if(documentation[DocumentationID::apiRef].selected)
1514             {
1515                documentation[DocumentationID::apiRef].GetFullPath(path, false);
1516                if(!settings.docDir[0])
1517                   settings.docDir = path;
1518             }
1519          }
1520
1521          settings.language = GetLanguageString();
1522
1523          // Set LANGUAGE environment variable
1524          {
1525             HKEY key = null;
1526             uint16 wLanguage[256];
1527             DWORD status;
1528             wLanguage[0] = 0;
1529
1530             if(options[0].selected)
1531                RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, KEY_ALL_ACCESS, &key);
1532             else
1533                RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1534             if(key)
1535             {
1536                UTF8toUTF16Buffer(settings.language, wLanguage, sizeof(wLanguage) / sizeof(uint16));
1537                RegSetValueExW(key, L"ECERE_LANGUAGE", 0, REG_EXPAND_SZ, (byte *)wLanguage, (uint)(wcslen(wLanguage)+1) * 2);
1538                RegCloseKey(key);
1539             }
1540          }
1541
1542          settingsContainer.Save();
1543          {
1544             AVLTree<String> cfgsToWrite { [ compiler.name ] };
1545             configs.write(settingsContainer, cfgsToWrite);
1546             delete cfgsToWrite;
1547          }
1548          delete settingsContainer;
1549          delete settings;
1550          delete configs;
1551
1552          // Set up Uninstaller
1553          ((GuiApplication)__thisModule).Lock();
1554          installProgress.installing.caption = $"Registering uninstaller...";
1555          ((GuiApplication)__thisModule).Unlock();
1556          ((GuiApplication)__thisModule).SignalEvent();
1557
1558          {
1559             HKEY key;
1560             DWORD status;
1561             const char * displayName = "Ecere SDK 0.44";
1562             char uninstaller[MAX_LOCATION];
1563             //bool nomodify = true;
1564
1565             strcpy(uninstaller, installDir);
1566             PathCat(uninstaller, "uninstall_ecere.exe");
1567
1568             RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Ecere SDK", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1569
1570             RegSetValueEx(key, "DisplayName", 0, REG_SZ, (byte *)displayName, (uint)strlen(displayName)+1);
1571             RegSetValueEx(key, "UninstallString", 0, REG_SZ, (byte *)uninstaller, (uint)strlen(uninstaller)+1);
1572             RegSetValueEx(key, "DisplayIcon", 0, REG_SZ, (byte *)idePath, (uint)strlen(idePath)+1);
1573             //RegSetValueEx(key, "NoModify", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1574             //RegSetValueEx(key, "NoRepair", 0, REG_DWORD, (byte *)&nomodify, sizeof(nomodify));
1575             RegCloseKey(key);
1576          }
1577
1578          // Add paths
1579          if(pathOptions[PathOptions::AddECEREPaths].selected
1580 #ifndef NOMINGW
1581             || pathOptions[PathOptions::AddMinGWPaths].selected
1582 #endif
1583             )
1584          {
1585             HKEY userKey = null, systemKey = null;
1586             DWORD status, size;
1587             char userPath[8192] = "";
1588             char systemPath[8192] = "";
1589             uint16 wUserPath[8192];
1590             uint16 wSystemPath[8192];
1591
1592             wUserPath[0] = 0;
1593             wSystemPath[0] = 0;
1594
1595             ((GuiApplication)__thisModule).Lock();
1596             installProgress.installing.caption = "Registering paths...";
1597             ((GuiApplication)__thisModule).Unlock();
1598             ((GuiApplication)__thisModule).SignalEvent();
1599
1600             if(options[0].selected)
1601             {
1602                if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, KEY_ALL_ACCESS, &systemKey) == ERROR_SUCCESS)
1603                {
1604                   size = sizeof(wSystemPath);
1605                   RegQueryValueExW(systemKey, L"path", null, null, (byte *)wSystemPath, &size);
1606                   UTF16toUTF8Buffer(wSystemPath, systemPath, sizeof(systemPath));
1607                   ModifyPath(systemPath, null);
1608
1609                   UTF8toUTF16Buffer(systemPath, wSystemPath, sizeof(wSystemPath) / sizeof(uint16));
1610                   RegSetValueExW(systemKey, L"path", 0, REG_EXPAND_SZ, (byte *)wSystemPath, (uint)(wcslen(wSystemPath)+1) * 2);
1611                   RegCloseKey(systemKey);
1612                }
1613             }
1614             else
1615             {
1616                if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, KEY_QUERY_VALUE, &systemKey) == ERROR_SUCCESS)
1617                {
1618                   size = sizeof(wSystemPath);
1619                   RegQueryValueExW(systemKey, L"path", null, null, (byte *)wSystemPath, &size);
1620                   UTF16toUTF8Buffer(wSystemPath, systemPath, sizeof(systemPath));
1621                   RegCloseKey(systemKey);
1622                }
1623
1624                RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &userKey, &status);
1625                if(status == REG_OPENED_EXISTING_KEY)
1626                {
1627                   size = sizeof(wUserPath);
1628                   RegQueryValueExW(userKey, L"path", null, null, (byte *)wUserPath, &size);
1629                   UTF16toUTF8Buffer(wUserPath, userPath, sizeof(userPath));
1630                }
1631                ModifyPath(systemPath, userPath);
1632                UTF8toUTF16Buffer(userPath, wUserPath, sizeof(wUserPath) / sizeof(uint16));
1633                RegSetValueExW(userKey, L"path", 0, REG_EXPAND_SZ, (byte *)wUserPath, (uint)(wcslen(wUserPath)+1) * 2);
1634                RegCloseKey(userKey);
1635             }
1636             // SendMessageTimeout (HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)"Environment", SMTO_NORMAL, 1000, NULL);
1637          }
1638
1639          // Install Program Group Icons
1640          GetEnvironment(options[0].selected ? "ALLUSERSPROFILE" : "USERPROFILE", userProfile, sizeof(userProfile));
1641
1642          if(options[IconOptions::StartMenuIcon].selected)
1643          {
1644             char destPath[MAX_LOCATION];
1645             char startMenuPath[MAX_LOCATION] = "";
1646             HKEY key;
1647
1648             ((GuiApplication)__thisModule).Lock();
1649             installProgress.installing.caption = $"Installing Start Menu Icons...";
1650             ((GuiApplication)__thisModule).Unlock();
1651             ((GuiApplication)__thisModule).SignalEvent();
1652
1653             strcpy(destPath, userProfile);
1654
1655             if(RegOpenKeyEx(options[0].selected ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
1656                "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
1657             {
1658                uint16 wStartMenuPath[2048];
1659                DWORD size = sizeof(wStartMenuPath);
1660                // RegQueryValueEx(key, "Start Menu", null, null, startMenuPath, &size);
1661                RegQueryValueExW(key, options[0].selected ? L"Common Programs" : L"Programs", null, null, (byte *)wStartMenuPath, &size);
1662                UTF16toUTF8Buffer(wStartMenuPath, startMenuPath, sizeof(startMenuPath));
1663                RegCloseKey(key);
1664             }
1665             if(!startMenuPath[0] && userProfile[0])
1666             {
1667                strcpy(startMenuPath, userProfile);
1668                PathCat(startMenuPath, "Start Menu\\Programs");
1669             }
1670
1671             if(startMenuPath[0])
1672             {
1673                strcpy(destPath, startMenuPath);
1674                PathCat(destPath, "Ecere SDK");
1675                MakeDir(destPath);
1676
1677                strcpy(destPath, startMenuPath);
1678                PathCat(destPath, "Ecere SDK\\Ecere IDE.lnk");
1679                CreateLink(idePath, destPath, null); //"Ecere IDE");
1680                if(components[ComponentID::samples].selected)
1681                {
1682                   char samplesPath[MAX_LOCATION] = "";
1683                   components[ComponentID::samples].GetFullPath(samplesPath, false);
1684
1685                   strcpy(destPath, startMenuPath);
1686                   PathCat(destPath, "Ecere SDK\\Sample Projects.lnk");
1687                   CreateLink(samplesPath, destPath, null);//"Sample Projects");
1688                }
1689                if(components[ComponentID::documentation].selected && documentation[DocumentationID::ecereBook].selected)
1690                {
1691                   char docPath[MAX_LOCATION] = "";
1692                   documentation[DocumentationID::ecereBook].GetFullPath(docPath, false);
1693                   PathCat(docPath, "Ecere Tao of Programming [work in progress].pdf");
1694
1695                   {
1696                      char tao[MAX_LOCATION] ;
1697                      documentation[DocumentationID::ecereBook].GetFullPath(tao, false);
1698                      PathCat(tao, "tao.pdf");
1699                      RenameFile(tao, docPath);
1700                   }
1701
1702                   strcpy(destPath, startMenuPath);
1703                   PathCat(destPath, "Ecere SDK\\The Ecere Tao of Programming.lnk");
1704                   CreateLink(docPath, destPath, null);
1705                }
1706             }
1707          }
1708
1709          // Install Desktop Icon
1710          if(options[IconOptions::DesktopIcon].selected)
1711          {
1712             HKEY key;
1713             char desktopPath[MAX_LOCATION];
1714
1715             if(RegOpenKeyEx(options[0].selected ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
1716                "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
1717             {
1718                uint16 wDesktopPath[MAX_LOCATION];
1719                DWORD size = sizeof(wDesktopPath);
1720                RegQueryValueExW(key, options[0].selected ? L"Common Desktop" : L"Desktop", null, null, (byte *)wDesktopPath, &size);
1721                UTF16toUTF8Buffer(wDesktopPath, desktopPath, sizeof(desktopPath));
1722                RegCloseKey(key);
1723             }
1724             if(!desktopPath[0] && userProfile[0])
1725             {
1726                strcpy(desktopPath, userProfile);
1727                PathCat(desktopPath, "Desktop");
1728             }
1729
1730             if(desktopPath[0])
1731             {
1732                PathCat(desktopPath, "Ecere IDE.lnk");
1733
1734                ((GuiApplication)__thisModule).Lock();
1735                installProgress.installing.caption = $"Installing Desktop Icon...";
1736                ((GuiApplication)__thisModule).Unlock();
1737                ((GuiApplication)__thisModule).SignalEvent();
1738
1739                CreateLink(idePath, desktopPath, null);//"Ecere IDE");
1740             }
1741          }
1742
1743          SendMessageTimeout (HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)"Environment", SMTO_NORMAL, 1000, NULL);
1744
1745          // Install QuickLaunch Icon
1746          if(options[IconOptions::QuickLaunchIcon].selected)
1747          {
1748             char appData[MAX_LOCATION];
1749             GetEnvironment("APPDATA", appData, sizeof(appData));
1750             if(appData[0])
1751             {
1752                char destPath[MAX_LOCATION];
1753
1754                if(appData[0] && options[0].selected)
1755                {
1756                   char dir[MAX_FILENAME];
1757                   GetLastDirectory(appData, dir);
1758                   if(!strcmpi(dir, "Roaming"))
1759                      PathCat(appData, "../../../Default/AppData/Roaming");
1760                   else
1761                      PathCat(appData, "../Default");
1762                }
1763
1764                ((GuiApplication)__thisModule).Lock();
1765                installProgress.installing.caption = $"Installing Quicklaunch Icon...";
1766                ((GuiApplication)__thisModule).Unlock();
1767                ((GuiApplication)__thisModule).SignalEvent();
1768
1769                strcpy(destPath, appData);
1770                PathCat(destPath, "Microsoft\\Internet Explorer\\Quick Launch\\Ecere IDE.lnk");
1771                CreateLink(idePath, destPath, null);
1772
1773                // Set it up on the dock for Windows 7 -- not working
1774                /*
1775                StripLastDirectory(destPath, destPath);
1776                PathCat(destPath, "User Pinned\\TaskBar");
1777                MakeDir(destPath);
1778                PathCat(destPath, "Ecere IDE.lnk");
1779                CreateLink(idePath, destPath, null);
1780                */
1781             }
1782          }
1783
1784          // Register File Types
1785          if(associateOptions[AssociateOptions::AssociateEPJ].selected ||
1786             associateOptions[AssociateOptions::AssociateEC].selected ||
1787             associateOptions[AssociateOptions::AssociateC].selected ||
1788             associateOptions[AssociateOptions::AssociateCPP].selected ||
1789             associateOptions[AssociateOptions::AssociateTXT].selected ||
1790             associateOptions[AssociateOptions::Associate3DS].selected ||
1791             associateOptions[AssociateOptions::AssociateIMG].selected)
1792          {
1793             ((GuiApplication)__thisModule).Lock();
1794             installProgress.installing.caption = $"Registering File Types...";
1795             ((GuiApplication)__thisModule).Unlock();
1796             ((GuiApplication)__thisModule).SignalEvent();
1797
1798             if(associateOptions[AssociateOptions::AssociateEPJ].selected)
1799             {
1800                AssociateExtension(".epj", "Ecere IDE Project", "epj_file", "Open", idePath);
1801             }
1802             if(associateOptions[AssociateOptions::AssociateEC].selected)
1803             {
1804                AssociateExtension(".ec", "eC Source File", "ec_file", "Open", idePath);
1805                AssociateExtension(".eh", "eC Header File", "eh_file", "Open", idePath);
1806             }
1807             if(associateOptions[AssociateOptions::AssociateC].selected)
1808             {
1809                AssociateExtension(".c", "C Source File", "c_file", "Open", idePath);
1810                AssociateExtension(".h", "C Header File", "h_file", "Open", idePath);
1811             }
1812             if(associateOptions[AssociateOptions::AssociateCPP].selected)
1813             {
1814                AssociateExtension(".cpp", "C++ Source File", "cpp_file", "Open", idePath);
1815                AssociateExtension(".cc", "C++ Source File", "cpp_file", "Open", idePath);
1816                AssociateExtension(".cxx", "C++ Source File", "cpp_file", "Open", idePath);
1817                AssociateExtension(".chh", "C++ Header File", "chh_file", "Open", idePath);
1818                AssociateExtension(".hh", "C++ Header File", "chh_file", "Open", idePath);
1819                AssociateExtension(".hxx", "C++ Header File", "chh_file", "Open", idePath);
1820             }
1821             if(associateOptions[AssociateOptions::AssociateTXT].selected)
1822             {
1823                AssociateExtension(".txt", "Text File", "txt_file", "Open", idePath);
1824             }
1825             if(associateOptions[AssociateOptions::Associate3DS].selected)
1826             {
1827                AssociateExtension(".3ds", "3D Studio Model", "3ds_file", "View", idePath);
1828             }
1829             if(associateOptions[AssociateOptions::AssociateIMG].selected)
1830             {
1831                AssociateExtension(".bmp", "BMP Image", "bmp_file", "View", idePath);
1832                AssociateExtension(".pcx", "PCX Image", "pcx_file", "View", idePath);
1833                AssociateExtension(".png", "PNG Image", "png_file", "View", idePath);
1834                AssociateExtension(".jpg", "JPEG Image", "jpg_file", "View", idePath);
1835                AssociateExtension(".jpeg", "JPEG Image", "jpg_file", "View", idePath);
1836                AssociateExtension(".gif", "GIF Image", "gif_file", "View", idePath);
1837             }
1838          }
1839          ((GuiApplication)__thisModule).Lock();
1840
1841          installProgress.cancel.Destroy(0);
1842          installProgress.finish.caption = $"Finish";
1843          installProgress.finish.disabled = false;
1844          installProgress.finish.Activate();
1845          installProgress.installing.caption = $"Installation Complete.";
1846          installProgress.title.caption = $"Installation Complete";
1847          installProgress.titleInfo.contents = $"Thank you for using the Ecere SDK.";
1848       }
1849       ((GuiApplication)__thisModule).Unlock();
1850       return 0;
1851    }
1852 }