转载请标明是引用于 http://blog.csdn.net/chenyujing1234
此工程编译出winvnc4.exe二进制文件。它得到的二进制是作为VNC服务端来使用的。
一、从main说起
int main(int argc, const char* argv[]) {
int result = 0;
try {
// - Initialise the available loggers
//freopen("\\\\drupe\\tjr\\WinVNC4.log","ab",stderr);
//setbuf(stderr, 0);
initStdIOLoggers();
initFileLogger("C:\\temp\\WinVNC4.log");
rfb::win32::initEventLogLogger(VNCServerService::Name);
// - By default, just log errors to stderr
logParams.setParam("*:stderr:0");
// - Print program details and process the command line
programInfo();
processParams(argc, argv);
// - Run the server if required
if (runServer) {
if (close_console) {
vlog.info("closing console");
if (!FreeConsole())
vlog.info("unable to close console:%u", GetLastError());
}
network::TcpSocket::initTcpSockets();
VNCServerWin32 server;
if (runAsService) {
printf("Starting Service-Mode VNC Server.\n");
VNCServerService service(server);
///>启动服务
service.start();
result = service.getStatus().dwWin32ExitCode;
} else {
printf("Starting User-Mode VNC Server.\n");
///>启动server
result = server.run();
}
}
vlog.debug("WinVNC service destroyed");
} catch (rdr::Exception& e) {
try {
vlog.error("Fatal Error: %s", e.str());
} catch (...) {
fprintf(stderr, "WinVNC: Fatal Error: %s\n", e.str());
}
if (!runAsService)
MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK);
}
vlog.debug("WinVNC process quitting");
return result;
}
步一、打印信息设置
步二、打印程序详细信息
programInfo();
步三、处理命令行
processParams(argc, argv);
void
processParams(int argc, const char* argv[]) {
for (int i=1; i<argc; i++) {
try {
if (strcasecmp(argv[i], "-connect") == 0) {
runServer = false;
CharArray host;
if (i+1 < argc) {
host.buf = strDup(argv[i+1]);
} else {
AddNewClientDialog ancd;
if (ancd.showDialog())
host.buf = strDup(ancd.getHostName());
}
if (host.buf) {
HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));
COPYDATASTRUCT copyData;
copyData.dwData = 1; // *** AddNewClient
copyData.cbData = strlen(host.buf);
copyData.lpData = (void*)host.buf;
i++;
SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data);
printf("Sent connect request to VNC Server...\n");
}
} else if (strcasecmp(argv[i], "-disconnect") == 0) {
HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));
COPYDATASTRUCT copyData;
copyData.dwData = 2; // *** DisconnectClients
copyData.lpData = 0;
copyData.cbData = 0;
SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data);
printf("Sent disconnect request to VNC Server...\n");
runServer = false;
} else if (strcasecmp(argv[i], "-start") == 0) {
printf("Attempting to start service...\n");
runServer = false;
if (rfb::win32::startService(VNCServerService::Name))
printf("Started service successfully\n");
} else if (strcasecmp(argv[i], "-stop") == 0) {
printf("Attempting to stop service...\n");
runServer = false;
if (rfb::win32::stopService(VNCServerService::Name))
printf("Stopped service successfully\n");
} else if (strcasecmp(argv[i], "-status") == 0) {
printf("Querying service status...\n");
runServer = false;
rfb::win32::printServiceStatus(VNCServerService::Name);
} else if (strcasecmp(argv[i], "-service") == 0) {
printf("Run in service mode\n");
runAsService = true;
} else if (strcasecmp(argv[i], "-register") == 0) {
printf("Attempting to register service...\n");
runServer = false;
int j = i;
i = argc;
if (rfb::win32::registerService(VNCServerService::Name,
_T("VNC Server Version 4"),
argc-(j+1), &argv[j+1]))
printf("Registered service successfully\n");
} else if (strcasecmp(argv[i], "-unregister") == 0) {
printf("Attempting to unregister service...\n");
runServer = false;
if (rfb::win32::unregisterService(VNCServerService::Name))
printf("Unregistered service successfully\n");
} else if (strcasecmp(argv[i], "-noconsole") == 0) {
close_console = true;
} else if ((strcasecmp(argv[i], "-help") == 0) ||
(strcasecmp(argv[i], "--help") == 0) ||
(strcasecmp(argv[i], "-h") == 0) ||
(strcasecmp(argv[i], "/?") == 0)) {
runServer = false;
programUsage();
break;
} else {
// Try to process <option>=<value>, or -<bool>
if (Configuration::setParam(argv[i], true))
continue;
// Try to process -<option> <value>
if ((argv[i][0] == '-') && (i+1 < argc)) {
if (Configuration::setParam(&argv[i][1], argv[i+1], true)) {
i++;
continue;
}
}
// Nope. Show them usage and don't run the server
runServer = false;
programUsage();
break;
}
} catch (rdr::Exception& e) {
vlog.error(e.str());
}
}
}
(1)从命令行中提取出IP地址串,并组成把它放到COPYDATASTRUCT中。
找到窗口_T("winvnc::IPC_Interface"),将得到的COPYDATASTRUCT数据发送到窗口中。
因为在服务端此窗口的创建是在 “第一个构造函数:”, 而此时窗口一定是找不到的,那么还有一种情况可以找到就是VNC服务端与VNC客户端并存时就会出现。
这是因为VNC客户端也会创建此窗口。
那么可以说,这时的发送WM_COPYDATA其实是发送给客户端的。
if (host.buf) {
HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));
COPYDATASTRUCT copyData;
copyData.dwData = 1; // *** AddNewClient
copyData.cbData = strlen(host.buf);
copyData.lpData = (void*)host.buf;
i++;
SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)©Data);
printf("Sent connect request to VNC Server...\n");
}
(2)
(3)
(4)
(5)
(6)
(7)
步四、启动服务
有两种方式可以选择:服务方式、用户模式(!!!!!!!!最重要)
if (runAsService) {
printf("Starting Service-Mode VNC Server.\n");
VNCServerService service(server);
///>启动服务
service.start();
result = service.getStatus().dwWin32ExitCode;
} else {
printf("Starting User-Mode VNC Server.\n");
///>启动server
result = server.run();
}
启动时得保证
runServer == true;
runServer的初始值是static bool runServer = true;,当我们有指定命令行时,此变量会变为false。
如在 “步三、处理命令行" ,命令行是“-connect”、"-disconnect"、“-start”、"-stop"、"-status"、"-register"、"-unregister"、"-help"
意思是如果指定了命令行,那么是不会启动服务的。
如果是要关闭console界面那么调用系统API
if (!FreeConsole())
来实现
(1)以服务方式启动客户端
VNCServerWin32 server;
if (runAsService) {
printf("Starting Service-Mode VNC Server.\n");
VNCServerService service(server);
///>启动服务
service.start();
result = service.getStatus().dwWin32ExitCode;
(2)以用户模式启动客户端服务
VNCServerWin32 server;
result = server.run();
///>服务器启动
int VNCServerWin32::run() {
{ Lock l(runLock);
hostThread = Thread::self();
runServer = true;
}
// - Register for notification of configuration changes
///>注册表设置
if (isServiceProcess())
config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
else
config.setKey(HKEY_CURRENT_USER, RegConfigPath);
config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);
// - Create the tray icon if possible
STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY);
DWORD result = 0;
try {
// - Create some managed listening sockets
///>创建监听socket
ManagedListener rfb(&sockMgr, &vncServer);
ManagedListener http(&sockMgr, httpServer);
// - Continue to operate until WM_QUIT is processed
MSG msg;
do {
// -=- Make sure we're listening on the right ports.
///>设置监听端口
rfb.setPort(port_number, localHost);
http.setPort(http_port, localHost);
// -=- Update the Java viewer's web page port number.
httpServer->setRFBport(rfb.sock ? port_number : 0);
// -=- Update the TCP address filter for both ports, if open.
CharArray pattern;
pattern.buf = hosts.getData();
if (!localHost) {
rfb.setFilter(pattern.buf);
http.setFilter(pattern.buf);
}
// - If there is a listening port then add the address to the
// tray icon's tool-tip text.
{
const TCHAR* prefix = isServiceProcess() ?
_T("VNC Server (Service):") : _T("VNC Server (User):");
std::list<char*> addrs;
if (rfb.sock)
rfb.sock->getMyAddresses(&addrs);
else
addrs.push_front(strDup("Not accepting connections"));
std::list<char*>::iterator i, next_i;
int length = _tcslen(prefix)+1;
for (i=addrs.begin(); i!= addrs.end(); i++)
length += strlen(*i) + 1;
TCharArray toolTip(length);
_tcscpy(toolTip.buf, prefix);
for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
next_i = i; next_i ++;
TCharArray addr = *i; // Assumes ownership of string
_tcscat(toolTip.buf, addr.buf);
if (next_i != addrs.end())
_tcscat(toolTip.buf, _T(","));
}
trayIcon.setToolTip(toolTip.buf);
}
vlog.debug("Entering message loop");
// - Run the server until the registry changes, or we're told to quit
///>进入socket监听,进入消息循环
while (sockMgr.getMessage(&msg, NULL, 0, 0)) {
if (msg.hwnd == 0) {
if (msg.message == VNCM_REG_CHANGED)
break;
if (msg.message == VNCM_COMMAND)
doCommand();
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} while ((msg.message != WM_QUIT) || runServer);
vlog.debug("Server exited cleanly");
} catch (rdr::SystemException &s) {
vlog.error(s.str());
result = s.err;
} catch (rdr::Exception &e) {
vlog.error(e.str());
}
{ Lock l(runLock);
runServer = false;
hostThread = 0;
}
return result;
}
(2、1)注册表设置,并向线程发送NCM_REG_CHANGED消息
if (isServiceProcess())
config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
else
config.setKey(HKEY_CURRENT_USER, RegConfigPath);
config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);
发送的消息最后是在 “(2、4)进入一个while的操作,直到收到WM_QUIT后才退出” 中被收到的
(2、2) 创建 tray icon
// - Create the tray icon if possible
STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY);
创建时会调用STrayIconThread的构造函数:
STrayIconThread::STrayIconThread(VNCServerWin32& sm, UINT inactiveIcon_, UINT activeIcon_, UINT menu_)
: server(sm), inactiveIcon(inactiveIcon_), activeIcon(activeIcon_), menu(menu_),
windowHandle(0), runTrayIcon(true) {
start();
}
因为STrayIconThread继承自Thread,所以这里的start其实会调用到STrayIconThread::run()
void STrayIconThread::run() {
while (runTrayIcon) {
if (rfb::win32::desktopChangeRequired() &&
!rfb::win32::changeDesktop())
Sleep(2000);
STrayIcon icon(*this);
windowHandle = icon.getHandle();
MSG msg;
while (runTrayIcon && ::GetMessage(&msg, 0, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
windowHandle = 0;
}
}
(2、2、1)
if (rfb::win32::desktopChangeRequired() &&
!rfb::win32::changeDesktop())
Sleep(2000);
(2、2、2)创建STrayIcon,并获得它的窗口句柄。
STrayIcon icon(*this);
windowHandle = icon.getHandle();
STrayIcon继承自TrayIcon, TrayIcon继承自MsgWindow
class winvnc::STrayIcon : public TrayIcon
class TrayIcon : public MsgWindow
class MsgWindow {
public:
MsgWindow(const TCHAR* _name);
virtual ~MsgWindow();
const TCHAR* getName() {return name.buf;}
HWND getHandle() const {return handle;}
virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
protected:
TCharArray name;
HWND handle;
};
创建STrayIcon时会调用到三个构造函数:
第一个构造函数:
MsgWindow的构造函数就是创建一个窗口,窗口名称为"VNCTray",大小为10*10;
MsgWindow::MsgWindow(const TCHAR* name_) : name(tstrDup(name_)), handle(0) {
vlog.debug("creating window \"%s\"", (const char*)CStr(name.buf));
handle = CreateWindow((const TCHAR*)baseClass.classAtom, name.buf, WS_OVERLAPPED,
0, 0, 10, 10, 0, 0, baseClass.instance, this);
if (!handle) {
throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
}
vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name.buf), handle);
}
窗口类名为(const TCHAR*)baseClass.classAtom; _T("rfb::win32::MsgWindowClass")
baseClass是类MsgWindowClass,它用来管理窗口类。
MsgWindowClass::MsgWindowClass() : classAtom(0) {
WNDCLASS wndClass;
wndClass.style = 0;
wndClass.lpfnWndProc = MsgWindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = instance = GetModuleHandle(0);
wndClass.hIcon = 0;
wndClass.hCursor = 0;
wndClass.hbrBackground = 0;
wndClass.lpszMenuName = 0;
wndClass.lpszClassName = _T("rfb::win32::MsgWindowClass");
classAtom = RegisterClass(&wndClass);
if (!classAtom) {
throw rdr::SystemException("unable to register MsgWindow window class", GetLastError());
}
}
特别重要的是它的
窗口消息处理函数MsgWindowProc。
如果消息是在创建窗口时,即CreateWindows时,会收到消息WM_CREATE,此时把(long)((CREATESTRUCT*)lParam)->lpCreateParams,即MsgWindow的this指针作为
窗口数据存起来。之后的MsgWindowsProc中对各消息的处理就是取出窗口中的数据,强转为MsgWindows后,调用它的processMessage 函数,
因为MsgWindows类中的processMessage成员是虚函数,所以如果继承类有去重载它,那么是只会调用了继承类的processMessage函数。如:
STrayIcon的成员processMessage.
LRESULT CALLBACK MsgWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
LRESULT result;
if (msg == WM_CREATE)
SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
else if (msg == WM_DESTROY)
SetWindowLong(wnd, GWL_USERDATA, 0);
MsgWindow* _this = (MsgWindow*) GetWindowLong(wnd, GWL_USERDATA);
if (!_this) {
vlog.info("null _this in %x, message %x", wnd, msg);
return SafeDefWindowProc(wnd, msg, wParam, lParam);
}
try {
result = _this->processMessage(msg, wParam, lParam);
} catch (rdr::Exception& e) {
vlog.error("untrapped: %s", e.str());
}
return result;
};
第二个构造函数:
TrayIcon() : MsgWindow(_T("VNCTray")) {
#ifdef NOTIFYICONDATA_V1_SIZE
nid.cbSize = NOTIFYICONDATA_V1_SIZE;
#else
nid.cbSize = sizeof(NOTIFYICONDATA);
#endif
nid.hWnd = getHandle();
nid.uID = 0;
nid.hIcon = 0;
nid.uFlags = NIF_ICON | NIF_MESSAGE;
nid.uCallbackMessage = WM_USER;
}
第三个构造函数:STrayIcon构造函数
STrayIcon(STrayIconThread& t) : thread(t),
vncConfig(_T("vncconfig.exe"), isServiceProcess() ? _T("-noconsole -service") : _T("-noconsole")),
vncConnect(_T("winvnc4.exe"), _T("-connect")) {
// ***
SetWindowText(getHandle(), _T("winvnc::IPC_Interface"));
// ***
SetTimer(getHandle(), 1, 3000, 0);
PostMessage(getHandle(), WM_TIMER, 1, 0);
PostMessage(getHandle(), WM_SET_TOOLTIP, 0, 0);
}
在STrayIcon的构造函数中:
首先构造两对象
LaunchProcess vncConfig;
LaunchProcess vncConnect;
作用:在窗口收到消息WM_COMMAND,命令为ID_CONNECT时就连接服务端:
CurrentUserToken token;
if (token.isValid())
vncConnect.start(isServiceProcess() ? (HANDLE)token : 0);
else
vlog.error("Options: unknown current user");
void LaunchProcess::start(HANDLE userToken) {
if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0))
return;
await();
// - Create storage for the process startup information
STARTUPINFO sinfo;
memset(&sinfo, 0, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
// - Concoct a suitable command-line
TCharArray exePath;
if (!tstrContains(exeName.buf, _T('\\'))) {
ModuleFileName filename;
TCharArray path; splitPath(filename.buf, &path.buf, 0);
exePath.buf = new TCHAR[_tcslen(path.buf) + _tcslen(exeName.buf) + 2];
_stprintf(exePath.buf, _T("%s\\%s"), path.buf, exeName.buf);
} else {
exePath.buf = tstrDup(exeName.buf);
}
// - Start the VNC server
// Note: We specify the exe's precise path in the ApplicationName parameter,
// AND include the name as the first part of the CommandLine parameter,
// because CreateProcess doesn't make ApplicationName argv[0] in C programs.
TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1);
_stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf);
#ifdef _DEBUG
DWORD flags = CREATE_NEW_CONSOLE;
#else
DWORD flags = CREATE_NO_WINDOW;
#endif
BOOL success;
if (userToken)
success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
else
success = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
if (!success)
throw rdr::SystemException("unable to launch process", GetLastError());
// Wait for it to finish initialising
WaitForInputIdle(procInfo.hProcess, 15000);
}
然后,设置窗口名称为_T("winvnc::IPC_Interface"
然后开启定时器1,每3S触发一次;
最后给窗口发送WM_TIMER与WM_SET_TOOLTIP消息;
作用:WM_TIMER用于刷新图标状态
如果桌面不是输入桌面,那么就把图标关闭,如果(“当前桌面和名称”与"输入桌面名称“是否一致来判断);
如果桌面还是存在那么更新当前的图标状态。(激活或非激活)
if (rfb::win32::desktopChangeRequired()) {
SendMessage(getHandle(), WM_CLOSE, 0, 0);
return 0;
}
setIcon(thread.server.isServerInUse() ? thread.activeIcon : thread.inactiveIcon);
return 0;
WM_SET_TOOLTIP用于设置”工具提示信息“
case WM_SET_TOOLTIP:
{
rfb::Lock l(thread.lock);
if (thread.toolTip.buf)
setToolTip(thread.toolTip.buf);
}
(2、2、3)消息循环
MSG msg;
while (runTrayIcon && ::GetMessage(&msg, 0, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
(2、3)创建监听socket
ManagedListener rfb(&sockMgr, &vncServer);
ManagedListener http(&sockMgr, httpServer);
(2、4)进入一个while的操作,直到收到WM_QUIT后才退出
MSG msg;
do {
// 第一件事:设置监听端口
// 第二件事:Update the Java viewer's web page port number
// Update the TCP address filter for both ports, if open.
//
// 第三件事:如果有一个正在监听的端口,那么就增加地址到icon的tooltip(提示信息)
//
// - Run the server until the registry changes, or we're told to quit
///>进入socket监听,进入消息循环
while (sockMgr.getMessage(&msg, NULL, 0, 0)) {
if (msg.hwnd == 0) {
if (msg.message == VNCM_REG_CHANGED)
break;
if (msg.message == VNCM_COMMAND)
doCommand();
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} while ((msg.message != WM_QUIT) || runServer);
这里最重要的是 sockMgr.getMessage(&msg, NULL, 0, 0 函数!!!!!!!!
三、扩展知识
1、TLSAlloc()
WINBASEAPI
BOOL
WINAPI
TlsSetValue(
__in DWORD dwTlsIndex,
__in_opt LPVOID lpTlsValue
);
DWORD WINAPI
Thread::threadProc(LPVOID lpParameter) {
Thread* thread = (Thread*) lpParameter;
TlsSetValue(threadStorage, thread);
logAction(thread, "started");
try {
thread->run();
logAction(thread, "stopped");
} catch (rdr::Exception& e) {
logError(thread, e.str());
}
bool deleteThread = false;
{
Lock l(thread->mutex);
thread->state = ThreadStopped;
thread->sig->signal();
deleteThread = thread->deleteAfterRun;
}
if (deleteThread)
delete thread;
return 0;
}
WINBASEAPI
LPVOID
WINAPI
TlsGetValue(
__in DWORD dwTlsIndex
);
Thread*
Thread::self() {
Thread* thread = (Thread*) TlsGetValue(threadStorage);
if (!thread) {
thread = new Thread(GetCurrentThread(), GetCurrentThreadId());
TlsSetValue(threadStorage, thread);
}
return thread;
}
2、__declspec(thread)的使用
#include <stdio.h>
#include <assert.h>
// 这就是两个线程都要访问的变量
__declspec(thread) int g_nData = 0;
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
g_nData = 5;
// 辅线程睡眠100ms,保证主线程的g_nData = 10;语句执行成功
Sleep(100);
TCHAR szMsg[100] = {0};
wsprintf(szMsg, L"Auxi thread, g_nData:%d\n", g_nData);
MessageBox(NULL, szMsg, L"AuxiThread", MB_ICONINFORMATION);
return 0;
}
int main()
{
DWORD dwId;
// 创建线程,并立即启动它
HANDLE hThread = CreateThread(NULL, 1024, ThreadProc, NULL, 0, &dwId);
assert(hThread);
// 主线程睡50ms,保证辅线程的g_nData = 5语句执行成功。
Sleep(50);
g_nData = 10;
TCHAR szMsg[100] = {0};
wsprintf(szMsg, L"Result %d\n", g_nData);
MessageBox(NULL, szMsg, L"MainThread", MB_ICONINFORMATION);
return 0;
}