Win32 C++ Basic Integration

📘

UWP & .NET

The Branch Win32 C++ SDK is not compatible with Universal Windows Platform (UWP) or .NET. Support for UWP and .NET applications is not currently available.

👍

SDK Stats

GitHub release (latest by date)GitHub release (latest by date)

Open Source Github Repo:
https://github.com/BranchMetrics/cpp-branch-deep-linking-attribution
Current SDK download:
https://github.com/BranchMetrics/cpp-branch-deep-linking-attribution/releases/latest

1. Download & Install the Branch SDK

🚧

PREREQUISITE

Before you implement the SDK, please ensure you have Configured Link Behaviors for Redirects and Link Domains.
imageimage

NOTE: Visual Studio 2019+ is required (Earlier versions are not supported.)

Download the SDK

The Branch SDK Installer is available for download here. This downloads a file called BranchSDK.msi.

Note: If you have installed any release of the MSI, you should manually uninstall before installing the new version.

Install the SDK

Double-click the BranchSDK.msi file to begin installation. The Branch SDK requires Poco for HTTP, JSON and other purposes. For convenience, these third-party libraries are included in the distribution and will be installed alongside the Branch SDK.

The MSI contains Branch SDK and Poco headers and binaries.

Once the installation finishes, all headers and libraries will be installed under C:\Program Files (x86)\Branch SDK.

📘

Antimalware Alert

The MSI may be flagged by malware blockers in some cases. For example, with Windows Defender, it may be necessary to click the "More info" link first to allow installation to proceed via the "Run anyway" button. Please be sure only to install the MSI from https://github.com/BranchMetrics/cpp-branch-deep-linking-attribution/releases/download/latest/BranchSDK.msi.

2. Build Against the Installed Branch SDK

The SDK will be installed in C:\Program Files (x86)\Branch SDK.

Open your project's Properties in Visual Studio.

a. Add include path

Under C/C++ > General, add C:\Program Files (x86)\Branch SDK\include to Additional Include Directories for All Configurations and All Platforms. The corresponding command-line option is /I"C:\Program Files (x86)\Branch SDK\include".

b. Add preprocessor definitions

Under C/C++ > Preprocessor, add POCO_STATIC=ON and POCO_NO_AUTOMATIC_LIBS in Preprocessor Defines. Also define DEBUG in Debug builds. All screenshots are for Debug x64 where configurations differ. The command-line flags:
/D "POCO_STATIC=ON" /D "POCO_NO_AUTOMATIC_LIBS" (MD)
/D "DEBUG" /D "POCO_STATIC=ON" /D "POCO_NO_AUTOMATIC_LIBS" (MDd)
/D "POCO_STATIC=ON" /D "POCO_NO_AUTOMATIC_LIBS" /D "POCO_MT=ON" (MT)
/D "DEBUG" /D "POCO_STATIC=ON" /D "POCO_NO_AUTOMATIC_LIBS" /D "POCO_MT=ON (MTd)

c. Verify Runtime Library

We support MD, MDd, MT and MTd runtimes. Under C/C++ > Code Generation, select the appropriate option.

d. Add library paths

Under Linker > General, add the library directory for each combination of configuration and platform to Additional Library Search Paths:

  • Debug MDd:
    C:\Program Files (x86)\Branch SDK\lib\MDd_64
  • Release MD:
    C:\Program Files (x86)\Branch SDK\lib\MD_64
  • Debug MTd:
    C:\Program Files (x86)\Branch SDK\lib\MTd_64
  • Release MT:
    C:\Program Files (x86)\Branch SDK\lib\MT_64

The command-line option for MDd, e.g., is /LIBPATH:"C:\Program Files (x86)\Branch SDK\lib\MDd_64".

Screenshots show an MTd example.

e. Add libraries

Under Linker > Input, add the following (in order) to Additional Dependencies, depending on the configuration:

BranchIOmdd.lib
PocoNetSSLWinmdd.lib
PocoJSONmdd.lib
PocoFoundationmdd.lib
Iphlpapi.lib
crypt32.lib
ws2_32.lib
BranchIOmtd.lib
PocoNetSSLWinmtd.lib
PocoJSONmtd.lib
PocoFoundationmtd.lib
Iphlpapi.lib
crypt32.lib
ws2_32.lib
BranchIOmd.lib
PocoNetSSLWinmd.lib
PocoJSONmd.lib
PocoFoundationmd.lib
Iphlpapi.lib
crypt32.lib
ws2_32.lib
BranchIOmt.lib
PocoNetSSLWinmt.lib
PocoJSONmt.lib
PocoFoundationmt.lib
Iphlpapi.lib
crypt32.lib
ws2_32.lib

Your app should now build and run. See the TestBed apps for reference.

3. Set up URI Redirection

For Win32 apps, Branch uses URI-based redirection. When a user opens a link to a Win32 app, Branch responds with a URI redirect using the custom URI scheme you configured in the Desktop Redirects section of the Branch Dashboard. In order to launch the app from a Branch link, it is necessary to make certain entries in the Registry to open the app from a custom URI scheme. There are two ways to do this.

via MSIX

Microsoft's MSIX packager is used for UWP apps and required for Windows Store publication. If you are using MSIX for your app, you need only add a protocol declaration on the Declarations tab of the appxmanifest. If your URI scheme is myurischeme, this results in a uap:Extension element inserted
into the XML.

<Application Id="App"
    Executable="$targetnametoken$.exe"
    EntryPoint="$targetentrypoint$">
    <!-- ... -->
    <Extensions>
      <uap:Extension Category="windows.protocol">
        <uap:Protocol Name="myurischeme"/>
      </uap:Extension>
    </Extensions>
  </Application>

Now when you build and deploy the project, URI redirects will open the app. See the TestBed and TestBed-Local applications in the repo for examples of using MSIX for URI association.

via Custom Registry Entries

When not using MSIX, it is necessary to make custom Registry entries to associate your URI scheme with your app. If your URI scheme is myurischeme, these entries should be made under HKEY_CURRENT_USER\SOFTWARE\Classes\myurischeme. If your app supports system-wide installation, you may optionally us HKEY_CLASSES_ROOT\myurischeme.

It is recommended to add this Registry key as part of your app's installation. For example, using the Wix packager with the WixUI_InstallDir template:

<!-- Add Registry entries for URI redirection -->
<!-- testbedbasic is the URI scheme from the Branch Dashboard -->
<RegistryKey Root="HKCU" Key="SOFTWARE\Classes\myurischeme" >
  <RegistryValue Value="My Application" Type="string" KeyPath="yes" />
  <RegistryValue Name="URL Protocol" Value="" Type="string" />
  <RegistryKey Key="DefaultIcon">
    <RegistryValue Value="[INSTALLBINFOLDER]MyApp.exe,1" Type="string"/>
  </RegistryKey>
  <RegistryKey Key="shell\open\command">
    <RegistryValue Value='"[INSTALLBINFOLDER]MyApp.exe" "%1"' Type="string"/>
  </RegistryKey>
</RegistryKey>

The (Default) value of the base registry key (HKCU\SOFTWARE\Classes\myurischeme) is a descriptive label. In many cases, it is URL:myurischeme by default.

The shell\open\command key passes the URI to the app as the command-line argument (lpCmdLine). You may also opt to pass the URI in any other way, e.g. using a command-line argument if your apps supports it.

<RegistryKey Key="shell\open\command">
    <RegistryValue
      Value='"[INSTALLBINFOLDER]MyApp.exe" "/uri:%1"' 
      Type="string"/>
  </RegistryKey>

Note that the command-line argument may be received with surrounding quotes that need to be stripped.

Note that when using MSIX, the package automatically sets up a similar Registry structure on installation.

See the TestBed-Basic app in the repo for an example using Wix to create the required Registry keys. These Registry entries cause URIs with the testbedbasic scheme to open the TestBed-Basic app.

4. Initialize Branch

Initialize the Branch SDK

Whenever the app starts, it is necessary to initialize the Branch SDK by calling openSession on the Branch instance returned by Branch::create, passing any URI received at the command line as the first argument to Branch::create. If no URI is received, pass a blank string. Because initialization involves an API call, it is recommended to initialize the Branch SDK at the earliest opportunity from wWinMain.

Create a callback object

All asynchronous Branch SDK methods require a callback object implementing the BranchIO::IRequestCallback interface. This interface has three methods: onSuccess, onStatus and onError. Either onSuccess or onError is guaranteed to called exactly once. The onStatus method may or may not be called one or more times before onSuccess or onError. Link data and other results are returned to the caller in the second argument to onSuccess, a BranchIO::JSONObject.

The caller manages the lifetime of the request callback. It's only necessary to guarantee that the callback object live until onSuccess or onError is called. In the examples here and in the repo, the callbacks delete themselves at the end of each of these methods.

#include <BranchIO/Branch.h>

using namespace BranchIO;
using namespace std;

struct MyCallback: public virtual BranchIO::IRequestCallback {
    void onSuccess(int id, JSONObject payload) {
        // onSuccess or onError guaranteed to be called exactly once
        done();
    }
    
    void onStatus(int id, int code, string message) {
        // onStatus may be called one or more times before onStatus or onError
    }
    
    void onError(int id, int code, string message) {
        // onSuccess or onError guaranteed to be called exactly once
        done();
    }
    
private:
    void done() {
        delete this;
    }
};

Create a Branch instance and initialize a Branch session

Create an instance of a callback object, as above, for use with the Branch::openSession method. This callback will receive any link data when the app is opened from a link.

#include <BranchIO/Branch.h>

using namespace BranchIO;
using namespace std;

struct MyCallback : public virtual IRequestCallback;

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);

    // ----- Initialize the Branch SDK
    AppInfo appInfo;
    appInfo.setAppVersion("1.0");

    auto* branch = Branch::create(branchKey, &appInfo);

    wstring cmdLineArg(lpCmdLine ? lpCmdLine : L"");
    wstring uriScheme(L"myurischeme");
    if (cmdLineArg[0] == '"') {
        // Strip off any leading and trailing quotes
        cmdLineArg = cmdLineArg.substr(1, cmdLineArg.length() - 2);
    }
    wstring::size_type prefixLength = min(uriScheme.length(), cmdLineArg.length());
    wstring prefix = cmdLineArg.substr(0, prefixLength);
    if (prefix == uriScheme)
    {
        // Open any URI passed at the command line
        uriToOpen = cmdLineArg;
    }

    auto* myCallback = new MyCallback;
    branch->openSession(uriToOpen, myCallback);

    // ----- Continue with normal Win32 startup
    MyRegisterClass(hInstance);

Receive the URI from the Command line

Note that this entire process is very similar to associating a file extension with an app via the Registry.

The Registry association via shell\open\command passes the URI to the app as a command-line argument. This is passed to the app as lpCmdLine, the third parameter in wWinMain.

The URI received from Windows will have the form myurischeme://open?link_click_id=xxxx, where xxxx is a 64-bit integer. This link_click_id parameter is used to correlate the action of opening the app with the link in the browser.

Note that opening a session with Branch will notify the app of any deferred deep links in the callback.
If a URI was passed to openSession, link data will be passed to the onSuccess method in myCallback.

Important: It is necessary to call openSession to initialize the SDK even if a URI is not received.
Passing a blank string initializes the SDK and records an open event.

Configuring

  • You may configure your app with these classes:
    BranchIO::AppInfo - Application Information, such as version and language.
    BranchIO::AdvertiserInfo - Advertiser Information, including the ability to disable tracking.
    Any information you provide here is included in events transmitted to Branch.

Example to disable ad tracking:

_branchInstance->getAdvertiserInfo().disableTracking();

Handle Multiple App Instances

Whenever a Branch link is opened, the URI redirect will always result in Windows starting a new instance of the application and passing the URI at launch via the lpCmdLine parameter. This is true not only when opened from a URI, but any time the application is launched from a shortcut, e.g. in the Start menu. It is common practice to enforce a single running instance of an application via a shared mutex or similar IPC mechanism. In this case, it is necessary to forward the URI to the running instance. A common approach is to use a WM_COPYDATA message. Pass the URI to the running app in a message before exiting.

Important notes:

  • It is necessary to call openSession from the running instance, not from the ephemeral instance that exits after transmitting the URI.
  • It is necessary to call openSession from the running instance even when no URI is received by the ephemeral instance, e.g. when it is launched from the Start menu. A blank string should be forwarded in a WM_COPYDATA message in order that the running instance records an open event. This is necessary for accurate analytics.
  • It's recommended to call SetForegroundWindow to bring the running instance to the foreground when a link is opened.

Did this page help you?