Skip to content

Commit 8bdeaf7

Browse files
v1.0.3 in branch
1 parent 215b0cf commit 8bdeaf7

File tree

12 files changed

+1596
-0
lines changed

12 files changed

+1596
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
FLEXINSTALLER - ALL-IN-ONE INSTALLER v3.0
2+
=========================================
3+
4+
STEP 1: CONFIGURE YOUR APP
5+
- Edit Config.cs with your app details
6+
- Set your Dropbox or github download URL
7+
- Customize messages, paths, and options
8+
9+
STEP 2: BUILD INSTALLER
10+
- Run build.bat
11+
- Enter output filename
12+
- .exe file created in output\ folder
13+
14+
STEP 3: DISTRIBUTE
15+
- Just share the .exe file!
16+
17+
(Recommended) GitHub setup:
18+
1. Go to GitHub and make a new public repo
19+
2. Put the .exe you want to download in a release
20+
3. Get the download url (e.g. "https://github.com/iamsopotatoe-coder/test1/releases/download/test/XeninesBrowser.exe")
21+
4. Put the url into the config file
22+
23+
DROPBOX SETUP:
24+
1. Go to https://www.dropbox.com/developers/apps
25+
2. Create new app
26+
3. Upload your program file
27+
4. Get direct download link (replace "?dl=0" at the end with "?dl=1" to share URL) !!!VERY IMPORTANT OR IT WONT WORK!!!
28+
5. Put the URL into config
29+
30+
HOW IT WORKS:
31+
- Single .exe contains both installer and uninstaller
32+
- During installation, copies itself as "uninstall.exe"
33+
- Windows Add/Remove Programs points to uninstall.exe
34+
- When uninstall.exe runs it detects its an uninstaller and shows uninstall GUI
35+
- Completely removes app, shortcuts registry entries, and itself

FlexInstaller/Config.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
3+
namespace FlexInstaller
4+
{
5+
public static class AppConfig
6+
{
7+
public static string appName="Sample App";
8+
public static string appVer="1.0.0";
9+
public static string pubName="Your Company Name";
10+
public static string dlUrl="https://github.com/iamsopotatoe-coder/test1/releases/download/test/XeninesBrowser.exe";
11+
public static string instPath=@"C:\Program Files\Sample Application";
12+
public static string exeName="XeninesBrowser.exe";
13+
public static bool showLicense=true;
14+
public static bool createDesktop=true;
15+
public static bool createStartMenu=true;
16+
public static bool runAfter=true;
17+
public static string welcomeMsg="Welcome to the installation wizard!";
18+
public static string licenseText=@"
19+
END USER LICENSE AGREEMENT
20+
21+
This software is provided 'as is' without warranty of any kind.
22+
By installing this software, you agree to these terms and conditions.
23+
24+
1. You may install and use this software on your computer.
25+
2. You may not redistribute this software without permission.
26+
3. The publisher is not liable for any damages caused by this software.
27+
28+
Do you accept these terms?";
29+
public static string completeMsg="Installation completed successfully!";
30+
public static bool requireAdmin=true;
31+
public static string supportUrl="https://support.example.com";
32+
public static string website="https://example.com";
33+
}
34+
}

FlexInstaller/LICENSE

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FlexInstaller License
2+
3+
Copyright (c) 2025 iamsopotatoe
4+
5+
Permission is hereby granted to use this software for personal and commercial purposes, subject to the following restrictions:
6+
7+
PERMITTED:
8+
- Use the software to create installers for your own applications
9+
- Distribute installers created with this software
10+
- Study the source code for educational purposes
11+
12+
PROHIBITED:
13+
- Modifying, adapting, or creating derivative works of this source code
14+
- Redistributing, selling, or sublicensing this source code or modified versions
15+
- Removing or modifying copyright notices
16+
- Using this software to create competing installer tools
17+
18+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

FlexInstaller/build.bat

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
@echo off
2+
cls
3+
echo FLEXINSTALLER BUILDER v3.0
4+
echo ============================
5+
6+
set /p OUTPUT_NAME=Filename:
7+
if "%OUTPUT_NAME%"=="" set OUTPUT_NAME=Installer
8+
9+
for %%I in (csc.exe) do set CSC_PATH=%%~$PATH:I
10+
if "%CSC_PATH%"=="" (
11+
for /d %%F in (C:\Windows\Microsoft.NET\Framework*) do (
12+
for /d %%V in (%%F\v*) do (
13+
if exist "%%V\csc.exe" set CSC_PATH=%%V\csc.exe
14+
)
15+
)
16+
)
17+
18+
if not exist "%CSC_PATH%" (
19+
echo ERROR: C# compiler not found!
20+
echo.
21+
echo Download Microsoft Build Tools:
22+
echo https://aka.ms/vs/17/release/vs_buildtools.exe
23+
echo.
24+
echo Or install Visual Studio Community (free^):
25+
echo https://visualstudio.microsoft.com/downloads/
26+
goto :end
27+
)
28+
29+
echo Building %OUTPUT_NAME%.exe...
30+
if not exist output mkdir output
31+
32+
"%CSC_PATH%" /target:winexe /out:output\%OUTPUT_NAME%.exe /win32icon:src\installer.ico /reference:System.dll /reference:System.Core.dll /reference:System.Drawing.dll /reference:System.Windows.Forms.dll /reference:System.Net.Http.dll src\Program.cs src\MainForm.cs src\DownloadManager.cs src\InstallationManager.cs src\UninstallerForm.cs Config.cs >nul 2>&1
33+
34+
if exist output\%OUTPUT_NAME%.exe (
35+
echo BUILD SUCCESS: output\%OUTPUT_NAME%.exe
36+
for %%A in (output\%OUTPUT_NAME%.exe) do echo Size: %%~zA bytes
37+
) else (
38+
echo BUILD FAILED - Check your Config.cs file
39+
)
40+
41+
:end
42+
pause
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
using System;
2+
using System.IO;
3+
using System.Net;
4+
using System.Net.Security;
5+
using System.Threading.Tasks;
6+
using System.Windows.Forms;
7+
using System.ComponentModel;
8+
using System.Text;
9+
10+
namespace FlexInstaller {
11+
public class FileTransferManager
12+
{
13+
private ProgressBar barProgress;
14+
private Label txtStatus;
15+
private WebClient client;
16+
private bool finished;
17+
private bool worked;
18+
19+
public FileTransferManager(ProgressBar progress,Label status) {
20+
barProgress=progress;
21+
txtStatus=status;
22+
}
23+
24+
public async Task<bool> RetrieveFileFromCloud(string webUrl,string localFile)
25+
{
26+
try {
27+
if(string.IsNullOrEmpty(webUrl)||!Uri.IsWellFormedUriString(webUrl,UriKind.Absolute)) {
28+
txtStatus.Text="Invalid download URL";
29+
return false;
30+
}
31+
32+
string processedUrl = ProcessDownloadVariation(webUrl);
33+
if(processedUrl.Contains("api.github.com")) {
34+
return await HandleGitHubApiDownload(processedUrl, localFile);
35+
}
36+
37+
txtStatus.Text="Connecting to download server...";
38+
Application.DoEvents();
39+
40+
ServicePointManager.SecurityProtocol=(SecurityProtocolType)3072|(SecurityProtocolType)768|(SecurityProtocolType)192;
41+
ServicePointManager.ServerCertificateValidationCallback=delegate{return true;};
42+
43+
client=new WebClient();
44+
client.Headers.Add("User-Agent",AppConfig.appName+" Installer v"+AppConfig.appVer);
45+
46+
finished=false;
47+
worked=false;
48+
49+
client.DownloadProgressChanged+=(sender,e)=> {
50+
barProgress.Value=e.ProgressPercentage;
51+
txtStatus.Text=string.Format("Downloading... {0}% ({1:F1} MB of {2:F1} MB)",
52+
e.ProgressPercentage,
53+
e.BytesReceived/1024.0/1024.0,
54+
e.TotalBytesToReceive/1024.0/1024.0);
55+
Application.DoEvents();
56+
};
57+
58+
client.DownloadFileCompleted+=(sender,e)=> {
59+
finished=true;
60+
if(e.Error!=null) {
61+
txtStatus.Text=string.Format("Download failed: {0}",e.Error.Message);
62+
worked=false;
63+
} else if(e.Cancelled) {
64+
txtStatus.Text="Download was cancelled";
65+
worked=false;
66+
} else {
67+
txtStatus.Text="Download completed successfully";
68+
worked=true;
69+
}
70+
};
71+
72+
client.DownloadFileAsync(new Uri(processedUrl),localFile);
73+
74+
while(!finished) {
75+
await Task.Delay(100);
76+
Application.DoEvents();
77+
}
78+
79+
client.Dispose();
80+
return worked;
81+
} catch(Exception ex) {
82+
txtStatus.Text=string.Format("Download error: {0}",ex.Message);
83+
if(client!=null) {
84+
client.Dispose();
85+
}
86+
return false;
87+
}
88+
}
89+
90+
private async Task<bool> HandleGitHubApiDownload(string apiUrl, string localFile)
91+
{
92+
try {
93+
txtStatus.Text="Getting GitHub release info...";
94+
Application.DoEvents();
95+
96+
ServicePointManager.SecurityProtocol=(SecurityProtocolType)3072|(SecurityProtocolType)768|(SecurityProtocolType)192;
97+
ServicePointManager.ServerCertificateValidationCallback=delegate{return true;};
98+
99+
using(WebClient apiClient = new WebClient()) {
100+
apiClient.Headers.Add("User-Agent",AppConfig.appName+" Installer v"+AppConfig.appVer);
101+
apiClient.Headers.Add("Accept","application/vnd.github.v3+json");
102+
string response = await apiClient.DownloadStringTaskAsync(apiUrl);
103+
104+
string downloadUrl = ExtractAssetUrl(response);
105+
if(string.IsNullOrEmpty(downloadUrl)) {
106+
txtStatus.Text="Could not find download asset";
107+
return false;
108+
}
109+
110+
return await DownloadFromUrl(downloadUrl, localFile);
111+
}
112+
} catch(Exception ex) {
113+
txtStatus.Text=string.Format("GitHub API error: {0}",ex.Message);
114+
return false;
115+
}
116+
}
117+
118+
private async Task<bool> DownloadFromUrl(string url, string localFile)
119+
{
120+
txtStatus.Text="Connecting to download server...";
121+
Application.DoEvents();
122+
123+
client=new WebClient();
124+
client.Headers.Add("User-Agent",AppConfig.appName+" Installer v"+AppConfig.appVer);
125+
126+
finished=false;
127+
worked=false;
128+
129+
client.DownloadProgressChanged+=(sender,e)=> {
130+
barProgress.Value=e.ProgressPercentage;
131+
txtStatus.Text=string.Format("Downloading... {0}% ({1:F1} MB of {2:F1} MB)",
132+
e.ProgressPercentage,
133+
e.BytesReceived/1024.0/1024.0,
134+
e.TotalBytesToReceive/1024.0/1024.0);
135+
Application.DoEvents();
136+
};
137+
138+
client.DownloadFileCompleted+=(sender,e)=> {
139+
finished=true;
140+
if(e.Error!=null) {
141+
txtStatus.Text=string.Format("Download failed: {0}",e.Error.Message);
142+
worked=false;
143+
} else if(e.Cancelled) {
144+
txtStatus.Text="Download was cancelled";
145+
worked=false;
146+
} else {
147+
txtStatus.Text="Download completed successfully";
148+
worked=true;
149+
}
150+
};
151+
152+
client.DownloadFileAsync(new Uri(url),localFile);
153+
154+
while(!finished) {
155+
await Task.Delay(100);
156+
Application.DoEvents();
157+
}
158+
159+
client.Dispose();
160+
return worked;
161+
}
162+
163+
private string ExtractAssetUrl(string jsonResponse)
164+
{
165+
try {
166+
int assetsStart = jsonResponse.IndexOf("\"assets\":");
167+
if(assetsStart == -1) return null;
168+
169+
string assetsSection = jsonResponse.Substring(assetsStart);
170+
int nameIndex = assetsSection.IndexOf("\"name\":\"" + AppConfig.exeName + "\"");
171+
if(nameIndex == -1) {
172+
nameIndex = assetsSection.IndexOf("\"name\":");
173+
if(nameIndex == -1) return null;
174+
}
175+
176+
int urlStart = assetsSection.IndexOf("\"browser_download_url\":\"", nameIndex);
177+
if(urlStart == -1) return null;
178+
179+
urlStart += 25;
180+
int urlEnd = assetsSection.IndexOf("\"", urlStart);
181+
if(urlEnd == -1) return null;
182+
183+
return assetsSection.Substring(urlStart, urlEnd - urlStart);
184+
} catch {
185+
return null;
186+
}
187+
}
188+
189+
private string ProcessDownloadVariation(string originalUrl)
190+
{
191+
try {
192+
if(originalUrl.Contains("github.com") && originalUrl.Contains("/releases/")) {
193+
if(originalUrl.Contains("/download/")) {
194+
return originalUrl;
195+
} else if(originalUrl.Contains("/tag/")) {
196+
string tagUrl = originalUrl;
197+
string[] parts = tagUrl.Split('/');
198+
if(parts.Length >= 7) {
199+
string owner = parts[3];
200+
string repo = parts[4];
201+
string tag = parts[6];
202+
return string.Format("https://api.github.com/repos/{0}/{1}/releases/tags/{2}",owner,repo,tag);
203+
}
204+
}
205+
} else if(originalUrl.Contains("dropbox.com") && originalUrl.Contains("?dl=0")) {
206+
return originalUrl.Replace("?dl=0","?dl=1");
207+
} else if(originalUrl.Contains("dropbox.com") && !originalUrl.Contains("?dl=")) {
208+
return originalUrl + "?dl=1";
209+
}
210+
return originalUrl;
211+
} catch {
212+
return originalUrl;
213+
}
214+
}
215+
216+
public static void Dispose() {
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)