Integrating Appsalt SDK with Windows .NET/C# applications
This guide will go through the process of integrating and using Appsalt SDK in your .NET/C# desktop application.
Don't forget to generate your Appsalt SDK API Key and download the SDK from the Developers dashboard first.
Integration
On Windows, the SDK service is provided for x64, x86, and arm64 architectures. The SDK service dynamic-link libraries x64\bin\appsalt.dll, x86\bin\appsalt.dll, and arm64\bin\appsalt.dll should be accessible to your application's executable. It is recommended to place them in the same directory tree as your application's executable. If you need to support only one architecture, you can use only one of the provided libraries.
You cannot mix architectures (e.g., an x64 process cannot load an arm64 DLL).
With Visual Studio
If you are using Visual Studio, you can automatically place the SDK service dynamic-link libraries in the output directory using a post-build event:
- Right-click on your project in Solution Explorer and select Properties.
- In the properties window, go to Build Events on the left sidebar.
- In the Post-build event command line field add the following command:
mkdir "$(TargetDir)\x86"
mkdir "$(TargetDir)\x64"
mkdir "$(TargetDir)\arm64"
copy /Y "<path to>\x86\bin\appsalt.dll" "$(TargetDir)\x86\appsalt.dll"
copy /Y "<path to>\x64\bin\appsalt.dll" "$(TargetDir)\x64\appsalt.dll"
copy /Y "<path to>\arm64\bin\appsalt.dll" "$(TargetDir)\arm64\appsalt.dll" - Replace
<path to>with the path to the directory where the SDK service dynamic-link libraries are located.
SDK service functions can be called from managed code using Platform Invoke (P/Invoke) functionality.
In order to call the SDK service functions, add the following class to your application's source code:
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
static class Appsalt
{
private const string File = "appsalt.dll";
static Appsalt()
{
var root = AppDomain.CurrentDomain.BaseDirectory;
string arch;
if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
arch = "x86";
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
arch = "x64";
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
arch = "arm64";
else
throw new PlatformNotSupportedException("Unsupported architecture");
string path = Path.Combine(root, arch, File);
if (LoadLibrary(path) == IntPtr.Zero)
throw new DllNotFoundException($"{path} (Win32 {Marshal.GetLastWin32Error()})");
AppDomain.CurrentDomain.ProcessExit += OnExit;
AppDomain.CurrentDomain.DomainUnload += OnExit;
}
public static void Init(string api_key)
{
if (appsalt_init(api_key) < 0)
throw new Exception("Failed to initialize the service");
}
public static bool Start()
{
if (appsalt_start(out var consent) < 0)
throw new Exception("Failed to start the service");
return consent;
}
public static void Stop()
{
if (appsalt_stop() < 0)
throw new Exception("Failed to stop the service");
}
public static string Identify()
{
UIntPtr size = UIntPtr.Zero;
if (appsalt_identify(null, ref size) < 0)
throw new Exception("Failed to get size of identity data");
var data = new StringBuilder((int)size);
size = (UIntPtr)data.Capacity;
if (appsalt_identify(data, ref size) < 0)
throw new Exception("Failed to get identity data");
return data.ToString();
}
public static bool IsRunning()
{
if (appsalt_is_running(out var running) < 0)
throw new Exception("Failed to check if the service is running");
return running;
}
public static void OptIn()
{
if (appsalt_opt_in() < 0)
throw new Exception("Failed to opt in user");
}
public static void OptOut()
{
if (appsalt_opt_out() < 0)
throw new Exception("Failed to opt out user");
}
public static bool IsOptedIn()
{
if (appsalt_is_opted_in(out var consent) < 0)
throw new Exception("Failed to check if user is opted in");
return consent;
}
public static bool RequestConsent()
{
if (appsalt_request_consent(out var consent) < 0)
throw new Exception("Failed to request user consent");
return consent;
}
public static void Log(string dir)
{
if (appsalt_log(dir) < 0)
throw new Exception("Failed to enable logging for the service");
}
public static void Mute()
{
if (appsalt_mute() < 0)
throw new Exception("Failed to disable logging for the service");
}
private static void OnExit(object sender, EventArgs e)
{
try
{
Stop();
}
catch { }
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr LoadLibrary(string file);
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_init(string api_key);
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_start(out bool consent);
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_stop();
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_identify([MarshalAs(UnmanagedType.LPStr)] StringBuilder data, ref UIntPtr size);
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_is_running(out bool running);
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_opt_in();
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_opt_out();
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_is_opted_in(out bool consent);
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_request_consent(out bool consent);
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_log(string dir);
[DllImport(File, CallingConvention = CallingConvention.Cdecl)]
private static extern int appsalt_mute();
}
Full working example can be found in the samples/dotnet directory of the downloaded Appsalt SDK.
Usage
In the above example, the class calls .Stop() when the application is closing. It is recommended to do this in your application as well so the SDK service shuts down cleanly.
Note that .Start() and .Stop() are non-blocking. Internally, starting and stopping the SDK service are asynchronous, so there may be a slight delay before the action takes effect.
Initializing SDK service
To initialize the SDK service, call .Init():
Appsalt.Init("your-api-key");
The parameter is your Appsalt SDK API Key. If the service is already initialized and a different API key is provided, the old instance is terminated and a new one is initialized. If the service is already initialized and the same API key is provided, the call effectively does nothing.
It is required to call this function before any other SDK service function.
Starting SDK service
To start the SDK service, call .Start():
bool consent = Appsalt.Start();
It checks whether explicit user consent was given previously. The current consent state is returned by the method. If consent was given previously, the SDK service starts. If not, the SDK service does not start and the method returns false.
If the SDK service is already running, the call effectively does nothing.
Stopping SDK service
To stop the SDK service, call .Stop():
Appsalt.Stop();
Stops the SDK service if it is running. If the SDK service is not running, this method does nothing.
Identifying SDK service
To retrieve the SDK service identifier string, call .Identify():
string identifier = Appsalt.Identify();
This is an immediate operation and returns an opaque string that uniquely identifies the running SDK service.
Verifying SDK service state
To verify whether the SDK service is running, call .IsRunning():
bool running = Appsalt.IsRunning();
This is an immediate operation and reports SDK service state at the current time without blocking.
Providing user consent
To provide user consent, call .OptIn():
Appsalt.OptIn();
Records that user consent was given and allows the SDK service to start. Subsequent calls to .Start() are allowed to start the SDK service.
Revoking user consent
To revoke user consent, call .OptOut():
Appsalt.OptOut();
Records that user consent was revoked. The SDK service stops if it is running. Subsequent calls to .Start() are not allowed to start the SDK service.
Verifying user consent state
To verify whether user consent was given, call .IsOptedIn():
bool consent = Appsalt.IsOptedIn();
Returns the stored consent state.
Requesting user consent
To request user consent, call .RequestConsent():
bool consent = Appsalt.RequestConsent();
Displays the default user agreement window.
If the user accepts the agreement, consent is stored and subsequent calls to .Start() are allowed to start the SDK service.
If the user declines or closes the window, subsequent calls to .Start() are not allowed unless consent was previously given.
This function is blocking and returns only after the user accepts or declines.
Enable logging
To enable logging, call .Log():
Appsalt.Log("C:\\path\\to\\log\\directory");
Enables logging for the SDK service. Log files are created in the specified directory. If the directory is not specified, log files will be created in the current working directory of your application. Logs are also written to standard output.
Disable logging
To disable logging, call .Mute():
Appsalt.Mute();
Disables logging for the SDK service. Any log file is closed and writing to standard output is stopped.
Function reference
appsalt_start() and appsalt_stop() are non-blocking. Internally, starting and stopping the Appsalt SDK service are asynchronous operations, so there can be a slight delay before the action takes effect.
appsalt_init
Initialize the SDK service.
int32_t appsalt_init(const char *api_key);
Parameters
| Name | Type | Description |
|---|---|---|
api_key | const char* | Your API key provided by Appsalt SDK. |
Returns
0 on success; otherwise a negative error code.
Remarks
- If the service is already initialized and a different API key is provided, the old instance is terminated and a new one is initialized.
- If the service is already initialized and the same API key is provided, the function call effectively does nothing.
api_keyis copied by the SDK; its memory does not need to remain valid after the call returns.
It is required to call this function before any other SDK service function.
appsalt_start
Start the SDK service.
int32_t appsalt_start(int32_t *state);
Parameters
| Name | Type | Description |
|---|---|---|
state | int32_t* | Out: consent state. Set to 1 if user consent was previously given, otherwise 0. |
Returns
0 on success; otherwise a negative error code.
Remarks
- Checks whether explicit user consent was given before. The current consent state is returned via
*state. - If consent was previously given, the SDK service starts. If not, the service does not start and
*stateis set to0. - If the service is already running, the function call effectively does nothing.
It is recommended to obtain user consent before starting the SDK service.
appsalt_stop
Stop the SDK service.
int32_t appsalt_stop(void);
Returns
0 on success; otherwise a negative error code.
Remarks
- Stops the SDK service if it is running.
- If the service is not running, this function does nothing.
It is recommended to stop the SDK service before closing your application so the service shuts down cleanly.
appsalt_identify
Get the SDK service identifier.
int32_t appsalt_identify(char *data, size_t *size);
Parameters
| Name | Type | Description |
|---|---|---|
data | char* | Buffer to receive a NUL-terminated ASCII string. If not NULL, the function writes up to *size bytes and sets *size to the number of bytes actually written. |
size | size_t* | In/out. If data is NULL, on return *size is set to the number of bytes required to store the full NUL-terminated string. If data is not NULL, on return *size is set to the number of bytes actually written. |
Returns
0 on success; otherwise a negative error code.
Remarks
- The identifier is stable across runs on the device.
- Use the two-call pattern to retrieve the full value: call with
data == NULLto get required size (in bytes), allocate that many bytes, then call again withdataandsize. - The returned string is NUL-terminated and plain ASCII.
- If the provided buffer (
data/*size) is smaller than required, the string is truncated to fit (ensuring NUL termination when*size > 0),*sizeis set to the number of bytes actually written, and the function returns0.
appsalt_is_running
Check if the SDK service is running.
int32_t appsalt_is_running(int32_t *state);
Parameters
| Name | Type | Description |
|---|---|---|
state | int32_t* | Out: set to 1 if the service is running, otherwise 0. |
Returns
0 on success; otherwise a negative error code.
appsalt_opt_in
Provide user consent.
int32_t appsalt_opt_in(void);
Returns
0 on success; otherwise a negative error code.
Remarks
- Persists that user consent was given and informs the SDK service that it may start.
- Subsequent calls to
appsalt_start()will be allowed to start the service.
appsalt_opt_out
Revoke user consent.
int32_t appsalt_opt_out(void);
Returns
0 on success; otherwise a negative error code.
Remarks
- Persists that user consent was revoked and informs the SDK service that it should stop if running.
- Subsequent calls to
appsalt_start()will not be allowed to start the service.
appsalt_is_opted_in
Check whether user consent was given.
int32_t appsalt_is_opted_in(int32_t *state);
Parameters
| Name | Type | Description |
|---|---|---|
state | int32_t* | Out: set to 1 if consent was given, otherwise 0. |
Returns
0 on success; otherwise a negative error code.
Remarks
- Returns the stored consent state via
*state.
appsalt_request_consent
Display the default user agreement window and capture consent.
int32_t appsalt_request_consent(int32_t *state);
Parameters
| Name | Type | Description |
|---|---|---|
state | int32_t* | Out: set to 1 if the user accepts, otherwise 0. |
Returns
0 on success; otherwise a negative error code.
Remarks
- Shows the default user agreement UI.
- If the user accepts, consent is stored and the SDK service may start. Subsequent
appsalt_start()calls are allowed. - If the user declines or closes the window, subsequent
appsalt_start()calls are not allowed unless consent was previously given.
This function is blocking and returns only after the user accepts or declines the agreement.
appsalt_log
Enable logging for the SDK service.
int32_t appsalt_log(const char *dir);
Parameters
| Name | Type | Description |
|---|---|---|
dir | const char* | Directory where log files will be stored. If NULL or empty, logs are created in the current working directory. |
Returns
0 on success; otherwise a negative error code.
Remarks
- Enables logging and writes logs to the specified directory (created if it does not exist).
- Logs are also written to standard output.
- Subsequent calls to
appsalt_log()create a new log file in the specified directory.
appsalt_mute
Disable logging for the SDK service.
int32_t appsalt_mute(void);
Returns
0 on success; otherwise a negative error code.
Remarks
- Disables logging: closes any open log file and stops writing to standard output.
- Existing log files are not deleted and can be inspected for debugging.