C++ Builder调用Python加密脚本失败求助(Argon2+AES-256)
文件加密项目:C++调用Python脚本失败的解决方法
问题背景
在Windows11 64位环境下,使用Embarcadero C++ Builder 12开发基于Argon2和AES-256(50万次迭代)的文件加密项目,核心加密逻辑由Python脚本AEScrypt.py实现(与C编译的.exe同目录)。直接在CMD中执行python AEScrypt.py password FilePath <encrypt/decrypt>可正常完成加解密(生成带.aes后缀的加密文件,保留原文件),但在C程序中通过CreateProcess()、WinExec()、System()等方法调用脚本时,出现无法读取脚本、无响应或不生成加密文件的问题。仅需实现一个能正确传递参数并验证目标文件生成的函数,其余界面及功能已完成。
提供的代码
Python脚本(AEScrypt.py)
import sys import os import hashlib import base64 import secrets from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding from cryptography.hazmat.backends import default_backend from argon2 import PasswordHasher def generate_key(password): password = password[::-1] ph = PasswordHasher() hashed_password = ph.hash(password) salt = hashed_password.split('$')[3] key = hashlib.pbkdf2_hmac('sha256', password.encode(), base64.b64decode(salt), 500000, dklen=32) return key def encrypt_file(file_path, password): key = generate_key(password) iv = secrets.token_bytes(16) cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) encryptor = cipher.encryptor() with open(file_path, 'rb') as f: data = f.read() padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encrypted_data = encryptor.update(padded_data) + encryptor.finalize() encrypted_file_path = file_path + '.aes' with open(encrypted_file_path, 'wb') as f: f.write(iv + encrypted_data) def decrypt_file(file_path, password): key = generate_key(password) with open(file_path, 'rb') as f: iv = f.read(16) # 从文件开头读取IV data = f.read() cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() decrypted_data = decryptor.update(data) + decryptor.finalize() unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() data = unpadder.update(decrypted_data) + unpadder.finalize() decrypted_file_path = file_path.replace('.aes', '') with open(decrypted_file_path, 'wb') as f: f.write(data) def main(): if len(sys.argv) != 4: print("Usage: AEScrypt.py <password> <file> <encrypt|decrypt>") sys.exit(1) password = sys.argv[1] file_path = sys.argv[2] # 检查路径是否为绝对路径 if not os.path.isabs(file_path): print("Error: The file path must be absolute.") sys.exit(1) action = sys.argv[3] if action == 'encrypt': encrypt_file(file_path, password) print(f"File encrypted and saved to {file_path}.aes") elif action == 'decrypt': decrypt_file(file_path, password) print(f"File decrypted and saved to {file_path.replace('.aes', '')}") else: print("Invalid action. Use 'encrypt' or 'decrypt'.") sys.exit(1) if __name__ == '__main__': main()
尝试过的C++代码
void __fastcall TForm3::Button3Click(TObject *Sender) { if (Edit1->Text.IsEmpty() || Edit2->Text.IsEmpty()) { ShowMessage("Por favor, preencha o caminho do arquivo e a senha."); return; } String filePath = ExpandFileName(Edit1->Text); String password = Edit2->Text; String command = "cmd /C python AEScrypt.py \"" + password + "\" \"" + filePath + "\" encrypt"; STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; ZeroMemory(&pi, sizeof(pi)); if (CreateProcess(NULL, command.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else { DWORD dwError = GetLastError(); String errorMsg = "Erro ao executar o script Python. Código do erro: " + IntToStr(dwError); ShowMessage(errorMsg); } }
解决方法
问题分析
CreateProcess参数错误:第二个参数lpCommandLine要求是可写字符串,直接传递const char*会触发未定义行为。- 工作目录与路径问题:程序运行时的工作目录可能不是脚本所在目录,导致找不到Python或脚本文件。
- 参数转义不完整:密码或文件路径含特殊字符时,现有转义逻辑无法正确处理。
修正后的C++函数
void __fastcall TForm3::Button3Click(TObject *Sender) { if (Edit1->Text.IsEmpty() || Edit2->Text.IsEmpty()) { ShowMessage("请填写文件路径和密码。"); return; } // 获取绝对路径和脚本位置 String filePath = ExpandFileName(Edit1->Text); String password = Edit2->Text; String exeDir = ExtractFilePath(Application->ExeName); String scriptPath = exeDir + "AEScrypt.py"; String targetEncryptedFile = filePath + ".aes"; // 构建命令行:确保Python和脚本路径正确,参数完整转义 String command = L"cmd /C \"\"" + exeDir + L"python.exe\" \"" + scriptPath + L"\" \"" + password + L"\" \"" + filePath + L"\" encrypt\""; // 为CreateProcess分配可写缓冲区 wchar_t* cmdBuffer = new wchar_t[command.Length() + 1]; wcscpy(cmdBuffer, command.c_str()); STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0}; si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; // 隐藏CMD窗口 if (CreateProcess(NULL, cmdBuffer, NULL, NULL, FALSE, 0, NULL, exeDir.c_str(), &si, &pi)) { // 等待进程结束,设置超时避免无响应(30秒,可根据文件大小调整) DWORD waitStatus = WaitForSingleObject(pi.hProcess, 30000); if (waitStatus == WAIT_OBJECT_0) { // 验证加密文件是否生成 if (FileExists(targetEncryptedFile)) { ShowMessage("文件加密成功!"); } else { ShowMessage("加密完成但未找到目标文件,请检查脚本执行日志。"); } } else if (waitStatus == WAIT_TIMEOUT) { ShowMessage("加密超时,请检查文件大小或脚本逻辑。"); TerminateProcess(pi.hProcess, 1); // 终止无响应进程 } // 释放进程句柄 CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else { DWORD errorCode = GetLastError(); ShowMessage("执行脚本失败,错误代码:" + IntToStr(errorCode)); } // 释放缓冲区 delete[] cmdBuffer; }
额外优化建议
- 指定Python完整路径:将Python.exe复制到程序目录,避免环境变量中Python版本冲突。
- 捕获脚本输出:通过重定向标准输出/错误流,获取脚本的报错信息,便于调试。
- 处理长路径:启用Windows长路径支持,避免因路径过长导致失败。
内容的提问来源于stack exchange,提问作者Victor Melo
相关产品推荐
相关产品推荐

