Windows插件化连接管理SDK

一、无影开放SDK集成与使用

1、要求

客户端要求

WYSDK API是无影云电脑应用程序的Windows安装包的一部分。在客户端设备上安装适用于Windows的无影云电脑应用程序。确保WYSDK.dll(适用于64位机器)存在于无影云电脑应用程序的Windows安装文件夹中。

服务器端要求

WYSDK要求在客户端和服务器之间完成登录、选择云电脑和获取Ticket。无影服务端开放平台对接请参考API概览

2、使用SDK的步骤

  • 验证WYSDK二进制文件是否安装在客户端上(WYSDK.dll)。

  • 从安装路径加载WYSDK.DLL。在这里,客户端可以使用无影云电脑应用程序注册表项来获取WYSDK二进制文件的安装路径。

  • HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{D19D277C-E465-4F61-A725-231B7DB9255D}_is1是安装的注册表路径。

  • 使用LoadLibrary加载WYSDK.DLL。

  • 使用GetProcAddress获取API并相应使用,但要使用API,需要初始化SDK。

3、获取WYSDK.DLL路径

通过注册获取到无影云电脑的安装目录

std::string getDicOfSDK() {
    HKEY hkey = nullptr;
    std::string sub_key = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{D19D277C-E465-4F61-A725-231B7DB9255D}_is1";
    LSTATUS res = ::RegOpenKeyExA(HKEY_LOCAL_MACHINE, sub_key.c_str(), 0, KEY_READ, &hkey);
    if (res != ERROR_SUCCESS) {
        printf("RegOpenKeyExA status:%d failed: %d\n", res, GetLastError());
        return "";
    }
    std::string valueName = std::string("InstallLocation");
    DWORD dwType = REG_SZ;
    DWORD dwSize = 0;
    LSTATUS ret = RegQueryValueExA(hkey, valueName.c_str(), NULL, &dwType, nullptr, &dwSize);
    if (ret != ERROR_SUCCESS || dwSize <= 0) {
        printf("RegQueryValueExA status:%d size:%d valueName:%s failed: %d\n", ret, dwSize, valueName.c_str(), GetLastError());
        RegCloseKey(hkey);
        return "";
    }

    std::vector<BYTE> value_data(dwSize);
    ret = RegQueryValueExA(hkey, valueName.c_str(), NULL, &dwType, value_data.data(), &dwSize);
    if (ret != ERROR_SUCCESS) {
        printf("RegQueryValueExA status:%d valueName:%s failed: %d\n", ret, valueName.c_str(), GetLastError());
        RegCloseKey(hkey);
        return "";
    }

    RegCloseKey(hkey);
    std::string path(value_data.begin(), value_data.end());
    if (path.back() == '\0') {
        path.pop_back();
    }
    path.append(std::string("bin"));

    printf("RegQueryValueExA11 valueName:%s res: %s\n", valueName.c_str(), path.c_str());
    return path;
}

4、Demo

示例代码:WYSDKDemo.zip

#include <iostream>
#include <windows.h>
#include <string>
#include <vector>
#include <thread>
#include <chrono>

struct WYResponse {
    int code;
    const char* content;
    const char* requestKey;
    const char* requestParams;
};

typedef WYResponse(*WYSyncRequest)(const char* key, const char* params);
typedef void (*WYAsyncRequest)(const char* key, const char* params, void (*callback)(WYResponse));
typedef void (*WYFreeResponse)(WYResponse& response);

WYSyncRequest SyncRequest;
WYAsyncRequest AsyncRequest;
WYFreeResponse FreeResponse;

std::string getConnectParams(const std::string& desktopId, const std::string& desktopName, const std::string& ticket) {
    std::string operationInfo = std::string("\"operation\":\"openDesktop\"");
    std::string bizParams = std::string("\"bizParam\":{\"ticket\":\"") + ticket + std::string("\",\"desktopId\":\"") + desktopId + std::string("\",\"desktopName\":\"") + desktopName + std::string("\"}");
    std::string extInfo = std::string("\"extInfo\":{\"language\":\"zh\",\"fullscreen\":false,\"hideFloatingBall\":false,\"cloudDpi\":150}");
    std::string connectParams = "";
    connectParams.append(std::string("{"));
    connectParams.append(operationInfo);
    connectParams.append(std::string(","));
    connectParams.append(bizParams);
    connectParams.append(std::string(","));
    connectParams.append(extInfo);
    connectParams.append(std::string("}"));
    return connectParams;
}

std::string getCloudAppConnectParams(const std::string& desktopId, const std::string& desktopName, const std::string& ticket) {
    std::string operationInfo = std::string("\"operation\":\"openApp\"");
    std::string bizParams = std::string("\"bizParam\":{\"ticket\":\"") + ticket + std::string("\",\"appInstanceGroupId\":\"") + desktopId + std::string("\",\"appName\":\"") + desktopName + std::string("\",") + std::string("\"osType\":\"Windows\"}");
    std::string connectParams = "";
    connectParams.append(std::string("{"));
    connectParams.append(operationInfo);
    connectParams.append(std::string(","));
    connectParams.append(bizParams);
    connectParams.append(std::string("}"));
    return connectParams;
}

void connectDesktop(bool isCloudApp, const std::string& desktopId, const std::string& desktopName, const std::string& ticket) {
    std::string listenerParams = std::string("{\"tag\": \"onStatusChange\", \"connectId\":\"");
    listenerParams.append(desktopId);
    listenerParams.append("\"}");
    AsyncRequest("WYRegisterListener", listenerParams.c_str(), [](WYResponse value) {
        printf("Listener callback value %s\n", value.content);
    });

    std::string connectParams = isCloudApp ? getCloudAppConnectParams(desktopId, desktopName, ticket) : getConnectParams(desktopId, desktopName, ticket);
    WYResponse createConnectValue = SyncRequest("WYCreateConnect", connectParams.c_str());
    FreeResponse(createConnectValue);
}

void disconnectDesktop(const std::string& desktopId) {
    std::string disparams = std::string("{\"connectId\":\"");
    disparams.append(desktopId);
    disparams.append("\"}");
    WYResponse disConnectValue = SyncRequest("WYDisconnect", disparams.c_str());
    FreeResponse(disConnectValue);

    std::string unListenerParams = std::string("{\"tag\": \"onStatusChange\", \"connectId\":\"");
    unListenerParams.append(desktopId);
    unListenerParams.append("\"}");
    WYResponse unStatusValue = SyncRequest("WYUnregisterListener", unListenerParams.c_str());
    FreeResponse(unStatusValue);
}

void toEnumerateConnectInfo() {
    WYResponse infoListValue = SyncRequest("enumConnections", NULL);
    printf("WYEnumerateConnectInfo info %s\n", infoListValue.content);
    FreeResponse(infoListValue);
}

void toGetUUID() {
    WYResponse uuidValue = SyncRequest("uuid", NULL);
    printf("uuid is %s\n", uuidValue.content);
    FreeResponse(uuidValue);
}

int runSDK(const std::string& directory) {
    //初始化SDK
    std::string config = std::string("{\"partnerInfo\":{\"partner\":\"wuyingPartnerTest\",\"partnerApp\":\"WuyingPartnerTestApp\"}, \"launcherPath\":\"");
    config.append(directory);
    config.append(std::string("\\stream_launcher.exe"));
    config.append(std::string("\"}"));
    WYResponse initValue = SyncRequest("WYInitialize", config.c_str());
    FreeResponse(initValue);
    //监听SDK日志输出回调
    AsyncRequest("WYRegisterListener", "{\"tag\": \"onOutputLog\"}", [](WYResponse value) {
        printf("%s\n", value.content);
    });
    //监听SDK错误回调
    AsyncRequest("WYRegisterListener", "{\"tag\": \"onErrorCode\"}", [](WYResponse value) {
        int code = value.code;
        if (code == 200 || code == 201 || code == 202) {
            //建议: 最好去重启进程, 如果不想重启进程,可以去重新加载WYSDK.dll,但进程里会存留一些垃圾静态实例
        }
        printf("Listener callback tag %s value %d\n", value.requestParams, code);
    });
    //运行SDK
    WYResponse runValue = SyncRequest("WYRun", NULL);
    int runCode = runValue.code;
    FreeResponse(runValue);
    printf("sdk run %s\n", runCode == 0 ? "ok" : "fail");
    if (runCode == 0) {
        toGetUUID();
        // 唤出问题反馈
        //WYResponse feedbackValue = WYSyncRequest("feedback", NULL);
        //FreeResponse(feedbackValue);
        // 唤出版本升级
        //WYResponse upgradeValue = WYSyncRequest("upgrade", NULL);
        //FreeResponse(upgradeValue);

        std::string desktopId = std::string("ecd-iolwxthr1vcpoefhp");
        std::string desktopName = std::string("wenzhang-20220927");
        std::string desktopTicket = std::string("*****");
        
        std::string appInstanceGroupId = std::string("ca-6p9vj2m79tlwcsl8z");
        std::string appName = std::string("Chrome");
        std::string appTicket = std::string("****");

        for (int i = 0; i < 1; i++) {
            //连接云电脑/云应用
            connectDesktop(false, desktopId, desktopName, desktopTicket);
            //connectDesktop(true, appInstanceGroupId, appName, appTicket);
            std::this_thread::sleep_for(std::chrono::seconds(5));

            toEnumerateConnectInfo();

            //断开云电脑/云应用
            disconnectDesktop(desktopId);
            //std::this_thread::sleep_for(std::chrono::seconds(2));
            //disconnectDesktop(appInstanceGroupId);
        }
    }
    //取消监听SDK错误回调
    WYResponse unErrorValue = SyncRequest("WYUnregisterListener", "{\"tag\": \"onErrorCode\"}");
    FreeResponse(unErrorValue);
    //取消监听SDK日志输出回调
    WYResponse unLogValue = SyncRequest("WYUnregisterListener", "{\"tag\": \"onOutputLog\"}");
    FreeResponse(unLogValue);
    //销毁SDK
    WYResponse unInitValue = SyncRequest("WYUninitialize", NULL);
    FreeResponse(unInitValue);
    printf("\n\n\n\n\n");
    return 0;
}

int main() {
    printf("begin to load SDK dll \n");
    std::string directory = "D:\\Code\\20230731\\output\\windows_amd64_release\\target\\bin";// getDicOfSDK();
    std::wstring wideDirectory(directory.begin(), directory.end());
    LPCWSTR widePath = wideDirectory.c_str();
    SetDllDirectory(widePath);
    //加载动态库,动态库的生命周期需要跟着进程生命周期走
    HMODULE hModule = LoadLibrary(L"WYSDK.dll");
    if (hModule == NULL) {
        std::cout << "Failed to load DLL" << GetLastError() << std::endl;
        return 1;
    }
    printf("load SDK dll success \n");
    printf("begin to load function \n");
    // 获取函数指针
    SyncRequest = (WYSyncRequest)GetProcAddress(hModule, "WYSyncRequest");
    AsyncRequest = (WYAsyncRequest)GetProcAddress(hModule, "WYAsyncRequest");
    FreeResponse = (WYFreeResponse)GetProcAddress(hModule, "WYFreeResponse");
    if (!SyncRequest || !AsyncRequest || !FreeResponse) {
        printf("load function fail \n");
        return 1;
    }
    printf("load function success \n");
    for (int  i = 0; i < 1; i++)
    {
        runSDK(directory);
    }
    //不建议 卸载动态链接库
    //FreeLibrary(hModule);
    //printf("finished to free SDK dll\n");
    return 0;
}

二、支持功能列表

功能

说明

打开云电脑

支持

断开云电脑

支持

打开云应用

支持

断开云应用

支持

设置语言(中文/英文)

支持 连接云电脑配置

设置缩放比

支持 连接云电脑配置

隐藏悬浮球

支持 连接云电脑配置

是否全屏

支持 连接云电脑配置

获取设备uuid

支持

获取SDK版本号

支持

获取所有云电脑信息

支持

监听SDK错误回调

支持

监听云电脑/云应用连接状态

支持

唤出问题反馈界面

支持

唤出版本升级界面

支持

三、WYSDK的API

1、API简介

WYSyncRequest

同步执行请求

WYResponse WYSyncRequest(constchar* key, constchar* params); 
  • 参数:

key: 请求唯一标识

params:请求参数

  • 返回值:

请求结构,结构如下

struct WYResponse {
    int code;
    const char* content;
    const char* requestKey;
    const char* requestParams;
};

code为0,表示请求成功,content为请求到的数据,requestKey和requestParams为请求入参的key和params。

重要

注意:返回值在使用完成的时候,调用WYFreeResponse去释放WYResponse内存。

WYAsyncRequest

void WYAsyncRequest(constchar* key, constchar* params, void (*callback)(WYResponse)); 

异步执行请求,(备注:异步请求接口支持所有同步执行的key)

  • 参数:

key: 请求唯一标识

params:请求参数

callback:异常执行的回调

WYFreeResponse

void WYFreeResponse(WYResponse& response); 

释放请求到的reponse对象

2、API使用

初始化SDK

std::string directory = getDicOfSDK();
std::string config = std::string("{\"partnerInfo\":{\"partner\":\"***\",\"partnerApp\":\"***\"}, \"launcherPath\":\"");
config.append(directory);
config.append(std::string("\\stream_launcher.exe"));
config.append(std::string("\"}"));
WYResponse value = SyncRequest("WYInitialize", config.c_str());
FreeResponse(value);

调用WYSyncRequest接口,参数为"WYInitialize"和配置信息。

配置信息,格式为JSON,其中必现包含partnerInfo和launcherPath相关信息

partnerInfo:调用无影服务的合作方的信息

○ partner:合作方公司名称

○ partnerApp:合作方应用的名称

launcherPath:为stream_launcher.exe的绝对路径

返回值:value.code 0-成功 其他值-失败

释放SDK

WYResponse value = SyncRequest("WYUninitialize", NULL);
FreeResponse(value);

调用WYSyncRequest接口,参数为"WYUninitialize"。

返回值:value.code 0-成功 其他值-失败

运行SDK

WYResponse value = SyncRequest("WYRun", NULL);
FreeResponse(value);

调用WYSyncRequest接口,参数为"WYRun"。

返回值:value.code 0-成功 其他值-失败

监听日志输出Listener

AsyncRequest("WYRegisterListener", "{\"tag\": \"onOutputLog\"}", [](WYResponse value) {
     printf("%s\n", value.content);
});

调用WYAsyncRequest接口,参数为"WYRegisterListener"、Listener参数(JSON结构)和执行回调。

Listener参数:tag唯一标识,状态回调唯一标识为:onOutputLog。

监听错误回调Listener

AsyncRequest("WYRegisterListener", "{\"tag\": \"onErrorCode\"}", [](WYResponse value) {
     int code = value.code;
     if (code == 200 || code == 201 || code == 202) {
         //建议: 最好去重启进程, 如果不想重启进程,可以去重新加载WYSDK.dll,但进程里会存留一些垃圾静态实例
     }
     printf("Listener callback tag %s value %d\n", value.requestParams, code);
});

调用WYAsyncRequest接口,参数为"WYRegisterListener"、Listener参数(JSON结构)和执行回调。

Listener参数:tag唯一标识,状态回调唯一标识为:"onErrorCode"。

监听云电脑/云应用状态回调Listener

std::string listenerParams = std::string("{\"tag\": \"onStatusChange\", \"connectId\":\"");
listenerParams.append(desktopId);
listenerParams.append("\"}");
AsyncRequest("WYRegisterListener", listenerParams.c_str(), [](WYResponse value) {
    printf("Listener callback value %s\n", value.content);
});

调用WYAsyncRequest接口,参数为"WYRegisterListener"、Listener参数(JSON结构)和执行回调。

Listener参数:tag唯一标识,状态回调唯一标识为:onStatusChange,connectId云电脑/云应用的ID

取消监听Listener

std::string unListenerParams = std::string("{\"tag\": \"onStatusChange\", \"connectId\":\"");
unListenerParams.append(desktopId);
unListenerParams.append("\"}");
WYResponse unStatusValue = SyncRequest("WYUnregisterListener", unListenerParams.c_str());
FreeResponse(unStatusValue);

调用WYAsyncRequest接口,参数为"WYUnregisterListener"、unlistener参数(JSON结构)。unlistener参数与listener对应。

创建连接云电脑/云应用

std::string connectParams = “{***}”;
WYResponse createConnectValue = SyncRequest("WYCreateConnect", connectParams.c_str());
FreeResponse(createConnectValue);

调用WYSyncRequest接口,参数为"WYCreateConnect"和连接参数。

连接参数:是一个JSON结构,格式如下

名称

类型

必填

描述

示例

operation

String

操作名称

openDesktop-打开云电脑

openApp-打开云应用

  • bizParam

object

透传从无影服务端获取到的连接信息

-- ticket

String

ticket为调用无影开放平台GetConnectionTicket - 获取连接凭证接口返回的ticket信息

-- desktopId

String

云电脑Id

GetConnectionTicket - 获取连接凭证 返回

ecd-7nvbz4ccjly95xxxx

-- desktopName

String

云电脑名称

GetConnectionTicket - 获取连接凭证 返回

xx的云电脑

-- osType

String

系统类型

GetConnectionTicket - 获取连接凭证 返回

windows

  • partnerInfo

object

调用无影服务的合作方的信息

-- partner

String

合作方公司名称

xxx公司

-- partnerApp

String

合作方应用的名称

xxx应用

  • extInfo

object

扩展字段,外部配置参数

-- language

String

设置一次后所有打开的窗口UI都会显示设置的语言(未设置时默认为中文)

“zh” --- 中文

“en” --- 英文

-- cloudDpi

int

云电脑启动时的默认dpi缩放比例

  • 仅限启动Windows系统云电脑时设置。若非有特殊需求,本参数无需设置

  • 参数为100-200之间的整数,例如150代表缩放比例150%

100

-- fullscreen

bool

是否在全屏窗口状态打开云资源

  • 仅限云资源打开请求时设置。若非有特殊需求,本参数无需设置

true

-- isVpc

bool

是否走vpc网络

  • 仅限云资源打开请求时设置。若非有特殊需求,本参数无需设置

false

-- hideFloatingBall

bool

是否隐藏云资源窗口内的悬浮球

  • 仅限云资源打开请求时设置。若非有特殊需求,本参数无需设置

false

  • proxy

object

网络代理配置

-- type

String

代理类型

目前支持系统代理、socket代理、HTTP代理3种方式

system - 系统代理

socket - socket代理

HTTP - HTTP代理

-- host

String

代理的host

127.x.x.x

-- port

int

代理端口号

50550

-- account

String

代理账号

-- password

String

代理密码

示例

JSON格式

{
  "operation": "openDesktop",
  "bizParam": {
    "ticket": "xxxxxxxxxxxxxxxxxxxxxxx",
    "desktopId": "ecd-7nvbz4ccjly95xxxx",
    "desktopName": "xx的云电脑",
    "osType": "windows"
  },
  "partnerInfo": {
    "partner": "wuyingPartnerTest",
    "partnerApp": "WuyingPartnerTestApp"
  },
  "extInfo": {
    "language": "zh",
    "cloudDpi": 150,
    "fullscreen": true,
    "isVpc": true,
    "hideFloatingBall": true,
    "proxy": {
      "type": "system",
      "host": "xxxxxx",
      "port": 1111,
      "account": "xxxx",
      "password": "xxx"
    }
  }

断开连接云电脑/云应用

WYResponse disConnectValue = SyncRequest("WYDisconnect", "{\"connectId\":\"***\"}");
FreeResponse(disConnectValue);

调用WYSyncRequest接口,参数为"WYDisconnect"和断连参数。

断连参数:是一个JSON结构,包含connectId

connectId: 使用云电脑Id或云应用id

遍历获取所有的云电脑/云应用信息

WYResponse value = SyncRequest("enumConnections", NULL);
printf("WYEnumerateConnectInfo info %s\n", value.content);
FreeResponse(value);

调用WYSyncRequest接口,参数为“enumConnections”。

获取结果value.content是一个JSON结构,

{
        "connection" : "***",
        "connectionPid" : 27324, //云电脑的进程
        "partner" : "***",
        "partnerApp" : "***",
        "processId" : 8696,
        "status" : "start" //状态值包含start和exit
}

获取设备的UUID

WYResponse value = SyncRequest("uuid", NULL);
printf("uuid is %s\n", value.content);
FreeResponse(value);

调用WYSyncRequest接口,参数为“uuid”。

获取SDK版本号

WYResponse value = SyncRequest("sdkVersion", NULL);
printf("version is %s\n", value.content);
FreeResponse(value);

调用WYSyncRequest接口,参数为"sdkVersion"。

唤出问题反馈面板

WYResponse value = WYSyncRequest("feedback", NULL);
FreeResponse(value);

调用WYSyncRequest接口,参数为"feedback"。

唤出版本升级面板

WYResponse value = WYSyncRequest("upgrade", NULL);
FreeResponse(value);

调用WYSyncRequest接口,参数为"upgrade"。

四、错误码列表

错误码

说明

1

SDK初始化失败

2

SDK配置没有无影服务的合作方的信息

100

打开Launcher失败

101

Launcher初始化失败

200

获取通信service失败(需要杀进程/卸载SDK,重新加载)

201

通信service中断(需要杀进程/卸载SDK,重新加载)

202

创建通信service失败(需要杀进程/卸载SDK,重新加载)

300

连接云电脑/云应用参数无效

301

连接云电脑/云应用失败