前言
公司业务需要,PC端,移动端都用到了第三方 网易云信 IM 来实现在线客服咨询。
在这当中难免遇到一些需求是网易云信没有提供,需要自行编码进行扩展的。写此篇文章的目的正是因业务需要,需要在网易云信的基础上进行消息类型的扩展。
此篇文章里的代码是基于 网易云信 NIM_PC_Demo_x86_x64_v4.6.0 版 进行修改的。
开发环境
下载VS2013 update5版本根据网易云信官网 NIM Windows(PC) Demo导读 需要安装相应的环境
- Visual Studio 2013 Update5(必须使用Update5版本) vs版本下载见下图
我在安装完此环境后依然运行不起来(由于我是c++小白,环境搭建花了点时间),然后网上各种找原因,终于找到解决方案,安装 Windows SDK 8.1 ,然后终于才把下载过来的项目跑起来。(出现问题时忘记截图了,抱歉!)
业务需要
如下图所示的消息类型
带标题图片和说明且点击打开浏览器跳转相关网页的消息标题是PC版,可想而知,肯定还有其他如 Android版,iOS版,Web版等,不可能此类型的消息(我称它为图文消息
)只支持PC,而在iOS,Android或Web端无法显示问题。以下附上其他版本扩展的链接
正文
- 将demo运行起来后,首先我们要修改的就是将 appkey 改为自己的。
我这里说下VS里如何运行C++项目(可能不是标准的姿势)。
设置完成后,以后运行项目就可以直接按F5或者点击本地Windows调试器启动项目,不用每次都去点击 nim_demo右键->调试->启动新实例
启动项目直接点击或者按F5启动如果你不嫌麻烦的话,也可以按以下方式启动
vs里选中nim_demo右键 -> 调试 -> 启动新实例 停止调试在菜单栏或者按Shift+F5
- 运行没有问题后,修改以下几个文件配置,将demo修改为自己所用。
- 修改
shared/util.h
中的DEMO_GLOBAL_APP_KEY
和DEMO_GLOBAL_TEST_APP_KEY
,填入自己的appKey
- 修改
修改后点击 停止调试
,然后在 启动新实例
,启动后再使用自己的云信帐号帐号登录
3.替换图片
替换主要与logo有关的图片
bin/themes/default/login/login.png
,
bin/themes/default/about/login.png
,
bin/themes/default/main/duoduan01.png
4.注释掉我们不需要的功能 (我这里只注释关键代码,以防哪天需要此功能时放开注释就好了)
- 登录页 去除 注册 和 免登录,体验匿名聊天室功能。
打开登录界面的布局文件 bin/themes/default/login/login.xml
,
找到name值为register_account的Button节点,添加属性 visible="false",将此按钮隐藏。
找到name值为anonymous_chatroom的Button节点,添加属性 visible="false",将此按钮隐藏。
上面两步操作完成后,点击运行新实例,发现帐号输入框无法获得焦点,解决办法是在底下的 VBox name
为enter_panel
的margin
属性上添加name
值为register_account
的Button
的高 15,修改前的margin
值为20,0,20,0
,修改后的margin
值为20,15,20,0
,目的是让注册按钮隐藏后,表单距顶部的高度不变。
//...
<Button name="register_account" margin="0,5,15,10" halign="right" textid="STRID_LOGIN_FORM_REGISTER" cursortype="hand" font="24" normaltextcolor="link_green" visible="false"/>
//...
<!-- margin top 需要设置为 register_account button 的高度,解决input输入框获取不到焦点问题 -->
<VBox name="enter_panel" width="240" height="auto" margin="20,15,20,0" bkimage="user_password.png">
//...
</VBox>
//...
<Button name="anonymous_chatroom" margin="0,0,0,20" halign="center" textid="STRID_LOGIN_FORM_ANONYMOUSCHATROOM" cursortype="hand" font="24" normaltextcolor="link_green" visible="false"/>
//...
- 登录后的主界面 去除底部的 直播间 和 浏览器测试
打开登录界面的布局文件 bin/themes/default/main/main.xml
,注释掉以下节点
<!--
<HBox height="36">
<Button name="chatroom" width="stretch" height="stretch" textid="STRID_MAINWINDOW_CHATROOM_ENTRANCE" font="7" normaltextcolor="white" cursortype="hand" normalcolor="bk_main_wnd_search" hotcolor="bk_main_wnd_title" pushedcolor="bk_main_wnd_search"/>
<Control class="splitline_ver_level1" height="stretch"/>
<Button name="cef_test" width="stretch" height="stretch" textid="STRID_MAINWINDOW_BROWSER_TEST" font="7" normaltextcolor="white" cursortype="hand" normalcolor="bk_main_wnd_search" hotcolor="bk_main_wnd_title" pushedcolor="bk_main_wnd_search"/>
</HBox>
-->
- 一对一单聊,聊天界面移除 语音,视频,白板,提醒消息
打开 tool_kits/ui_component/ui_kit/gui/session/session_box.cpp
,注释掉以下内容(大约在94行)
//...
void SessionBox::InitSessionBox()
{
//...
if (session_type_ == nim::kNIMSessionTypeP2P && !IsFileTransPhone())
{
// 将语音,视频,白板注释掉
//btn_audio->SetVisible(true);
//btn_video->SetVisible(true);
//btn_rts->SetVisible(true);
}
//...
}
//...
打开 bin/themes/default/session/session_box.xml
,找到 name
为 btn_tip
的 Button,添加属性visible="false"
,隐藏掉提醒消息。
<Button name="btn_tip" width="30" height="30" valign="center" margin="4" tooltiptextid="STRID_SESSION_NOTIFICATION_MSG"
forenormalimage="file='btn_tip.png' dest='5,5,25,25'" foredisabledimage="file='btn_tip_disable.png' dest='5,5,25,25' fade='80'"
hotimage="icon_hover.png" pushedimage="icon_pushed.png" visible="false"/>
5.新增自定义 图文链接消息的显示
- 创建自定义的消息类型,在
tool_kits/ui_component/ui_kit/gui/session/control/bubbles
目录下创建bubble_link.h
和bubble_link.cpp
文件
1.到指定目录下右键
创建完成后从同级目录下的其他文件里复制代码过来,稍作修改,比如我复制的是 bubble_text.h
和 bubble_text.cpp
文件内容到我自己创建的文件中,复制后的代码如下:
bubble_link.h 内容如下
#pragma once
#include "bubble_item.h"
namespace nim_comp
{
/** @class MsgBubbleLink
* @brief 会话窗体中聊天框内的图文链接消息项
* @copyright (c) 2015, NetEase Inc. All rights reserved
* @author Andy
* @date 2018/1/8
*/
class MsgBubbleLink : public MsgBubbleItem
{
public:
/**
* 初始化控件内部指针
* @param[in] bubble_right 是否显示到右侧
* @return void 无返回值
*/
virtual void InitControl(bool bubble_right);
/**
* 初始化控件外观
* @param[in] msg 消息信息结构体
* @return void 无返回值
*/
virtual void InitInfo(const nim::IMMessage &msg);
/**
* 响应此消息项的单击消息,打开浏览器
*@param[in] param 被单击的按钮的相关信息
* @return bool 返回值true: 继续传递控件消息, false: 停止传递控件消息
*/
virtual bool OnClicked(ui::EventArgs* arg);
/**
* 响应此消息项的右击消息,弹出菜单
* @param[in] param 被单击的菜单项的相关信息
* @return bool 返回值true: 继续传递控件消息, false: 停止传递控件消息
*/
bool OnMenu(ui::EventArgs* arg);
private:
/**
* 设置图片资源的路径
* @return void 无返回值
*/
void InitResPath();
protected:
// 显示消息的box容器
ui::ButtonBox* msg_link_;
// 显示图片的box容器
ui::Box* image_box_;
// 标题
ui::RichEdit* title_;
// 图片
ui::Control* image_;
// 描述
ui::RichEdit* describe_;
// 图片的目录
std::wstring path_;
// 需要跳转的地址
std::wstring link_;
};
}
bubble_link.cpp 内容如下
#include "bubble_link.h"
using namespace ui;
namespace nim_comp
{
void MsgBubbleLink::InitControl(bool bubble_right)
{
__super::InitControl(bubble_right);
}
void MsgBubbleLink::InitInfo(const nim::IMMessage &msg)
{
__super::InitInfo(msg);
}
void MsgBubbleLink::InitResPath()
{
}
bool MsgBubbleLink::OnMenu(ui::EventArgs* arg)
{
return false;
}
bool MsgBubbleLink::OnClicked(ui::EventArgs* arg)
{
return true;
}
}
函数内的实现代码暂时没写,别急,我们继续往下走。
- 添加图文链接消息枚举类型
编辑文件tool_kits/ui_component/ui_kit/module/session/session_util.h
,新增CustomMsgType_Link = 5
表示图文链接消息,如下图所示
=5 可写可不写,不写默认是5,因为从1开始刚好在第5位,强列建议加上 = 5,我其他平台的图文链接消息type是5,所以为了统一消息类型 type
- 导入上面自己创建的头文件
编辑文件tool_kits/ui_component/ui_kit/gui/session/session_box.h
,在头部导入自己创建的bubble_link.h
这步完成后,项目需要重新生成下,方便在其他模块下能识别获取到你刚刚创建的 bubble_link
文件,如下图。
- 接下来在接收消息和发送消息处理的地方添加我们自定义的
bubble_link
。
编辑tool_kits/ui_component/ui_kit/gui/session/session_box.cpp
,在函数ShowMsg
添加如下代码,大约在409行
// 头部导入相关内容
#include <tchar.h>
#include <iostream>
#include <UrlMon.h>
#pragma comment(lib,"urlmon.lib")
// 此处省略无关代码
MsgBubbleItem* SessionBox::ShowMsg(const nim::IMMessage &msg, bool first, bool show_time)
{
// 此处忽略部分代码
else if (msg.type_ == nim::kNIMMessageTypeCustom)
{
Json::Value json;
if (StringToJson(msg.attach_, json) && json.isObject())
{
// 此处忽略部分代码
// 添加此处 else if 代码
else if (sub_type == CustomMsgType_Link)
{
item = new MsgBubbleLink;
if (StringToJson(msg.attach_, json)
&& json.isObject()
&& json.isMember("data")
&& json["data"]["image_url"].asString() != "")
{
std::wstring image_dir = GetUserImagePath();
if (!nbase::FilePathIsExist(image_dir, true))
nbase::CreateDirectoryW(image_dir);
std::wstring image_path = image_dir + nbase::UTF8ToUTF16(msg.client_msg_id_);
// 将网络图片保存到本地
std::string url = json["data"]["image_url"].asString();
size_t len = url.length();//获取字符串长度
int nmlen = MultiByteToWideChar(CP_ACP, 0, url.c_str(), len + 1, NULL, 0);//如果函数运行成功,并且cchWideChar为零,
//返回值是接收到待转换字符串的缓冲区所需求的宽字符数大小。
wchar_t* buffer = new wchar_t[nmlen];
MultiByteToWideChar(CP_ACP, 0, url.c_str(), len + 1, buffer, nmlen);
HRESULT hr = URLDownloadToFile(NULL, buffer, image_path.c_str(), 0, NULL);
if (hr == S_OK)
{
printf("下载OK");
}
}
else
{
QLOG_ERR(L"There is not image_link property.");
}
}
}
}
}
编辑 tool_kits/ui_component/ui_kit/gui/session/msg_record.cpp
,在函数 ShowMsg
添加如下代码,大约在120行
void MsgRecordForm::ShowMsg(const nim::IMMessage &msg, bool first, bool show_time)
{
// ...
MsgBubbleItem* item = NULL;
if (msg.type_ == nim::kNIMMessageTypeText
|| IsNetCallMsg(msg.type_, msg.attach_))
{
//...
}
else if (msg.type_ == nim::kNIMMessageTypeCustom)
{
Json::Value json;
if (StringToJson(msg.attach_, json) && json.isObject())
{
//...
else if (sub_type == CustomMsgType_Rts)
{
//...
}
// 添加此处 else if 代码
else if (sub_type == CustomMsgType_Link)
{
item = new MsgBubbleLink;
}
}
}
//...
}
- 添加发送图文链接测试消息,用于开发测试(正式上线前注释掉此步骤的代码)
在发送文本消息时,如果消息是以custom::
开头的消息,就触发发送图文链接消息,如下所示。
以custom::开头的文本消息就触发发送图文链接消息
编辑文件 tool_kits/ui_component/ui_kit/gui/session/session_box.cpp
,修改SendText
函数,添加如下代码
//...
void SessionBox::SendText( const std::string &text )
{
nim::IMMessage msg;
//...
if (msg.type_ != nim::kNIMMessageTypeRobot)
{
// 添加测试发送图文链接消息,当用户发送的文本内容是以 custom:: 开头时,触发如下业务
std::string prefix = "custom::";
if (strncmp(text.c_str(), prefix.c_str(), prefix.length()) == 0)
{
msg.type_ = nim::kNIMMessageTypeCustom;
// 消息内容体格式如下
/*
{
type: 5,
data: {
title: '消息标题',
link_url: '点击跳转链接',
image_url: '图片URL',
describe: '消息描述',
}
}
*/
Json::Value json;
Json::FastWriter writer;
json["type"] = CustomMsgType_Link;
json["data"]["title"] = Json::Value(text.substr(prefix.length()));
json["data"]["link_url"] = Json::Value("https://www.jianshu.com/u/bd57ade96e8a");
json["data"]["image_url"] = Json::Value("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2544269114,2104066965&fm=27&gp=0.jpg");
json["data"]["describe"] = Json::Value(text.substr(prefix.length()));
//msg.content_ = writer.write(json);
msg.attach_ = writer.write(json);
// 转json字符串
json_msg = msg.ToJsonString(true);
}
else
{
//...
}
}
else
{
//...
}
AddSendingMsg(msg);
nim::Talk::SendMsg(json_msg);
}
//...
点击发送后,会发现自己发送的内容是空白,那是因为我们还没做图文链接消息的显示代码,发送之后,我们在其他客户端查看消息。
- 发送图文链接消息后,接下来添加图文链接消息的显示
消息的显示都是由xml文件来控制布局的,打开文件目录bin/themes/default/session
(见下图)可以看到一些消息类型如视频消息
,文件消息
,猜拳消息
,图片消息
等都分成left
(接收消息的显示布局文件) 和right
(发送消息的显示布局文件)结尾的 xml 文件。
同样的,我们在这个目录下创建 link_left.xml
和 link_right.xml
文件,文件内容如下
link_left.xml
<?xml version="1.0" encoding="UTF-8"?>
<Window>
<!-- 可点击的消息体 -->
<ButtonBox name="msg_link" width="auto" height="auto" menu="true" mousechild="false" cursortype="hand" bkimage="file='bubble_left.png' corner='25,15,15,15'" padding="0,0,0,10">
<!-- 消息标题 -->
<RichEdit name="title" width="270" height="30" margin="15,15,10,9" padding="10,6,10,6" font="7" normaltextcolor="darkcolor" readonly="true" multiline="true" vscrollbar="false" autovscroll="false" bkcolor="white"/>
<!-- 图片,默认不显示 -->
<Box name="image_box" width="270" height="170" margin="15,42,10,0" padding="10,0,0,0" bkcolor="white" visible="false">
<Box width="250" height="auto" margin="0,0,0,0" padding="0,10,0,10" topbordersize="1" bottombordersize="1" bordercolor="transbkblack1" bkcolor="white">
<Control name="image" width="250" height="140" bkimage="image_def" bkcolor="lightcolor"/>
</Box>
</Box>
<!-- 描述,默认不显示 -->
<RichEdit name="describe" height="auto" maxheight="65" margin="15,212,10,0" padding="10,8,0,10" font="11" normaltextcolor="darkcolor" readonly="true" multiline="true" vscrollbar="false" autovscroll="false" bkcolor="white" visible="false"/>
</ButtonBox>
</Window>
link_right.xml
<?xml version="1.0" encoding="UTF-8"?>
<Window>
<!-- 可点击的消息体 -->
<ButtonBox name="msg_link" width="auto" height="auto" menu="true" mousechild="false" cursortype="hand" bkimage="file='bubble_right.png' corner='15,15,25,15'" padding="0,0,0,10">
<!-- 消息标题 -->
<RichEdit name="title" width="270" height="30" margin="10,15,15,9" padding="10,6,10,6" font="7" normaltextcolor="darkcolor" readonly="true" multiline="true" vscrollbar="false" autovscroll="false" bkcolor="white"/>
<!-- 图片,默认不显示 -->
<Box name="image_box" width="270" height="170" margin="10,42,15,0" padding="10,0,0,0" bkcolor="white" visible="false">
<Box width="250" height="auto" margin="0,0,0,0" padding="0,10,0,10" topbordersize="1" bottombordersize="1" bordercolor="transbkblack1" bkcolor="white">
<Control name="image" width="250" height="140" bkimage="image_def" bkcolor="lightcolor"/>
</Box>
</Box>
<!-- 描述,默认不显示 -->
<RichEdit name="describe" height="auto" maxheight="65" margin="10,212,15,0" padding="10,8,0,10" font="11" normaltextcolor="darkcolor" readonly="true" multiline="true" vscrollbar="false" autovscroll="false" bkcolor="white" visible="false"/>
</ButtonBox>
</Window>
其实两个文件内容几乎一样,只是 bkimage 和 margin 值稍微有点不一样。
接着我们在前面创建好的 bubble_link.cpp
中写业务逻辑代码控制布局文件的显示隐藏和赋值
bubble_link.cpp
#include "bubble_link.h"
#include "util/user.h"
#include <windows.h>
#include <tchar.h>
#include <assert.h>
using namespace ui;
namespace nim_comp
{
void MsgBubbleLink::InitControl(bool bubble_right)
{
__super::InitControl(bubble_right);
msg_link_ = new ButtonBox;
if (bubble_right)
GlobalManager::FillBoxWithCache(msg_link_, L"session/link_right.xml");
else
GlobalManager::FillBoxWithCache(msg_link_, L"session/link_left.xml");
bubble_box_->Add(msg_link_);
image_box_ = (Box *)msg_link_->FindSubControl(L"image_box");
title_ = (RichEdit*)msg_link_->FindSubControl(L"title");
image_ = this->FindSubControl(L"image");
describe_ = (RichEdit*)msg_link_->FindSubControl(L"describe");
// 添加鼠标右键点击事件
msg_link_->AttachMenu(nbase::Bind(&MsgBubbleLink::OnMenu, this, std::placeholders::_1));
msg_link_->AttachClick(nbase::Bind(&MsgBubbleLink::OnClicked, this, std::placeholders::_1));
}
// 字符串转宽字符
std::wstring StringToWString(const std::string& str) {
int num = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
wchar_t *wide = new wchar_t[num];
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, wide, num);
std::wstring w_str(wide);
delete[] wide;
return w_str;
}
void MsgBubbleLink::InitInfo(const nim::IMMessage &msg)
{
__super::InitInfo(msg);
InitResPath();
Json::Value json;
if (StringToJson(msg.attach_, json) && json.isObject())
{
int sub_type = json["type"].asInt();
if (sub_type == CustomMsgType_Link && json["data"].isObject())
{
link_ = nbase::UTF8ToUTF16(json["data"]["link_url"].asString());
title_->SetText(StringToWString(json["data"]["title"].asString()));
// 是否有图片参数
bool hasImage = !json["data"]["image_url"].asString().empty();
if (hasImage)
{
image_box_->SetVisible(TRUE);
std::wstring imgeUri = path_ + nbase::UTF8ToUTF16(msg.client_msg_id_);
if (nbase::FilePathIsExist(imgeUri, false))
image_->SetBkImage(imgeUri);
else
image_->SetBkImage(L"image_def");
}
else
{
image_box_->SetVisible(FALSE);
}
if (json["data"]["describe"].asString().empty())
{
describe_->SetVisible(FALSE);
}
else
{
describe_->SetVisible(TRUE);
describe_->SetText(StringToWString(json["data"]["describe"].asString()));
UiRect rect = describe_->GetMargin();
LONG top = hasImage ? 202 : 42;
describe_->SetMargin(UiRect(rect.left, top, rect.right, rect.bottom));
}
}
}
QLOG_WAR(L"user type msg undefine, attach={0}") << msg.attach_;
}
void MsgBubbleLink::InitResPath()
{
std::wstring wpath = GetUserImagePath();
std::string path = nim::Talk::GetAttachmentPathFromMsg(msg_);
if (wpath.empty() || !nbase::FilePathIsExist(wpath, false))
{
path_ = nbase::UTF8ToUTF16(path);
std::wstring directory, filename;
nbase::FilePathApartDirectory(path_, directory);
nbase::FilePathApartFileName(path_, filename);
}
else
{
std::wstring directory, filename;
nbase::FilePathApartDirectory(nbase::UTF8ToUTF16(path), directory);
nbase::FilePathApartFileName(wpath, filename);
path_ = wpath;
}
}
bool MsgBubbleLink::OnMenu(ui::EventArgs* arg)
{
PopupMenu(false, true, false);
return false;
}
bool MsgBubbleLink::OnClicked(ui::EventArgs* arg)
{
const TCHAR szOperation[] = _T("open");
TCHAR szAddress[1024];
_tcscpy(szAddress, link_.c_str());
HINSTANCE hRslt = ShellExecute(NULL, szOperation,
szAddress, NULL, NULL, SW_SHOWNORMAL);
assert(hRslt > (HINSTANCE)HINSTANCE_ERROR);
return true;
}
}
- 上步操作完成后,我们就可以来测试发送图文链接消息了
根据我们上面的规则,发送文本消息以custom::
开头的消息就触发图文链接消息,当然也可以注释掉部分参数,下面列出我们上面在session_box.cpp
中写到的如下代码
Json::Value json;
Json::FastWriter writer;
json["type"] = CustomMsgType_Link;
// title 必须要有
json["data"]["title"] = Json::Value(text.substr(prefix.length()));
// link_url 跳转的链接,必须要有
json["data"]["link_url"] = Json::Value("https://www.jianshu.com/u/bd57ade96e8a");
// 图片链接地址,可选
json["data"]["image_url"] = Json::Value("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2544269114,2104066965&fm=27&gp=0.jpg");
// 商品描述,可选项
json["data"]["describe"] = Json::Value(text.substr(prefix.length()));
注释掉后,重新运行发送消息测试,经过测试,发送相关消息,最终显示如下。
消息支持只有title,只有title和image,只有title和describe等类型点击消息可打开浏览器跳转相应的网页
- 最后在主界面消息列表的显示修改
首先打开文件目录 bin/lang
下的 zh_CN
目录 和 en_US
目录下的 gdstrings.ini
文件
使用编辑器打开这两个 gdstrings.ini
文件,分别添加一行
en_US/gdstrings.ini
STRID_SESSION_ITEM_MSG_TYPE_LINK = [Link]
zh_CN/gdstrings.ini
STRID_SESSION_ITEM_MSG_TYPE_LINK = [图文链接]
分别在两个文件中添加一行图文链接消息的显示文字
接着回到VS编辑器打开文件 tool_kits/ui_component/ui_kit/module/session/session_util.cpp
,在
GetCustomMsg
函数中添加如下 else if 代码块
std::wstring GetCustomMsg(const std::string &sender_accid, const std::string &msg_attach)
{
ui::MutiLanSupport* mls = ui::MutiLanSupport::GetInstance();
std::wstring show_text = mls->GetStringViaID(L"STRID_SESSION_ITEM_MSG_TYPE_CUSTOM_MSG");
Json::Value json;
if (StringToJson(msg_attach, json) && json.isObject())
{
//...
// 在最后添加如下判断,如果是 CustomMsgType_Link 类型消息
else if (sub_type == CustomMsgType_Link)
{
show_text = mls->GetStringViaID(L"STRID_SESSION_ITEM_MSG_TYPE_LINK");
}
}
return show_text;
}
完成后,我们再次运行,从消息列表可以看到。
语言为中文时显示 语言为英文时显示
- 上面有个bug,用户在线状态。
我另一个帐号明明是在线的,而消息列表却显示[离线]
这个地方我不管他是在线还是离线,我都返回空字符串,不显示用户在线状态,打开online_state_event_helper.cpp
在函数 GetOnlineState 中直接返回空字符串,大概在 140行,添加如下代码
std::wstring OnlineStateEventHelper::GetOnlineState(const nim::EventOnlineClientType& online_client_type, const EventMultiConfig& multi_config, bool is_simple)
{
return L""; // 在线离线状态有bug,所以直接返回空字符串
//...
}
- 替换logo图片
首先我们准备好一张图片,图片可以通过 https://www.ico.la/ 这个在线工具转成 128px * 128px 的 ico 文件,然后替换掉 nim_win_demo 目录下的 nim.ico 文件
替换掉之后,你可能看到的还是显示原来的图片,不要紧,先不管它。
在VS工具里修改 VS_VERSION_INFO 信息
修改此处信息为自己的修改完后保存,然后退出visual studio,接下来是打包发布过程
到此步基本完成了,最后是打包
打包过程我参考的是此文章,此文章有些步骤我是不需要操作的,有些步骤又没讲详细,所以下我也会列出自己打包是的步骤
- 首先使用管理员打开 VS 开发工具,如果不是以管理员身份打开,后面的打包步骤会失败。(这只针对Windows10权限问题)
VS自带的打包程序默认是没有安装的,如果有打包的需要,需要自己去下载一个安装程序。
文件->新建->项目如果没有安装 InstallShield Limited Edition Project,(按照 此文章 内的1,2,3步骤安装下 。
我这是已安装的
名称可以是中文,我填写的就是我的应用名称 上步操作完成后显示这个界面,点击Application Information 填写应用相关信息 箭头所指的需要稍微修改下,其他项目根据自己需求来 我的应用不需要任何依赖,所以上面俩项都选择No以下这个步骤,需要添加的文件和文件夹我们可以安装网易云信demo,然后打开安装目录,可以看到他依赖的所有文件和文件夹
安装完网易云信demo后打开安装目录 添加应用程序所依赖的文件和文件夹,依赖项参考上图 应用程序启动和快捷方式设置 修改安装完成后在桌面显示的应用名称 将“Releases”双击打开,然后单击树状节点“SingleImage”,在展开的内容窗口中选择“Setup.exe”选项卡并进行设置 安装时协议安装路径等配置 最后右键重新生成解决方案 最后我们打开文件目录可以查看到安装文件尾篇
到此,云信PC端的扩展自定义消息已经完成。当然,这只是PC的显示正常了,其他如web,Android,iOS等客户端收到此类的消息,显示有问题,也是需要扩展调整的。此篇文章其他端的文章我会陆续更新,如果有需要的同学可以关注下。
以下附上其他版本扩展的链接
网友评论