[C] 纯文本查看 复制代码
#include <windows.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <shlobj.h>
#pragma comment(lib, "shlwapi.lib")
#define IDI_ICON1 101
HWND g_hMain; HMENU g_hPopup, g_hRight;
BOOL g_bDrag, g_bHover; POINT g_ptDrag, g_ptMouseDown;
int g_nTextIdx = 1; UINT_PTR g_uTimer;
WCHAR* g_szTexts[] = {L"剪", L"映", L"助", L"手"};
DWORD WINAPI ThreadImport(LPVOID p);
DWORD WINAPI ThreadRestore(LPVOID p);
BOOL IsRunning() {
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe = {sizeof(pe)};
for (BOOL ok = Process32FirstW(h, &pe); ok; ok = Process32NextW(h, &pe))
if (!_wcsicmp(pe.szExeFile, L"JianyingPro.exe")) { CloseHandle(h); return TRUE; }
CloseHandle(h); return FALSE;
}
HWND FindWnd() {
HWND h = FindWindowW(NULL, L"剪映专业版");
return h ? h : (h = FindWindowW(L"Qt5QWindowIcon", NULL)) ? h : FindWindowW(NULL, L"剪映");
}
void ActivateWnd(HWND h) {
if (!h) return;
WINDOWPLACEMENT wp = {sizeof(wp)};
ShowWindow(h, GetWindowPlacement(h, &wp) && wp.showCmd == SW_SHOWMINIMIZED ? SW_RESTORE : SW_SHOWNA);
SetForegroundWindow(h); BringWindowToTop(h);
DWORD tid = GetWindowThreadProcessId(h, NULL);
AttachThreadInput(GetCurrentThreadId(), tid, TRUE); SetFocus(h);
AttachThreadInput(GetCurrentThreadId(), tid, FALSE);
}
BOOL GetRegPath(HKEY root, LPCWSTR sub, LPCWSTR val, WCHAR* buf, DWORD* sz) {
HKEY h; DWORD t;
if (RegOpenKeyExW(root, sub, 0, KEY_READ, &h) || RegQueryValueExW(h, val, 0, &t, (LPBYTE)buf, sz)) { RegCloseKey(h); return FALSE; }
RegCloseKey(h); return t == REG_SZ;
}
BOOL GetJianyingPath(WCHAR* buf) {
WCHAR path[MAX_PATH]; DWORD sz = MAX_PATH * 2;
return GetRegPath(HKEY_CURRENT_USER, L"SOFTWARE\\Bytedance\\JianyingPro", L"installDir", path, &sz) && (PathCombineW(buf, path, L"JianyingPro.exe"), TRUE);
}
BOOL GetDraftPath(WCHAR* buf) {
WCHAR path[MAX_PATH]; DWORD sz = MAX_PATH * 2;
if (GetRegPath(HKEY_CURRENT_USER, L"SOFTWARE\\Bytedance\\JianyingPro\\GlobalSettings\\History", L"currentCustomDraftPath", path, &sz)) { wcscpy(buf, path); return TRUE; }
SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, path);
PathCombineW(buf, path, L"JianyingPro\\User Data\\Projects\\com.lveditor.draft"); return TRUE;
}
BOOL FindLatestFileRecursive(WCHAR* dir, LPCWSTR endStr, FILETIME* ft, WCHAR* out) {
WIN32_FIND_DATAW fd; WCHAR path[MAX_PATH];
wsprintfW(path, L"%s\\*", dir);
HANDLE h = FindFirstFileW(path, &fd);
if (h == INVALID_HANDLE_VALUE) return FALSE;
BOOL found = FALSE; FILETIME lt = {0, 0}; WCHAR lo[MAX_PATH] = {0};
do {
if (wcscmp(fd.cFileName, L".") == 0 || wcscmp(fd.cFileName, L"..") == 0) continue;
wsprintfW(path, L"%s\\%s", dir, fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (FindLatestFileRecursive(path, endStr, ft, out)) found = TRUE;
} else {
int len = wcslen(fd.cFileName), elen = wcslen(endStr);
if (len >= elen && !_wcsicmp(fd.cFileName + len - elen, endStr) && CompareFileTime(&fd.ftLastWriteTime, <) > 0) {
lt = fd.ftLastWriteTime; wcscpy(lo, path); found = TRUE;
}
}
} while (FindNextFileW(h, &fd));
FindClose(h); if (found) { *ft = lt; wcscpy(out, lo); }
return found;
}
BOOL FindLatestDraftFolder(WCHAR* dir, WCHAR* out) {
WIN32_FIND_DATAW fd; WCHAR path[MAX_PATH];
wsprintfW(path, L"%s\\*", dir);
HANDLE h = FindFirstFileW(path, &fd);
if (h == INVALID_HANDLE_VALUE) return FALSE;
BOOL found = FALSE; FILETIME lt = {0, 0};
do {
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && wcscmp(fd.cFileName, L".") && wcscmp(fd.cFileName, L"..")) {
if (!found || CompareFileTime(&fd.ftLastWriteTime, <) > 0) { lt = fd.ftLastWriteTime; wsprintfW(out, L"%s\\%s", dir, fd.cFileName); found = TRUE; }
}
} while (FindNextFileW(h, &fd));
FindClose(h); return found;
}
void SimCombo(WORD mod, WORD vk) {
keybd_event(mod, 0, 0, 0); Sleep(50); keybd_event(vk, 0, 0, 0); Sleep(50);
keybd_event(vk, 0, KEYEVENTF_KEYUP, 0); Sleep(50); keybd_event(mod, 0, KEYEVENTF_KEYUP, 0); Sleep(50);
}
void SimCombo3(WORD mod1, WORD mod2, WORD vk) {
keybd_event(mod1, 0, 0, 0); Sleep(50); keybd_event(mod2, 0, 0, 0); Sleep(50);
keybd_event(vk, 0, 0, 0); Sleep(50);
keybd_event(vk, 0, KEYEVENTF_KEYUP, 0); Sleep(50);
keybd_event(mod2, 0, KEYEVENTF_KEYUP, 0); Sleep(50);
keybd_event(mod1, 0, KEYEVENTF_KEYUP, 0); Sleep(50);
}
void KillProc(LPCWSTR n) {
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (h == INVALID_HANDLE_VALUE) return;
PROCESSENTRY32W pe = {sizeof(pe)};
for (BOOL ok = Process32FirstW(h, &pe); ok; ok = Process32NextW(h, &pe))
if (!_wcsicmp(pe.szExeFile, n)) { HANDLE p = OpenProcess(PROCESS_TERMINATE, 0, pe.th32ProcessID); if (p) { TerminateProcess(p, 0); CloseHandle(p); } }
CloseHandle(h);
}
void RestartJianying() {
WCHAR path[MAX_PATH], exe[MAX_PATH]; DWORD sz = MAX_PATH, t; HKEY hKey;
if (!RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Bytedance\\JianyingPro", 0, KEY_READ, &hKey) && !RegQueryValueExW(hKey, L"installDir", 0, &t, (LPBYTE)path, &sz) && t == REG_SZ) {
RegCloseKey(hKey); PathCombineW(exe, path, L"JianyingPro.exe");
KillProc(L"JianyingPro.exe"), KillProc(L"Vedetector.exe"); Sleep(200);
if (PathFileExistsW(exe)) ShellExecuteW(0, L"open", exe, 0, 0, SW_SHOWNORMAL);
}
}
void OpenJianying() {
HWND h = FindWindowW(NULL, L"剪映专业版");
if (h) { ActivateWnd(h); return; }
RestartJianying();
}
void SetCompositeKey() {
WCHAR app[MAX_PATH], files[2][MAX_PATH];
if (FAILED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, app))) {
MessageBoxW(g_hMain, L"获取用户目录失败", L"错误", MB_OK);
return;
}
wsprintfW(files[0], L"%s\\JianyingPro\\User Data\\Config\\Shortcut\\Final Cut Pro X.json", app);
wsprintfW(files[1], L"%s\\JianyingPro\\User Data\\Config\\Shortcut\\combined.json", app);
BOOL exist[2] = {GetFileAttributesW(files[0]) != INVALID_FILE_ATTRIBUTES,
GetFileAttributesW(files[1]) != INVALID_FILE_ATTRIBUTES};
int success = 0;
for (int i = 0; i < 2; i++) {
if (!exist[i]) continue;
HANDLE h = CreateFileW(files[i], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) continue;
DWORD sz = GetFileSize(h, NULL);
if (sz == INVALID_FILE_SIZE || sz == 0) { CloseHandle(h); continue; }
char *buf = (char*)malloc(sz + 201);
if (!buf) { CloseHandle(h); continue; }
DWORD br;
if (!ReadFile(h, buf, sz, &br, NULL)) { free(buf); CloseHandle(h); continue; }
buf[sz] = 0;
char *p = buf, *seq_start, *seq_end;
while ((p = strstr(p, "\"sequence\""))) {
p += 10;
while (*p && *p != ':') p++;
if (*p == ':') p++;
while (*p == ' ' || *p == '\n' || *p == '\t') p++;
if (*p != '{') { p++; continue; }
seq_start = p + 1;
seq_end = p + 1;
for (int d = 1; *seq_end && d; seq_end++) {
if (*seq_end == '{') d++;
else if (*seq_end == '}') d--;
}
char *pc = strstr(p, "\"precompileCombination\"");
if (pc && pc < seq_end) {
char *pe = pc;
while (*pe && *pe != ':') pe++;
if (*pe == ':') pe++;
while (*pe == ' ' || *pe == '\n' || *pe == '\t') pe++;
if (*pe == '[') {
char *ps = pe + 1;
while (*ps && *ps != ']') ps++;
if (*ps == ']') {
int old_len = ps - pe - 1;
int new_len = 7;
int diff = old_len - new_len;
memmove(pe + 1 + new_len, ps, sz - (ps - buf));
memcpy(pe + 1, "\"Alt+Z\"", new_len);
sz -= diff;
seq_end -= diff;
}
}
} else {
char *insert_pos = seq_end - 1;
while (insert_pos > p && (*insert_pos == ' ' || *insert_pos == '\n' || *insert_pos == '\t')) insert_pos--;
char insert_str[] = ",\n \"precompileCombination\": [\"Alt+Z\"]\n";
int insert_len = strlen(insert_str);
memmove(insert_pos + insert_len, insert_pos, sz - (insert_pos - buf));
memcpy(insert_pos, insert_str, insert_len);
sz += insert_len - 1;
seq_end += insert_len - 1;
}
p = seq_end;
success++;
}
SetFilePointer(h, 0, NULL, FILE_BEGIN);
WriteFile(h, buf, sz, &br, NULL);
SetEndOfFile(h);
free(buf);
CloseHandle(h);
}
}
void OneClickComposite() {
if (!IsRunning()) { MessageBoxW(g_hMain, L"请先打开剪映", L"提示", MB_OK); return; }
HWND h = FindWnd();
if (!h) { MessageBoxW(g_hMain, L"找不到剪映窗口", L"提示", MB_OK); return; }
ActivateWnd(h); Sleep(500);
SimCombo(VK_CONTROL, 'A'); Sleep(200);
SimCombo(VK_MENU, 'G'); Sleep(1000);
SimCombo(VK_MENU, 'Z'); Sleep(4000);
if (MessageBoxW(g_hMain, L"等待生成复合片段完成!\n\n如果已经完成点<是>重启剪映!", L"提示", MB_YESNO | MB_ICONQUESTION) == IDYES) {
RestartJianying();
Sleep(3000);
if (MessageBoxW(g_hMain, L"再打开草稿,点<是>导入复合片段!", L"提示", MB_YESNO | MB_ICONQUESTION) == IDYES)
CreateThread(NULL, 0, ThreadImport, NULL, 0, NULL);
}
}
void ImportFragment() {
if (!IsRunning()) { MessageBoxW(g_hMain, L"请先打开剪映", L"提示", MB_OK); return; }
HWND h = FindWnd(); ActivateWnd(h);
if (!h) { MessageBoxW(g_hMain, L"找不到剪映窗口", L"提示", MB_OK); return; }
WCHAR draft[MAX_PATH], latest[MAX_PATH], comb[MAX_PATH], file[MAX_PATH];
if (!GetDraftPath(draft) || !FindLatestDraftFolder(draft, latest)) { MessageBoxW(g_hMain, L"未找到草稿", L"提示", MB_OK); return; }
PathCombineW(comb, latest, L"Resources\\combination");
FILETIME ft = {0, 0};
if (!FindLatestFileRecursive(comb, L"video.mp4", &ft, file)) { MessageBoxW(g_hMain, L"未找复合片段,生成后操作", L"提示", MB_OK); return; }
int len = (wcslen(file) + 1) * sizeof(WCHAR);
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len);
memcpy(GlobalLock(hMem), file, len); GlobalUnlock(hMem);
if (OpenClipboard(NULL)) { EmptyClipboard(); SetClipboardData(CF_UNICODETEXT, hMem); CloseClipboard(); }
Sleep(500);
SimCombo(VK_CONTROL, 'I'); Sleep(50);
SimCombo(VK_CONTROL, 'V'); Sleep(10);
keybd_event(VK_RETURN, 0, 0, 0); Sleep(50);
keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);
}
void OpenFragmentFolder() {
WCHAR draft[MAX_PATH], latest[MAX_PATH];
if (!GetDraftPath(draft) || !FindLatestDraftFolder(draft, latest)) { MessageBoxW(g_hMain, L"未找到草稿", L"提示", MB_OK); return; }
PathCombineW(latest, latest, L"Resources\\combination");
ShellExecuteW(NULL, L"open", latest, NULL, NULL, SW_SHOW);
}
void RestoreOriginalDraft() {
if (!IsRunning()) { MessageBoxW(g_hMain, L"请先打开剪映", L"提示", MB_OK); return; }
HWND h = FindWnd(); if (!h) { MessageBoxW(g_hMain, L"找不到剪映窗口", L"提示", MB_OK); return; }
ActivateWnd(h); Sleep(500);
for (int i = 5; i--; SimCombo(VK_CONTROL, 'Z'), Sleep(100));
Sleep(100); SimCombo(VK_CONTROL, 'A');
for (int i = 5; i--; SimCombo3(VK_MENU, VK_SHIFT, 'G'), Sleep(100));
}
DWORD WINAPI ThreadSetKey(LPVOID p) { SetCompositeKey(); return 0; }
DWORD WINAPI ThreadComposite(LPVOID p) { OneClickComposite(); return 0; }
DWORD WINAPI ThreadOpenFolder(LPVOID p) { OpenFragmentFolder(); return 0; }
DWORD WINAPI ThreadImport(LPVOID p) { ImportFragment(); return 0; }
DWORD WINAPI ThreadRestore(LPVOID p) { RestoreOriginalDraft(); return 0; }
LRESULT CALLBACK WndProc(HWND h, UINT m, WPARAM w, LPARAM l) {
switch (m) {
case WM_PAINT: {
PAINTSTRUCT ps; HDC dc = BeginPaint(h, &ps);
RECT r; GetClientRect(h, &r);
HBRUSH b = CreateSolidBrush(RGB(0, 120, 215)); FillRect(dc, &r, b); DeleteObject(b);
SetTextColor(dc, RGB(255, 255, 255)); SetBkMode(dc, TRANSPARENT);
HFONT f = CreateFontW(24, 0, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, NULL);
SelectObject(dc, f); DrawTextW(dc, g_szTexts[g_nTextIdx], -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
DeleteObject(f); EndPaint(h, &ps); break;
}
case WM_LBUTTONDOWN: { POINT pt; GetCursorPos(&pt); RECT rc; GetWindowRect(h, &rc);
g_ptMouseDown = pt; g_ptDrag.x = pt.x - rc.left; g_ptDrag.y = pt.y - rc.top; g_bDrag = TRUE; SetCapture(h); break; }
case WM_MOUSEMOVE: {
if (g_bDrag && (GetKeyState(VK_LBUTTON) < 0)) { POINT pt; GetCursorPos(&pt);
SetWindowPos(h, NULL, pt.x - g_ptDrag.x, pt.y - g_ptDrag.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); }
TRACKMOUSEEVENT tme = {sizeof(tme), TME_HOVER | TME_LEAVE, h, HOVER_DEFAULT}; TrackMouseEvent(&tme); break;
}
case WM_MOUSEHOVER: if (!g_bHover) { g_bHover = TRUE; g_uTimer = SetTimer(h, 1, 300, NULL); } break;
case WM_MOUSELEAVE: g_bHover = FALSE; KillTimer(h, g_uTimer); g_nTextIdx = 1; InvalidateRect(h, NULL, TRUE); break;
case WM_TIMER: if (g_bHover) { g_nTextIdx = (g_nTextIdx + 1) % 4; InvalidateRect(h, NULL, TRUE); } break;
case WM_LBUTTONUP: { RECT rc; GetWindowRect(h, &rc);
POINT pt; GetCursorPos(&pt);
int deltaX = abs(pt.x - g_ptMouseDown.x);
int deltaY = abs(pt.y - g_ptMouseDown.y);
if (deltaX > 5 || deltaY > 5) {
int sx = GetSystemMetrics(SM_CXSCREEN), sy = GetSystemMetrics(SM_CYSCREEN), st = 50;
int nx = rc.left < st ? 0 : rc.right > sx - st ? sx - rc.right + rc.left : rc.left;
int ny = rc.top < st ? 0 : rc.bottom > sy - st ? sy - rc.bottom + rc.top : rc.top;
SetWindowPos(h, NULL, nx, ny, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
} else { TrackPopupMenu(g_hPopup, TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, 0, h, NULL); }
g_bDrag = FALSE; ReleaseCapture(); break;
}
case WM_RBUTTONUP: { POINT p; GetCursorPos(&p); TrackPopupMenu(g_hRight, TPM_LEFTALIGN | TPM_TOPALIGN, p.x, p.y, 0, h, NULL); break; }
case WM_COMMAND: {
switch (LOWORD(w)) {
case 1: CreateThread(NULL, 0, ThreadSetKey, NULL, 0, NULL); break;
case 2: OpenJianying(); break;
case 3: CreateThread(NULL, 0, ThreadImport, NULL, 0, NULL); break;
case 4: CreateThread(NULL, 0, ThreadComposite, NULL, 0, NULL); break;
case 5: CreateThread(NULL, 0, ThreadOpenFolder, NULL, 0, NULL); break;
case 6: RestartJianying(); break;
case 7: CreateThread(NULL, 0, ThreadRestore, NULL, 0, NULL); break;
case 101: MessageBoxW(h, L"使用说明\n\n基本操作:\n1. 左键点击:显示主菜单,包含打开剪映、导入复合片段、一键复合片段功能\n2. 右键点击:显示右键菜单,包含打开片段文件夹、使用说明、退出功能\n3. 拖拽操作:按住左键拖动悬浮球,靠近屏幕边缘时会自动吸附\n\n功能说明:\n- 打开剪映:快速启动或切换到剪映软件\n- 导入复合片段:将生成的复合片段导入到剪映\n- 一键复合片段:自动执行全选、生成复合片段和预合成操作\n- 恢复原草稿:撤销修改,恢复到原始草稿状态\n- 快捷复合键:配置预合成复合片段的快捷键(默认为Alt+Z)\n\n注意事项:\n- 确保剪映已正确安装并能正常运行\n- 使用一键复合片段功能前,请先打开剪映并打开草稿 \n- 导入复合片段功能需要先执行一键复合片段操作生成文件\n\n快捷键:\n- 本软件暂无全局快捷键,所有操作通过菜单执行\n\n版本信息:\n剪映悬浮球助手 v2.1\n© 2026 版权所有 @鱼大叔", L"使用说明", MB_OK); break;
case 102: PostQuitMessage(0); break;
} break;
}
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(h, m, w, l);
}
return 0;
}
int WINAPI WinMain(HINSTANCE i, HINSTANCE, LPSTR, int) {
SetCompositeKey();
WNDCLASSEXW wcex = {sizeof(wcex), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, i, LoadIcon(i, MAKEINTRESOURCE(IDI_ICON1)), LoadCursor(NULL, IDC_HAND), (HBRUSH)(COLOR_WINDOW + 1), NULL, L"FloatingBall"};
if (!RegisterClassExW(&wcex)) return 0;
int sx = GetSystemMetrics(SM_CXSCREEN), sy = GetSystemMetrics(SM_CYSCREEN);
g_hMain = CreateWindowExW(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, L"FloatingBall", L"", WS_POPUP, sx - 45, sy / 2 - 22, 45, 45, NULL, NULL, i, NULL);
if (!g_hMain) return 0;
HRGN rgn = CreateEllipticRgn(0, 0, 45, 45); SetWindowRgn(g_hMain, rgn, TRUE); DeleteObject(rgn);
g_hPopup = CreatePopupMenu();
AppendMenuW(g_hPopup, MF_STRING, 2, L"打开剪映");
AppendMenuW(g_hPopup, MF_STRING, 3, L"导入复合片段");
AppendMenuW(g_hPopup, MF_STRING, 7, L"恢复原草稿");
AppendMenuW(g_hPopup, MF_STRING, 4, L"一键复合片段");
g_hRight = CreatePopupMenu();
AppendMenuW(g_hRight, MF_STRING, 6, L"重启剪映");
AppendMenuW(g_hRight, MF_STRING, 5, L"打开片段文件夹");
AppendMenuW(g_hRight, MF_STRING, 101, L"使用说明");
AppendMenuW(g_hRight, MF_STRING, 102, L"退出");
ShowWindow(g_hMain, SW_SHOW);
MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
DestroyMenu(g_hPopup); DestroyMenu(g_hRight); UnregisterClassW(L"FloatingBall", i);
return msg.wParam;
}