api: update README.md
Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
parent
fbb9098393
commit
202f1dc9b8
217
README.md
217
README.md
@ -5,158 +5,123 @@ This is a layer 3 TUN driver for Windows 7, 8, 8.1, and 10. Originally created f
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
The following snippet may be used inside of a WiX `<Product>` element for including Wintun inside of a Windows Installer. Note that **MSI is the only supported method of installing Wintun**; if you're using a different install system (such as NSIS) and cannot bother to switch to MSI/WiX, then simply bundle an embedded MSI that you can execute with `msiexec.exe`.
|
Wintun is deployed as a platform-specific `wintun.dll` file. Install the `wintun.dll` file side-by-side with your application.
|
||||||
|
|
||||||
```xml
|
|
||||||
<DirectoryRef Id="INSTALLFOLDER">
|
|
||||||
<Merge Id="WintunMergeModule" Language="0" DiskId="1" SourceFile="path\to\wintun-x.y-amd64.msm" />
|
|
||||||
</DirectoryRef>
|
|
||||||
<Feature Id="WintunFeature" Title="Wintun" Level="1">
|
|
||||||
<MergeRef Id="WintunMergeModule" />
|
|
||||||
</Feature>
|
|
||||||
```
|
|
||||||
|
|
||||||
It is advisable to use the prebuilt and Microsoft-signed MSM files from [Wintun.net](https://www.wintun.net/).
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
After loading the driver and creating a network interface the typical way using [SetupAPI](https://docs.microsoft.com/en-us/windows-hardware/drivers/install/setupapi), open the NDIS device object associated with the PnPInstanceId, enabling all forms of file sharing:
|
Include `wintun.h` file in your project and dynamically load the `wintun.dll` using [`LoadLibraryEx()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa) and [`GetProcAddress()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress).
|
||||||
|
|
||||||
|
Each function has its function typedef in `wintun.h` with additional usage documentation.
|
||||||
|
|
||||||
```C
|
```C
|
||||||
TCHAR *InterfaceList = NULL;
|
#include "wintun.h"
|
||||||
|
⋮
|
||||||
|
HMODULE Wintun = LoadLibraryExW(L"wintun.dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
|
||||||
|
if (!Wintun)
|
||||||
|
return GetLastError();
|
||||||
|
WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter = (WINTUN_CREATE_ADAPTER_FUNC)GetProcAddress(Wintun, "WintunCreateAdapter");
|
||||||
|
WINTUN_DELETE_ADAPTER_FUNC WintunDeleteAdapter = (WINTUN_DELETE_ADAPTER_FUNC)GetProcAddress(Wintun, "WintunDeleteAdapter");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adapter management
|
||||||
|
|
||||||
|
Adapters are grouped together in pools to allow various clients on the same machine. Each client vendor should pick own unique pool name.
|
||||||
|
|
||||||
|
Manage the network adapters using following functions:
|
||||||
|
|
||||||
|
- `WintunCreateAdapter()` creates a new adapter.
|
||||||
|
- `WintunDeleteAdapter()` deletes the adapter.
|
||||||
|
- `WintunEnumAdapters()` enumerates all existing adapters.
|
||||||
|
- `WintunGetAdapter()` gets existing adapter handle.
|
||||||
|
- `WintunFreeAdapter()` frees adapter handle.
|
||||||
|
- `WintunGetAdapterDeviceObject()` opens adapter device object.
|
||||||
|
- `WintunGetAdapterGUID()` gets adapter GUID.
|
||||||
|
- `WintunGetAdapterLUID()` gets adapter LUID.
|
||||||
|
- `WintunGetAdapterName()` gets adapter name.
|
||||||
|
- `WintunSetAdapterName()` sets adapter name.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```C
|
||||||
|
DWORD Result;
|
||||||
|
WINTUN_ADAPTER_HANDLE Adapter;
|
||||||
|
BOOL RebootRequired;
|
||||||
|
Result = WintunCreateAdapter(L"com.contoso.myapp", "My VPN adapter", NULL, &Adapter, &RebootRequired);
|
||||||
|
if (Result != ERROR_SUCCESS)
|
||||||
|
return Result;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Session management
|
||||||
|
|
||||||
|
Once adapter is created, use the following functions to start a session and transfer packets:
|
||||||
|
|
||||||
|
- `WintunStartSession()` starts a session. One adapter may have only one session.
|
||||||
|
- `WintunEndSession()` ends and frees the session.
|
||||||
|
- `WintunIsPacketAvailable()` checks if there is a receive packet available.
|
||||||
|
- `WintunWaitForPacket()` waits for a receive packet to become available.
|
||||||
|
- `WintunReceivePackets()` receives one or more packets.
|
||||||
|
- `WintunSendPackets()` sends one or more packets.
|
||||||
|
|
||||||
|
#### Writing to and from rings
|
||||||
|
|
||||||
|
Reading packets from the adapter may be done as:
|
||||||
|
|
||||||
|
```C
|
||||||
|
// TODO: Preallocate WINTUN_PACKET *Queue linked list with WINTUN_MAX_IP_PACKET_SIZE packets.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
free(InterfaceList);
|
if (!WintunIsPacketAvailable(Session))
|
||||||
DWORD RequiredBytes;
|
WintunWaitForPacket(Session, INFINITE);
|
||||||
if (CM_Get_Device_Interface_List_Size(&RequiredBytes, (LPGUID)&GUID_DEVINTERFACE_NET,
|
for (WINTUN_PACKET *p = Queue; p && p->Size <= WINTUN_MAX_IP_PACKET_SIZE; p = p->Next)
|
||||||
InstanceId, CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)
|
p->Size = DWORD_MAX;
|
||||||
return FALSE;
|
DWORD Result = WintunReceivePackets(Session, Queue);
|
||||||
InterfaceList = calloc(sizeof(*InterfaceList), RequiredBytes);
|
if (Result != ERROR_SUCCESS && Result != ERROR_NO_MORE_ITEMS)
|
||||||
if (!InterfaceList)
|
return Result;
|
||||||
return FALSE;
|
for (WINTUN_PACKET *p = Queue; p && p->Size <= WINTUN_MAX_IP_PACKET_SIZE; p = p->Next) {
|
||||||
CONFIGRET Ret = CM_Get_Device_Interface_List((LPGUID)&GUID_DEVINTERFACE_NET, InstanceId,
|
// TODO: Process packet p.
|
||||||
InterfaceList, RequiredBytes, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
|
|
||||||
if (Ret == CR_SUCCESS)
|
|
||||||
break;
|
|
||||||
if (Ret != CR_BUFFER_SMALL) {
|
|
||||||
free(InterfaceList);
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HANDLE WintunHandle = CreateFile(InterfaceList, GENERIC_READ | GENERIC_WRITE,
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
||||||
NULL, OPEN_EXISTING, 0, NULL);
|
|
||||||
free(InterfaceList);
|
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ring layout
|
It may be desirable to spin on `WintunIsPacketAvailable()` for some time under heavy use before waiting with `WintunWaitForPacket()`, in order to reduce latency.
|
||||||
|
|
||||||
You must allocate two ring structs, one for receiving and one for sending:
|
Writing packets to the adapter may be done as:
|
||||||
|
|
||||||
```C
|
```C
|
||||||
typedef struct _TUN_RING {
|
// TODO: Prepare WINTUN_PACKET *Queue linked list with packets.
|
||||||
volatile ULONG Head;
|
DWORD Result = WintunSendPackets(Session, Queue);
|
||||||
volatile ULONG Tail;
|
|
||||||
volatile LONG Alertable;
|
|
||||||
UCHAR Data[];
|
|
||||||
} TUN_RING;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- `Head`: Byte offset of the first packet in the ring. Its value must be a multiple of 4 and less than ring capacity.
|
### Misc functions
|
||||||
|
|
||||||
- `Tail`: Byte offset of the start of free space in the ring. Its value must be multiple of 4 and less than ring capacity.
|
Other `wintun.dll` functions are:
|
||||||
|
|
||||||
- `Alertable`: Zero when the consumer is processing packets, non-zero when the consumer has processed all packets and is waiting for `TailMoved` event.
|
- `WintunGetVersion()` returns driver and NDIS major and minor versions.
|
||||||
|
- `WintunSetLogger()` sets global logging callback function.
|
||||||
|
|
||||||
- `Data`: The ring data.
|
Example:
|
||||||
|
|
||||||
In order to determine the size of the `Data` array:
|
|
||||||
|
|
||||||
1. Pick a ring capacity ranging from 128kiB to 64MiB bytes. This capacity must be a power of two (e.g. 1MiB). The ring can hold up to this much data.
|
|
||||||
2. Add 0x10000 trailing bytes to the capacity, in order to allow for always-contigious packet segments.
|
|
||||||
|
|
||||||
The total ring size memory is then `sizeof(TUN_RING) + capacity + 0x10000`.
|
|
||||||
|
|
||||||
Each packet is stored in the ring aligned to `sizeof(ULONG)` as:
|
|
||||||
|
|
||||||
```C
|
```C
|
||||||
typedef struct _TUN_PACKET {
|
static BOOL CALLBACK
|
||||||
ULONG Size;
|
ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_ const WCHAR *LogLine)
|
||||||
UCHAR Data[];
|
{
|
||||||
} TUN_PACKET;
|
const WCHAR *Template;
|
||||||
```
|
switch (Level)
|
||||||
|
{
|
||||||
- `Size`: Size of packet (max 0xFFFF).
|
case WINTUN_LOG_INFO: Template = L"[+] %s\n"; break;
|
||||||
|
case WINTUN_LOG_WARN: Template = L"[-] %s\n"; break;
|
||||||
- `Data`: Layer 3 IPv4 or IPv6 packet.
|
case WINTUN_LOG_ERR: Template = L"[!] %s\n"; break;
|
||||||
|
default: return FALSE;
|
||||||
### Registering rings
|
|
||||||
|
|
||||||
In order to register the two `TUN_RING`s, prepare a registration struct as:
|
|
||||||
|
|
||||||
```C
|
|
||||||
typedef struct _TUN_REGISTER_RINGS {
|
|
||||||
struct {
|
|
||||||
ULONG RingSize;
|
|
||||||
TUN_RING *Ring;
|
|
||||||
HANDLE TailMoved;
|
|
||||||
} Send, Receive;
|
|
||||||
} TUN_REGISTER_RINGS;
|
|
||||||
```
|
|
||||||
|
|
||||||
- `Send.RingSize`, `Receive.RingSize`: Sizes of the rings (`sizeof(TUN_RING) + capacity + 0x10000`, as above).
|
|
||||||
|
|
||||||
- `Send.Ring`, `Receive.Ring`: Pointers to the rings.
|
|
||||||
|
|
||||||
- `Send.TailMoved`: A handle to an [`auto-reset event`](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa) created by the client that Wintun signals after it moves the `Tail` member of the send ring.
|
|
||||||
|
|
||||||
- `Receive.TailMoved`: A handle to an [`auto-reset event`](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa) created by the client that the client will signal when it changes `Receive.Ring->Tail` and `Receive.Ring->Alertable` is non-zero.
|
|
||||||
|
|
||||||
With events created, send and receive rings allocated, and registration struct populated, [`DeviceIoControl`](https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol)(`TUN_IOCTL_REGISTER_RINGS`: 0xca6ce5c0) with pointer and size of descriptor struct specified as `lpInBuffer` and `nInBufferSize` parameters. You may call `TUN_IOCTL_REGISTER_RINGS` on one handle only.
|
|
||||||
|
|
||||||
|
|
||||||
### Writing to and from rings
|
|
||||||
|
|
||||||
Reading packets from the send ring may be done as:
|
|
||||||
|
|
||||||
```C
|
|
||||||
for (;;) {
|
|
||||||
TUN_PACKET *Next = PopFromRing(Rings->Send.Ring);
|
|
||||||
if (!Next) {
|
|
||||||
Rings->Send.Ring->Alertable = TRUE;
|
|
||||||
Next = PopFromRing(Rings->Send.Ring);
|
|
||||||
if (!Next) {
|
|
||||||
WaitForSingleObject(Rings->Send.TailMoved, INFINITE);
|
|
||||||
Rings->Send.Ring->Alertable = FALSE;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
Rings->Send.Ring->Alertable = FALSE;
|
fwprintf(stderr, Template, LogLine);
|
||||||
ResetEvent(Rings->Send.TailMoved);
|
return TRUE;
|
||||||
}
|
|
||||||
SendToClientProgram(Next);
|
|
||||||
}
|
}
|
||||||
|
⋮
|
||||||
|
WintunSetLogger(ConsoleLogger);
|
||||||
```
|
```
|
||||||
|
|
||||||
It may be desirable to spin for some time under heavy use before waiting on the `TailMoved` event, in order to reduce latency.
|
|
||||||
|
|
||||||
When closing the handle, Wintun will set the `Tail` to 0xFFFFFFFF and set the `TailMoved` event to unblock the waiting user process.
|
|
||||||
|
|
||||||
Writing packets to the receive ring may be done as:
|
|
||||||
|
|
||||||
```C
|
|
||||||
for (;;) {
|
|
||||||
TUN_PACKET *Next = ReceiveFromClientProgram();
|
|
||||||
WriteToRing(Rings->Receive.Ring, Next);
|
|
||||||
if (Rings->Receive.Ring->Alertable)
|
|
||||||
SetEvent(Rings->Recieve.TailMoved);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Wintun will abort reading the receive ring on invalid `Head` or `Tail` or on a bogus packet. In this case, Wintun will set the `Head` to 0xFFFFFFFF. In order to restart it, reopen the handle and call `TUN_IOCTL_REGISTER_RINGS` again. However, it should be entirely possible to avoid feeding Wintun bogus packets and invalid offsets.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
**Do not distribute drivers named "Wintun", as they will most certainly clash with official deployments. Instead distribute [the signed MSMs from Wintun.net](https://www.wintun.net/).** If you are unable to use MSMs, [consult the MSI creation instructions](msi-example/README.md).
|
**Do not distribute drivers named "Wintun", as they will most certainly clash with official deployments. Instead distribute `wintun.dll`.**
|
||||||
|
|
||||||
General requirements:
|
General requirements:
|
||||||
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
## Example Standalone MSI
|
|
||||||
|
|
||||||
The best way to include Wintun in your software is by including the MSMs in your final MSI,
|
|
||||||
as described by [the main README](../README.md). However, if you're stuck with an installation
|
|
||||||
system such as NSIS, which can not bundle MSM files, then you must build your own MSI, which
|
|
||||||
NSIS can then invoke. ***Do not use an MSI from elsewhere. You must build it yourself and
|
|
||||||
distribute only the MSI that you yourself build.*** Otherwise different projects will wind up
|
|
||||||
uninstalling each other by accident and disturbing the MSM reference counting. The steps in
|
|
||||||
this file should only be taken if you're not able to include an MSM into a MSI, something that
|
|
||||||
is easily possible using WiX or most commercial installation solutions.
|
|
||||||
|
|
||||||
This `msi-example` folder contains a WiX skeleton and a build script that handles all
|
|
||||||
dependencies. use it as follows below.
|
|
||||||
|
|
||||||
#### Steps:
|
|
||||||
|
|
||||||
1. Generate a UUID using uuidgen.exe and replace `{{{FIXED AMD64 UUID}}}` in exampletun.wxs
|
|
||||||
with that UUID. For the life time of your entire product, even across versions, do not change
|
|
||||||
that UUID.
|
|
||||||
|
|
||||||
2. Generate a UUID using uuidgen.exe and replace `{{{FIXED ARM64 UUID}}}` in exampletun.wxs
|
|
||||||
with that UUID. For the life time of your entire product, even across versions, do not change
|
|
||||||
that UUID.
|
|
||||||
|
|
||||||
3. Generate another UUID using uuidgen.exe and replace `{{{FIXED X86 UUID}}}` in
|
|
||||||
exampletun.wxs with that UUID. For the life time of your entire product, even across versions,
|
|
||||||
do not change that UUID.
|
|
||||||
|
|
||||||
4. Go to [Wintun.net](https://www.wintun.net/) and look at what the latest version is (`0.6`,
|
|
||||||
for example). Replace `{{{VERSION}}}` in build.bat with that version.
|
|
||||||
|
|
||||||
5. Download the amd64 MSM from [Wintun.net](https://www.wintun.net/) and compute its SHA2-256
|
|
||||||
sum in all lowercase hex digits using `CertUtil -hashfile "path/to/file" SHA256`, and replace
|
|
||||||
`{{{64BIT HASH}}}` in build.bat with that value.
|
|
||||||
|
|
||||||
6. Download the arm64 MSM from [Wintun.net](https://www.wintun.net/) and compute its SHA2-256
|
|
||||||
sum in all lowercase hex digits using `CertUtil -hashfile "path/to/file" SHA256`, and replace
|
|
||||||
`{{{64BIT HASH}}}` in build.bat with that value.
|
|
||||||
|
|
||||||
7. Download the x86 MSM from [Wintun.net](https://www.wintun.net/) and compute its SHA2-256
|
|
||||||
sum in all lowercase hex digits using `CertUtil -hashfile "path/to/file" SHA256`, and replace
|
|
||||||
`{{{32BIT HASH}}}` in build.bat with that value.
|
|
||||||
|
|
||||||
8. Run build.bat.
|
|
||||||
|
|
||||||
9. Distribute dist\exampletun-*.msi for your own software only.
|
|
@ -1,62 +0,0 @@
|
|||||||
@echo off
|
|
||||||
rem SPDX-License-Identifier: GPL-2.0
|
|
||||||
rem Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
|
||||||
|
|
||||||
setlocal
|
|
||||||
set PATHEXT=.exe
|
|
||||||
set BUILDDIR=%~dp0
|
|
||||||
cd /d %BUILDDIR% || exit /b 1
|
|
||||||
|
|
||||||
set WIX_CANDLE_FLAGS=-nologo
|
|
||||||
set WIX_LIGHT_FLAGS=-nologo -spdb -sice:ICE71 -sice:ICE61
|
|
||||||
|
|
||||||
if exist .deps\prepared goto :build
|
|
||||||
:installdeps
|
|
||||||
rmdir /s /q .deps 2> NUL
|
|
||||||
mkdir .deps || goto :error
|
|
||||||
cd .deps || goto :error
|
|
||||||
call :download wintun-x86.msm https://www.wintun.net/builds/wintun-x86-{{{VERSION}}}.msm {{{32BIT HASH}}} || goto :error
|
|
||||||
call :download wintun-amd64.msm https://www.wintun.net/builds/wintun-amd64-{{{VERSION}}}.msm {{{64BIT HASH}}} || goto :error
|
|
||||||
call :download wintun-arm64.msm https://www.wintun.net/builds/wintun-arm64-{{{VERSION}}}.msm {{{64BIT HASH}}} || goto :error
|
|
||||||
call :download wix-binaries.zip https://wixtoolset.org/downloads/v3.14.0.3910/wix314-binaries.zip 0904a88a4bcd9dd3c2274caabe73989cd72767ee90c8fa0bf813d004eec90d32 || goto :error
|
|
||||||
echo [+] Extracting wix-binaries.zip
|
|
||||||
mkdir wix\bin || goto :error
|
|
||||||
tar -xf wix-binaries.zip -C wix\bin || goto :error
|
|
||||||
echo [+] Cleaning up wix-binaries.zip
|
|
||||||
del wix-binaries.zip || goto :error
|
|
||||||
copy /y NUL prepared > NUL || goto :error
|
|
||||||
cd .. || goto :error
|
|
||||||
|
|
||||||
:build
|
|
||||||
set WIX=%BUILDDIR%.deps\wix\
|
|
||||||
call :msi x86 x86 || goto :error
|
|
||||||
call :msi amd64 x64 || goto :error
|
|
||||||
call :msi arm64 arm64 || goto :error
|
|
||||||
if exist ..\sign.bat call ..\sign.bat
|
|
||||||
if "%SigningCertificate%"=="" goto :success
|
|
||||||
if "%TimestampServer%"=="" goto :success
|
|
||||||
echo [+] Signing
|
|
||||||
signtool sign /sha1 "%SigningCertificate%" /fd sha256 /tr "%TimestampServer%" /td sha256 /d "ExampleTun Setup" "dist\exampletun-*.msi" || goto :error
|
|
||||||
|
|
||||||
:success
|
|
||||||
echo [+] Success.
|
|
||||||
exit /b 0
|
|
||||||
|
|
||||||
:download
|
|
||||||
echo [+] Downloading %1
|
|
||||||
curl -#fLo %1 %2 || exit /b 1
|
|
||||||
echo [+] Verifying %1
|
|
||||||
for /f %%a in ('CertUtil -hashfile %1 SHA256 ^| findstr /r "^[0-9a-f]*$"') do if not "%%a"=="%~3" exit /b 1
|
|
||||||
goto :eof
|
|
||||||
|
|
||||||
:msi
|
|
||||||
if not exist "%~1" mkdir "%~1"
|
|
||||||
echo [+] Compiling %1
|
|
||||||
"%WIX%bin\candle" %WIX_CANDLE_FLAGS% -dEXAMPLETUN_PLATFORM="%~1" -out "%~1\exampletun.wixobj" -arch %2 exampletun.wxs || exit /b %errorlevel%
|
|
||||||
echo [+] Linking %1
|
|
||||||
"%WIX%bin\light" %WIX_LIGHT_FLAGS% -out "dist\exampletun-%~1.msi" "%~1\exampletun.wixobj" || exit /b %errorlevel%
|
|
||||||
goto :eof
|
|
||||||
|
|
||||||
:error
|
|
||||||
echo [-] Failed with error #%errorlevel%.
|
|
||||||
cmd /c exit %errorlevel%
|
|
@ -1,56 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
SPDX-License-Identifier: GPL-2.0
|
|
||||||
|
|
||||||
Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
|
||||||
-->
|
|
||||||
<?if $(var.EXAMPLETUN_PLATFORM) = "amd64"?>
|
|
||||||
<?define UpgradeCode = "{{{FIXED AMD64 UUID}}}"?>
|
|
||||||
<?elseif $(var.EXAMPLETUN_PLATFORM) = "arm64"?>
|
|
||||||
<?define UpgradeCode = "{{{FIXED ARM64 UUID}}}"?>
|
|
||||||
<?elseif $(var.EXAMPLETUN_PLATFORM) = "x86"?>
|
|
||||||
<?define UpgradeCode = "{{{FIXED X86 UUID}}}"?>
|
|
||||||
<?else?>
|
|
||||||
<?error Unknown platform ?>
|
|
||||||
<?endif?>
|
|
||||||
|
|
||||||
|
|
||||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
|
||||||
<Product
|
|
||||||
Id="*"
|
|
||||||
Name="ExampleTun"
|
|
||||||
Language="1033"
|
|
||||||
Version="1.0"
|
|
||||||
Manufacturer="Acme Widgets Corporation"
|
|
||||||
UpgradeCode="$(var.UpgradeCode)">
|
|
||||||
<Package
|
|
||||||
InstallerVersion="400"
|
|
||||||
Compressed="yes"
|
|
||||||
InstallScope="perMachine"
|
|
||||||
Description="ExampleTun: Acme Widget's Distribution of Wintun"
|
|
||||||
ReadOnly="yes" />
|
|
||||||
|
|
||||||
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
|
|
||||||
|
|
||||||
<Property Id="ARPNOMODIFY" Value="yes" />
|
|
||||||
<Property Id="ARPSYSTEMCOMPONENT" Value="1" />
|
|
||||||
<Property Id="DISABLEADVTSHORTCUTS" Value="yes" />
|
|
||||||
<Property Id="DISABLEROLLBACK" Value="yes" />
|
|
||||||
<Property Id="MSIDISABLERMRESTART" Value="1" />
|
|
||||||
<Property Id="MSIRMSHUTDOWN" Value="1" />
|
|
||||||
|
|
||||||
<MajorUpgrade
|
|
||||||
AllowDowngrades="no"
|
|
||||||
AllowSameVersionUpgrades="yes"
|
|
||||||
DowngradeErrorMessage="A newer version of [ProductName] is already installed."
|
|
||||||
Schedule="afterInstallExecute" />
|
|
||||||
|
|
||||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
|
||||||
<Merge Id="WintunMergeModule" Language="0" DiskId="1" SourceFile=".deps\wintun-$(var.EXAMPLETUN_PLATFORM).msm" />
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
<Feature Id="WintunFeature" Title="Wintun" Level="1">
|
|
||||||
<MergeRef Id="WintunMergeModule" />
|
|
||||||
</Feature>
|
|
||||||
</Product>
|
|
||||||
</Wix>
|
|
Loading…
Reference in New Issue
Block a user