#include "main.h" #include "resource.h" #include "api_mldb.h" #include "../Winamp/strutil.h" enum { STATUS_SEARCHING, STATUS_GETINFO, STATUS_DONE, }; extern HWND g_bgrescan_status_hwnd; extern nde_scanner_t m_media_scanner; #define MAX_RECURSE_DEPTH 32 /* Event handles */ static HANDLE scan_killswitch=0; static HANDLE scan_cancel=0; static HANDLE scan_cancel_complete=0; /* Thread handle */ static HANDLE scan_thread=0; /* extension list */ static wchar_t *scan_extlist=0; static void SyncTable() { EnterCriticalSection(&g_db_cs); NDE_Table_Sync(g_table); g_table_dirty=0; LeaveCriticalSection(&g_db_cs); } static bool ScanCancelled(HANDLE *events, int count) { // make sure no one cancelled us DWORD eventFired=WaitForMultipleObjectsEx(count, events, FALSE, 0, TRUE); if (eventFired >= WAIT_OBJECT_0 && eventFired < (WAIT_OBJECT_0+count)) return true; return false; } static bool SupportedType(const wchar_t *ext) { if (!scan_extlist) scan_extlist=(wchar_t*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GET_EXTLISTW); // dunno how this would happen but should verify if (!scan_extlist || scan_extlist == (wchar_t *)1) return false; const wchar_t *a = scan_extlist; while (a && *a) { if (CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, a, -1, ext, -1) == CSTR_EQUAL) { return true; } a+=wcslen(a)+1; } return false; } static void CountFolder(const wchar_t *folder, int recurse, HANDLE cancelswitch, volatile int *found) { wchar_t filespec[MAX_PATH] = {0}; PathCombineW(filespec, folder, L"*.*"); WIN32_FIND_DATAW findData = {0}; HANDLE h = FindFirstFileW(filespec, &findData); if (h != INVALID_HANDLE_VALUE) { if (IsWindow(g_bgrescan_status_hwnd)) { wchar_t status[150+MAX_PATH] = {0}; WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_DIR, status, 150); const wchar_t *p=folder+wcslen(folder); while (p > folder && *p != '\\') p--; p--; while (p >= folder && *p != '\\') p--; StringCbCatW(status, sizeof(status), ++p); SetWindowTextW(g_bgrescan_status_hwnd,status); } HANDLE events[2] = { scan_killswitch, cancelswitch}; do { // make sure no one cancelled us if (ScanCancelled(events, 2)) break; /* if it's a directory (And not either of the two special dirs */ if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && lstrcmpiW(findData.cFileName, L".") && lstrcmpiW(findData.cFileName, L"..")) { if (recurse && recurse < MAX_RECURSE_DEPTH) { PathCombineW(filespec, folder, findData.cFileName); CountFolder(filespec, recurse+1, cancelswitch, found); // add 1 so we can verify recurse depth } } if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { wchar_t *ext=extensionW(findData.cFileName); if (ext && ext[0] && SupportedType(ext)) { PathCombineW(filespec, folder, findData.cFileName); if (IsWindow(g_bgrescan_status_hwnd)) { wchar_t b[150+MAX_PATH] = {0}; WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, b, 150); StringCbCatW(b, sizeof(b), filespec); SetWindowTextW(g_bgrescan_status_hwnd,b); } if (found) (*found)++; } } } while (FindNextFileW(h, &findData)); FindClose(h); } else if (!(GetFileAttributesW(folder) & FILE_ATTRIBUTE_DIRECTORY)) { if (IsWindow(g_bgrescan_status_hwnd)) { wchar_t b[150+MAX_PATH] = {0}; WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, b, 150); StringCbCatW(b, sizeof(b), folder); SetWindowTextW(g_bgrescan_status_hwnd,b); } if (found) (*found)++; } } static void ScanFolder(const wchar_t *folder, int recurse, int metadata, int guessmode, HANDLE cancelswitch, volatile int *scanned) { if ((unsigned long)folder < 65536) return; wchar_t filespec[MAX_PATH] = {0}; wchar_t status[150+MAX_PATH] = {0}; PathCombineW(filespec, folder, L"*.*"); WIN32_FIND_DATAW findData = {0}; HANDLE h = FindFirstFileW(filespec, &findData); if (h != INVALID_HANDLE_VALUE) { if (IsWindow(g_bgrescan_status_hwnd)) { WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_DIR, status, 150); const wchar_t *p=folder+wcslen(folder); while (p > folder && *p != '\\') p--; p--; while (p >= folder && *p != '\\') p--; StringCbCatW(status, sizeof(status), ++p); SetWindowTextW(g_bgrescan_status_hwnd,status); } HANDLE events[2] = { scan_killswitch, cancelswitch}; do { // make sure no one cancelled us if (ScanCancelled(events, 2)) break; /* if it's a directory (And not either of the two special dirs */ if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && lstrcmpiW(findData.cFileName, L".") && lstrcmpiW(findData.cFileName, L"..")) { if (recurse && recurse < MAX_RECURSE_DEPTH) { PathCombineW(filespec, folder, findData.cFileName); ScanFolder(filespec, recurse+1, metadata, guessmode, cancelswitch, scanned); // add 1 so we can verify recurse depth } } if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { wchar_t *ext=extensionW(findData.cFileName); if (ext && ext[0] && SupportedType(ext)) { PathCombineW(filespec, folder, findData.cFileName); if (IsWindow(g_bgrescan_status_hwnd)) { WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, status, 150); StringCbCatW(status, sizeof(status), filespec); SetWindowTextW(g_bgrescan_status_hwnd,status); } addFileToDb(filespec, 0, metadata, guessmode); if (scanned) (*scanned)++; } } } while (FindNextFileW(h, &findData)); FindClose(h); } else if (!(GetFileAttributesW(folder) & FILE_ATTRIBUTE_DIRECTORY)) { if (IsWindow(g_bgrescan_status_hwnd)) { WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_FILE, status, 150); StringCbCatW(status, sizeof(status), folder); SetWindowTextW(g_bgrescan_status_hwnd,status); } addFileToDb(folder, 0, metadata, guessmode); if (scanned) (*scanned)++; } } static DWORD CALLBACK ScanThreadProc(LPVOID param) { /* sit and run APCs until we get signalled to die */ HANDLE events[2] = { scan_killswitch, scan_cancel}; int eventFired; do { eventFired=WaitForMultipleObjectsEx(2, events, FALSE, INFINITE, TRUE); switch(eventFired) { case WAIT_OBJECT_0+1: // cancel event ResetEvent(scan_cancel); SetEvent(scan_cancel_complete); break; } } while (eventFired != WAIT_OBJECT_0); if (scan_extlist && scan_extlist != (wchar_t *)1) GlobalFree((HGLOBAL)scan_extlist); scan_extlist=0; return 0; } static bool ScanCreateThread() { if (!scan_thread) { /* create events */ scan_killswitch = CreateEvent(NULL, TRUE, FALSE, NULL); scan_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); scan_cancel_complete = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset event /* start thread */ scan_thread = CreateThread(NULL, 0, ScanThreadProc, 0, 0, 0); } return !!scan_thread; } void Scan_Cancel() { HWND old = g_bgrescan_status_hwnd; // clear g_bgrescan_status_hwnd so that we don't deadlock when the BG thread calls SetWindowText g_bgrescan_status_hwnd = 0; if (scan_cancel) SignalObjectAndWait(scan_cancel, scan_cancel_complete, INFINITE, FALSE); g_bgrescan_status_hwnd = old; } void Scan_Kill() { HWND old = g_bgrescan_status_hwnd; // clear g_bgrescan_status_hwnd so that we don't deadlock when the BG thread calls SetWindowText g_bgrescan_status_hwnd = 0; if (scan_thread) SignalObjectAndWait(scan_killswitch, scan_thread, INFINITE, FALSE); g_bgrescan_status_hwnd = old; } /* --------------- * Scan_ScanFolder * --------------- */ struct ScanFolderParams { ScanFolderParams(const wchar_t *_path, int _guess, int _meta, int _recurse) { path = _wcsdup(_path); guess = _guess >= 0 ? _guess : g_config->ReadInt(L"guessmode",0);; meta = _meta >= 0 ? _meta : g_config->ReadInt(L"usemetadata",1); recurse = _recurse; } ~ScanFolderParams() { free(path); } wchar_t *path; int guess; int meta; int recurse; }; static VOID CALLBACK ScanFolderAPC(ULONG_PTR param) { // clear extension list to get latest config if (scan_extlist && scan_extlist != (wchar_t *)1) GlobalFree((HGLOBAL)scan_extlist); scan_extlist = 0; ScanFolderParams *params = (ScanFolderParams *)param; ScanFolder(params->path, params->recurse, params->meta, params->guess, scan_cancel, 0); SyncTable(); delete params; } void Scan_ScanFolderBackground(const wchar_t *path, int guess, int meta, int recurse) { if (ScanCreateThread()) { ScanFolderParams *params = new ScanFolderParams(path, guess, meta, recurse); if (QueueUserAPC(ScanFolderAPC, scan_thread, (ULONG_PTR)params) == 0) delete params; } } /* --------------- * Scan_ScanFolders * --------------- */ struct ScanFoldersParams { ScanFoldersParams(wchar_t **_path, size_t _count, int *_guess, int *_meta, int *_recurse) { path = _path; count = _count; guess = _guess; meta = _meta; recurse = _recurse; found = 0; scanned = 0; cancel_switch = CreateEvent(NULL, TRUE, FALSE, NULL); status = STATUS_SEARCHING; ui = 0; in_timer = 0; } ~ScanFoldersParams() { for (size_t i=0;i!=count;i++) free(path[i]); free(path); free(guess); free(meta); free(recurse); CloseHandle(cancel_switch); } wchar_t **path; size_t count; int *guess; int *meta; int *recurse; volatile int found; volatile int scanned; volatile int status; int in_timer; HANDLE cancel_switch; HWND ui; }; static VOID CALLBACK ScanFoldersAPC(ULONG_PTR param) { // clear extension list to get latest config if (scan_extlist && scan_extlist != (wchar_t *)1) GlobalFree((HGLOBAL)scan_extlist); scan_extlist = 0; ScanFoldersParams *params = (ScanFoldersParams *)param; HANDLE events[2] = { scan_killswitch, params->cancel_switch}; for (size_t i=0;i!=params->count;i++) { if (ScanCancelled(events, 2)) break; CountFolder(params->path[i], params->recurse[i], params->cancel_switch, ¶ms->found); } params->status = STATUS_GETINFO; for (size_t i=0;i!=params->count;i++) { if (ScanCancelled(events, 2)) break; int guess = params->guess[i] >= 0 ? params->guess[i] : g_config->ReadInt(L"guessmode",0);; int meta = params->meta[i] >= 0 ? params->meta[i] : g_config->ReadInt(L"usemetadata",1); ScanFolder(params->path[i], params->recurse[i], meta, guess, params->cancel_switch, ¶ms->scanned); } params->status = STATUS_DONE; PostMessage(params->ui, WM_APP, 0, 0); } static INT_PTR CALLBACK ScanFileUI(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) { switch(uMsg) { case WM_INITDIALOG: { SetDlgItemTextW(hwndDlg,IDC_STATUS,WASABI_API_LNGSTRINGW(IDS_INITIALIZING)); ScanFoldersParams *params = (ScanFoldersParams *)lParam; params->ui = hwndDlg; SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); if (QueueUserAPC(ScanFoldersAPC, scan_thread, (ULONG_PTR)lParam) == 0) EndDialog(hwndDlg, 0); else { SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETRANGE,0,MAKELPARAM(0, 100)); SetTimer(hwndDlg,0x123,300,NULL); } // show window and restore last position as applicable POINT pt = {g_config->ReadInt(L"scan_x", -1), g_config->ReadInt(L"scan_y", -1)}; if (!windowOffScreen(hwndDlg, pt)) SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); } break; case WM_TIMER: { ScanFoldersParams *params = (ScanFoldersParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); if (params->in_timer) break; params->in_timer++; if(params->status==STATUS_SEARCHING) { wchar_t tmp[512] = {0}; StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_SEARCHING_X_FILES_FOUND), params->found); SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp); } else if(params->status==STATUS_GETINFO) { wchar_t tmp[512] = {0}; int perc=params->found?(params->scanned*100/params->found):0; StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_GETTING_INFO_FROM_FILES_PERCENT),perc); SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp); SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,perc,0); } params->in_timer--; } break; case WM_APP: { KillTimer(hwndDlg,0x123); SyncTable(); EndDialog(hwndDlg,0); } break; case WM_COMMAND: if (LOWORD(wParam)==IDCANCEL) { ScanFoldersParams *params = (ScanFoldersParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); SetEvent(params->cancel_switch); } break; case WM_DESTROY: { RECT scan_rect = {0}; GetWindowRect(hwndDlg, &scan_rect); g_config->WriteInt(L"scan_x", scan_rect.left); g_config->WriteInt(L"scan_y", scan_rect.top); KillTimer(hwndDlg,0x123); ScanFoldersParams *params = (ScanFoldersParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); delete params; return FALSE; } } return FALSE; } /* When you call this function, it will own the memory and release it with free() */ void Scan_ScanFolders(HWND parent, size_t count, wchar_t **paths, int *guess, int *meta, int *recurse) { openDb(); if (g_table && ScanCreateThread()) { ScanFoldersParams *params = new ScanFoldersParams(paths, count, guess, meta, recurse); WASABI_API_LNG->LDialogBoxParamW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID), GetModuleHandleW(L"winamp.exe"), IDD_ADDSTUFF, parent, (DLGPROC)ScanFileUI, (LPARAM)params); PostMessage(plugin.hwndWinampParent,WM_WA_IPC,NDE_Table_GetRecordsCount(g_table),IPC_STATS_LIBRARY_ITEMCNT); } else { for (size_t i=0;i!=count;i++) free(paths[i]); free(paths); } } void Scan_ScanFolder(HWND parent, const wchar_t *path, int guess, int meta, int recurse) { // kind of a hack ... if (ScanCreateThread()) { wchar_t **paths = (wchar_t **)calloc(1, sizeof(wchar_t*)); int *guesses = (int *)calloc(1, sizeof(int)); int *metas = (int *)calloc(1, sizeof(int)); int *recs = (int *)calloc(1, sizeof(int)); *guesses = guess; *metas = meta; *recs = recurse; paths[0] = _wcsdup(path); Scan_ScanFolders(parent, 1, paths, guesses, metas, recs); } } static VOID CALLBACK BackgroundScanAPC(ULONG_PTR param) { openDb(); if (!g_table) return; HANDLE events[2] = { scan_killswitch, scan_cancel}; // clear extension list to get latest config if (scan_extlist && scan_extlist != (wchar_t *)1) GlobalFree((HGLOBAL)scan_extlist); scan_extlist = 0; // read list from config UINT codePage = CP_ACP; char scandirlist[65536] = {0}; if (!g_config->ReadString("scandirlist", 0, scandirlist, 65536)) { g_config->ReadString("scandirlist_utf8","", scandirlist, 65536); codePage = CP_UTF8; } AutoWide s1(scandirlist, codePage); size_t len = wcslen(s1)+2; wchar_t *s =(wchar_t*)calloc(len, sizeof(wchar_t)); if (s) { lstrcpynW(s, s1, len); s[wcslen(s)+1]=0; wchar_t *p=s; while (p && *p == L'|') p++; while ((p=wcsstr(p,L"|"))) { *p++=0; while (p && *p == L'|') p++; } p=s; // iterate through list while (p && *p && !ScanCancelled(events, 2)) { while (p && *p == L'|') p++; int use_metadata=g_config->ReadInt(L"usemetadata",1); int guess_mode=g_config->ReadInt(L"guessmode",0); int recurse=1; if (*p == L'<' && wcsstr(p,L">")) { p++; while (p && *p != L'>') { // can prefix directory // M=metadata use override // m=no metadata // S=smart guessing // s=stupid guessing if (*p == L'M') use_metadata=1; else if (*p == L'm') use_metadata=0; else if (*p == L'S') guess_mode=0; else if (*p == L's') guess_mode=1; else if (*p == L'r') recurse=0; else if (*p == L'g') guess_mode=2; p++; } p++; } ScanFolder(p, recurse, use_metadata, guess_mode, scan_cancel, 0); p+=wcslen(p)+1; } free(s); } /* Remove missing files */ if (!ScanCancelled(events, 2) && g_config->ReadInt(L"bgrescan_compact",1)) { EnterCriticalSection(&g_db_cs); nde_scanner_t scanner = NDE_Table_CreateScanner(g_table); NDE_Scanner_Query(scanner, L""); NDE_Scanner_First(scanner); again: nde_field_t f=NDE_Scanner_GetFieldByID(scanner, MAINTABLE_ID_FILENAME); wchar_t *gs=0; if (f) { gs = NDE_StringField_GetString(f); ndestring_retain(gs); if (GetFileAttributesW(gs) != INVALID_FILE_ATTRIBUTES) { NDE_Scanner_Next(scanner); } else { // Issue wasabi callback for pre removal WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)gs, 0); NDE_Scanner_Delete(scanner); NDE_Scanner_Post(scanner); // Issue wasabi callback for pre removal WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)gs, 0); } } LeaveCriticalSection(&g_db_cs); if (f) // done checking for unused files { if (IsWindow(g_bgrescan_status_hwnd)) { wchar_t b[150+MAX_PATH] = {0}; WASABI_API_LNGSTRINGW_BUF(IDS_CHECKING_FOR_FILE, b, 150); StringCbCatW(b, sizeof(b), PathFindFileNameW(gs)); SetWindowTextW(g_bgrescan_status_hwnd,b); } ndestring_release(gs); gs=0; if (!ScanCancelled(events, 2)) { EnterCriticalSection(&g_db_cs); goto again; } } EnterCriticalSection(&g_db_cs); NDE_Table_DestroyScanner(g_table, scanner); LeaveCriticalSection(&g_db_cs); if (IsWindow(g_bgrescan_status_hwnd)) { wchar_t b[150] = {0}; WASABI_API_LNGSTRINGW_BUF(IDS_COMPACTING, b, 150); SetWindowTextW(g_bgrescan_status_hwnd,b); } } // TODO: hmmm, safe to do on separate thread? EnterCriticalSection(&g_db_cs); if (!ScanCancelled(events, 2)) { wchar_t *last_query = NULL; if (m_media_scanner) { const wchar_t *lq = NDE_Scanner_GetLastQuery(m_media_scanner); if (lq) last_query = _wcsdup(lq); NDE_Table_DestroyScanner(g_table, m_media_scanner); } NDE_Table_Sync(g_table); // this is currently b0rk3d -- fucko :) NDE_Table_Compact(g_table); g_table_dirty=0; if (m_media_scanner) { m_media_scanner=NDE_Table_CreateScanner(g_table); if (last_query != NULL) { NDE_Scanner_Query(m_media_scanner, last_query); free(last_query); } } PostMessage(plugin.hwndWinampParent,WM_WA_IPC,NDE_Table_GetRecordsCount(g_table),IPC_STATS_LIBRARY_ITEMCNT); } else { NDE_Table_Sync(g_table); // this is currently b0rk3d -- fucko :) g_table_dirty=0; } LeaveCriticalSection(&g_db_cs); if (IsWindow(g_bgrescan_status_hwnd)) SetWindowTextW(g_bgrescan_status_hwnd,L""); g_bgscan_last_rescan = time(NULL); g_bgscan_scanning = 0; } void Scan_BackgroundScan() { if (ScanCreateThread()) { g_bgrescan_force = 0; g_bgscan_last_rescan = time(NULL); g_bgscan_scanning = 1; QueueUserAPC(BackgroundScanAPC, scan_thread, (ULONG_PTR)0); } } static void RemoveFiles(HANDLE cancelswitch, volatile int *found, volatile int *count, volatile int *scanned) { // TODO: benski> we might need to keep the database lock the whole time. need to think it thru EnterCriticalSection(&g_db_cs); nde_scanner_t myscanner=NDE_Table_CreateScanner(g_table); NDE_Scanner_Query(myscanner, L""); NDE_Scanner_First(myscanner); *found=0; *scanned=0; *count=NDE_Table_GetRecordsCount(g_table); LeaveCriticalSection(&g_db_cs); HANDLE events[2] = { scan_killswitch, cancelswitch}; bool fileRemoved = false; wchar_t *filename; while (!ScanCancelled(events, 2)) { EnterCriticalSection(&g_db_cs); if (!NDE_Scanner_BOF(myscanner) && !NDE_Scanner_EOF(myscanner)) { nde_field_t f= NDE_Scanner_GetFieldByID(myscanner, MAINTABLE_ID_FILENAME); if (f) { (*scanned)++; filename = (NDE_StringField_GetString(f)); if (GetFileAttributesW(NDE_StringField_GetString(f)) != INVALID_FILE_ATTRIBUTES) { NDE_Scanner_Next(myscanner); } else { // Issue wasabi callback for pre removal WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)filename, 0); //remove file NDE_Scanner_Delete(myscanner); NDE_Scanner_Post(myscanner); (*found)++; fileRemoved = true; } } else { //remove file NDE_Scanner_Delete(myscanner); NDE_Scanner_Post(myscanner); (*found)++; fileRemoved = false; } } else // last file { LeaveCriticalSection(&g_db_cs); break; } LeaveCriticalSection(&g_db_cs); if (fileRemoved) { // Issue wasabi callback for pre removal WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)filename, 0); fileRemoved = false; } } EnterCriticalSection(&g_db_cs); NDE_Table_DestroyScanner(g_table, myscanner); // important that we delete the scanner BEFORE myscanner=0; wchar_t *last_query = NULL; if (m_media_scanner) { const wchar_t *lq = NDE_Scanner_GetLastQuery(m_media_scanner); if (lq) last_query = _wcsdup(lq); NDE_Table_DestroyScanner(g_table, m_media_scanner); } NDE_Table_Sync(g_table); // this is currently b0rk3d -- fucko :) NDE_Table_Compact(g_table); g_table_dirty=0; if (m_media_scanner) { m_media_scanner=NDE_Table_CreateScanner(g_table); if (last_query != NULL) { NDE_Scanner_Query(m_media_scanner, last_query); free(last_query); } } LeaveCriticalSection(&g_db_cs); } struct RemoveFilesParams { RemoveFilesParams() { found = 0; scanned = 0; total = 0; cancel_switch = CreateEvent(NULL, TRUE, FALSE, NULL); ui = 0; in_timer = 0; } ~RemoveFilesParams() { CloseHandle(cancel_switch); } volatile int found; volatile int scanned; volatile int total; int in_timer; HANDLE cancel_switch; HWND ui; }; static VOID CALLBACK RemoveFilesAPC(ULONG_PTR param) { RemoveFilesParams *params = (RemoveFilesParams *)param; RemoveFiles(params->cancel_switch, ¶ms->found, ¶ms->total, ¶ms->scanned); PostMessage(params->ui, WM_APP, 0, 0); } static INT_PTR CALLBACK RemoveFilesUI(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) { switch(uMsg) { case WM_INITDIALOG: { SetWindowTextW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_REMOVING_FILES_NOT_EXISTING)); SetDlgItemTextW(hwndDlg,IDC_STATUS,WASABI_API_LNGSTRINGW(IDS_INITIALIZING)); RemoveFilesParams *params = (RemoveFilesParams *)lParam; params->ui = hwndDlg; SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); if (QueueUserAPC(RemoveFilesAPC, scan_thread, (ULONG_PTR)lParam) == 0) EndDialog(hwndDlg, 0); else { SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETRANGE,0,MAKELPARAM(0, 100)); SetTimer(hwndDlg,0x123,300,NULL); } // show window and restore last position as applicable POINT pt = {g_config->ReadInt(L"scan_x", -1), g_config->ReadInt(L"scan_y", -1)}; if (!windowOffScreen(hwndDlg, pt)) SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); } break; case WM_TIMER: { RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); if (params->in_timer) break; params->in_timer++; if(params->total) { wchar_t tmp[512] = {0}; int perc=(params->scanned*100/(params->total?params->total:1)); StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_SCANNING_X_OF_X_X_REMOVED),params->scanned,params->total,params->found); SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp); SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,perc,0); } params->in_timer--; } break; case WM_APP: { RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); KillTimer(hwndDlg,0x123); wchar_t tmp[512] = {0}; int perc=(params->scanned*100/(params->total?params->total:1)); StringCchPrintfW(tmp, 512, WASABI_API_LNGSTRINGW(IDS_SCANNED_X_FILES_X_REMOVED),params->total,params->found); SetDlgItemTextW(hwndDlg,IDC_STATUS,tmp); SendDlgItemMessage(hwndDlg,IDC_PROGRESS1,PBM_SETPOS,perc,0); SyncTable(); EndDialog(hwndDlg,0); } break; case WM_COMMAND: if (LOWORD(wParam)==IDCANCEL) { RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); SetEvent(params->cancel_switch); } break; case WM_DESTROY: { RECT scan_rect = {0}; GetWindowRect(hwndDlg, &scan_rect); g_config->WriteInt(L"scan_x", scan_rect.left); g_config->WriteInt(L"scan_y", scan_rect.top); KillTimer(hwndDlg,0x123); RemoveFilesParams *params = (RemoveFilesParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); delete params; } return FALSE; } return FALSE; } void Scan_RemoveFiles(HWND parent) { openDb(); if (g_table && ScanCreateThread()) { RemoveFilesParams *params = new RemoveFilesParams(); WASABI_API_LNG->LDialogBoxParamW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID), GetModuleHandleW(L"winamp.exe"), IDD_ADDSTUFF, parent, (DLGPROC)RemoveFilesUI, (LPARAM)params); PostMessage(plugin.hwndWinampParent,WM_WA_IPC,NDE_Table_GetRecordsCount(g_table),IPC_STATS_LIBRARY_ITEMCNT); } }