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);
//監(jiān)聽SDK日志輸出回調
AsyncRequest("WYRegisterListener", "{\"tag\": \"onOutputLog\"}", [](WYResponse value) {
printf("%s\n", value.content);
});
//監(jiān)聽SDK錯誤回調
AsyncRequest("WYRegisterListener", "{\"tag\": \"onErrorCode\"}", [](WYResponse value) {
int code = value.code;
if (code == 200 || code == 201 || code == 202) {
//建議: 最好去重啟進程, 如果不想重啟進程,可以去重新加載WYSDK.dll,但進程里會存留一些垃圾靜態(tài)實例
}
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);
}
}
//取消監(jiān)聽SDK錯誤回調
WYResponse unErrorValue = SyncRequest("WYUnregisterListener", "{\"tag\": \"onErrorCode\"}");
FreeResponse(unErrorValue);
//取消監(jiān)聽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);
//加載動態(tài)庫,動態(tài)庫的生命周期需要跟著進程生命周期走
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");
// 獲取函數(shù)指針
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);
}
//不建議 卸載動態(tài)鏈接庫
//FreeLibrary(hModule);
//printf("finished to free SDK dll\n");
return 0;
}
二、支持功能列表
功能 | 說明 |
打開云電腦 | 支持 |
斷開云電腦 | 支持 |
打開云應用 | 支持 |
斷開云應用 | 支持 |
設置語言(中文/英文) | 支持 連接云電腦配置 |
設置縮放比 | 支持 連接云電腦配置 |
隱藏懸浮球 | 支持 連接云電腦配置 |
是否全屏 | 支持 連接云電腦配置 |
獲取設備uuid | 支持 |
獲取SDK版本號 | 支持 |
獲取所有云電腦信息 | 支持 |
監(jiān)聽SDK錯誤回調 | 支持 |
監(jiān)聽云電腦/云應用連接狀態(tài) | 支持 |
喚出問題反饋界面 | 支持 |
喚出版本升級界面 | 支持 |
三、WYSDK的API
1、API簡介
WYSyncRequest
同步執(zhí)行請求
WYResponse WYSyncRequest(constchar* key, constchar* params);
參數(shù):
key: 請求唯一標識
params:請求參數(shù)
返回值:
請求結構,結構如下
struct WYResponse {
int code;
const char* content;
const char* requestKey;
const char* requestParams;
};
code為0,表示請求成功,content為請求到的數(shù)據(jù),requestKey和requestParams為請求入?yún)⒌膋ey和params。
注意:返回值在使用完成的時候,調用WYFreeResponse去釋放WYResponse內存。
WYAsyncRequest
void WYAsyncRequest(constchar* key, constchar* params, void (*callback)(WYResponse));
異步執(zhí)行請求,(備注:異步請求接口支持所有同步執(zhí)行的key)
參數(shù):
key: 請求唯一標識
params:請求參數(shù)
callback:異常執(zhí)行的回調
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接口,參數(shù)為"WYInitialize"和配置信息。
配置信息,格式為JSON,其中必現(xiàn)包含partnerInfo和launcherPath相關信息
partnerInfo:調用無影服務的合作方的信息
○ partner:合作方公司名稱
○ partnerApp:合作方應用的名稱
launcherPath:為stream_launcher.exe的絕對路徑
返回值:value.code 0-成功 其他值-失敗
釋放SDK
WYResponse value = SyncRequest("WYUninitialize", NULL);
FreeResponse(value);
調用WYSyncRequest接口,參數(shù)為"WYUninitialize"。
返回值:value.code 0-成功 其他值-失敗
運行SDK
WYResponse value = SyncRequest("WYRun", NULL);
FreeResponse(value);
調用WYSyncRequest接口,參數(shù)為"WYRun"。
返回值:value.code 0-成功 其他值-失敗
監(jiān)聽日志輸出Listener
AsyncRequest("WYRegisterListener", "{\"tag\": \"onOutputLog\"}", [](WYResponse value) {
printf("%s\n", value.content);
});
調用WYAsyncRequest接口,參數(shù)為"WYRegisterListener"、Listener參數(shù)(JSON結構)和執(zhí)行回調。
Listener參數(shù):tag唯一標識,狀態(tài)回調唯一標識為:onOutputLog。
監(jiān)聽錯誤回調Listener
AsyncRequest("WYRegisterListener", "{\"tag\": \"onErrorCode\"}", [](WYResponse value) {
int code = value.code;
if (code == 200 || code == 201 || code == 202) {
//建議: 最好去重啟進程, 如果不想重啟進程,可以去重新加載WYSDK.dll,但進程里會存留一些垃圾靜態(tài)實例
}
printf("Listener callback tag %s value %d\n", value.requestParams, code);
});
調用WYAsyncRequest接口,參數(shù)為"WYRegisterListener"、Listener參數(shù)(JSON結構)和執(zhí)行回調。
Listener參數(shù):tag唯一標識,狀態(tài)回調唯一標識為:"onErrorCode"。
監(jiān)聽云電腦/云應用狀態(tài)回調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接口,參數(shù)為"WYRegisterListener"、Listener參數(shù)(JSON結構)和執(zhí)行回調。
Listener參數(shù):tag唯一標識,狀態(tài)回調唯一標識為:onStatusChange,connectId云電腦/云應用的ID
取消監(jiān)聽Listener
std::string unListenerParams = std::string("{\"tag\": \"onStatusChange\", \"connectId\":\"");
unListenerParams.append(desktopId);
unListenerParams.append("\"}");
WYResponse unStatusValue = SyncRequest("WYUnregisterListener", unListenerParams.c_str());
FreeResponse(unStatusValue);
調用WYAsyncRequest接口,參數(shù)為"WYUnregisterListener"、unlistener參數(shù)(JSON結構)。unlistener參數(shù)與listener對應。
創(chuàng)建連接云電腦/云應用
std::string connectParams = “{***}”;
WYResponse createConnectValue = SyncRequest("WYCreateConnect", connectParams.c_str());
FreeResponse(createConnectValue);
調用WYSyncRequest接口,參數(shù)為"WYCreateConnect"和連接參數(shù)。
連接參數(shù):是一個JSON結構,格式如下
名稱 | 類型 | 必填 | 描述 | 示例 |
operation | String | 是 | 操作名稱 | openDesktop-打開云電腦 openApp-打開云應用 |
| object | 是 | 透傳從無影服務端獲取到的連接信息 | |
-- ticket | String | 是 | ticket為調用無影開放平臺GetConnectionTicket - 獲取連接憑證接口返回的ticket信息 | |
-- desktopId | String | 是 | 云電腦Id | ecd-7nvbz4ccjly95xxxx |
-- desktopName | String | 是 | 云電腦名稱 | xx的云電腦 |
-- osType | String | 是 | 系統(tǒng)類型 | windows |
| object | 是 | 調用無影服務的合作方的信息 | |
-- partner | String | 是 | 合作方公司名稱 | xxx公司 |
-- partnerApp | String | 是 | 合作方應用的名稱 | xxx應用 |
| object | 否 | 擴展字段,外部配置參數(shù) | |
-- language | String | 否 | 設置一次后所有打開的窗口UI都會顯示設置的語言(未設置時默認為中文) | “zh” --- 中文 “en” --- 英文 |
-- cloudDpi | int | 否 | 云電腦啟動時的默認dpi縮放比例
| 100 |
-- fullscreen | bool | 否 | 是否在全屏窗口狀態(tài)打開云資源
| true |
-- isVpc | bool | 否 | 是否走vpc網(wǎng)絡
| false |
-- hideFloatingBall | bool | 否 | 是否隱藏云資源窗口內的懸浮球
| false |
| object | 否 | 網(wǎng)絡代理配置 | |
-- type | String | 否 | 代理類型 目前支持系統(tǒng)代理、socket代理、HTTP代理3種方式 | system - 系統(tǒng)代理 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接口,參數(shù)為"WYDisconnect"和斷連參數(shù)。
斷連參數(shù):是一個JSON結構,包含connectId
connectId: 使用云電腦Id或云應用id
遍歷獲取所有的云電腦/云應用信息
WYResponse value = SyncRequest("enumConnections", NULL);
printf("WYEnumerateConnectInfo info %s\n", value.content);
FreeResponse(value);
調用WYSyncRequest接口,參數(shù)為“enumConnections”。
獲取結果value.content是一個JSON結構,
{
"connection" : "***",
"connectionPid" : 27324, //云電腦的進程
"partner" : "***",
"partnerApp" : "***",
"processId" : 8696,
"status" : "start" //狀態(tài)值包含start和exit
}
獲取設備的UUID
WYResponse value = SyncRequest("uuid", NULL);
printf("uuid is %s\n", value.content);
FreeResponse(value);
調用WYSyncRequest接口,參數(shù)為“uuid”。
獲取SDK版本號
WYResponse value = SyncRequest("sdkVersion", NULL);
printf("version is %s\n", value.content);
FreeResponse(value);
調用WYSyncRequest接口,參數(shù)為"sdkVersion"。
喚出問題反饋面板
WYResponse value = WYSyncRequest("feedback", NULL);
FreeResponse(value);
調用WYSyncRequest接口,參數(shù)為"feedback"。
喚出版本升級面板
WYResponse value = WYSyncRequest("upgrade", NULL);
FreeResponse(value);
調用WYSyncRequest接口,參數(shù)為"upgrade"。
四、錯誤碼列表
錯誤碼 | 說明 |
1 | SDK初始化失敗 |
2 | SDK配置沒有無影服務的合作方的信息 |
100 | 打開Launcher失敗 |
101 | Launcher初始化失敗 |
200 | 獲取通信service失敗(需要殺進程/卸載SDK,重新加載) |
201 | 通信service中斷(需要殺進程/卸載SDK,重新加載) |
202 | 創(chuàng)建通信service失敗(需要殺進程/卸載SDK,重新加載) |
300 | 連接云電腦/云應用參數(shù)無效 |
301 | 連接云電腦/云應用失敗 |