#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <shlobj.h>
#include <shellapi.h>
#include <process.h>
#include <commdlg.h>
#include <sddl.h>
#include "libwdi.h"
#include "zadig_resource.h"
#include "zadig.h"
#include "zadig_license.h"
#include "../libwdi/msapi_utf8.h"
#if (_WIN32_WINNT >= 0x0600)
static HRESULT (WINAPI *pSHCreateItemFromParsingName)(PCWSTR, IBindCtx*, REFIID, void **) = NULL;
#endif
#define INIT_VISTA_SHELL32 if (pSHCreateItemFromParsingName == NULL) { \
pSHCreateItemFromParsingName = (HRESULT (WINAPI *)(PCWSTR, IBindCtx*, REFIID, void **)) \
GetProcAddress(GetModuleHandleA("SHELL32"), "SHCreateItemFromParsingName"); \
}
#define IS_VISTA_SHELL32_AVAILABLE (pSHCreateItemFromParsingName != NULL)
static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPath)(PCWSTR pszPath) = NULL;
#define INIT_XP_SHELL32 if (pSHSimpleIDListFromPath == NULL) { \
pSHSimpleIDListFromPath = (LPITEMIDLIST (WINAPI *)(PCWSTR)) \
GetProcAddress(GetModuleHandleA("SHELL32"), "SHSimpleIDListFromPath"); \
}
static HICON hMessageIcon = (HICON)INVALID_HANDLE_VALUE;
static char* message_text = NULL;
static char* message_title = NULL;
enum windows_version windows_version = WINDOWS_UNSUPPORTED;
extern HFONT bold_font;
extern float fScale;
static HWND browse_edit;
static WNDPROC org_browse_wndproc;
char* to_valid_filename(char* name, char* ext)
{
size_t i, j, k;
bool found;
char* ret;
wchar_t unauthorized[] = L"\x0001\x0002\x0003\x0004\x0005\x0006\x0007\x0008\x000a"
L"\x000b\x000c\x000d\x000e\x000f\x0010\x0011\x0012\x0013\x0014\x0015\x0016\x0017"
L"\x0018\x0019\x001a\x001b\x001c\x001d\x001e\x001f\x007f\"*/:<>?\\|";
wchar_t to_underscore[] = L" \t";
wchar_t *wname, *wext, *wret;
if ((name == NULL) || (ext == NULL)) {
return NULL;
}
if (strlen(name) > WDI_MAX_STRLEN) return NULL;
wname = utf8_to_wchar(name);
wext = utf8_to_wchar(ext);
if ((wname == NULL) || (wext == NULL)) {
safe_free(wname); safe_free(wext); return NULL;
}
wret = (wchar_t*)calloc(2*(wcslen(wname) + wcslen(wext) + 2), 1);
if (wret == NULL) {
safe_free(wname); safe_free(wext); return NULL;
}
wcscpy(wret, wname);
safe_free(wname);
wcscat(wret, wext);
safe_free(wext);
for (i=0, k=0; i<wcslen(wret); i++) {
found = false;
for (j=0; j<wcslen(unauthorized); j++) {
if (wret[i] == unauthorized[j]) {
found = true; break;
}
}
if (found) continue;
found = false;
for (j=0; j<wcslen(to_underscore); j++) {
if (wret[i] == to_underscore[j]) {
wret[k++] = '_';
found = true; break;
}
}
if (found) continue;
wret[k++] = wret[i];
}
wret[k] = 0;
ret = wchar_to_utf8(wret);
safe_free(wret);
return ret;
}
static char *windows_error_str(DWORD retval)
{
#define ERR_BUFFER_SIZE 256
static char err_string[ERR_BUFFER_SIZE];
DWORD size;
DWORD error_code, format_error;
error_code = retval?retval:GetLastError();
safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%d] ", error_code);
size = FormatMessageU(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &err_string[strlen(err_string)],
ERR_BUFFER_SIZE-(DWORD)strlen(err_string), NULL);
if (size == 0) {
format_error = GetLastError();
if (format_error)
safe_sprintf(err_string, ERR_BUFFER_SIZE,
"Windows error code %u (FormatMessage error code %u)", error_code, format_error);
else
safe_sprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", error_code);
}
return err_string;
}
void detect_windows_version(void)
{
OSVERSIONINFO os_version;
memset(&os_version, 0, sizeof(OSVERSIONINFO));
os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
windows_version = WINDOWS_UNSUPPORTED;
if ((GetVersionEx(&os_version) != 0) && (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)) {
if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 0)) {
windows_version = WINDOWS_2K;
} else if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 1)) {
windows_version = WINDOWS_XP;
} else if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 2)) {
windows_version = WINDOWS_2003_XP64;
} else if (os_version.dwMajorVersion == 6) {
if (os_version.dwBuildNumber < 7000) {
windows_version = WINDOWS_VISTA;
} else {
windows_version = WINDOWS_7;
}
} else if (os_version.dwMajorVersion >= 8) {
windows_version = WINDOWS_8;
}
}
}
static PSID get_sid(void) {
TOKEN_USER* tu = NULL;
DWORD len;
HANDLE token;
PSID ret = NULL;
char* psid_string = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
dprintf("OpenProcessToken failed: %s", windows_error_str(0));
return NULL;
}
if (!GetTokenInformation(token, TokenUser, tu, 0, &len)) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
dprintf("GetTokenInformation (pre) failed: %s", windows_error_str(0));
return NULL;
}
tu = (TOKEN_USER*)calloc(1, len);
}
if (tu == NULL) {
return NULL;
}
if (GetTokenInformation(token, TokenUser, tu, len, &len)) {
if (!ConvertSidToStringSidA(tu->User.Sid, &psid_string)) {
dprintf("unable to convert SID to string: %s", windows_error_str(0));
ret = NULL;
} else {
if (!ConvertStringSidToSidA(psid_string, &ret)) {
dprintf("unable to convert string back to SID: %s", windows_error_str(0));
ret = NULL;
}
LocalFree(psid_string);
}
} else {
ret = NULL;
dprintf("GetTokenInformation (real) failed: %s", windows_error_str(0));
}
free(tu);
return ret;
}
INT CALLBACK browsedlg_callback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message) {
case WM_DESTROY:
GetWindowTextU(browse_edit, extraction_path, sizeof(extraction_path));
break;
}
return (INT)CallWindowProc(org_browse_wndproc, hDlg, message, wParam, lParam);
}
INT CALLBACK browseinfo_callback(HWND hDlg, UINT message, LPARAM lParam, LPARAM pData)
{
char dir[MAX_PATH];
wchar_t* wpath;
LPITEMIDLIST pidl;
switch(message) {
case BFFM_INITIALIZED:
org_browse_wndproc = (WNDPROC)SetWindowLongPtr(hDlg, GWLP_WNDPROC, (LONG_PTR)browsedlg_callback);
browse_edit = FindWindowExA(hDlg, NULL, "Edit", NULL);
SetWindowTextU(browse_edit, extraction_path);
SetFocus(browse_edit);
if (windows_version <= WINDOWS_XP) {
SendMessageLU(hDlg, BFFM_SETSELECTION, (WPARAM)TRUE, extraction_path);
} else {
wpath = utf8_to_wchar(extraction_path);
pidl = (*pSHSimpleIDListFromPath)(wpath);
safe_free(wpath);
SendMessageW(hDlg, BFFM_SETSELECTION, (WPARAM)FALSE, (LPARAM)pidl);
Sleep(100);
PostMessageW(hDlg, BFFM_SETSELECTION, (WPARAM)FALSE, (LPARAM)pidl);
}
break;
case BFFM_SELCHANGED:
if (SHGetPathFromIDListU((LPITEMIDLIST)lParam, dir)) {
SendMessageLU(hDlg, BFFM_SETSTATUSTEXT, 0, dir);
SetWindowTextU(browse_edit, dir);
}
break;
}
return 0;
}
void browse_for_folder(void) {
BROWSEINFOW bi;
LPITEMIDLIST pidl;
#if (_WIN32_WINNT >= 0x0600)
WCHAR *wpath;
size_t i;
HRESULT hr;
IShellItem *psi = NULL;
IShellItem *si_path = NULL; IFileOpenDialog *pfod = NULL;
WCHAR *fname;
char* tmp_path = NULL;
INIT_VISTA_SHELL32;
if (IS_VISTA_SHELL32_AVAILABLE) {
hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, CLSCTX_INPROC,
&IID_IFileOpenDialog, (LPVOID)&pfod);
if (FAILED(hr)) {
dprintf("CoCreateInstance for FileOpenDialog failed: error %X", hr);
pfod = NULL; goto fallback;
}
hr = pfod->lpVtbl->SetOptions(pfod, FOS_PICKFOLDERS);
if (FAILED(hr)) {
dprintf("Failed to set folder option for FileOpenDialog: error %X", hr);
goto fallback;
}
wpath = utf8_to_wchar(extraction_path);
fname = NULL;
if ((wpath != NULL) && (wcslen(wpath) >= 1)) {
for (i=wcslen(wpath)-1; i!=0; i--) {
if (wpath[i] == L'\\') {
wpath[i] = 0;
fname = &wpath[i+1];
break;
}
}
}
hr = (*pSHCreateItemFromParsingName)(wpath, NULL, &IID_IShellItem, (LPVOID)&si_path);
if (SUCCEEDED(hr)) {
if (wpath != NULL) {
hr = pfod->lpVtbl->SetFolder(pfod, si_path);
}
if (fname != NULL) {
hr = pfod->lpVtbl->SetFileName(pfod, fname);
}
}
safe_free(wpath);
hr = pfod->lpVtbl->Show(pfod, hMain);
if (SUCCEEDED(hr)) {
hr = pfod->lpVtbl->GetResult(pfod, &psi);
if (SUCCEEDED(hr)) {
psi->lpVtbl->GetDisplayName(psi, SIGDN_FILESYSPATH, &wpath);
tmp_path = wchar_to_utf8(wpath);
CoTaskMemFree(wpath);
if (tmp_path == NULL) {
dprintf("Could not convert path");
} else {
safe_strcpy(extraction_path, MAX_PATH, tmp_path);
safe_free(tmp_path);
}
} else {
dprintf("Failed to set folder option for FileOpenDialog: error %X", hr);
}
} else if ((hr & 0xFFFF) != ERROR_CANCELLED) {
dprintf("could not show FileOpenDialog: error %X", hr);
goto fallback;
}
pfod->lpVtbl->Release(pfod);
return;
}
fallback:
if (pfod != NULL) {
pfod->lpVtbl->Release(pfod);
}
#endif
INIT_XP_SHELL32;
memset(&bi, 0, sizeof(BROWSEINFOW));
bi.hwndOwner = hMain;
bi.lpszTitle = L"Please select the installation folder:";
bi.lpfn = browseinfo_callback;
bi.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS |
BIF_DONTGOBELOWDOMAIN | BIF_EDITBOX | 0x00000200;
pidl = SHBrowseForFolderW(&bi);
if (pidl != NULL) {
CoTaskMemFree(pidl);
}
}
bool file_io(bool save, char* path, char** buffer, DWORD* size)
{
SECURITY_ATTRIBUTES s_attr, *ps = NULL;
SECURITY_DESCRIPTOR s_desc;
PSID sid = NULL;
HANDLE handle;
BOOL r;
bool ret = false;
sid = get_sid();
if ( (sid != NULL)
&& InitializeSecurityDescriptor(&s_desc, SECURITY_DESCRIPTOR_REVISION)
&& SetSecurityDescriptorOwner(&s_desc, sid, FALSE) ) {
s_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
s_attr.bInheritHandle = FALSE;
s_attr.lpSecurityDescriptor = &s_desc;
ps = &s_attr;
} else {
dprintf("could not set security descriptor: %s", windows_error_str(0));
}
if (!save) {
*buffer = NULL;
}
handle = CreateFileU(path, save?GENERIC_WRITE:GENERIC_READ, FILE_SHARE_READ,
ps, save?CREATE_ALWAYS:OPEN_EXISTING, 0, NULL);
if (handle == INVALID_HANDLE_VALUE) {
dprintf("Could not %s file '%s'", save?"create":"open", path);
goto out;
}
if (save) {
r = WriteFile(handle, *buffer, *size, size, NULL);
} else {
*size = GetFileSize(handle, NULL);
*buffer = (char*)malloc(*size);
if (*buffer == NULL) {
dprintf("Could not allocate buffer for reading file");
goto out;
}
r = ReadFile(handle, *buffer, *size, size, NULL);
}
if (!r) {
dprintf("I/O Error: %s", windows_error_str(0));
goto out;
}
dsprintf("%s '%s'", save?"Saved file as":"Opened file", path);
ret = true;
out:
CloseHandle(handle);
if (!ret) {
*size = 0;
if (!save) {
safe_free(*buffer);
}
}
return ret;
}
char* file_dialog(bool save, char* path, char* filename, char* ext, char* ext_desc)
{
DWORD tmp;
OPENFILENAMEA ofn;
char selected_name[STR_BUFFER_SIZE];
char* ext_string = NULL;
size_t i, ext_strlen;
BOOL r;
char* filepath = NULL;
#if (_WIN32_WINNT >= 0x0600)
HRESULT hr = FALSE;
IFileDialog *pfd;
IShellItem *psiResult;
COMDLG_FILTERSPEC filter_spec[2];
char* ext_filter;
wchar_t *wpath = NULL, *wfilename = NULL;
IShellItem *si_path = NULL;
INIT_VISTA_SHELL32;
if (IS_VISTA_SHELL32_AVAILABLE) {
ext_filter = (char*)malloc(strlen(ext)+3);
if (ext_filter != NULL) {
safe_sprintf(ext_filter, strlen(ext)+3, "*.%s", ext);
filter_spec[0].pszSpec = utf8_to_wchar(ext_filter);
safe_free(ext_filter);
filter_spec[0].pszName = utf8_to_wchar(ext_desc);
filter_spec[1].pszSpec = L"*.*";
filter_spec[1].pszName = L"All files";
}
hr = CoCreateInstance(save?&CLSID_FileSaveDialog:&CLSID_FileOpenDialog, NULL, CLSCTX_INPROC,
&IID_IFileDialog, (LPVOID)&pfd);
if (FAILED(hr)) {
dprintf("CoCreateInstance for FileOpenDialog failed: error %X", hr);
pfd = NULL; goto fallback;
}
pfd->lpVtbl->SetFileTypes(pfd, 2, filter_spec);
wpath = utf8_to_wchar(path);
hr = (*pSHCreateItemFromParsingName)(wpath, NULL, &IID_IShellItem, (LPVOID) &si_path);
if (SUCCEEDED(hr)) {
pfd->lpVtbl->SetFolder(pfd, si_path);
}
safe_free(wpath);
wfilename = utf8_to_wchar(filename);
if (wfilename != NULL) {
pfd->lpVtbl->SetFileName(pfd, wfilename);
}
hr = pfd->lpVtbl->Show(pfd, hMain);
safe_free(wfilename);
safe_free(filter_spec[0].pszSpec);
safe_free(filter_spec[0].pszName);
if (SUCCEEDED(hr)) {
hr = pfd->lpVtbl->GetResult(pfd, &psiResult);
if (SUCCEEDED(hr)) {
hr = psiResult->lpVtbl->GetDisplayName(psiResult, SIGDN_FILESYSPATH, &wpath);
if (SUCCEEDED(hr)) {
filepath = wchar_to_utf8(wpath);
CoTaskMemFree(wpath);
}
psiResult->lpVtbl->Release(psiResult);
}
} else if ((hr & 0xFFFF) != ERROR_CANCELLED) {
dprintf("could not show FileOpenDialog: error %X", hr);
goto fallback;
}
pfd->lpVtbl->Release(pfd);
return filepath;
}
fallback:
if (pfd != NULL) {
pfd->lpVtbl->Release(pfd);
}
#endif
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hMain;
safe_strcpy(selected_name, STR_BUFFER_SIZE, filename);
ofn.lpstrFile = selected_name;
ofn.nMaxFile = STR_BUFFER_SIZE;
ext_strlen = strlen(ext_desc) + 2*strlen(ext) + sizeof(" (*.)\0*.\0All Files (*.*)\0*.*\0\0");
ext_string = (char*)malloc(ext_strlen);
safe_sprintf(ext_string, ext_strlen, "%s (*.%s)\r*.%s\rAll Files (*.*)\r*.*\r\0", ext_desc, ext, ext);
for (i=0; i<ext_strlen; i++) {
if (ext_string[i] == '\r') {
ext_string[i] = 0;
}
}
ofn.lpstrFilter = ext_string;
ofn.lpstrInitialDir = path;
ofn.Flags = OFN_OVERWRITEPROMPT;
if (save) {
r = GetSaveFileNameU(&ofn);
} else {
r = GetOpenFileNameU(&ofn);
}
if (r) {
filepath = safe_strdup(selected_name);
} else {
tmp = CommDlgExtendedError();
if (tmp != 0) {
dprintf("Could not selected file for %s. Error %X", save?"save":"open", tmp);
}
}
safe_free(ext_string);
return filepath;
}
void create_status_bar(void)
{
RECT rect;
int edge[2];
hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE,
0, 0, 0, 0, hMain, (HMENU)IDC_STATUS, main_instance, NULL);
GetClientRect(hMain, &rect);
edge[0] = rect.right - (int)(100.0f*fScale);
edge[1] = rect.right;
SendMessage(hStatus, SB_SETPARTS, (WPARAM) 2, (LPARAM)&edge);
}
void center_dialog(HWND dialog)
{
POINT Point;
RECT DialogRect;
RECT ParentRect;
int nWidth;
int nHeight;
GetWindowRect(dialog, &DialogRect);
GetClientRect(hMain, &ParentRect);
nWidth = DialogRect.right - DialogRect.left;
nHeight = DialogRect.bottom - DialogRect.top;
Point.x = (ParentRect.right - ParentRect.left) / 2;
Point.y = (ParentRect.bottom - ParentRect.top) / 2;
ClientToScreen(hMain, &Point);
Point.x -= nWidth / 2;
Point.y -= nHeight / 2 + 35;
MoveWindow(dialog, Point.x, Point.y, nWidth, nHeight, FALSE);
}
INT_PTR CALLBACK license_callback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_INITDIALOG:
center_dialog(hDlg);
SetDlgItemTextA(hDlg, IDC_LICENSE_TEXT, gplv3);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
}
return (INT_PTR)FALSE;
}
INT_PTR CALLBACK about_callback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_INITDIALOG:
center_dialog(hDlg);
SetDlgItemTextA(hDlg, IDC_ABOUT_COPYRIGHTS, additional_copyrights);
break;
case WM_CTLCOLORSTATIC:
if ((HWND)lParam == GetDlgItem(hDlg, IDC_ZADIG_BOLD)) {
SetBkMode((HDC)wParam, TRANSPARENT);
SelectObject((HDC)wParam, bold_font);
return (INT_PTR)CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
}
break;
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code) {
case NM_CLICK:
case NM_RETURN:
switch (LOWORD(wParam)) {
case IDC_ABOUT_LIBWDI_URL:
ShellExecuteA(hDlg, "open", LIBWDI_URL, NULL, NULL, SW_SHOWNORMAL);
break;
case IDC_ABOUT_BUG_URL:
ShellExecuteA(hDlg, "open", BUG_URL, NULL, NULL, SW_SHOWNORMAL);
break;
}
break;
}
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
case IDC_ABOUT_LICENSE:
DialogBoxA(main_instance, MAKEINTRESOURCEA(IDD_LICENSE), hDlg, license_callback);
break;
}
break;
}
return (INT_PTR)FALSE;
}
INT_PTR CALLBACK notification_callback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT loc;
int i;
static LRESULT disabled[9] = { HTLEFT, HTRIGHT, HTTOP, HTBOTTOM, HTSIZE,
HTTOPLEFT, HTTOPRIGHT, HTBOTTOMLEFT, HTBOTTOMRIGHT };
static HBRUSH white_brush, separator_brush;
switch (message) {
case WM_INITDIALOG:
white_brush = CreateSolidBrush(WHITE);
separator_brush = CreateSolidBrush(SEPARATOR_GREY);
center_dialog(hDlg);
if (Static_SetIcon(GetDlgItem(hDlg, IDC_NOTIFICATION_ICON), hMessageIcon) == 0) {
dprintf("could not set dialog icon");
}
if (message_title != NULL) {
SetWindowTextA(hDlg, message_title);
}
if (message_text != NULL) {
SetWindowTextA(GetDlgItem(hDlg, IDC_NOTIFICATION_TEXT), message_text);
}
return (INT_PTR)TRUE;
case WM_CTLCOLORSTATIC:
SetBkMode((HDC)wParam, TRANSPARENT);
if ((HWND)lParam == GetDlgItem(hDlg, IDC_NOTIFICATION_LINE)) {
return (INT_PTR)separator_brush;
}
return (INT_PTR)white_brush;
case WM_NCHITTEST:
loc = DefWindowProc(hDlg, message, wParam, lParam);
for(i = 0; i < 9; i++) {
if (loc == disabled[i]) {
return (INT_PTR)TRUE;
}
}
return (INT_PTR)FALSE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
case IDCANCEL:
case IDC_NOTIFICATION_CLOSE:
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
void notification(int type, char* text, char* title)
{
message_text = text;
message_title = title;
switch(type) {
case MSG_WARNING:
hMessageIcon = LoadIcon(NULL, IDI_WARNING);
break;
case MSG_ERROR:
hMessageIcon = LoadIcon(NULL, IDI_ERROR);
break;
case MSG_INFO:
default:
hMessageIcon = LoadIcon(NULL, IDI_INFORMATION);
break;
}
DialogBox(main_instance, MAKEINTRESOURCE(IDD_NOTIFICATION), hMain, notification_callback);
message_text = NULL;
}
struct {
HWND hTip;
WNDPROC original_proc;
LPWSTR wstring;
} ttlist[MAX_TOOLTIPS] = { {0} };
INT_PTR CALLBACK tooltip_callback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
LPNMTTDISPINFOW lpnmtdi;
int i = MAX_TOOLTIPS;
for (i=0; i<MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == hDlg) break;
}
if (i == MAX_TOOLTIPS) {
return (INT_PTR)FALSE;
}
switch (message)
{
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code) {
case TTN_GETDISPINFOW:
lpnmtdi = (LPNMTTDISPINFOW)lParam;
lpnmtdi->lpszText = ttlist[i].wstring;
SendMessage(hDlg, TTM_SETMAXTIPWIDTH, 0, 300);
return (INT_PTR)TRUE;
}
break;
}
return CallWindowProc(ttlist[i].original_proc, hDlg, message, wParam, lParam);
}
HWND create_tooltip(HWND hControl, char* message, int duration)
{
TOOLINFOW toolInfo = {0};
int i;
if ( (hControl == NULL) || (message == NULL) ) {
return (HWND)NULL;
}
for (i=0; i<MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == NULL) break;
}
if (i == MAX_TOOLTIPS) {
return (HWND)NULL; }
ttlist[i].hTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hMain, NULL,
main_instance, NULL);
if (ttlist[i].hTip == NULL) {
return (HWND)NULL;
}
ttlist[i].original_proc = (WNDPROC)SetWindowLongPtr(ttlist[i].hTip, GWLP_WNDPROC, (LONG_PTR)tooltip_callback);
ttlist[i].wstring = utf8_to_wchar(message);
PostMessage(ttlist[i].hTip, TTM_SETDELAYTIME, (WPARAM)TTDT_AUTOPOP, (LPARAM)duration);
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.hwnd = ttlist[i].hTip; toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
toolInfo.uId = (UINT_PTR)hControl;
toolInfo.lpszText = LPSTR_TEXTCALLBACKW;
SendMessageW(ttlist[i].hTip, TTM_ADDTOOLW, 0, (LPARAM)&toolInfo);
return ttlist[i].hTip;
}
void destroy_tooltip(HWND hWnd)
{
int i;
if (hWnd == NULL) return;
for (i=0; i<MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == hWnd) break;
}
if (i == MAX_TOOLTIPS) return;
DestroyWindow(hWnd);
safe_free(ttlist[i].wstring);
ttlist[i].original_proc = NULL;
ttlist[i].hTip = NULL;
}
void destroy_all_tooltips(void)
{
int i;
for (i=0; i<MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == NULL) continue;
DestroyWindow(ttlist[i].hTip);
safe_free(ttlist[i].wstring);
}
}