5 |
6 | static const struct
7 | {
8 | std::string OK, NotFound;
9 | static std::string Download(const std::string& filename)
10 | {
11 | std::string header = "HTTP/1.1 200 OK\r\n"
12 | "Content-Type: application/octet-stream\r\n"
13 | "Content-Disposition: attachment; filename=\"" + filename + "\"\r\n";
14 | return header;
15 | }
16 | } HttpHeader =
17 | {
18 | "HTTP/1.1 200 OK\r\n"
19 | "Content-Type: text/html\r\n",
20 |
21 | "HTTP/1.1 404 Not Found\r\n"
22 | "Content-Type: text/html\r\n",
23 | };
24 |
25 | static std::string HtmlLink(const std::string& text, const std::string& target, bool enabled);
26 | static std::string ClientScript(const std::string& ip, const std::string& raport, int httpport);
27 |
28 | std::string RDPShareServer::OnRequest(const std::string& s)
29 | {
30 | std::string response = "ok\r\n";
31 | if (s == "quit")
32 | Terminate();
33 | else if (s == "start")
34 | AsyncMessage(start);
35 | else if (s == "stop")
36 | AsyncMessage(stop);
37 | else if (s == "get_ConnectionString")
38 | response = mConnectionString;
39 | else if (s == "get_SharedRect")
40 | response = mSharedRect;
41 | else if (s == "get_RASessionID")
42 | response = mRaInfo.SessionID;
43 | else if (s.find("set_ScreenSize") == 0)
44 | response = OnSetScreenSize(s.substr(::strlen("set_ScreenSize")));
45 | else if (s.find("GET /") == 0)
46 | response = OnHttpGet( s.substr(::strlen("GET /")));
47 | else
48 | response = "error: unknown request\r\n";
49 | return response;
50 | }
51 |
52 | void RDPShareServer::OnAsyncMessage(int msg, void*)
53 | {
54 | switch( msg )
55 | {
56 | case start:
57 | Start();
58 | break;
59 | case stop:
60 | Stop();
61 | break;
62 | }
63 | }
64 |
65 | RDPShareServer::RDPShareServer(int addr, int httpport, int screen, Sharer* pSharer)
66 | : mAddress(addr), mHttpPort(httpport), mScreen(screen), mpSharer(pSharer)
67 | {
68 | }
69 |
70 | int RDPShareServer::Run()
71 | {
72 | return Server::Run(mAddress, mHttpPort);
73 | }
74 |
75 | void RDPShareServer::Start()
76 | {
77 | mError.clear();
78 | RECT r;
79 | if (GetScreenRect(mScreen, &r))
80 | {
81 | try
82 | {
83 | mpSharer->SetRect(r).Start();
84 | mConnectionString = mpSharer->ConnectionString();
85 | std::ostringstream oss;
86 | oss << r.left << " " << r.top << " " << r.right << " " << r.bottom << "\n";
87 | mSharedRect = oss.str();
88 | ParseConnectionString(mConnectionString, mRaInfo);
89 | }
90 | catch( HR_SucceedOrDie hr)
91 | {
92 | mError = GetErrorText(hr);
93 | }
94 | catch(...)
95 | {
96 | mError = "unknown error";
97 | }
98 | }
99 | else
100 | {
101 | std::ostringstream oss;
102 | oss << "screen " << mScreen << " not found";
103 | mError = oss.str();
104 | }
105 | }
106 |
107 | void RDPShareServer::Stop()
108 | {
109 | mSharedRect.clear();
110 | mConnectionString.clear();
111 | mpSharer->Stop();
112 | }
113 |
114 | std::string RDPShareServer::OnSetScreenSize(const std::string s)
115 | {
116 | std::string screen, size;
117 | std::istringstream iss(s);
118 | iss >> screen >> size;
119 | if (size.empty())
120 | {
121 | size = screen;
122 | screen = "1";
123 | }
124 | int width = -1, height = -1, screenIdx = ::atoi(screen.c_str());
125 | size_t pos = size.find('x');
126 | if (pos < size.length())
127 | {
128 | width = ::atoi(size.c_str());
129 | height = ::atoi(size.c_str() + pos + 1);
130 | }
131 | if (width < 0 || height < 0)
132 | return "error: bad format: " + s + "\n";
133 | if (screenIdx == 0)
134 | return "error: cannot set desktop size, specify a screen > 0\n";
135 | RECT r;
136 | if (!GetScreenRect(screenIdx, &r))
137 | return "error: unknown screen " + screen + " \n";
138 | int result = SetScreenSize(screenIdx, width, height);
139 | std::string message = "error: could not set screen size";
140 | switch (result)
141 | {
142 | case DISP_CHANGE_SUCCESSFUL:
143 | message = "ok";
144 | break;
145 | #define _(x) case x: message += " (" #x ")"; break;
146 | _(DISP_CHANGE_BADDUALVIEW)
147 | _(DISP_CHANGE_BADFLAGS)
148 | _(DISP_CHANGE_BADMODE)
149 | _(DISP_CHANGE_BADPARAM)
150 | _(DISP_CHANGE_FAILED)
151 | _(DISP_CHANGE_RESTART)
152 | _(DISP_CHANGE_NOTUPDATED)
153 | #undef _
154 | }
155 | return message + "\n";
156 | }
157 |
158 | std::string RDPShareServer::OnHttpGet( const std::string s)
159 | {
160 | std::string header, content;
161 | if (s.find("?Start")==0)
162 | {
163 | AsyncMessage(start);
164 | header = HttpHeader.OK;
165 | content = HtmlRedirect();
166 | }
167 | else if (s.find("?Stop") == 0)
168 | {
169 | AsyncMessage(stop);
170 | header = HttpHeader.OK;
171 | content = HtmlRedirect();
172 | }
173 | else if (s.find("?ClientScript") == 0)
174 | {
175 | header = HttpHeader.Download("RDPShare_Connect_" + mRaInfo.IP + ".sh");
176 | content = ClientScript(mRaInfo.IP, mRaInfo.Port, mHttpPort);
177 | }
178 | else
179 | {
180 | header = HttpHeader.OK;
181 | content = HtmlMainPage();
182 | }
183 | if (content.empty())
184 | header = HttpHeader.NotFound;
185 | std::ostringstream oss;
186 | oss << "Content-Length: " << content.length() << "\r\n\r\n";
187 | return header + oss.str() + content;
188 | }
189 |
190 | std::string RDPShareServer::HtmlMainPage()
191 | {
192 | bool stopped = mConnectionString.empty();
193 | std::ostringstream oss;
194 | oss << "RDPShare"
195 | << ""
196 | << "RDPShare server on ports " << mHttpPort << "/" << mpSharer->Port() << " is "
197 | << (stopped ? "stopped" : "running") << ".";
198 | if (!mError.empty())
199 | oss << "Could not start sharing session: " << mError;
200 | oss << "
\n"
201 | << HtmlLink(stopped ? "Start" : "Restart", "/?Start", true) << ""
202 | << HtmlLink("Stop", "/?Stop", !stopped) << ""
203 | << "Download "
204 | << HtmlLink("client wrapper shell script", "/?ClientScript", true) << "."
205 | << "The script depends on xfreerdp and netcat, and "
206 | << "must be made executable using chmod 775 before it can be used.
"
207 | << "";
208 | return oss.str();
209 | }
210 |
211 | std::string RDPShareServer::HtmlRedirect()
212 | {
213 | std::ostringstream oss;
214 | oss << ""
215 | << "Reloading ...
";
216 | return oss.str();
217 | }
218 |
219 | static std::string HtmlLink(const std::string& text, const std::string& target, bool enabled)
220 | {
221 | std::string code = "" + text + "";
227 | return code;
228 | }
229 |
230 | static std::string ClientScript(const std::string& ip, const std::string& raport, int httpport)
231 | {
232 | std::ostringstream oss;
233 | oss <<
234 | "#!/bin/sh\n"
235 | "send_command()\n"
236 | "{\n"
237 | " echo $1 | nc " << ip << " " << httpport << "\n"
238 | "}\n"
239 | "send_command stop > /dev/null\n"
240 | "send_command start > /dev/null\n"
241 | "APP=xfreerdp\n"
242 | "OPTIONS=\"/f /cert:ignore /sec:rdp -encryption +compression /u:null@null /p:null\"\n"
243 | "SESSIONID=`send_command get_RASessionID`\n"
244 | "exec $APP /v:" << ip << ":" << raport << " /shell-dir:$SESSIONID $OPTIONS\n"
245 | ;
246 | return oss.str();
247 | }
248 |
--------------------------------------------------------------------------------
/RDPShareServer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * RDPShare: Windows Desktop Sharing remote control interface
3 | *
4 | * Copyright 2016 Simul Piscator
5 | *
6 | * RDPShare is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * RDPShare is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with RDPShare. If not, see .
18 | */
19 | #include "Server.h"
20 | #include "RDPFile.h"
21 |
22 | class Sharer;
23 |
24 | class RDPShareServer: Server
25 | {
26 | public:
27 | RDPShareServer(int addr, int httpport, int screen, Sharer* pSharer);
28 | int Run();
29 | void Start();
30 | void Stop();
31 |
32 | protected:
33 | std::string OnRequest(const std::string& s) override;
34 | enum { start, stop };
35 | void OnAsyncMessage(int msg, void*) override;
36 |
37 | private:
38 | std::string OnSetScreenSize(const std::string s);
39 | std::string OnHttpGet(const std::string s);
40 | std::string HtmlMainPage();
41 | std::string HtmlRedirect();
42 |
43 | int mAddress, mHttpPort, mScreen;
44 | Sharer* mpSharer;
45 | RemoteAssistanceInfo mRaInfo;
46 | std::string mConnectionString, mSharedRect, mError;
47 | };
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RDPShare
2 | ## Windows Desktop Sharing remote control interface
3 |
4 | Windows Desktop Sharing (WDS) allows remote interaction with an existing user session.
5 | Typically this requires extra programming effort on the server and client side, and is limited to clients running Windows.
6 | Exploiting the fact that WDS is based on the Windows Remote Assistance (RA) protocol,
7 | RDPShare lets you connect to a WDS session from a machine running the [FreeRDP](https://github.com/FreeRDP/FreeRDP)
8 | [xfreerdp client program](https://github.com/awakecoding/FreeRDP-Manuals/blob/master/User/FreeRDP-User-Manual.markdown).
9 |
10 | RDPShare is a Win32 command line program supposed to be run from within an existing user session.
11 | In a typical scenario, a Windows machine would be attached to a projector,
12 | and you would like to control its output from a mobile computer through a wireless network connection.
13 | You might configure the Windows machine to automatically log on the desired user on startup.
14 | Using Windows Task Scheduler to create a task that starts the RDPShare executable whenever that user logs on,
15 | you will be able to control the Windows machine's desktop from a Linux laptop.
16 |
17 | By default, RDPShare runs a combined HTTP and control server on port 8080, and a WDS session on port 3390.
18 | Through the control server, the WDS session may be enabled and disabled, and WDS connection information may be retrieved,
19 | which may then be fed to xfreedrp on the command line.
20 | When accessing the control port through a web browser, status information is displayed,
21 | and a simple client connection script is offered for download.
22 |
23 | ## Usage
24 |
25 | RDPShare [idle] [http <port>] [rdp <port>] [screen {0..n}]
26 |
27 | Defaults are: http 8080 rdp 3390 screen 0
28 |
29 | If the "idle" option is given, RDPShare does not start a WDS session at startup but waits for the "start" request
30 | before initiating one (see below).
31 |
32 | The "screen" option allows to choose which part of the Windows desktop is shared.
33 | For "screen 0", the entire Windows desktop is shared; for arguments greater 0, only the area of the respective display is shared.
34 |
35 | In addition to HTTP GET requests, the following requests are recognized when sent to the HTTP port:
36 | * start: start WDS session
37 | * stop: stop WDS session
38 | * quit: exit RDAShare process
39 | * get_ConnectionString: get WDS connection string
40 | * get_RASessionID: get Remote Assistance session ID
41 | * get_SharedRect: get shared rect
42 |
--------------------------------------------------------------------------------
/Server.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * RDPShare: Windows Desktop Sharing remote control interface
3 | *
4 | * Copyright 2016 Simul Piscator
5 | *
6 | * RDPShare is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * RDPShare is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with RDPShare. If not, see .
18 | */
19 | #include "Server.h"
20 |
21 | #include
22 | #include
23 | #include
24 | #include "Utils.h"
25 | #include
26 |
27 | static struct WSAInit
28 | {
29 | WSAInit()
30 | {
31 | WSADATA ignored;
32 | ::WSAStartup(MAKEWORD(2, 2), &ignored);
33 | }
34 | ~WSAInit()
35 | {
36 | ::WSACleanup();
37 | }
38 | } sWSAInit;
39 |
40 | struct Server::Private
41 | {
42 | static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
43 | {
44 | Server* this_ = (Server*)::GetWindowLongPtrA(hwnd, GWLP_USERDATA);
45 | switch (msg)
46 | {
47 | case WM_CREATE:
48 | ::SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)((CREATESTRUCT*)lparam)->lpCreateParams);
49 | return 0;
50 | case WM_USER:
51 | this_->OnAsyncMessage(int(wparam), (void*)lparam);
52 | return 0;
53 | }
54 | return DefWindowProc(hwnd, msg, wparam, lparam);
55 | }
56 | Private( Server* pSelf)
57 | : mpSelf(pSelf), mMessageThread(0), mServerThread(0), mListeningSocket(-1)
58 | {
59 | WNDCLASSA c = { 0 };
60 | c.lpfnWndProc = &WindowProc;
61 | c.hInstance = ::GetModuleHandleA(nullptr);
62 | c.lpszClassName = "ServerMessageWindow";
63 | ::RegisterClassA(&c);
64 | mHwnd = ::CreateWindowA(c.lpszClassName, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, c.hInstance, mpSelf);
65 | }
66 | ~Private()
67 | {
68 | }
69 | void InitAddress(unsigned long ip, unsigned short port);
70 | static void Send(SOCKET s, const std::string&);
71 | static std::string Receive(SOCKET s);
72 | static unsigned int WINAPI ThreadProc(LPVOID);
73 | unsigned int WaitForServerThread();
74 |
75 | Server* mpSelf;
76 | std::mutex mMutex;
77 | HWND mHwnd;
78 | sockaddr_in mAddress;
79 | unsigned int mMessageThread;
80 | HANDLE mServerThread;
81 | SOCKET mListeningSocket;
82 | };
83 |
84 | Server::Server(unsigned long ip, unsigned short port)
85 | : p(new Private(this))
86 | {
87 | p->InitAddress(ip, port);
88 | }
89 |
90 | Server::~Server()
91 | {
92 | Terminate();
93 | p->WaitForServerThread();
94 | delete p;
95 | }
96 |
97 | void
98 | Server::Private::InitAddress(unsigned long ip, unsigned short port)
99 | {
100 | ::memset(&mAddress, 0, sizeof(mAddress));
101 | mAddress.sin_family = AF_INET;
102 | mAddress.sin_addr.s_addr = ip;
103 | mAddress.sin_port = htons(port);
104 | }
105 |
106 | DWORD
107 | Server::Run(unsigned long ip, unsigned short port)
108 | {
109 | p->mMessageThread = ::GetCurrentThreadId();
110 | p->InitAddress(ip, port);
111 |
112 | WSA_SucceedOrDie r;
113 | SOCKET ls = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
114 | r = ::bind(ls, (sockaddr*)&p->mAddress, sizeof(p->mAddress));
115 | r = ::listen(ls, SOMAXCONN);
116 | p->mListeningSocket = ls;
117 |
118 | p->mServerThread = (HANDLE)::_beginthreadex(nullptr, 0, &Private::ThreadProc, p, 0, nullptr);
119 | BOOL_SucceedOrDie b = p->mServerThread != 0;
120 | MSG msg;
121 | while (::GetMessageA(&msg, NULL, 0, 0) > 0)
122 | {
123 | std::lock_guard lock(p->mMutex);
124 | ::TranslateMessage(&msg);
125 | ::DispatchMessageA(&msg);
126 | }
127 | Terminate();
128 | return p->WaitForServerThread();
129 | }
130 |
131 | unsigned int
132 | Server::Private::WaitForServerThread()
133 | {
134 | DWORD exitCode = 0;
135 | HANDLE hThread = ::InterlockedExchangePointer(&mServerThread, 0);
136 | if (hThread)
137 | {
138 | BOOL_SucceedOrDie b = BOOL(WAIT_OBJECT_0 == ::WaitForSingleObject(hThread, INFINITE));
139 | b = ::GetExitCodeThread(hThread, &exitCode);
140 | ::CloseHandle(hThread);
141 | }
142 | return exitCode;
143 | }
144 |
145 | void
146 | Server::Terminate()
147 | {
148 | SOCKET s = ::InterlockedExchange(&p->mListeningSocket,-1);
149 | if ( s != -1)
150 | WSA_SucceedOrDie r = ::closesocket(s);
151 | }
152 |
153 | void
154 | Server::Private::Send(SOCKET s, const std::string& data)
155 | {
156 | int remaining = static_cast(data.length());
157 | const char* p = data.c_str();
158 | while (remaining > 0)
159 | {
160 | fd_set writefds;
161 | FD_ZERO(&writefds);
162 | FD_SET(s, &writefds);
163 | timeval t = { 1, 0 };
164 | WSA_SucceedOrDie r = ::select(static_cast(s) + 1, nullptr, &writefds, nullptr, &t);
165 | if (FD_ISSET(s, &writefds))
166 | r = ::send(s, p, remaining, 0);
167 | else
168 | r = remaining;
169 | p += r;
170 | remaining -= r;
171 | }
172 | }
173 |
174 | std::string
175 | Server::Private::Receive(SOCKET s)
176 | {
177 | std::string data;
178 | char c = -1;
179 | while (c)
180 | {
181 | c = 0;
182 | fd_set readfds;
183 | FD_ZERO(&readfds);
184 | FD_SET(s, &readfds);
185 | timeval t = { 20, 0 };
186 | WSA_SucceedOrDie r = ::select(static_cast(s) + 1, &readfds, nullptr, nullptr, &t);
187 | if (FD_ISSET(s, &readfds))
188 | r = ::recv(s, &c, 1, 0);
189 | if (c == '\n')
190 | c = 0;
191 | if (c && c != '\r')
192 | data += c;
193 | }
194 | return data;
195 | }
196 |
197 | unsigned int WINAPI
198 | Server::Private::ThreadProc(LPVOID data)
199 | {
200 | Server::Private* this_ = static_cast(data);
201 | unsigned int result = 0;
202 | try
203 | {
204 | while (this_->mListeningSocket != -1)
205 | {
206 | SOCKET s = ::accept(this_->mListeningSocket, nullptr, nullptr);
207 | if (s != -1)
208 | {
209 | std::lock_guard lock(this_->mMutex);
210 | std::string request = Receive(s);
211 | Send(s, this_->mpSelf->OnRequest(request));
212 | WSA_SucceedOrDie r = ::closesocket(s);
213 | }
214 | }
215 | }
216 | catch( WSA_SucceedOrDie)
217 | {
218 | result = ::WSAGetLastError();
219 | }
220 | catch (BOOL_SucceedOrDie)
221 | {
222 | result = ::GetLastError();
223 | }
224 | ::PostThreadMessageA(this_->mMessageThread, WM_QUIT, 0, 0);
225 | return result;
226 | }
227 |
228 | std::string
229 | Server::Request( const std::string& command )
230 | {
231 | SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
232 | WSA_SucceedOrDie r = ::connect(s, (sockaddr*)&p->mAddress, sizeof(p->mAddress));
233 | Private::Send(s,command + "\r\n");
234 | std::string data = Private::Receive(s);
235 | r = ::closesocket(s);
236 | return data;
237 | }
238 |
239 | void
240 | Server::AsyncMessage(int msg, void* arg)
241 | {
242 | BOOL_SucceedOrDie b = ::PostMessageA(p->mHwnd, WM_USER, msg, LPARAM(arg));
243 | }
244 |
--------------------------------------------------------------------------------
/Server.h:
--------------------------------------------------------------------------------
1 | /*
2 | * RDPShare: Windows Desktop Sharing remote control interface
3 | *
4 | * Copyright 2016 Simul Piscator
5 | *
6 | * RDPShare is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * RDPShare is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with RDPShare. If not, see .
18 | */
19 | #ifndef SERVER_H
20 | #define SERVER_H
21 |
22 | #include
23 | #include
24 | #include
25 |
26 | class Server
27 | {
28 | public:
29 | Server(unsigned long ip = 0, unsigned short port = 0);
30 | ~Server();
31 | DWORD Run(unsigned long ip, unsigned short port);
32 | std::string Request(const std::string&);
33 |
34 | protected:
35 | void Terminate();
36 | void AsyncMessage(int, void* = nullptr);
37 | virtual void OnAsyncMessage(int, void*) {}
38 | virtual std::string OnRequest(const std::string&) { return ""; }
39 |
40 | private:
41 | struct Private;
42 | Private* p;
43 | };
44 |
45 | #endif // SERVER_H
46 |
--------------------------------------------------------------------------------
/Sharer.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * RDPShare: Windows Desktop Sharing remote control interface
3 | *
4 | * Copyright 2016 Simul Piscator
5 | *
6 | * RDPShare is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * RDPShare is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with RDPShare. If not, see .
18 | */
19 | #include
20 | #include
21 | #include
22 | #include "Sharer.h"
23 | #include "Utils.h"
24 | #include "RDPSessionEvents.h"
25 |
26 | struct Sharer::Private
27 | {
28 | struct Events : RDPSessionEvents
29 | {
30 | Sharer* mpSelf;
31 | Events(Sharer* self) : mpSelf(self) {}
32 | void OnAttendeeConnected(ComPtr p) override
33 | {
34 | BStr name;
35 | HR_SucceedOrDie hr = p->get_RemoteName(&name);
36 | if (name == L"Viewer")
37 | hr = p->put_ControlLevel(CTRL_LEVEL_VIEW);
38 | else
39 | hr = p->put_ControlLevel(CTRL_LEVEL_INTERACTIVE);
40 | if (mpSelf->p->mAttendees < 0)
41 | mpSelf->p->mAttendees = 1;
42 | else
43 | ++mpSelf->p->mAttendees;
44 | }
45 | void OnAttendeeDisconnected(ComPtr) override
46 | {
47 | --mpSelf->p->mAttendees;
48 | }
49 | void OnControlLevelChangeRequest(ComPtr p, CTRL_LEVEL level) override
50 | {
51 | HR_SucceedOrDie hr = p->put_ControlLevel(level);
52 | }
53 | };
54 | ComPtr mpSession;
55 | ComPtr mpEvents;
56 |
57 | RECT mRect;
58 | int mPort;
59 | std::string mConnectionString;
60 | int mAttendees;
61 | bool mStarted;
62 |
63 | Private( Sharer* pSelf )
64 | : mPort(0), mAttendees(-1), mStarted(false)
65 | {
66 | ::memset(&mRect, 0, sizeof(mRect));
67 | }
68 | };
69 |
70 | Sharer::Sharer()
71 | : p( new Private(this) )
72 | {
73 | }
74 |
75 | Sharer::~Sharer()
76 | {
77 | delete p;
78 | }
79 |
80 | Sharer&
81 | Sharer::SetRect(const RECT& r)
82 | {
83 | p->mRect = r;
84 | return *this;
85 | }
86 |
87 | const RECT&
88 | Sharer::Rect() const
89 | {
90 | return p->mRect;
91 | }
92 |
93 | Sharer&
94 | Sharer::SetPort(int i)
95 | {
96 | p->mPort = i;
97 | return *this;
98 | }
99 |
100 | int
101 | Sharer::Port() const
102 | {
103 | return p->mPort;
104 | }
105 |
106 | const std::string&
107 | Sharer::ConnectionString() const
108 | {
109 | return p->mConnectionString;
110 | }
111 |
112 | bool
113 | Sharer::LastAttendeeDisconnected() const
114 | {
115 | return p->mAttendees == 0;
116 | }
117 |
118 | Sharer&
119 | Sharer::Start(int maxAttendees)
120 | {
121 | if (p->mStarted)
122 | Stop();
123 | HR_SucceedOrDie hr = ::CoCreateInstance(
124 | CLSID_RDPSession, nullptr,
125 | CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
126 | IID_IRDPSRAPISharingSession, (void**)&p->mpSession
127 | );
128 | ComPtr pCPC;
129 | hr = p->mpSession->QueryInterface(&pCPC);
130 | ComPtr pCP;
131 | hr = pCPC->FindConnectionPoint(DIID__IRDPSessionEvents, &pCP);
132 | DWORD dwCookie = 0;
133 | p->mpEvents = ComPtr(new Private::Events(this));
134 | hr = pCP->Advise(p->mpEvents, &dwCookie);
135 |
136 | p->mAttendees = -1;
137 | p->mConnectionString.clear();
138 |
139 | ComPtr pProperties;
140 | hr = p->mpSession->get_Properties(&pProperties);
141 | hr = pProperties->put_Property(BStr(L"DrvConAttach"), Variant(true));
142 | hr = pProperties->put_Property(BStr(L"PortProtocol"), Variant(AF_INET));
143 | hr = pProperties->put_Property(BStr(L"PortId"), Variant(p->mPort));
144 |
145 | hr = p->mpSession->put_ColorDepth(24);
146 | hr = p->mpSession->SetDesktopSharedRect(
147 | p->mRect.left, p->mRect.top,
148 | p->mRect.right, p->mRect.bottom
149 | );
150 |
151 | hr = p->mpSession->Open();
152 | ComPtr pInvitations;
153 | hr = p->mpSession->get_Invitations(&pInvitations);
154 | ComPtr pInvitation;
155 | hr = pInvitations->CreateInvitation(nullptr, BStr(L""), BStr(L""), maxAttendees, &pInvitation);
156 | BStr connectionString;
157 | hr = pInvitation->get_ConnectionString(&connectionString);
158 | p->mConnectionString = connectionString.ToUtf8();
159 | p->mStarted = true;
160 | return *this;
161 | }
162 |
163 | Sharer&
164 | Sharer::Stop()
165 | {
166 | if (p->mStarted)
167 | {
168 | p->mStarted = false;
169 | p->mAttendees = 0;
170 | p->mConnectionString.clear();
171 | HR_SucceedOrDie hr = p->mpSession->Close();
172 | }
173 | return *this;
174 | }
175 |
176 | Sharer&
177 | Sharer::Suspend()
178 | {
179 | HR_SucceedOrDie(p->mpSession->Pause());
180 | return *this;
181 | }
182 |
183 | Sharer&
184 | Sharer::Resume()
185 | {
186 | HR_SucceedOrDie(p->mpSession->Resume());
187 | return *this;
188 | }
189 |
--------------------------------------------------------------------------------
/Sharer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * RDPShare: Windows Desktop Sharing remote control interface
3 | *
4 | * Copyright 2016 Simul Piscator
5 | *
6 | * RDPShare is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * RDPShare is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with RDPShare. If not, see .
18 | */
19 | #ifndef SHARER_H
20 | #define SHARER_H
21 |
22 | #include
23 | #include
24 |
25 | class Sharer
26 | {
27 | public:
28 | Sharer();
29 | ~Sharer();
30 |
31 | Sharer& SetRect(const RECT&);
32 | const RECT& Rect() const;
33 |
34 | Sharer& SetPort(int);
35 | int Port() const;
36 |
37 | const std::string& ConnectionString() const;
38 | bool LastAttendeeDisconnected() const;
39 |
40 | Sharer& Start(int maxAttendees = INT32_MAX );
41 | Sharer& Stop();
42 | Sharer& Suspend();
43 | Sharer& Resume();
44 |
45 | private:
46 | struct Private;
47 | Private* p;
48 | };
49 |
50 | #endif // SHARER_H
51 |
--------------------------------------------------------------------------------
/Utils.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * RDPShare: Windows Desktop Sharing remote control interface
3 | *
4 | * Copyright 2016 Simul Piscator
5 | *
6 | * RDPShare is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * RDPShare is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with RDPShare. If not, see .
18 | */
19 | #include "Utils.h"
20 | #include
21 | #include
22 |
23 | std::string
24 | StringReplace(const std::string& what, const std::string& with, const std::string& in)
25 | {
26 | std::string s = in;
27 | for (size_t pos = s.npos; (pos = s.find(what)) != s.npos; )
28 | s = s.substr(0, pos) + with + s.substr(pos + what.length());
29 | return s;
30 | }
31 |
32 | std::string
33 | GetErrorText(DWORD err)
34 | {
35 | DWORD winerr = err;
36 | if (HRESULT_FACILITY(err) == FACILITY_WIN32)
37 | winerr = HRESULT_CODE(err);
38 | else if (err & 0xffff0000)
39 | winerr = 0;
40 | std::ostringstream oss;
41 | oss << "Error 0x" << std::hex << err;
42 | std::string s = oss.str();
43 | char* text = nullptr;
44 | if (winerr)
45 | ::FormatMessageA(
46 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
47 | nullptr, winerr, 0, (LPSTR)&text, 0, 0
48 | );
49 | if (text)
50 | {
51 | s.append(":\n").append(text);
52 | ::HeapFree(::GetProcessHeap(), 0, text);
53 | }
54 | return s;
55 | }
56 |
57 | std::string GetProgramName()
58 | {
59 | char buf1[MAX_PATH + 1];
60 | ::GetModuleFileNameA(NULL, buf1, sizeof(buf1));
61 | int length = ::GetLongPathNameA(buf1, nullptr, 0);
62 | std::vector buf2(length);
63 | ::GetLongPathNameA(buf1, buf2.data(), length);
64 | const char* begin = ::PathFindFileNameA(buf2.data()), *end = ::PathFindExtensionA(begin);
65 | return std::string(begin, end - begin);
66 | }
67 |
68 | bool
69 | GetScreenRect(unsigned int screen, RECT* pRect)
70 | {
71 | if (screen == 0)
72 | {
73 | pRect->left = ::GetSystemMetrics(SM_XVIRTUALSCREEN);
74 | pRect->top = ::GetSystemMetrics(SM_YVIRTUALSCREEN);
75 | pRect->right = pRect->left + ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
76 | pRect->bottom = pRect->top + ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
77 | return true;
78 | }
79 | struct MonitorEnum
80 | {
81 | static BOOL CALLBACK Proc(HMONITOR, HDC, LPRECT pRect, LPARAM pData)
82 | {
83 | MonitorEnum* this_ = (MonitorEnum*)pData;
84 | if (--this_->count == 0)
85 | {
86 | *this_->pRect = *pRect;
87 | return FALSE;
88 | }
89 | return TRUE;
90 | }
91 | unsigned int count;
92 | RECT* pRect;
93 | } monitorEnum = { screen, pRect };
94 | ::EnumDisplayMonitors(NULL, nullptr, &MonitorEnum::Proc, (LPARAM)&monitorEnum);
95 | return monitorEnum.count == 0;
96 | }
97 |
98 | int
99 | SetScreenSize(unsigned int screen, int width, int height)
100 | {
101 | if (screen == 0)
102 | return false;
103 | struct MonitorEnum
104 | {
105 | static BOOL CALLBACK Proc(HMONITOR hMonitor, HDC, LPRECT, LPARAM pData)
106 | {
107 | MonitorEnum* this_ = (MonitorEnum*)pData;
108 | if (--this_->count == 0)
109 | {
110 | MONITORINFOEXA info = {};
111 | info.cbSize = sizeof(MONITORINFOEXA);
112 | if (::GetMonitorInfoA(hMonitor, &info))
113 | {
114 | DEVMODEA mode = {};
115 | mode.dmSize = sizeof(DEVMODEA);
116 | if(::EnumDisplaySettingsA(info.szDevice, ENUM_CURRENT_SETTINGS, &mode))
117 | {
118 | mode.dmPelsWidth = this_->width;
119 | mode.dmPelsHeight = this_->height;
120 | this_->result = ::ChangeDisplaySettingsExA(info.szDevice, &mode, NULL, 0, NULL);
121 | }
122 | }
123 | return FALSE;
124 | }
125 | return TRUE;
126 | }
127 | unsigned int count;
128 | int width, height;
129 | int result;
130 | } monitorEnum = { screen, width, height, DISP_CHANGE_BADPARAM };
131 | ::EnumDisplayMonitors(NULL, nullptr, &MonitorEnum::Proc, (LPARAM)&monitorEnum);
132 | return monitorEnum.result;
133 | }
134 |
135 | std::string UrlEscape_(const std::string& in, const std::string& allow)
136 | {
137 | std::string out;
138 | for (auto c : in)
139 | {
140 | if (::isalnum(c) || allow.find(c) != allow.npos)
141 | out += c;
142 | else
143 | {
144 | out += '%';
145 | std::ostringstream oss;
146 | oss.width(2);
147 | oss.fill('0');
148 | oss << std::hex << int(c);
149 | out += oss.str();
150 | }
151 | }
152 | return out;
153 | }
154 |
155 | std::istream&
156 | QuotedString::Unserialize( std::istream& is )
157 | {
158 | std::string& self = *static_cast(this);
159 | is >> std::ws;
160 | if (is.peek() == '"')
161 | return std::getline(is.ignore(), self, '"');
162 | return is >> self;
163 | }
164 |
--------------------------------------------------------------------------------
/Utils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * RDPShare: Windows Desktop Sharing remote control interface
3 | *
4 | * Copyright 2016 Simul Piscator
5 | *
6 | * RDPShare is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * RDPShare is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with RDPShare. If not, see .
18 | */
19 | #ifndef UTILS_H
20 | #define UTILS_H
21 |
22 | #include
23 | #include
24 | #include
25 |
26 | struct WSA_SucceedOrDie
27 | {
28 | WSA_SucceedOrDie(int r = 0) : r(r) { if (r<0) throw *this; }
29 | operator int() const { return r; }
30 | int r;
31 | };
32 |
33 | struct HR_SucceedOrDie
34 | {
35 | HR_SucceedOrDie(HRESULT hr = 0) : hr( hr ) { if (FAILED(hr)) throw *this; }
36 | operator HRESULT() { return hr; }
37 | HRESULT hr;
38 | };
39 |
40 | struct BOOL_SucceedOrDie
41 | {
42 | BOOL_SucceedOrDie(BOOL b = TRUE) : b(b) { if (!b) { error = ::GetLastError(); throw *this; } }
43 | operator BOOL() { return b; }
44 | BOOL b;
45 | DWORD error;
46 | };
47 |
48 | template class ComPtr
49 | {
50 | public:
51 | ComPtr() : p( nullptr ) {}
52 | ComPtr(const ComPtr& other) : p(nullptr) { operator=(other); }
53 | explicit ComPtr(T* p) : p(p) { AddRef(); }
54 | ~ComPtr() { Release(); }
55 | void Reset() { Release(); p = nullptr; }
56 | ComPtr& operator=(const ComPtr& other) { other.AddRef(); Release(); p = other.p; return *this; }
57 | T** operator&() { Reset(); return &p; }
58 | T* operator->() { return p; }
59 | const T* operator->() const { return p; }
60 | operator T*() const { return p; }
61 | operator const T*() { return p; }
62 | private:
63 | void AddRef() const { if (p) p->AddRef(); }
64 | void Release() { if (p) p->Release(); }
65 | T* p;
66 | };
67 |
68 | class BStr
69 | {
70 | public:
71 | BStr() : data(nullptr) {}
72 | BStr(const wchar_t* str) : data(::SysAllocString(str)) {}
73 | BStr(const std::string& s) :data(nullptr) { FromUtf8(s); }
74 | BStr& operator=(const std::string& s) { return FromUtf8(s); }
75 | ~BStr() { ::SysFreeString(data); }
76 | operator BSTR() { return data; }
77 | BSTR* operator&() { ::SysFreeString(data); data = nullptr; return &data; }
78 | bool operator==(const wchar_t* p) const { return std::wstring(p) == data; }
79 | std::string ToUtf8() const
80 | {
81 | std::string s;
82 | if (data)
83 | {
84 | int size = ::WideCharToMultiByte(CP_UTF8, 0, data, -1, 0, 0, 0, 0);
85 | s.resize(size);
86 | ::WideCharToMultiByte(CP_UTF8, 0, data, -1, const_cast(s.data()), size, 0, 0);
87 | if (s.size() > 0)
88 | s.resize(s.size() - 1);
89 | }
90 | return s;
91 | }
92 | BStr& FromUtf8(const std::string& s)
93 | {
94 | ::SysFreeString(data);
95 | int size = ::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, 0, 0);
96 | std::vector buf(size);
97 | ::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buf.data(), size);
98 | data = ::SysAllocString(buf.data());
99 | return *this;
100 | }
101 |
102 | private:
103 | BSTR data;
104 | };
105 |
106 | struct Variant : VARIANT
107 | {
108 | Variant(bool b) { VARIANT::boolVal = b ? VARIANT_TRUE : VARIANT_FALSE; VARIANT::vt = VT_BOOL; }
109 | Variant(int i) { VARIANT::intVal = i; VARIANT::vt = VT_I4; }
110 | };
111 |
112 | struct QuotedString : std::string
113 | {
114 | std::istream& Unserialize(std::istream&);
115 | std::ostream& Serialize(std::ostream&) const;
116 | };
117 |
118 | inline std::istream& operator>>(std::istream& is, QuotedString& s) { return s.Unserialize(is); }
119 | inline std::ostream& operator<<(std::ostream& os, const QuotedString& s) { return s.Serialize(os); }
120 |
121 |
122 |
123 | std::string StringReplace(const std::string& what, const std::string& with, const std::string&);
124 | std::string GetErrorText(DWORD);
125 | std::string GetProgramName();
126 | bool GetScreenRect(unsigned int screen, RECT* pRect);
127 | int SetScreenSize(unsigned int screen, int width, int height);
128 | std::string UrlEscape_(const std::string& in, const std::string& allow = "");
129 |
130 | #endif // UTILS_H
131 |
--------------------------------------------------------------------------------