diff --git a/.gitattributes b/.gitattributes
index 2ed11bcb1..ecb5de549 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -24,6 +24,9 @@
*.csproj text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.sln text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
*.xml text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
+*.rb text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
+*.lock text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
+*.go text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
sqlite/sqlite/* linguist-vendored
diff --git a/README.md b/README.md
index 2fe8a7c6f..dbe0cff42 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ TDLib (Telegram Database library) is a cross-platform library for building [Tele
- [Installing dependencies](#installing-dependencies)
- [Using in CMake C++ projects](#using-cxx)
- [Using in Java projects](#using-java)
+- [Using in .NET projects](#using-dotnet)
- [Using with other programming languages](#using-json)
- [License](#license)
@@ -19,7 +20,7 @@ TDLib (Telegram Database library) is a cross-platform library for building [Tele
`TDLib` has many advantages. Notably `TDLib` is:
* **Cross-platform**: `TDLib` can be used on Android, iOS, Windows, macOS, Linux, Windows Phone, WebAssembly, watchOS, tvOS, Tizen, Cygwin. It should also work on other *nix systems with or without minimal effort.
-* **Multilanguage**: `TDLib` can be easily used with any programming language that is able to execute C functions. Additionally it already has native Java (using JNI) bindings and .NET (using C++/CLI and C++/CX) bindings.
+* **Multilanguage**: `TDLib` can be easily used with any programming language that is able to execute C functions. Additionally it already has native Java (using `JNI`) bindings and .NET (using `C++/CLI` and `C++/CX`) bindings.
* **Easy to use**: `TDLib` takes care of all network implementation details, encryption and local data storage.
* **High-performance**: in the [Telegram Bot API](https://core.telegram.org/bots/api), each `TDLib` instance handles more than 19000 active bots simultaneously.
* **Well-documented**: all `TDLib` API methods and public interfaces are fully documented.
@@ -117,10 +118,18 @@ See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example
## Using in Java projects
-`TDLib` provides native Java interface through JNI.
+`TDLib` provides native Java interface through JNI. To enable it, specify option `-DTD_ENABLE_JNI=ON` to CMake.
See [example/java](https://github.com/tdlib/td/tree/master/example/java) for example of using `TDLib` from Java and detailed build and usage instructions.
+
+## Using in .NET projects
+`TDLib` provides native .NET interface through `C++/CLI` and `C++/CX`. To enable it, specify option `-DTD_ENABLE_DOTNET=ON` to CMake.
+.NET Core doesn't support `C++/CLI`, so if .NET Core is used, then `TDLib` JSON interface should be used through P/Invoke instead.
+
+See [example/csharp](https://github.com/tdlib/td/tree/master/example/csharp) for example of using `TDLib` from C# and detailed build and usage instructions.
+See [example/uwp](https://github.com/tdlib/td/tree/master/example/uwp) for example of using `TDLib` from C# UWP application and detailed build and usage instructions for Visual Studio Extension "TDLib for Universal Windows Platform".
+
## Using from other programming languages
`TDLib` provides efficient native C++, Java, and .NET interfaces.
diff --git a/example/csharp/.gitignore b/example/csharp/.gitignore
new file mode 100644
index 000000000..5266ecccc
--- /dev/null
+++ b/example/csharp/.gitignore
@@ -0,0 +1,5 @@
+.vs/
+bin/
+obj/
+project.lock.json
+TdExample.csproj.user
diff --git a/example/csharp/README.md b/example/csharp/README.md
new file mode 100644
index 000000000..b66f124c4
--- /dev/null
+++ b/example/csharp/README.md
@@ -0,0 +1,29 @@
+# TDLib C# example
+
+This is an example of building TDLib with `C++/CLI` support and an example of TDLib usage from C#.
+
+## Building TDLib
+
+* Download and install Microsoft Visual Studio 2015 or later.
+* Download and install [CMake](https://cmake.org/download/).
+* Install [vcpkg](https://github.com/Microsoft/vcpkg#quick-start) or update it to the latest version using `vcpkg update` and following received instructions.
+* Install `zlib` and `openssl` for using `vcpkg`:
+```
+C:\src\vcpkg> .\vcpkg.exe install openssl:x64-windows openssl:x86-windows zlib:x64-windows zlib:x86-windows
+```
+* Download and install [gperf](https://sourceforge.net/projects/gnuwin32/files/gperf/3.0.1/). Add the path to gperf.exe to the PATH environment variable.
+* Build `TDLib` with CMake enabling .NET support and specifying correct path to `vcpkg` toolchain file:
+```
+cd /example/csharp
+mkdir build
+cd build
+cmake -DTD_ENABLE_DOTNET=ON -DCMAKE_TOOLCHAIN_FILE=C:\src\vcpkg\scripts\buildsystems\vcpkg.cmake ../../..
+cmake --build . --config Release
+cmake --build . --config Debug
+```
+
+## Example of usage
+
+After `TDLib` is built you can open and run TdExample project.
+It contains a simple console C# application with implementation of authorization and message sending.
+Just open it with Visual Studio 2015 or 2017 and run.
diff --git a/example/csharp/TdExample.cs b/example/csharp/TdExample.cs
new file mode 100644
index 000000000..e1d2e3c67
--- /dev/null
+++ b/example/csharp/TdExample.cs
@@ -0,0 +1,264 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+using Td = Telegram.Td;
+using TdApi = Telegram.Td.Api;
+
+using System;
+using System.Threading;
+
+/**
+ * Example class for TDLib usage from C#.
+ */
+namespace TdExample
+{
+ class Example
+ {
+ private static Td.Client _client = null;
+ private readonly static Td.ClientResultHandler _defaultHandler = new DefaultHandler();
+
+ private static TdApi.AuthorizationState _authorizationState = null;
+ private static volatile bool _haveAuthorization = false;
+ private static volatile bool _quiting = false;
+
+ private static volatile AutoResetEvent _gotAuthorization = new AutoResetEvent(false);
+
+ private static readonly string _newLine = Environment.NewLine;
+ private static readonly string _commandsLine = "Enter command (gc - GetChat, me - GetMe, sm - SendMessage, lo - LogOut, q - Quit): ";
+
+ private static Td.Client CreateTdClient()
+ {
+ Td.Client result = Td.Client.Create(new UpdatesHandler());
+ new Thread(() =>
+ {
+ Thread.CurrentThread.IsBackground = true;
+ result.Run();
+ }).Start();
+ return result;
+ }
+
+ private static void Print(string str)
+ {
+ Console.WriteLine();
+ Console.WriteLine(str);
+ if (_haveAuthorization)
+ {
+ Console.Write(_commandsLine);
+ }
+ }
+
+ private static string ReadLine(string str)
+ {
+ Console.WriteLine();
+ Console.Write(str);
+ return Console.ReadLine();
+ }
+
+ private static void OnAuthorizationStateUpdated(TdApi.AuthorizationState authorizationState)
+ {
+ if (authorizationState != null)
+ {
+ _authorizationState = authorizationState;
+ }
+ if (_authorizationState is TdApi.AuthorizationStateWaitTdlibParameters)
+ {
+ TdApi.TdlibParameters parameters = new TdApi.TdlibParameters();
+ parameters.DatabaseDirectory = "tdlib";
+ parameters.UseMessageDatabase = true;
+ parameters.UseSecretChats = true;
+ parameters.ApiId = 94575;
+ parameters.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
+ parameters.SystemLanguageCode = "en";
+ parameters.DeviceModel = "Desktop";
+ parameters.SystemVersion = "Unknown";
+ parameters.ApplicationVersion = "1.0";
+ parameters.EnableStorageOptimizer = true;
+
+ _client.Send(new TdApi.SetTdlibParameters(parameters), new AuthorizationRequestHandler());
+ }
+ else if (_authorizationState is TdApi.AuthorizationStateWaitEncryptionKey)
+ {
+ _client.Send(new TdApi.CheckDatabaseEncryptionKey(), new AuthorizationRequestHandler());
+ }
+ else if (_authorizationState is TdApi.AuthorizationStateWaitPhoneNumber)
+ {
+ string phoneNumber = ReadLine("Please enter phone number: ");
+ _client.Send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, false, false), new AuthorizationRequestHandler());
+ }
+ else if (_authorizationState is TdApi.AuthorizationStateWaitCode)
+ {
+ string code = ReadLine("Please enter authentication code: ");
+ _client.Send(new TdApi.CheckAuthenticationCode(code, "", ""), new AuthorizationRequestHandler());
+ }
+ else if (_authorizationState is TdApi.AuthorizationStateWaitPassword)
+ {
+ string password = ReadLine("Please enter password: ");
+ _client.Send(new TdApi.CheckAuthenticationPassword(password), new AuthorizationRequestHandler());
+ }
+ else if (_authorizationState is TdApi.AuthorizationStateReady)
+ {
+ _haveAuthorization = true;
+ _gotAuthorization.Set();
+ }
+ else if (_authorizationState is TdApi.AuthorizationStateLoggingOut)
+ {
+ _haveAuthorization = false;
+ Print("Logging out");
+ }
+ else if (_authorizationState is TdApi.AuthorizationStateClosing)
+ {
+ _haveAuthorization = false;
+ Print("Closing");
+ }
+ else if (_authorizationState is TdApi.AuthorizationStateClosed)
+ {
+ Print("Closed");
+ if (!_quiting)
+ {
+ _client = CreateTdClient(); // recreate _client after previous has closed
+ }
+ }
+ else
+ {
+ Print("Unsupported authorization state:" + _newLine + _authorizationState);
+ }
+ }
+
+ private static long GetChatId(string arg)
+ {
+ long chatId = 0;
+ try
+ {
+ chatId = Convert.ToInt64(arg);
+ }
+ catch (FormatException)
+ {
+ }
+ catch (OverflowException)
+ {
+ }
+ return chatId;
+ }
+
+ private static void GetCommand()
+ {
+ string command = ReadLine(_commandsLine);
+ string[] commands = command.Split(new char[] { ' ' }, 2);
+ try
+ {
+ switch (commands[0])
+ {
+ case "gc":
+ _client.Send(new TdApi.GetChat(GetChatId(commands[1])), _defaultHandler);
+ break;
+ case "me":
+ _client.Send(new TdApi.GetMe(), _defaultHandler);
+ break;
+ case "sm":
+ string[] args = commands[1].Split(new char[] { ' ' }, 2);
+ sendMessage(GetChatId(args[0]), args[1]);
+ break;
+ case "lo":
+ _haveAuthorization = false;
+ _client.Send(new TdApi.LogOut(), _defaultHandler);
+ break;
+ case "q":
+ _quiting = true;
+ _haveAuthorization = false;
+ _client.Send(new TdApi.Close(), _defaultHandler);
+ break;
+ default:
+ Print("Unsupported command: " + command);
+ break;
+ }
+ }
+ catch (IndexOutOfRangeException)
+ {
+ Print("Not enough arguments");
+ }
+ }
+
+ private static void sendMessage(long chatId, string message)
+ {
+ // initialize reply markup just for testing
+ TdApi.InlineKeyboardButton[] row = { new TdApi.InlineKeyboardButton("https://telegram.org?1", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?2", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?3", new TdApi.InlineKeyboardButtonTypeUrl()) };
+ TdApi.ReplyMarkup replyMarkup = new TdApi.ReplyMarkupInlineKeyboard(new TdApi.InlineKeyboardButton[][] { row, row, row });
+
+ TdApi.InputMessageContent content = new TdApi.InputMessageText(new TdApi.FormattedText(message, null), false, true);
+ _client.Send(new TdApi.SendMessage(chatId, 0, false, false, replyMarkup, content), _defaultHandler);
+ }
+
+ static void Main()
+ {
+ // disable TDLib log
+ Td.Log.SetVerbosityLevel(0);
+ if (!Td.Log.SetFilePath("tdlib.log"))
+ {
+ throw new System.IO.IOException("Write access to the current directory is required");
+ }
+
+ // create Td.Client
+ _client = CreateTdClient();
+
+ // test Client.Execute
+ _defaultHandler.OnResult(_client.Execute(new TdApi.GetTextEntities("@telegram /test_command https://telegram.org telegram.me @gif @test")));
+
+ // main loop
+ while (!_quiting)
+ {
+ // await authorization
+ _gotAuthorization.Reset();
+ _gotAuthorization.WaitOne();
+
+ _client.Send(new TdApi.GetChats(Int64.MaxValue, 0, 100), _defaultHandler); // preload chat list
+ while (_haveAuthorization)
+ {
+ GetCommand();
+ }
+ }
+ }
+
+ private class DefaultHandler : Td.ClientResultHandler
+ {
+ void Td.ClientResultHandler.OnResult(TdApi.BaseObject @object)
+ {
+ Print(@object.ToString());
+ }
+ }
+
+ private class UpdatesHandler : Td.ClientResultHandler
+ {
+ void Td.ClientResultHandler.OnResult(TdApi.BaseObject @object)
+ {
+ if (@object is TdApi.UpdateAuthorizationState)
+ {
+ OnAuthorizationStateUpdated((@object as TdApi.UpdateAuthorizationState).AuthorizationState);
+ }
+ else
+ {
+ // Print("Unsupported update: " + @object);
+ }
+ }
+ }
+
+ private class AuthorizationRequestHandler : Td.ClientResultHandler
+ {
+ void Td.ClientResultHandler.OnResult(TdApi.BaseObject @object)
+ {
+ if (@object is TdApi.Error)
+ {
+ Print("Receive an error:" + _newLine + @object);
+ OnAuthorizationStateUpdated(null); // repeat last action
+ }
+ else
+ {
+ // ok result is already received through UpdateAuthorizationState, nothing to do
+ }
+ }
+ }
+ }
+}
diff --git a/example/csharp/TdExample.csproj b/example/csharp/TdExample.csproj
new file mode 100644
index 000000000..ea2ad5653
--- /dev/null
+++ b/example/csharp/TdExample.csproj
@@ -0,0 +1,85 @@
+
+
+
+ Debug
+ x86
+ {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}
+ Exe
+ false
+ ConsoleApplication
+ v4.0
+ Client
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ x86
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ x86
+
+
+ TdExample
+
+
+
+
+
+
+
+
+
+
+ False
+ build\Debug\Telegram.Td.dll
+ build\Release\Telegram.Td.dll
+
+
+
+
+
+
+
+ LIBEAY32.dll
+ PreserveNewest
+
+
+ SSLEAY32.dll
+ PreserveNewest
+
+
+ zlibd1.dll
+ PreserveNewest
+
+
+
+
+ LIBEAY32.dll
+ PreserveNewest
+
+
+ SSLEAY32.dll
+ PreserveNewest
+
+
+ zlib1.dll
+ PreserveNewest
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/csharp/TdExample.sln b/example/csharp/TdExample.sln
new file mode 100644
index 000000000..7ebc73021
--- /dev/null
+++ b/example/csharp/TdExample.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TdExample", "TdExample.csproj", "{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x86.ActiveCfg = Debug|x86
+ {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x86.Build.0 = Debug|x86
+ {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x86.ActiveCfg = Release|x86
+ {3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/example/uwp/README.md b/example/uwp/README.md
index b129d1bbc..cfbda0ff5 100644
--- a/example/uwp/README.md
+++ b/example/uwp/README.md
@@ -25,7 +25,3 @@ Now `TDLib` can be freely used from any UWP project, built in Visual Studio.
## Example of usage
The `app/` directory contains a simple example of a C# application for Universal Windows Platform. Just open it with Visual Studio 2015 or 2017 and run.
-
-
-
-
diff --git a/td/generate/CMakeLists.txt b/td/generate/CMakeLists.txt
index e9ab1582d..6fa83e245 100644
--- a/td/generate/CMakeLists.txt
+++ b/td/generate/CMakeLists.txt
@@ -138,7 +138,7 @@ if (NOT CMAKE_CROSSCOMPILING)
add_custom_target(generate_dotnet_api
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND td_generate_dotnet_api ${TL_TD_API_TLO}
- COMMENT "Generate .Net API files"
+ COMMENT "Generate .NET API files"
DEPENDS td_generate_dotnet_api ${TL_TD_API_TLO}
)
endif()