22 #include <freerdp/config.h>
24 #include <winpr/windows.h>
25 #include <winpr/library.h>
27 #include <winpr/crt.h>
28 #include <winpr/assert.h>
35 #include <winpr/assert.h>
36 #include <sys/types.h>
39 #ifdef WITH_PROGRESS_BAR
43 #ifdef WITH_WINDOWS_CERT_STORE
47 #include <freerdp/log.h>
48 #include <freerdp/freerdp.h>
49 #include <freerdp/constants.h>
50 #include <freerdp/settings.h>
52 #include <freerdp/locale/keyboard.h>
53 #include <freerdp/codec/region.h>
54 #include <freerdp/client/cmdline.h>
55 #include <freerdp/client/channels.h>
56 #include <freerdp/channels/channels.h>
60 #include "wf_channels.h"
61 #include "wf_graphics.h"
63 #include "resource/resource.h"
65 #define TAG CLIENT_TAG("windows")
67 #define WM_FREERDP_SHOWWINDOW (WM_USER + 100)
69 static BOOL wf_has_console(
void)
71 #ifdef WITH_WIN_CONSOLE
72 int file = _fileno(stdin);
73 int tty = _isatty(file);
79 WLog_INFO(TAG,
"Detected stdin=%d -> %s mode", file, tty ?
"console" :
"gui");
83 static BOOL wf_end_paint(rdpContext* context)
92 wfContext* wfc = (wfContext*)context;
94 ninvalid = gdi->primary->hdc->hwnd->ninvalid;
95 cinvalid = gdi->primary->hdc->hwnd->cinvalid;
100 region16_init(&invalidRegion);
102 for (
int i = 0; i < ninvalid; i++)
104 invalidRect.left = cinvalid[i].x;
105 invalidRect.top = cinvalid[i].y;
106 invalidRect.right = cinvalid[i].x + cinvalid[i].w;
107 invalidRect.bottom = cinvalid[i].y + cinvalid[i].h;
108 region16_union_rect(&invalidRegion, &invalidRegion, &invalidRect);
111 if (!region16_is_empty(&invalidRegion))
113 extents = region16_extents(&invalidRegion);
114 updateRect.left = extents->left;
115 updateRect.top = extents->top;
116 updateRect.right = extents->right;
117 updateRect.bottom = extents->bottom;
119 wf_scale_rect(wfc, &updateRect);
121 InvalidateRect(wfc->hwnd, &updateRect, FALSE);
124 wf_rail_invalidate_region(wfc, &invalidRegion);
127 region16_uninit(&invalidRegion);
131 wfc->is_shown = TRUE;
133 #ifdef WITH_PROGRESS_BAR
134 if (wfc->taskBarList)
136 wfc->taskBarList->lpVtbl->SetProgressState(wfc->taskBarList, wfc->hwnd,
141 PostMessage(wfc->hwnd, WM_FREERDP_SHOWWINDOW, 0, 0);
142 WLog_INFO(TAG,
"Window is shown!");
147 static BOOL wf_begin_paint(rdpContext* context)
151 if (!context || !context->gdi || !context->gdi->primary || !context->gdi->primary->hdc)
154 hdc = context->gdi->primary->hdc;
156 if (!hdc || !hdc->hwnd || !hdc->hwnd->invalid)
159 hdc->hwnd->invalid->null = TRUE;
160 hdc->hwnd->ninvalid = 0;
164 static BOOL wf_desktop_resize(rdpContext* context)
168 rdpSettings* settings;
169 wfContext* wfc = (wfContext*)context;
171 if (!context || !context->settings)
174 settings = context->settings;
178 same = (wfc->primary == wfc->drawing) ? TRUE : FALSE;
179 wf_image_free(wfc->primary);
183 context->gdi->dstFormat, NULL);
188 context->gdi->dstFormat, wfc->primary->pdata, NULL))
192 wfc->drawing = wfc->primary;
194 if (wfc->fullscreen != TRUE)
197 SetWindowPos(wfc->hwnd, HWND_TOP, -1, -1,
204 wf_update_offset(wfc);
205 GetWindowRect(wfc->hwnd, &rect);
206 InvalidateRect(wfc->hwnd, &rect, TRUE);
212 static BOOL wf_pre_connect(freerdp* instance)
217 UINT32 desktopHeight;
219 rdpSettings* settings;
221 WINPR_ASSERT(instance);
222 WINPR_ASSERT(instance->context);
223 WINPR_ASSERT(instance->context->settings);
225 context = instance->context;
226 wfc = (wfContext*)instance->context;
227 settings = context->settings;
237 if (wfc->percentscreen > 0)
239 desktopWidth = (GetSystemMetrics(SM_CXSCREEN) * wfc->percentscreen) / 100;
242 desktopHeight = (GetSystemMetrics(SM_CYSCREEN) * wfc->percentscreen) / 100;
251 desktopWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
252 desktopHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
256 desktopWidth = GetSystemMetrics(SM_CXSCREEN);
257 desktopHeight = GetSystemMetrics(SM_CYSCREEN);
263 desktopWidth = (desktopWidth + 3) & (~3);
280 PubSub_SubscribeChannelConnected(instance->context->pubSub, wf_OnChannelConnectedEventHandler);
281 PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
282 wf_OnChannelDisconnectedEventHandler);
286 static void wf_append_item_to_system_menu(HMENU hMenu, UINT fMask, UINT wID,
const wchar_t* text,
289 MENUITEMINFO item_info = { 0 };
290 item_info.fMask = fMask;
291 item_info.cbSize =
sizeof(MENUITEMINFO);
293 item_info.fType = MFT_STRING;
294 item_info.dwTypeData = _wcsdup(text);
295 item_info.cch = (UINT)_wcslen(text);
297 item_info.dwItemData = (ULONG_PTR)wfc;
298 InsertMenuItem(hMenu, wfc->systemMenuInsertPosition++, TRUE, &item_info);
301 static void wf_add_system_menu(wfContext* wfc)
305 if (wfc->fullscreen && !wfc->fullscreen_toggle)
315 hMenu = GetSystemMenu(wfc->hwnd, FALSE);
317 wf_append_item_to_system_menu(hMenu,
318 MIIM_CHECKMARKS | MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_DATA,
319 SYSCOMMAND_ID_SMARTSIZING, L
"Smart sizing", wfc);
323 CheckMenuItem(hMenu, SYSCOMMAND_ID_SMARTSIZING, MF_CHECKED);
327 wf_append_item_to_system_menu(hMenu, MIIM_FTYPE | MIIM_ID | MIIM_STRING,
328 SYSCOMMAND_ID_REQUEST_CONTROL, L
"Request control", wfc);
331 static WCHAR* wf_window_get_title(rdpSettings* settings)
334 WCHAR* windowTitle = NULL;
336 WCHAR prefix[] = L
"FreeRDP:";
348 size = strlen(name) + 16 + wcslen(prefix);
349 windowTitle = calloc(size,
sizeof(WCHAR));
355 _snwprintf_s(windowTitle, size, _TRUNCATE, L
"%s %S", prefix, name);
357 _snwprintf_s(windowTitle, size, _TRUNCATE, L
"%s %S:%u", prefix, name,
363 static BOOL wf_post_connect(freerdp* instance)
370 rdpSettings* settings;
371 EmbedWindowEventArgs e;
372 const UINT32 format = PIXEL_FORMAT_BGRX32;
374 WINPR_ASSERT(instance);
376 context = instance->context;
377 WINPR_ASSERT(context);
379 settings = context->settings;
380 WINPR_ASSERT(settings);
382 wfc = (wfContext*)instance->context;
389 if (!gdi_init_ex(instance, format, 0, wfc->primary->pdata, NULL))
392 cache = instance->context->cache;
395 gdi = instance->context->gdi;
399 wf_gdi_register_update_callbacks(context->update);
402 wfc->window_title = wf_window_get_title(settings);
404 if (!wfc->window_title)
416 dwStyle = WS_CHILD | WS_BORDER;
419 WS_CAPTION | WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX | WS_MAXIMIZEBOX;
423 wfc->hwnd = CreateWindowEx(0, wfc->wndClassName, wfc->window_title, dwStyle, 0, 0, 0, 0,
424 wfc->hWndParent, NULL, wfc->hInstance, NULL);
425 SetWindowLongPtr(wfc->hwnd, GWLP_USERDATA, (LONG_PTR)wfc);
428 wf_resize_window(wfc);
429 wf_add_system_menu(wfc);
432 wfc->drawing = wfc->primary;
433 EventArgsInit(&e,
"wfreerdp");
435 e.handle = (
void*)wfc->hwnd;
436 PubSub_OnEmbedWindow(context->pubSub, context, &e);
437 #ifdef WITH_PROGRESS_BAR
438 if (wfc->taskBarList)
440 ShowWindow(wfc->hwnd, SW_SHOWMINIMIZED);
441 wfc->taskBarList->lpVtbl->SetProgressState(wfc->taskBarList, wfc->hwnd, TBPF_INDETERMINATE);
444 UpdateWindow(wfc->hwnd);
445 context->update->BeginPaint = wf_begin_paint;
446 context->update->DesktopResize = wf_desktop_resize;
447 context->update->EndPaint = wf_end_paint;
448 wf_register_pointer(context->graphics);
450 wfc->floatbar = wf_floatbar_new(wfc, wfc->hInstance,
455 static void wf_post_disconnect(freerdp* instance)
459 if (!instance || !instance->context)
462 wfc = (wfContext*)instance->context;
463 free(wfc->window_title);
466 static CREDUI_INFOW wfUiInfo = {
sizeof(CREDUI_INFOW), NULL, L
"Enter your credentials",
467 L
"Remote Desktop Security", NULL };
469 static BOOL wf_authenticate_ex(freerdp* instance,
char** username,
char** password,
char** domain,
470 rdp_auth_reason reason)
476 WCHAR UserNameW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
477 WCHAR UserW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
478 WCHAR DomainW[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = { 0 };
479 WCHAR PasswordW[CREDUI_MAX_PASSWORD_LENGTH + 1] = { 0 };
481 WINPR_ASSERT(instance);
482 WINPR_ASSERT(instance->context);
483 WINPR_ASSERT(instance->context->settings);
485 wfc = (wfContext*)instance->context;
488 WINPR_ASSERT(username);
489 WINPR_ASSERT(domain);
490 WINPR_ASSERT(password);
492 const WCHAR auth[] = L
"Target credentials requested";
493 const WCHAR authPin[] = L
"PIN requested";
494 const WCHAR gwAuth[] = L
"Gateway credentials requested";
495 const WCHAR* titleW = auth;
498 dwFlags = CREDUI_FLAGS_DO_NOT_PERSIST | CREDUI_FLAGS_EXCLUDE_CERTIFICATES |
499 CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
506 if ((*username) && (*password))
509 case AUTH_SMARTCARD_PIN:
510 dwFlags &= ~CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
511 dwFlags |= CREDUI_FLAGS_PASSWORD_ONLY_OK | CREDUI_FLAGS_KEEP_USERNAME;
516 *username = _strdup(
"PIN");
529 (void)ConvertUtf8ToWChar(*username, UserNameW, ARRAYSIZE(UserNameW));
530 (void)ConvertUtf8ToWChar(*username, UserW, ARRAYSIZE(UserW));
534 (void)ConvertUtf8ToWChar(*password, PasswordW, ARRAYSIZE(PasswordW));
537 (void)ConvertUtf8ToWChar(*domain, DomainW, ARRAYSIZE(DomainW));
539 if (_wcsnlen(PasswordW, ARRAYSIZE(PasswordW)) == 0)
541 if (!wfc->isConsole &&
543 WLog_ERR(TAG,
"Flag for stdin read present but stdin is redirected; using GUI");
544 if (wfc->isConsole &&
546 status = CredUICmdLinePromptForCredentialsW(titleW, NULL, 0, UserNameW,
547 ARRAYSIZE(UserNameW), PasswordW,
548 ARRAYSIZE(PasswordW), &fSave, dwFlags);
550 status = CredUIPromptForCredentialsW(&wfUiInfo, titleW, NULL, 0, UserNameW,
551 ARRAYSIZE(UserNameW), PasswordW,
552 ARRAYSIZE(PasswordW), &fSave, dwFlags);
553 if (status != NO_ERROR)
555 WLog_ERR(TAG,
"CredUIPromptForCredentials unexpected status: 0x%08lX", status);
559 if ((dwFlags & CREDUI_FLAGS_KEEP_USERNAME) == 0)
561 status = CredUIParseUserNameW(UserNameW, UserW, ARRAYSIZE(UserW), DomainW,
563 if (status != NO_ERROR)
565 CHAR User[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
566 CHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
567 CHAR Domain[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = { 0 };
569 (void)ConvertWCharNToUtf8(UserNameW, ARRAYSIZE(UserNameW), UserName,
570 ARRAYSIZE(UserName));
571 (void)ConvertWCharNToUtf8(UserW, ARRAYSIZE(UserW), User, ARRAYSIZE(User));
572 (void)ConvertWCharNToUtf8(DomainW, ARRAYSIZE(DomainW), Domain, ARRAYSIZE(Domain));
573 WLog_ERR(TAG,
"Failed to parse UserName: %s into User: %s Domain: %s", UserName,
580 *username = ConvertWCharNToUtf8Alloc(UserW, ARRAYSIZE(UserW), NULL);
583 WLog_ERR(TAG,
"ConvertWCharNToUtf8Alloc failed", status);
587 if (_wcsnlen(DomainW, ARRAYSIZE(DomainW)) > 0)
588 *domain = ConvertWCharNToUtf8Alloc(DomainW, ARRAYSIZE(DomainW), NULL);
590 *domain = _strdup(
"\0");
595 WLog_ERR(TAG,
"strdup failed", status);
599 *password = ConvertWCharNToUtf8Alloc(PasswordW, ARRAYSIZE(PasswordW), NULL);
610 static WCHAR* wf_format_text(
const WCHAR* fmt, ...)
614 WCHAR* buffer = NULL;
621 rc = _vsnwprintf(buffer, size, fmt, ap);
626 if ((
size_t)rc < size)
629 size = (size_t)rc + 1;
630 tmp = realloc(buffer, size *
sizeof(WCHAR));
642 #ifdef WITH_WINDOWS_CERT_STORE
650 static void wf_report_error(
char* wszMessage, DWORD dwErrCode)
652 LPSTR pwszMsgBuf = NULL;
654 if (NULL != wszMessage && 0 != *wszMessage)
656 WLog_ERR(TAG,
"%s", wszMessage);
659 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
664 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
674 if (NULL != pwszMsgBuf)
676 WLog_ERR(TAG,
"Error: 0x%08x (%d) %s", dwErrCode, dwErrCode, pwszMsgBuf);
677 LocalFree(pwszMsgBuf);
681 WLog_ERR(TAG,
"Error: 0x%08x (%d)", dwErrCode, dwErrCode);
685 static DWORD wf_is_x509_certificate_trusted(
const char* common_name,
const char* subject,
686 const char* issuer,
const char* fingerprint)
688 HRESULT hr = CRYPT_E_NOT_FOUND;
690 DWORD dwChainFlags = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
692 HCERTCHAINENGINE hChainEngine = NULL;
693 PCCERT_CHAIN_CONTEXT pChainContext = NULL;
695 CERT_ENHKEY_USAGE EnhkeyUsage = { 0 };
696 CERT_USAGE_MATCH CertUsage = { 0 };
697 CERT_CHAIN_PARA ChainPara = { 0 };
698 CERT_CHAIN_POLICY_PARA ChainPolicy = { 0 };
699 CERT_CHAIN_POLICY_STATUS PolicyStatus = { 0 };
700 CERT_CHAIN_ENGINE_CONFIG EngineConfig = { 0 };
702 DWORD derPubKeyLen = strlen(fingerprint);
703 char* derPubKey = calloc(derPubKeyLen,
sizeof(
char));
704 if (NULL == derPubKey)
706 WLog_ERR(TAG,
"Could not allocate derPubKey");
713 if (!CryptStringToBinaryA(fingerprint, 0, CRYPT_STRING_BASE64HEADER, derPubKey, &derPubKeyLen,
716 WLog_ERR(TAG,
"CryptStringToBinary failed. Err: %d", GetLastError());
723 EnhkeyUsage.cUsageIdentifier = 0;
724 EnhkeyUsage.rgpszUsageIdentifier = NULL;
726 CertUsage.dwType = USAGE_MATCH_TYPE_AND;
727 CertUsage.Usage = EnhkeyUsage;
729 ChainPara.cbSize =
sizeof(ChainPara);
730 ChainPara.RequestedUsage = CertUsage;
732 ChainPolicy.cbSize =
sizeof(ChainPolicy);
734 PolicyStatus.cbSize =
sizeof(PolicyStatus);
736 EngineConfig.cbSize =
sizeof(EngineConfig);
737 EngineConfig.dwUrlRetrievalTimeout = 0;
739 pCert = CertCreateCertificateContext(X509_ASN_ENCODING, derPubKey, derPubKeyLen);
742 WLog_ERR(TAG,
"FAILED: Certificate could not be parsed.");
746 dwChainFlags |= CERT_CHAIN_ENABLE_PEER_TRUST;
755 if (!CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine))
757 hr = HRESULT_FROM_WIN32(GetLastError());
764 if (!CertGetCertificateChain(hChainEngine,
775 hr = HRESULT_FROM_WIN32(GetLastError());
782 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE,
787 hr = HRESULT_FROM_WIN32(GetLastError());
791 if (PolicyStatus.dwError != S_OK)
793 wf_report_error(
"CertVerifyCertificateChainPolicy: Chain Status", PolicyStatus.dwError);
794 hr = PolicyStatus.dwError;
800 if (PolicyStatus.dwError == CRYPT_E_NO_REVOCATION_CHECK ||
801 PolicyStatus.dwError == CRYPT_E_REVOCATION_OFFLINE)
809 WLog_INFO(TAG,
"CertVerifyCertificateChainPolicy succeeded for %s (%s) issued by %s",
810 common_name, subject, issuer);
817 WLog_INFO(TAG,
"CertVerifyCertificateChainPolicy failed for %s (%s) issued by %s",
818 common_name, subject, issuer);
819 wf_report_error(NULL, hr);
824 if (NULL != pChainContext)
826 CertFreeCertificateChain(pChainContext);
829 if (NULL != hChainEngine)
831 CertFreeCertificateChainEngine(hChainEngine);
836 CertFreeCertificateContext(pCert);
843 static DWORD wf_cli_verify_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
844 const char* common_name,
const char* subject,
845 const char* issuer,
const char* fingerprint, DWORD flags)
847 #ifdef WITH_WINDOWS_CERT_STORE
848 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM && !(flags & VERIFY_CERT_FLAG_MISMATCH))
850 if (wf_is_x509_certificate_trusted(common_name, subject, issuer, fingerprint) == S_OK)
857 return client_cli_verify_certificate_ex(instance, host, port, common_name, subject, issuer,
861 static DWORD wf_verify_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
862 const char* common_name,
const char* subject,
863 const char* issuer,
const char* fingerprint, DWORD flags)
869 #ifdef WITH_WINDOWS_CERT_STORE
870 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM && !(flags & VERIFY_CERT_FLAG_MISMATCH))
872 if (wf_is_x509_certificate_trusted(common_name, subject, issuer, fingerprint) == S_OK)
879 buffer = wf_format_text(
880 L
"Certificate details:\n"
881 L
"\tCommonName: %S\n"
884 L
"\tThumbprint: %S\n"
885 L
"\tHostMismatch: %S\n"
887 L
"The above X.509 certificate could not be verified, possibly because you do not have "
888 L
"the CA certificate in your certificate store, or the certificate has expired. "
889 L
"Please look at the OpenSSL documentation on how to add a private CA to the store.\n"
891 L
"YES\tAccept permanently\n"
892 L
"NO\tAccept for this session only\n"
893 L
"CANCEL\tAbort connection\n",
894 common_name, subject, issuer, fingerprint,
895 flags & VERIFY_CERT_FLAG_MISMATCH ?
"Yes" :
"No");
896 caption = wf_format_text(L
"Verify certificate for %S:%hu", host, port);
898 WINPR_UNUSED(instance);
900 if (!buffer || !caption)
903 what = MessageBoxW(NULL, buffer, caption, MB_YESNOCANCEL);
921 static DWORD wf_verify_changed_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
922 const char* common_name,
const char* subject,
923 const char* issuer,
const char* new_fingerprint,
924 const char* old_subject,
const char* old_issuer,
925 const char* old_fingerprint, DWORD flags)
931 buffer = wf_format_text(
932 L
"New Certificate details:\n"
933 L
"\tCommonName: %S\n"
936 L
"\tThumbprint: %S\n"
937 L
"\tHostMismatch: %S\n"
939 L
"Old Certificate details:\n"
943 L
"The above X.509 certificate could not be verified, possibly because you do not have "
944 L
"the CA certificate in your certificate store, or the certificate has expired. "
945 L
"Please look at the OpenSSL documentation on how to add a private CA to the store.\n"
947 L
"YES\tAccept permanently\n"
948 L
"NO\tAccept for this session only\n"
949 L
"CANCEL\tAbort connection\n",
950 common_name, subject, issuer, new_fingerprint,
951 flags & VERIFY_CERT_FLAG_MISMATCH ?
"Yes" :
"No", old_subject, old_issuer, old_fingerprint);
952 caption = wf_format_text(L
"Verify certificate change for %S:%hu", host, port);
954 WINPR_UNUSED(instance);
955 if (!buffer || !caption)
958 what = MessageBoxW(NULL, buffer, caption, MB_YESNOCANCEL);
976 static BOOL wf_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
977 BOOL isConsentMandatory,
size_t length,
const WCHAR* message)
979 if (!isDisplayMandatory && !isConsentMandatory)
983 if (type == GATEWAY_MESSAGE_CONSENT && isConsentMandatory)
988 msg = wf_format_text(L
"%.*s\n\nI understand and agree to the terms of this policy", length,
990 mbRes = MessageBoxW(NULL, msg, L
"Consent Message", MB_YESNO);
997 return client_cli_present_gateway_message(instance, type, isDisplayMandatory,
998 isConsentMandatory, length, message);
1003 static DWORD WINAPI wf_client_thread(LPVOID lpParam)
1008 BOOL msg_ret = FALSE;
1012 freerdp* instance = (freerdp*)lpParam;
1013 WINPR_ASSERT(instance);
1015 if (!freerdp_connect(instance))
1018 rdpContext* context = instance->context;
1019 WINPR_ASSERT(context);
1021 wfContext* wfc = (wfContext*)instance->context;
1024 rdpChannels* channels = context->channels;
1025 WINPR_ASSERT(channels);
1027 rdpSettings* settings = context->settings;
1028 WINPR_ASSERT(settings);
1030 while (!freerdp_shall_disconnect_context(instance->context))
1032 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
1035 if (freerdp_focus_required(instance))
1037 wf_event_focus_in(wfc);
1038 wf_event_focus_in(wfc);
1042 DWORD tmp = freerdp_get_event_handles(context, &handles[nCount], 64 - nCount);
1046 WLog_ERR(TAG,
"freerdp_get_event_handles failed");
1053 DWORD status = MsgWaitForMultipleObjectsEx(nCount, handles, 5 * 1000, QS_ALLINPUT,
1054 MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
1055 if (status == WAIT_FAILED)
1057 WLog_ERR(TAG,
"wfreerdp_run: WaitForMultipleObjects failed: 0x%08lX", GetLastError());
1062 if (!freerdp_check_event_handles(context))
1064 if (client_auto_reconnect(instance))
1067 WLog_ERR(TAG,
"Failed to check FreeRDP file descriptor");
1072 if (freerdp_shall_disconnect_context(instance->context))
1077 while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
1079 msg_ret = GetMessage(&msg, NULL, 0, 0);
1083 if ((msg.message == WM_SETFOCUS) && (msg.lParam == 1))
1085 PostMessage(wfc->hwnd, WM_SETFOCUS, 0, 0);
1087 else if ((msg.message == WM_KILLFOCUS) && (msg.lParam == 1))
1089 PostMessage(wfc->hwnd, WM_KILLFOCUS, 0, 0);
1093 switch (msg.message)
1097 width = LOWORD(msg.lParam);
1098 height = HIWORD(msg.lParam);
1099 SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0, width, height, SWP_FRAMECHANGED);
1102 case WM_FREERDP_SHOWWINDOW:
1104 ShowWindow(wfc->hwnd, SW_NORMAL);
1111 if ((msg_ret == 0) || (msg_ret == -1))
1117 TranslateMessage(&msg);
1118 DispatchMessage(&msg);
1126 freerdp_disconnect(instance);
1129 error = freerdp_get_last_error(instance->context);
1130 WLog_DBG(TAG,
"Main thread exited with %" PRIu32, error);
1135 static DWORD WINAPI wf_keyboard_thread(LPVOID lpParam)
1141 wfc = (wfContext*)lpParam;
1142 WINPR_ASSERT(NULL != wfc);
1143 hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, wf_ll_kbd_proc, wfc->hInstance, 0);
1147 while ((status = GetMessage(&msg, NULL, 0, 0)) != 0)
1151 WLog_ERR(TAG,
"keyboard thread error getting message");
1156 TranslateMessage(&msg);
1157 DispatchMessage(&msg);
1161 UnhookWindowsHookEx(hook_handle);
1165 WLog_ERR(TAG,
"failed to install keyboard hook");
1168 WLog_DBG(TAG,
"Keyboard thread exited.");
1173 int freerdp_client_set_window_size(wfContext* wfc,
int width,
int height)
1175 WLog_DBG(TAG,
"freerdp_client_set_window_size %d, %d", width, height);
1177 if ((width != wfc->client_width) || (height != wfc->client_height))
1179 PostThreadMessage(wfc->mainThreadId, WM_SIZE, SIZE_RESTORED,
1180 ((UINT)height << 16) | (UINT)width);
1186 void wf_size_scrollbars(wfContext* wfc, UINT32 client_width, UINT32 client_height)
1188 const rdpSettings* settings;
1191 settings = wfc->common.context.settings;
1192 WINPR_ASSERT(settings);
1194 if (wfc->disablewindowtracking)
1198 wfc->disablewindowtracking = TRUE;
1203 wfc->xCurrentScroll = 0;
1204 wfc->yCurrentScroll = 0;
1206 if (wfc->xScrollVisible || wfc->yScrollVisible)
1208 if (ShowScrollBar(wfc->hwnd, SB_BOTH, FALSE))
1210 wfc->xScrollVisible = FALSE;
1211 wfc->yScrollVisible = FALSE;
1218 BOOL horiz = wfc->xScrollVisible;
1219 BOOL vert = wfc->yScrollVisible;
1228 settings, FreeRDP_DesktopWidth) )
1240 settings, FreeRDP_DesktopHeight) )
1245 if (horiz == vert && (horiz != wfc->xScrollVisible && vert != wfc->yScrollVisible))
1247 if (ShowScrollBar(wfc->hwnd, SB_BOTH, horiz))
1249 wfc->xScrollVisible = horiz;
1250 wfc->yScrollVisible = vert;
1254 if (horiz != wfc->xScrollVisible)
1256 if (ShowScrollBar(wfc->hwnd, SB_HORZ, horiz))
1258 wfc->xScrollVisible = horiz;
1262 if (vert != wfc->yScrollVisible)
1264 if (ShowScrollBar(wfc->hwnd, SB_VERT, vert))
1266 wfc->yScrollVisible = vert;
1277 wfc->xCurrentScroll = MIN(wfc->xCurrentScroll, wfc->xMaxScroll);
1278 si.cbSize =
sizeof(si);
1279 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1280 si.nMin = wfc->xMinScroll;
1282 si.nPage = client_width;
1283 si.nPos = wfc->xCurrentScroll;
1284 SetScrollInfo(wfc->hwnd, SB_HORZ, &si, TRUE);
1292 wfc->yMaxScroll = MAX(
1294 wfc->yCurrentScroll = MIN(wfc->yCurrentScroll, wfc->yMaxScroll);
1295 si.cbSize =
sizeof(si);
1296 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1297 si.nMin = wfc->yMinScroll;
1299 si.nPage = client_height;
1300 si.nPos = wfc->yCurrentScroll;
1301 SetScrollInfo(wfc->hwnd, SB_VERT, &si, TRUE);
1305 wfc->disablewindowtracking = FALSE;
1306 wf_update_canvas_diff(wfc);
1309 static BOOL wfreerdp_client_global_init(
void)
1313 WSAStartup(0x101, &wsaData);
1315 freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
1320 static void wfreerdp_client_global_uninit(
void)
1325 static BOOL wfreerdp_client_new(freerdp* instance, rdpContext* context)
1327 wfContext* wfc = (wfContext*)context;
1333 wfc->isConsole = wf_has_console();
1335 if (!(wfreerdp_client_global_init()))
1338 WINPR_ASSERT(instance);
1339 instance->PreConnect = wf_pre_connect;
1340 instance->PostConnect = wf_post_connect;
1341 instance->PostDisconnect = wf_post_disconnect;
1342 instance->AuthenticateEx = wf_authenticate_ex;
1344 #ifdef WITH_WINDOWS_CERT_STORE
1349 if (!wfc->isConsole)
1351 instance->VerifyCertificateEx = wf_verify_certificate_ex;
1352 instance->VerifyChangedCertificateEx = wf_verify_changed_certificate_ex;
1353 instance->PresentGatewayMessage = wf_present_gateway_message;
1356 #ifdef WITH_PROGRESS_BAR
1357 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
1358 CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_ALL, &IID_ITaskbarList3,
1359 (
void**)&wfc->taskBarList);
1365 static void wfreerdp_client_free(freerdp* instance, rdpContext* context)
1367 WINPR_UNUSED(instance);
1371 #ifdef WITH_PROGRESS_BAR
1376 static int wfreerdp_client_start(rdpContext* context)
1378 wfContext* wfc = (wfContext*)context;
1380 WINPR_ASSERT(context);
1381 WINPR_ASSERT(context->settings);
1383 freerdp* instance = context->instance;
1384 WINPR_ASSERT(instance);
1386 rdpSettings* settings = context->settings;
1387 WINPR_ASSERT(settings);
1389 HINSTANCE hInstance = GetModuleHandle(NULL);
1394 wfc->hWndParent = hWndParent;
1398 typedef UINT(WINAPI * GetDpiForWindow_t)(HWND hwnd);
1399 typedef BOOL(WINAPI * SetProcessDPIAware_t)(void);
1401 HMODULE module = GetModuleHandle(_T(
"User32"));
1404 GetDpiForWindow_t pGetDpiForWindow =
1405 GetProcAddressAs(module,
"GetDpiForWindow", GetDpiForWindow_t);
1406 SetProcessDPIAware_t pSetProcessDPIAware =
1407 GetProcAddressAs(module,
"SetProcessDPIAware", SetProcessDPIAware_t);
1408 if (pGetDpiForWindow && pSetProcessDPIAware)
1410 const UINT dpiAwareness = pGetDpiForWindow(hWndParent);
1411 if (dpiAwareness != USER_DEFAULT_SCREEN_DPI)
1412 pSetProcessDPIAware();
1414 FreeLibrary(module);
1422 wfc->systemMenuInsertPosition = 6;
1424 wfc->hInstance = hInstance;
1425 wfc->cursor = LoadCursor(NULL, IDC_ARROW);
1426 wfc->icon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
1427 wfc->wndClassName = _tcsdup(_T(
"FreeRDP"));
1428 wfc->wndClass.cbSize =
sizeof(WNDCLASSEX);
1429 wfc->wndClass.style = CS_HREDRAW | CS_VREDRAW;
1430 wfc->wndClass.lpfnWndProc = wf_event_proc;
1431 wfc->wndClass.cbClsExtra = 0;
1432 wfc->wndClass.cbWndExtra = 0;
1433 wfc->wndClass.hCursor = NULL;
1434 wfc->wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
1435 wfc->wndClass.lpszMenuName = NULL;
1436 wfc->wndClass.lpszClassName = wfc->wndClassName;
1437 wfc->wndClass.hInstance = hInstance;
1438 wfc->wndClass.hIcon = wfc->icon;
1439 wfc->wndClass.hIconSm = wfc->icon;
1440 RegisterClassEx(&(wfc->wndClass));
1441 wfc->keyboardThread =
1442 CreateThread(NULL, 0, wf_keyboard_thread, (
void*)wfc, 0, &wfc->keyboardThreadId);
1444 if (!wfc->keyboardThread)
1447 wfc->common.thread =
1448 CreateThread(NULL, 0, wf_client_thread, (
void*)instance, 0, &wfc->mainThreadId);
1450 if (!wfc->common.thread)
1456 static int wfreerdp_client_stop(rdpContext* context)
1459 wfContext* wfc = (wfContext*)context;
1462 PostThreadMessage(wfc->mainThreadId, WM_QUIT, 0, 0);
1463 rc = freerdp_client_common_stop(context);
1464 wfc->mainThreadId = 0;
1466 if (wfc->keyboardThread)
1468 PostThreadMessage(wfc->keyboardThreadId, WM_QUIT, 0, 0);
1469 (void)WaitForSingleObject(wfc->keyboardThread, INFINITE);
1470 (void)CloseHandle(wfc->keyboardThread);
1471 wfc->keyboardThread = NULL;
1472 wfc->keyboardThreadId = 0;
1478 int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
1480 pEntryPoints->Version = 1;
1481 pEntryPoints->Size =
sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
1482 pEntryPoints->GlobalInit = wfreerdp_client_global_init;
1483 pEntryPoints->GlobalUninit = wfreerdp_client_global_uninit;
1484 pEntryPoints->ContextSize =
sizeof(wfContext);
1485 pEntryPoints->ClientNew = wfreerdp_client_new;
1486 pEntryPoints->ClientFree = wfreerdp_client_free;
1487 pEntryPoints->ClientStart = wfreerdp_client_start;
1488 pEntryPoints->ClientStop = wfreerdp_client_stop;
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API UINT64 freerdp_settings_get_uint64(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt64 id)
Returns a UINT64 settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.