转载请标明是引用于 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 stderrlogParams.setParam("*:stderr:0");// - Print program details and process the command lineprogramInfo();processParams(argc, argv);// - Run the server if requiredif (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");///>启动serverresult = 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; // *** AddNewClientcopyData.cbData = strlen(host.buf);copyData.lpData = (void*)host.buf;i++;SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData);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; // *** DisconnectClientscopyData.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 serverrunServer = 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; // *** AddNewClientcopyData.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");}

针对此WM_COPYDATA的响应是:

(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");///>启动serverresult = 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);elseconfig.setKey(HKEY_CURRENT_USER, RegConfigPath);config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);// - Create the tray icon if possibleSTrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY);DWORD result = 0;try {// - Create some managed listening sockets///>创建监听socketManagedListener rfb(&sockMgr, &vncServer);ManagedListener http(&sockMgr, httpServer);// - Continue to operate until WM_QUIT is processedMSG 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);elseaddrs.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);elseconfig.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 possibleSTrayIconThread 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_SIZEnid.cbSize = NOTIFYICONDATA_V1_SIZE;
#elsenid.cbSize = sizeof(NOTIFYICONDATA);
#endifnid.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);elsevlog.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 informationSTARTUPINFO sinfo;memset(&sinfo, 0, sizeof(sinfo));sinfo.cb = sizeof(sinfo);// - Concoct a suitable command-lineTCharArray 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 _DEBUGDWORD flags = CREATE_NEW_CONSOLE;
#elseDWORD flags = CREATE_NO_WINDOW;
#endifBOOL success;if (userToken)success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);elsesuccess = 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 initialisingWaitForInputIdle(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()

缩写为TLS。进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG。

如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量(被称为static memory local to a thread 线程局部静态变量),就需要新的机制来实现。这就是TLS。
线程局部存储在不同的平台有不同的实现,可移植性不太好。幸好要实现线程局部存储并不难,最简单的办法就是建立一个全局表,通过当前线程ID去查询相应的数据,因为各个线程的ID不同,查到的数据自然也不同了。
Win32
方法一:每个线程创建时系统给它分配一个LPVOID指针的数组(叫做TLS数组),这个数组从C编程角度是隐藏着的不能直接访问,需要通过一些C API函数调用访问。首先定义些DWORD线程全局变量或函数静态变量,准备作为各个线程访问自己的TLS数组的索引变量。一个线程使用TLS时,第一步在线程内调用TlsAlloc()函数,为一个TLS数组索引变量与这个线程的TLS数组的某个槽(slot)关联起来,例如获得一个索引变量:
global_dwTLSindex=TLSAlloc();
注意,此步之后,当前线程实际上访问的是这个TLS数组索引变量的线程内的拷贝版本。也就说,不同线程虽然看起来用的是同名的TLS数组索引变量,但实际上各个线程得到的可能是不同DWORD值。其意义在于,每个使用TLS的线程获得了一个DWORD类型的线程局部静态变量作为TLS数组的索引变量。C/C++原本没有直接定义线程局部静态变量的机制,所以在如此大费周折。
第二步,为当前线程动态分配一块内存区域(使用LocalAlloc()函数调用),然后把指向这块内存区域的指针放入TLS数组相应的槽中(使用TlsValue()函数调用)。
第三步,在当前线程的任何函数内,都可以通过TLS数组的索引变量,使用TlsGetValue()函数得到上一步的那块内存区域的指针,然后就可以进行内存区域的读写操作了。这就实现了在一个线程内部这个范围处处可访问的变量。
最后,如果不再需要上述线程局部静态变量,要动态释放掉这块内存区域(使用LocalFree()函数),然后从TLS数组中放弃对应的槽(使用TlsFree()函数)。
在VNC-4.0-winsrc开源代码中看到,它用了TSL的方法:
(1)定义
static DWORD threadStorage = TlsAlloc();

为一个TLS数组索引变量与这个线程的TLS数组的某个槽(slot)关联起来,例如获得一个索引变量:

(2)设置值
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;
}

(3)获得值
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;
}

VNC源码研究(二十四)vnc-4.0-winsrc版本之winvnc工程分析相关推荐

  1. VNC源码研究(一)VNC简介

    VNC采用RFB通信协议.RFB ("remote 帧缓存 ") 是一个远程图形用户的简单协议,因为它工作在帧缓存级别上,所以它可以应用于所有的窗口系统,例如:X 11,Windo ...

  2. Spring Cloud 2.2.2 源码之二十九nacos客户端获取配置原理四

    Spring Cloud 2.2.2 源码之二十九nacos客户端获取配置原理四 MetricsHttpAgent的httpGet ServerHttpAgent的httpGet HttpSimple ...

  3. VNC源码研究(一)

    VNC采用RFB通信协议.RFB ("remote 帧缓存 ") 是一个远程图形用户的简单协议,因为它工作在帧缓存级别上,所以它可以应用于所有的窗口系统,例如:X 11,Windo ...

  4. linux 0.11 源码学习(十四)

    文件系统综述 linux 文件系统是基于MINIX 1.0文件系统,这部分的代码量是整个内核里最大的,但代码结构对应着MINIX文件系统的构成,还是比较清晰易读的. MINIX文件系统 MINIX的文 ...

  5. Heritrix 3.1.0 源码解析(十四)

    我在分析BdbFrontier对象的void schedule(CrawlURI caURI).CrawlURI next() .void finished(CrawlURI cURI)方法是,其实还 ...

  6. redis源码剖析(十四)—— dump.rdb文件分析工具

    分析rdb文件的工具 安装 git clone https://github.com/sripathikrishnan/redis-rdb-tools.git sudo pip install --u ...

  7. STL源码剖析学习十四:算法之set相关算法

    STL中定义的set要求元素不得重复且已经排序 set算法要求的都是有序区间,但元素可以重复出现 另外提供的hash版本的set因为其元素无序,因此不能作为set函数的参数 set算法前4个参数分别表 ...

  8. Soul网关源码阅读(十)自定义简单插件编写

    Soul网关源码阅读(十)自定义简单插件编写 简介     综合前面所分析的插件处理流程相关知识,此次我们来编写自定义的插件:统计请求在插件链中的经历时长 编写准备     首先我们先探究一下,一个P ...

  9. Alamofire源码解读系列(十二)之请求(Request)

    本篇是Alamofire中的请求抽象层的讲解 前言 在Alamofire中,围绕着Request,设计了很多额外的特性,这也恰恰表明,Request是所有请求的基础部分和发起点.这无疑给我们一个Req ...

  10. 二、Neo4j源码研究系列 - 单步调试

    二.Neo4j源码研究系列 - 单步调试 一.背景介绍 上一篇我们已经把了neo4j的源码准备以及打包流程完成了,本篇将讲解如何对neo4j进行单步调试.对于不了解如何编译打包neo4j的读者,请阅读 ...

最新文章

  1. C#中Split函数的使用
  2. C#实现(递归和非递归)快速排序和简单排序
  3. opencv 中 快速傅里叶变换 FFT
  4. [Java基础]JDK内置注解
  5. ENSP如何开启服务器的http_Centos\Liunx如何安装MySQL?
  6. Linux 命令(98)—— basename 命令
  7. executorservice 重启_iPhone7使用久了突然手机自动重启,多半原因出在这儿,进来看看...
  8. struts2默认action
  9. python官方帮助文档中文版-python中文版
  10. 计算机基础应用课件,中职计算机应用基础课件
  11. iPhone 二手手机到底去哪了
  12. TPP并不可怕,可怕的是我们开始自我封闭
  13. 华为天才少年主攻存储领域研究
  14. Chaya虚拟机学习 —— 启用Docker遇到的Bug
  15. MPN (Learning Normal Dynamics in Videos with Meta Prototype Network)代码阅读
  16. Plugin with id 'com.novoda.bintray-release' not foun
  17. 飞机躲子弹小游戏案例
  18. 用Altium Designer软件绘制一个stm32最小系统的电路原理图
  19. Kubernetes(k8s)CNI(Calico)网络模型原理
  20. JDP02-[策略模式]-鸭子模型

热门文章

  1. html如何让字体变形,jquery实现字体变形特效-css字体变成圆形
  2. 省市区级联SQL文件(MySQL)
  3. 一些服务器常见漏洞的修复方法
  4. 昼夜系统-游戏中的时间
  5. GitHub上1.5万Star的开源抢票神器!
  6. 中南大学2015年研究生复试淘汰率将超40%
  7. 关于linux的最新问题合集
  8. Spark编程核心抽象—RDD
  9. 父亲将房子过户给后代需要交税吗
  10. 安装 smartgit