Compare commits

..
17 Commits
Author SHA1 Message Date
Opportunity 70ec5df5b0 Add temp folder path selection (#1827)
* Translation
* Environment Variables support
2018-05-12 20:31:59 +08:00
celeron533 153727b3b7 Fix typo 2018-05-10 23:53:40 +08:00
celeron533 1a56ea23ec Update CHANGES and bump version 2018-05-10 23:29:06 +08:00
Allen Zhu 1c2e135d8e Merge pull request #1818 from celeron533/CLI
Add plugin CLI arguments support
2018-04-26 20:01:14 +08:00
celeron533 9439fc617a Config form UI updates
* Add tooltips
* Translations
2018-04-17 21:24:19 +08:00
celeron533 b7e30318cd Add plugin CLI arguments support 2018-04-17 20:41:57 +08:00
celeron533 2227595998 Update README.md
- Add section "Open Source Components / Libraries"
- Other minor changes
2018-04-12 23:42:51 +08:00
Allen Zhu 7fb75f27b1 Merge pull request #1769 from studentmain/master
[Fix] QRCodeSplashForm.cs无法在窗体设计工具中打开
2018-03-29 19:53:25 +08:00
Allen Zhu 78e46188f6 Merge pull request #1782 from tltltltl/patch-1
Update zh_TW.txt
2018-03-29 19:32:44 +08:00
tltltltl 58f2c3937b Update zh_TW.txt
change some translations
2018-03-29 18:53:07 +08:00
Zch 81dcad5df5 fix #1768
Move PerPixelAlphaForm's code into QRCodeSplashForm to avoid 2 derived class of Form in same file.

Before: QRCodeSplashForm -> PerPixelAlphaForm -> Form
After: QRCodeSplashForm -> Form
2018-03-24 21:32:48 +08:00
celeron533 5a78ee9212 Add square bracket for SIP002 IPv6 (RFC3986) 2018-03-23 22:39:06 +08:00
Max Lv 4431610205 Merge pull request #1762 from celeron533/Server_IPV6_URL
Server address should not be encoded
2018-03-22 22:07:23 +08:00
celeron533 257448a80a Misc 2018-03-20 21:03:20 +08:00
celeron533 c9ead3eee9 Server address should not be encoded 2018-03-20 13:30:44 +08:00
wzxjohn 226ad0cf50 Fix wrong splash on multi display (#1729)
Because the splash is already on the target display, the target rect doesn't need to calculate with the screen position.
2018-03-18 21:23:43 -05:00
Chen Shaoju 5138e43aff "Allow Clients from LAN" Description & Translation optimization (#1753)
"Allow Clients from LAN" --> "Allow other Devices to connect"
2018-03-16 21:15:15 -05:00
17 changed files with 1053 additions and 1013 deletions
+8
View File
@@ -1,3 +1,11 @@
4.0.10 2018-05-10
- Add square bracket for SIP002 IPv6 (RFC3986)
- Add plugin CLI arguments support
- Bug fix: Server address should not be encoded (#1758)
- Bug fix: Wrong splash on multi display (#1729)
- Fix PerPixelAlphaForm's issue in designer
- Other minor bug fixes and improvements
4.0.9 2018-03-14
- Fix port occupied issue
- Add xchacha20-ietf-poly1305
+33 -13
View File
@@ -17,7 +17,7 @@ Shadowsocks for Windows
#### Download
Download the [latest release].
Download the latest release from [release page].
#### Requirements
@@ -63,7 +63,7 @@ copy Shadowsocks to a new directory and choose a different local port.
If you would like to connect to server via a plugin, please set the plugin's
path (relative or absolute) on Edit Servers form.
Note: Forward Proxy will not be used while a plugin is enabled.
_Note_: Forward Proxy will not be used while a plugin is enabled.
#### Global hotkeys
@@ -100,22 +100,42 @@ if you want to deactivate all, please clear all textboxes.
Please visit [Servers] for more information.
#### Develop
#### Development
[Visual Studio 2015] & [.NET Framework 4.6.2 Developer Pack] are required.
1. [Visual Studio 2015] & [.NET Framework 4.6.2 Developer Pack] are required.
2. It is recommended to share your idea on the Issue Board before you start to work,
especially for feature development.
#### License
GPLv3
[GPLv3]
#### Open Source Components / Libraries
```
Caseless.Fody (MIT) https://github.com/Fody/Caseless
Costura.Fody (MIT) https://github.com/Fody/Costura
Fody (MIT) https://github.com/Fody/Fody
GlobalHotKey (GPLv3) https://github.com/kirmir/GlobalHotKey
Newtonsoft.Json (MIT) https://www.newtonsoft.com/json
StringEx.CS () https://github.com/LazyMode/StringEx
ZXing.Net (Apache 2.0) https://github.com/micjahn/ZXing.Net
libsscrypto (GPLv2) https://github.com/shadowsocks/libsscrypto
Privoxy (GPLv2) https://www.privoxy.org
Sysproxy () https://github.com/Noisyfox/sysproxy
```
[Appveyor]: https://ci.appveyor.com/project/celeron533/shadowsocks-windows
[Build Status]: https://ci.appveyor.com/api/projects/status/tfw57q6eecippsl5/branch/master?svg=true
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[GFWList]: https://github.com/gfwlist/gfwlist
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side
[中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
[Appveyor]: https://ci.appveyor.com/project/celeron533/shadowsocks-windows
[Build Status]: https://ci.appveyor.com/api/projects/status/tfw57q6eecippsl5/branch/master?svg=true
[release page]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[GFWList]: https://github.com/gfwlist/gfwlist
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side
[中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
[Visual Studio 2015]: https://www.visualstudio.com/downloads/
[.NET Framework 4.6.2]: https://www.microsoft.com/en-US/download/details.aspx?id=53344
[Visual Studio 2015]: https://www.visualstudio.com/downloads/
[.NET Framework 4.6.2 Developer Pack]: https://www.microsoft.com/download/details.aspx?id=53321
[Visual C++ 2015 Redistributable]: https://www.microsoft.com/en-us/download/details.aspx?id=53840
[Visual C++ 2015 Redistributable]: https://www.microsoft.com/en-us/download/details.aspx?id=53840
[GPLv3]: https://github.com/shadowsocks/shadowsocks-windows/blob/master/LICENSE.txt
@@ -33,10 +33,10 @@ namespace Shadowsocks.Controller.Service
return null;
}
return new Sip003Plugin(server.plugin, server.plugin_opts, server.server, server.server_port);
return new Sip003Plugin(server.plugin, server.plugin_opts, server.plugin_args, server.server, server.server_port);
}
private Sip003Plugin(string plugin, string pluginOpts, string serverAddress, int serverPort)
private Sip003Plugin(string plugin, string pluginOpts, string pluginArgs, string serverAddress, int serverPort)
{
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
if (string.IsNullOrWhiteSpace(serverAddress))
@@ -55,6 +55,7 @@ namespace Shadowsocks.Controller.Service
StartInfo = new ProcessStartInfo
{
FileName = plugin,
Arguments = pluginArgs,
UseShellExecute = false,
CreateNoWindow = true,
ErrorDialog = false,
@@ -24,7 +24,7 @@ namespace Shadowsocks.Controller
public string LatestVersionLocalName;
public event EventHandler CheckUpdateCompleted;
public const string Version = "4.0.9";
public const string Version = "4.0.10";
private class CheckUpdateTimer : System.Timers.Timer
{
@@ -182,6 +182,12 @@ namespace Shadowsocks.Controller
Configuration.Save(_config);
}
public void SaveTempFolder(string tempFolder)
{
_config.tempFolder = tempFolder;
Configuration.Save(_config);
}
public void SaveStrategyConfigurations(StatisticsStrategyConfiguration configuration)
{
StatisticsConfiguration = configuration;
@@ -250,7 +256,8 @@ namespace Shadowsocks.Controller
{
_config.isVerboseLogging = enabled;
SaveConfig(_config);
if ( VerboseLoggingStatusChanged != null ) {
if (VerboseLoggingStatusChanged != null)
{
VerboseLoggingStatusChanged(this, new EventArgs());
}
}
@@ -353,7 +360,7 @@ namespace Shadowsocks.Controller
url = string.Format(
"{0}@{1}:{2}/?plugin={3}",
websafeBase64,
HttpUtility.UrlEncode(server.server, Encoding.UTF8),
server.FormatHostName(server.server),
server.server_port,
HttpUtility.UrlEncode(pluginPart, Encoding.UTF8));
}
@@ -656,7 +663,7 @@ namespace Shadowsocks.Controller
{
previous = trafficPerSecondQueue.Last();
current = new TrafficPerSecond();
current.inboundCounter = InboundCounter;
current.outboundCounter = OutboundCounter;
current.inboundIncreasement = current.inboundCounter - previous.inboundCounter;
+3 -2
View File
@@ -13,7 +13,7 @@ Edit Servers...=サーバーの編集...
Statistics Config...=統計情報の設定...
Start on Boot=システムと同時に起動
Forward Proxy...=フォワードプロキシの設定...
Allow Clients from LAN=LAN からのアクセスを許可
Allow other Devices to connect=他のデバイスからの接続を許可する
Local PAC=ローカル PAC
Online PAC=オンライン PAC
Edit Local PAC File...=ローカル PAC ファイルの編集...
@@ -51,8 +51,9 @@ Server Port=サーバーポート
Password=パスワード
Show Password=パスワードを表示する
Encryption=暗号化
Plugin=プラグイン
Plugin Program=プラグインプログラム
Plugin Options=プラグインのオプション
Plugin Arguments=プラグインの引数
Proxy Port=プロキシポート
Remarks=付記
Timeout(Sec)=タイムアウト (秒)
+5 -3
View File
@@ -13,7 +13,7 @@ Edit Servers...=编辑服务器...
Statistics Config...=统计配置...
Start on Boot=开机启动
Forward Proxy...=正向代理设置...
Allow Clients from LAN=允许来自局域网的连接
Allow other Devices to connect=允许其他设备连入
Local PAC=使用本地 PAC
Online PAC=使用在线 PAC
Edit Local PAC File...=编辑本地 PAC 文件...
@@ -51,8 +51,9 @@ Server Port=服务器端口
Password=密码
Show Password=显示密码
Encryption=加密
Plugin=插件
Plugin Program=插件程序
Plugin Options=插件选项
Plugin Arguments=插件参数
Proxy Port=代理端口
Remarks=备注
Timeout(Sec)=超时(秒)
@@ -61,6 +62,7 @@ Cancel=取消
New server=未配置的服务器
Move &Up=上移(&U)
Move D&own=下移(&O)
Temp Folder=临时文件夹
# Proxy Form
@@ -143,4 +145,4 @@ Proxy request failed=代理请求失败
Proxy handshake failed=代理握手失败
Register hotkey failed=注册热键失败
Cannot parse hotkey: {0}=解析热键失败: {0}
Timeout is invalid, it should not exceed {0}=超时无效,不应超过 {0}
Timeout is invalid, it should not exceed {0}=超时无效,不应超过 {0}
+6 -4
View File
@@ -13,7 +13,7 @@ Edit Servers...=編輯伺服器...
Statistics Config...=統計設定檔...
Start on Boot=開機啟動
Forward Proxy...=正向 Proxy 設定...
Allow Clients from LAN=允許來自區域網路的連線
Allow other Devices to connect=允許其他裝置連入
Local PAC=使用本機 PAC
Online PAC=使用線上 PAC
Edit Local PAC File...=編輯本機 PAC 檔案...
@@ -51,8 +51,9 @@ Server Port=伺服器連接埠
Password=密碼
Show Password=顯示密碼
Encryption=加密
Plugin=插件
Plugin Options=插件選項
Plugin Program=外掛程式
Plugin Options=外掛程式選項
Plugin Arguments=外掛程式參數
Proxy Port=Proxy 連接埠
Remarks=註解
Timeout(Sec)=逾時 (秒)
@@ -61,6 +62,7 @@ Cancel=取消
New server=新伺服器
Move &Up=上移 (&U)
Move D&own=下移 (&O)
Temp Folder=臨時資料夾
# Proxy Form
@@ -69,7 +71,7 @@ Use Proxy=使用 Proxy
Proxy Type=Proxy 類型
Proxy Addr=Proxy 位址
Proxy Port=Proxy 連接埠
If server has a plugin, proxy will not be used=若伺服器含有插件,Proxy 將不被使用
If server has a plugin, proxy will not be used=若伺服器含有外掛程式,Proxy 將不被使用
# Log Form
@@ -30,6 +30,7 @@ namespace Shadowsocks.Model
public LogViewerConfig logViewer;
public ProxyConfig proxy;
public HotkeyConfig hotkey;
public string tempFolder;
private static string CONFIG_FILE = "gui-config.json";
@@ -145,6 +146,13 @@ namespace Shadowsocks.Model
throw new ArgumentException(I18N.GetString("Port can't be 8123"));
}
public static void CheckTempFolder(string tempPath)
{
if (string.IsNullOrWhiteSpace(tempPath))
return;
Path.GetFullPath(tempPath);
}
private static void CheckPassword(string password)
{
if (password.IsNullOrEmpty())
+16 -14
View File
@@ -26,6 +26,7 @@ namespace Shadowsocks.Model
public string method;
public string plugin;
public string plugin_opts;
public string plugin_args;
public string remarks;
public int timeout;
@@ -46,25 +47,25 @@ namespace Shadowsocks.Model
{
return I18N.GetString("New server");
}
string serverStr;
// CheckHostName() won't do a real DNS lookup
var hostType = Uri.CheckHostName(server);
switch (hostType)
{
case UriHostNameType.IPv6:
serverStr = $"[{server}]:{server_port}";
break;
default:
// IPv4 and domain name
serverStr = $"{server}:{server_port}";
break;
}
string serverStr = $"{FormatHostName(server)}:{server_port}";
return remarks.IsNullOrEmpty()
? serverStr
: $"{remarks} ({serverStr})";
}
public string FormatHostName(string hostName)
{
// CheckHostName() won't do a real DNS lookup
switch (Uri.CheckHostName(hostName))
{
case UriHostNameType.IPv6: // Add square bracket when IPv6 (RFC3986)
return $"[{hostName}]";
default: // IPv4 or domain name
return hostName;
}
}
public Server()
{
server = "";
@@ -72,6 +73,7 @@ namespace Shadowsocks.Model
method = "aes-256-cfb";
plugin = "";
plugin_opts = "";
plugin_args = "";
password = "";
remarks = "";
timeout = DefaultServerTimeoutSec;
@@ -141,7 +143,7 @@ namespace Shadowsocks.Model
Server server = new Server
{
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
server = parsedUrl.GetComponents(UriComponents.Host, UriFormat.Unescaped),
server = parsedUrl.IdnHost,
server_port = parsedUrl.Port,
};
+23 -11
View File
@@ -35,7 +35,8 @@ namespace Shadowsocks.Util.SystemProxy
static Sysproxy()
{
try {
try
{
FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"),
Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe);
}
@@ -111,8 +112,10 @@ namespace Shadowsocks.Util.SystemProxy
throw new ProxyException(stderr);
}
if (arguments == "query") {
if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty()) {
if (arguments == "query")
{
if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty())
{
// we cannot get user settings
throw new ProxyException("failed to query wininet settings");
}
@@ -123,25 +126,34 @@ namespace Shadowsocks.Util.SystemProxy
private static void Save()
{
try {
using (StreamWriter sw = new StreamWriter(File.Open(_userWininetConfigFile, FileMode.Create))) {
try
{
using (StreamWriter sw = new StreamWriter(File.Open(Utils.GetTempPath(_userWininetConfigFile), FileMode.Create)))
{
string jsonString = JsonConvert.SerializeObject(_userSettings, Formatting.Indented);
sw.Write(jsonString);
sw.Flush();
}
} catch (IOException e) {
}
catch (IOException e)
{
Logging.LogUsefulException(e);
}
}
private static void Read()
{
try {
string configContent = File.ReadAllText(_userWininetConfigFile);
try
{
string configContent = File.ReadAllText(Utils.GetTempPath(_userWininetConfigFile));
_userSettings = JsonConvert.DeserializeObject<SysproxyConfig>(configContent);
} catch(Exception) {
// Suppress all exceptions. finally block will initialize new user config settings.
} finally {
}
catch (Exception)
{
// Suppress all exceptions. finally block will initialize new user config settings.
}
finally
{
if (_userSettings == null) _userSettings = new SysproxyConfig();
}
}
+33 -3
View File
@@ -2,10 +2,12 @@
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.Win32;
using Shadowsocks.Controller;
using Shadowsocks.Model;
namespace Shadowsocks.Util
{
@@ -26,6 +28,24 @@ namespace Shadowsocks.Util
public static class Utils
{
private static string _tempPath = null;
private const string TEMP_LOG = "temp.log";
private static readonly string[] COMMON_ENV =
{
"%Tmp%",
"%Temp%",
"%AppData%",
"%LocalAppData%",
"%Home%",
"%UserProfile%",
"%Public%",
"%CommonProgramFiles%",
"%CommonProgramFiles(x86)%",
"%CommonProgramW6432%",
"%ProgramFiles%",
"%ProgramFiles(x86)%",
"%ProgramW6432%",
"%ProgramData%",
};
// return path to store temporary files
public static string GetTempPath()
@@ -34,9 +54,19 @@ namespace Shadowsocks.Util
{
try
{
Directory.CreateDirectory(Path.Combine(Application.StartupPath, "ss_win_temp"));
// don't use "/", it will fail when we call explorer /select xxx/ss_win_temp\xxx.log
_tempPath = Path.Combine(Application.StartupPath, "ss_win_temp");
var tempFolder = Configuration.Load().tempFolder;
if (string.IsNullOrWhiteSpace(tempFolder))
// don't use "/", it will fail when we call explorer /select xxx/ss_win_temp\xxx.log
tempFolder = "ss_win_temp";
else if (COMMON_ENV.Contains(tempFolder, StringComparer.OrdinalIgnoreCase))
// add subfolder for these common folders
tempFolder += (@"\Shadowsocks\ss_win_temp_" + Application.ExecutablePath.GetHashCode());
tempFolder = Environment.ExpandEnvironmentVariables(tempFolder);
// If `tempFolder` is an absolute path, `Application.StartupPath` will be ignored.
var tempDirectory = Directory.CreateDirectory(Path.Combine(Application.StartupPath, tempFolder));
_tempPath = tempDirectory.FullName;
File.AppendAllText(Path.Combine(_tempPath, TEMP_LOG), $"[{DateTimeOffset.Now.ToString("u")}] Temp folder used by \"{Application.ExecutablePath}\"{Environment.NewLine}");
}
catch (Exception e)
{
File diff suppressed because it is too large Load Diff
+14 -5
View File
@@ -9,6 +9,7 @@ using Microsoft.Win32;
using Shadowsocks.Controller;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using System.Threading.Tasks;
namespace Shadowsocks.View
{
@@ -22,11 +23,12 @@ namespace Shadowsocks.View
public ConfigForm(ShadowsocksController controller)
{
this.Font = System.Drawing.SystemFonts.MessageBoxFont;
this.Font = SystemFonts.MessageBoxFont;
InitializeComponent();
// a dirty hack
this.ServersListBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ServersListBox.Dock = DockStyle.Fill;
this.tableLayoutPanel5.Dock = DockStyle.Fill;
this.PerformLayout();
UpdateTexts();
@@ -48,9 +50,11 @@ namespace Shadowsocks.View
PasswordLabel.Text = I18N.GetString("Password");
ShowPasswdCheckBox.Text = I18N.GetString("Show Password");
EncryptionLabel.Text = I18N.GetString("Encryption");
PluginLabel.Text = I18N.GetString("Plugin");
PluginLabel.Text = I18N.GetString("Plugin Program");
PluginOptionsLabel.Text = I18N.GetString("Plugin Options");
PluginArgumentsLabel.Text = I18N.GetString("Plugin Arguments");
ProxyPortLabel.Text = I18N.GetString("Proxy Port");
TempFolderLabel.Text = I18N.GetString("Temp Folder");
RemarksLabel.Text = I18N.GetString("Remarks");
TimeoutLabel.Text = I18N.GetString("Timeout(Sec)");
ServerGroupBox.Text = I18N.GetString("Server");
@@ -99,6 +103,7 @@ namespace Shadowsocks.View
server.method = EncryptionSelect.Text;
server.plugin = PluginTextBox.Text;
server.plugin_opts = PluginOptionsTextBox.Text;
server.plugin_args = PluginArgumentsTextBox.Text;
server.remarks = RemarksTextBox.Text;
if (!int.TryParse(TimeoutTextBox.Text, out server.timeout))
{
@@ -107,6 +112,7 @@ namespace Shadowsocks.View
return false;
}
int localPort = int.Parse(ProxyPortTextBox.Text);
Configuration.CheckTempFolder(TempFolderTextBox.Text);
Configuration.CheckServer(server);
Configuration.CheckLocalPort(localPort);
_modifiedConfiguration.configs[_lastSelectedIndex] = server;
@@ -134,6 +140,7 @@ namespace Shadowsocks.View
EncryptionSelect.Text = server.method ?? "aes-256-cfb";
PluginTextBox.Text = server.plugin;
PluginOptionsTextBox.Text = server.plugin_opts;
PluginArgumentsTextBox.Text = server.plugin_args;
RemarksTextBox.Text = server.remarks;
TimeoutTextBox.Text = server.timeout.ToString();
}
@@ -160,6 +167,7 @@ namespace Shadowsocks.View
ServersListBox.SelectedIndex = _lastSelectedIndex;
UpdateMoveUpAndDownButton();
LoadSelectedServer();
TempFolderTextBox.Text = _modifiedConfiguration.tempFolder;
}
private void ConfigForm_Load(object sender, EventArgs e)
@@ -228,14 +236,14 @@ namespace Shadowsocks.View
_lastSelectedIndex = ServersListBox.SelectedIndex;
}
private void DuplicateButton_Click( object sender, EventArgs e )
private void DuplicateButton_Click(object sender, EventArgs e)
{
if (!SaveOldSelectedServer())
{
return;
}
Server currServer = _modifiedConfiguration.configs[_lastSelectedIndex];
var currIndex = _modifiedConfiguration.configs.IndexOf( currServer );
var currIndex = _modifiedConfiguration.configs.IndexOf(currServer);
_modifiedConfiguration.configs.Insert(currIndex + 1, currServer);
LoadConfiguration(_modifiedConfiguration);
ServersListBox.SelectedIndex = currIndex + 1;
@@ -272,6 +280,7 @@ namespace Shadowsocks.View
return;
}
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort);
controller.SaveTempFolder(TempFolderTextBox.Text);
// SelectedIndex remains valid
// We handled this in event handlers, e.g. Add/DeleteButton, SelectedIndexChanged
// and move operations
+122 -227
View File
@@ -1,228 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="tableLayoutPanel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="PluginOptionsLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="PluginTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="RemarksTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="RemarksLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="IPLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ServerPortLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="PasswordLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="IPTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ServerPortTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="PasswordTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="EncryptionLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="EncryptionSelect.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="TimeoutLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="TimeoutTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="PluginLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="PluginOptionsTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ShowPasswdCheckBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="panel2.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="OKButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="MyCancelButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="DeleteButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="AddButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ServerGroupBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ServersListBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="tableLayoutPanel2.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="tableLayoutPanel6.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="MoveDownButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="MoveUpButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="tableLayoutPanel5.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ProxyPortTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ProxyPortLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="tableLayoutPanel3.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="tableLayoutPanel4.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="DuplicateButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>
@@ -187,6 +187,10 @@ namespace Shadowsocks.View
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
+ "\n" + serverInfo;
if (text.Length > 127)
{
text = text.Substring(0, 126 - 3) + "...";
}
ViewUtils.SetNotifyIconText(_notifyIcon, text);
}
@@ -286,7 +290,7 @@ namespace Shadowsocks.View
this.proxyItem = CreateMenuItem("Forward Proxy...", new EventHandler(this.proxyItem_Click)),
new MenuItem("-"),
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Allow other Devices to connect", new EventHandler(this.ShareOverLANItem_Click)),
new MenuItem("-"),
this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
CreateMenuGroup("Help", new MenuItem[] {
@@ -707,7 +711,7 @@ namespace Shadowsocks.View
{
splash.FormClosed += splash_FormClosed;
}
else if (result.Text.StartsWith("http://") || result.Text.StartsWith("https://"))
else if (result.Text.ToLower().StartsWith("http://") || result.Text.ToLower().StartsWith("https://"))
{
_urlToOpen = result.Text;
splash.FormClosed += openURLFromQRCode;
@@ -738,7 +742,7 @@ namespace Shadowsocks.View
splash.Location = new Point(screen.Bounds.X, screen.Bounds.Y);
// we need a panel because a window has a minimal size
// TODO: test on high DPI
splash.TargetRect = new Rectangle((int)minX + screen.Bounds.X, (int)minY + screen.Bounds.Y, (int)maxX - (int)minX, (int)maxY - (int)minY);
splash.TargetRect = new Rectangle((int)minX, (int)minY, (int)maxX - (int)minX, (int)maxY - (int)minY);
splash.Size = new Size(fullImage.Width, fullImage.Height);
splash.Show();
return;
+65 -84
View File
@@ -12,12 +12,13 @@ using System.Diagnostics;
namespace Shadowsocks.View
{
public class QRCodeSplashForm : PerPixelAlphaForm
public class QRCodeSplashForm : Form
{
public Rectangle TargetRect;
public QRCodeSplashForm()
{
FormBorderStyle = FormBorderStyle.None;
this.Load += QRCodeSplashForm_Load;
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.BackColor = System.Drawing.Color.White;
@@ -69,16 +70,6 @@ namespace Shadowsocks.View
brush = new SolidBrush(Color.FromArgb(30, Color.Red));
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style
return cp;
}
}
void timer_Tick(object sender, EventArgs e)
{
double percent = (double)sw.ElapsedMilliseconds / 1000.0 / (double)ANIMATION_TIME;
@@ -148,6 +139,69 @@ namespace Shadowsocks.View
flashStep++;
}
}
// PerPixelAlphaForm.cs
// http://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C
// Rui Lopes
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style
return cp;
}
}
public void SetBitmap(Bitmap bitmap)
{
SetBitmap(bitmap, 255);
}
/// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para>
public void SetBitmap(Bitmap bitmap, byte opacity)
{
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");
// The idea of this is very simple,
// 1. Create a compatible DC with screen;
// 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
// 3. Call the UpdateLayeredWindow.
IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
try
{
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap
oldBitmap = Win32.SelectObject(memDc, hBitmap);
Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
Win32.Point pointSource = new Win32.Point(0, 0);
Win32.Point topPos = new Win32.Point(Left, Top);
Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
blend.BlendOp = Win32.AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = opacity;
blend.AlphaFormat = Win32.AC_SRC_ALPHA;
Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
Win32.SelectObject(memDc, oldBitmap);
//Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from Win32 GDI and it's working fine without any resource leak.
Win32.DeleteObject(hBitmap);
}
Win32.DeleteDC(memDc);
}
}
}
@@ -224,77 +278,4 @@ namespace Shadowsocks.View
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern int DeleteObject(IntPtr hObject);
}
public class PerPixelAlphaForm : Form
{
// http://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C
// Rui Lopes
public PerPixelAlphaForm()
{
// This form should not have a border or else Windows will clip it.
FormBorderStyle = FormBorderStyle.None;
}
public void SetBitmap(Bitmap bitmap)
{
SetBitmap(bitmap, 255);
}
/// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para>
public void SetBitmap(Bitmap bitmap, byte opacity)
{
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");
// The idea of this is very simple,
// 1. Create a compatible DC with screen;
// 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
// 3. Call the UpdateLayeredWindow.
IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
try
{
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap
oldBitmap = Win32.SelectObject(memDc, hBitmap);
Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
Win32.Point pointSource = new Win32.Point(0, 0);
Win32.Point topPos = new Win32.Point(Left, Top);
Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
blend.BlendOp = Win32.AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = opacity;
blend.AlphaFormat = Win32.AC_SRC_ALPHA;
Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
Win32.SelectObject(memDc, oldBitmap);
//Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from Win32 GDI and it's working fine without any resource leak.
Win32.DeleteObject(hBitmap);
}
Win32.DeleteDC(memDc);
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style
return cp;
}
}
}
}