You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

解决方法

问题分析

  1. CreateProcess参数错误:第二个参数lpCommandLine要求是可写字符串,直接传递const char*会触发未定义行为。
  2. 工作目录与路径问题:程序运行时的工作目录可能不是脚本所在目录,导致找不到Python或脚本文件。
  3. 参数转义不完整:密码或文件路径含特殊字符时,现有转义逻辑无法正确处理。

修正后的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:40:56