VBA Edge制御 更新自動化コマンドプロンプトプログラム

提供:yonewiki
2024年12月28日 (土) 01:40時点におけるYo-net (トーク | 投稿記録)による版 (→‎概要)

VBA Edge制御 導入に戻る。

概要

 msedgedriver.exeを自動で差し替えるコマンドプロンプトプログラムをC++言語でだらだら長めのコードになりましたが、作るには作りました。クラス化とかしてません。参考にして自分で組んでみて下さい。Chromeの対応はきっちりできていません。Chromeも同じようなやり方だと思うので、Chromeのdriverも答え合わせ的に直してみるといいかもしれない。つうかzlibとlibzipのビルドとかで手間取る。古いVisualStudio17 2022とかでやるとつまづく、最新のVisualStudio17 2022に更新した上でやるとうまく行く。



からダウンロードして解凍して、zlibはフォルダ階層のトップ下のCMakeFile.txtのあるフォルダをVisual Studioで開いて、ビルドのインストールっていうのと(zlibのパス)\out\install\x64-Debug\includeの下のzconf.hを(zlibのパス)の直下にコピーしてから、ビルドのビルドを実行するだけ。これで、zlibd.libとzlibd.dllとincludeディレクトリが生成されます。したらば次はlibzipも同じようにフォルダ階層のトップ下のCMakeFileをVisual Studioで開くんですけど、フォルダを開く前にCMakeFile.txtの中身を以下のように更新します。


cmake_minimum_required(VERSION 3.0.2)
set(ZLIB_ROOT C:/(zlibのパス)/)
set(LIB_INCLUDE_DIR C:/(zlibのパス)/out/install/x64-Debug/include)
set(ZLIB_LIBRARY_DEBUG C:/(zlibのパス)/out/install/x64-Debug/lib/zlibd.lib)
set(ZLIB_LIBRARY_RELEASE C:/(zlibのパス)/out/install/x64-Debug/lib/zlibd.lib)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)


その上でフォルダを開いて、インストールとビルドをするとzip.libとzip.dllとインクルードディレクトリが生成されます。したらば、このzip.dllとzlibd.libを自分で作成するプロジェクトで実行ファイル.exeが生成されるフォルダに保存するのと、インクルードディレクトリに以下の追加します。


  • インクルードディレクトリ
C:\(libzipパス)\out\install\x64-Debug\include;
  • ライブラリディレクトリ
C:\(libzipパス)\out\install\x64-Debug\lib;


そして、以下のようにコードを作成します。


起動引数は第1がブラウザでedge or chromeです。第2がWindowsLoginUserIDを入れます。

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <filesystem>
#include <windows.h>
#include <winhttp.h>
#include <typeinfo>
 
#include <zip.h>
 
#pragma comment(lib, "winhttp.lib")
#pragma comment(lib, "Version.lib")
#pragma comment(lib, "zip.lib")
 
std::string GetVersion(const std::string& path) {
  DWORD handle;
  DWORD size = GetFileVersionInfoSizeA(path.c_str(), &handle);
  if (size == 0) {
    throw std::runtime_error("Failed to get version info size.");
  }
 
  std::vector<char> data(size);
  if (!GetFileVersionInfoA(path.c_str(), handle, size, data.data())) {
    throw std::runtime_error("Failed to get version info.");
  }
 
  VS_FIXEDFILEINFO* versionInfo;
  UINT len;
  if (!VerQueryValueA(data.data(), "\\", (LPVOID*)&versionInfo, &len)) {
    throw std::runtime_error("Failed to query version info.");
  }
 
  DWORD major = (versionInfo->dwFileVersionMS >> 16) & 0xffff;
  DWORD minor = versionInfo->dwFileVersionMS & 0xffff;
  DWORD build = (versionInfo->dwFileVersionLS >> 16) & 0xffff;
  DWORD revision = versionInfo->dwFileVersionLS & 0xffff;
 
  return std::to_string(major) + "." +
    std::to_string(minor) + "." +
    std::to_string(build) + "." +
    std::to_string(revision);
}
 
std::string GetHttpResponse(const std::wstring& host, const std::wstring& path) {
  HINTERNET session = WinHttpOpen(L"Downloader", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, NULL, NULL, 0);
  if (!session) {
    throw std::runtime_error("Failed to open WinHTTP session.");
  }
 
  HINTERNET connect = WinHttpConnect(session, host.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0);
  if (!connect) {
    WinHttpCloseHandle(session);
    throw std::runtime_error("Failed to connect to host.");
  }
 
  HINTERNET request = WinHttpOpenRequest(connect, L"GET", path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
  if (!request) {
    WinHttpCloseHandle(connect);
    WinHttpCloseHandle(session);
    throw std::runtime_error("Failed to open HTTP request.");
  }
 
  if (!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) {
    WinHttpCloseHandle(request);
    WinHttpCloseHandle(connect);
    WinHttpCloseHandle(session);
    throw std::runtime_error("Failed to send HTTP request.");
  }
 
  if (!WinHttpReceiveResponse(request, NULL)) {
    WinHttpCloseHandle(request);
    WinHttpCloseHandle(connect);
    WinHttpCloseHandle(session);
    throw std::runtime_error("Failed to receive HTTP response.");
  }
 
  DWORD size = 0;
  WinHttpQueryDataAvailable(request, &size);
 
  std::string response(size, '\0');
  DWORD downloaded;
  WinHttpReadData(request, /*&response[0]*/ response.data(), size, &downloaded);
 
  WinHttpCloseHandle(request);
  WinHttpCloseHandle(connect);
  WinHttpCloseHandle(session);
 
  return response;
}
 
void Download(const std::wstring& url, const std::string& extractDir) {
  HINTERNET session = WinHttpOpen(L"Downloader", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, NULL, NULL, 0);
  URL_COMPONENTS components = { 0 };
  components.dwStructSize = sizeof(URL_COMPONENTS);
  components.dwHostNameLength = -1;
  components.dwUrlPathLength = -1;
 
  wchar_t host[256];
  wchar_t path[1024];
 
  components.lpszHostName = host;
  components.lpszUrlPath = path;
 
  if (!WinHttpCrackUrl(url.c_str(), 0, 0, &components)) {
    throw std::runtime_error("Failed to parse URL.");
  }
 
  HINTERNET connect = WinHttpConnect(session, components.lpszHostName, INTERNET_DEFAULT_HTTPS_PORT, 0);
  if (!connect) {
    WinHttpCloseHandle(session);
    throw std::runtime_error("Failed to connect to host.");
  }
 
  HINTERNET request = WinHttpOpenRequest(connect, L"GET", components.lpszUrlPath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
  if (!request) {
    WinHttpCloseHandle(connect);
    WinHttpCloseHandle(session);
    throw std::runtime_error("Failed to open HTTP request.");
  }
 
  if (!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) {
    WinHttpCloseHandle(request);
    WinHttpCloseHandle(connect);
    WinHttpCloseHandle(session);
    throw std::runtime_error("Failed to send HTTP request.");
  }
 
  if (!WinHttpReceiveResponse(request, NULL)) {
    WinHttpCloseHandle(request);
    WinHttpCloseHandle(connect);
    WinHttpCloseHandle(session);
    throw std::runtime_error("Failed to receive HTTP response.");
  }
 
  DWORD size = 0;
  WinHttpQueryDataAvailable(request, &size);
 
  std::vector<char> data;
  DWORD downloaded = 0;
  do {
    DWORD size = 0;
    WinHttpQueryDataAvailable(request, &size);
    if (size == 0) break;
    std::vector<char> buffer(size);
    DWORD bytesRead = 0;
    if (!WinHttpReadData(request, buffer.data(), size, &bytesRead)) {
      throw std::runtime_error("Failed to read HTTP response data.");
    }
    data.insert(data.end(), buffer.begin(), buffer.begin() + bytesRead);
    downloaded += bytesRead;
  } while (true);
 
  std::filesystem::create_directory(extractDir);
  std::ofstream out(extractDir + "\\driver.zip", std::ios::binary);
  out.write(data.data(), downloaded);
  out.close();
 
  WinHttpCloseHandle(request);
  WinHttpCloseHandle(connect);
  WinHttpCloseHandle(session);
}
 
std::string GetVersionFilePath(const std::string& browser, const std::string& version) {
  return browser + "_" + version;
}
 
bool IsVersionDownloaded(const std::string& versionFilePath) {
  return std::filesystem::exists(versionFilePath);
}
 
void SaveVersionFile(const std::string& versionFilePath) {
  std::ofstream file(versionFilePath);
  if (file) {
    file << "Downloaded";
    file.close();
  }
}
void extractZip(const std::string& zipPath, const std::string& extractDir) {
  int zipError = 0;
 
  // ZIPファイルを開く
  zip_t* zip = zip_open(zipPath.c_str(), 0, &zipError);
  if (!zip) {
    std::cerr << "Failed to open ZIP file. Error code: " << zipError << std::endl;
    return;
  }
 
  // エントリ数を取得
  zip_int64_t num_entries = zip_get_num_entries(zip, 0);
  for (zip_int64_t i = 0; i < num_entries; i++) {
    // ファイル名を取得
    const char* name = zip_get_name(zip, i, 0);
    if (!name) {
      std::cerr << "Failed to get entry name for index " << i << std::endl;
      continue;
    }
 
    // ファイル名をstd::stringに変換
    std::string entryName(name);
 
    // ファイル情報を取得
    zip_stat_t stat;
    if (zip_stat_index(zip, i, 0, &stat) == -1) {
      std::cerr << "Failed to get entry stats for " << entryName << std::endl;
      continue;
    }
 
    // フルパスを作成
    std::filesystem::path fullPath = std::filesystem::path(extractDir) / entryName;
 
    // フォルダの場合、ディレクトリを作成
    if (entryName.back() == '/') { // ZIP形式ではフォルダ名は末尾に'/'が付く
      std::filesystem::create_directories(fullPath);
      continue;
    }
 
    // ファイルを開く
    zip_file_t* zf = zip_fopen_index(zip, i, 0);
    if (!zf) {
      std::cerr << "Failed to open file inside ZIP: " << entryName << std::endl;
      continue;
    }
 
    // ファイル内容を読み込む
    std::string contents(stat.size, '\0'); // std::stringでメモリ確保
    if (zip_fread(zf, &contents[0], stat.size) == -1) {
      std::cerr << "Failed to read file: " << entryName << std::endl;
      zip_fclose(zf);
      continue;
    }
 
    // 親ディレクトリを作成(必要であれば)
    std::filesystem::create_directories(fullPath.parent_path());
 
    // ファイルを保存
    std::ofstream out(fullPath, std::ios::binary);
    if (!out) {
      std::cerr << "Failed to write file: " << fullPath << std::endl;
      zip_fclose(zf);
      continue;
    }
    out.write(contents.data(), contents.size());
    out.close();
 
    // ファイルを閉じる
    zip_fclose(zf);
  }
 
  // ZIPファイルを閉じる
  zip_close(zip);
}
 
void renameFile(const std::string& dirPath, const std::string& oldName, const std::string& newName) {
  // 古いファイルのフルパス
  std::filesystem::path oldFilePath = std::filesystem::path(dirPath) / oldName;
 
  // 新しいファイルのフルパス
  std::filesystem::path newFilePath = std::filesystem::path(dirPath) / newName;
 
  try {
    // ファイル名を変更
    std::filesystem::rename(oldFilePath, newFilePath);
    std::cout << "File renamed from " << oldFilePath << " to " << newFilePath << std::endl;
  }
  catch (const std::filesystem::filesystem_error& e) {
    std::cerr << "Error renaming file: " << e.what() << std::endl;
  }
}
 
void deleteDirectory(const std::string& dirPath) {
  try {
    // 指定したディレクトリが存在するか確認
    if (std::filesystem::exists(dirPath)) {
      // 再帰的に削除
      std::filesystem::remove_all(dirPath);
      std::cout << "Deleted directory and contents: " << dirPath << std::endl;
    }
    else {
      std::cerr << "Directory does not exist: " << dirPath << std::endl;
    }
  }
  catch (const std::filesystem::filesystem_error& e) {
    std::cerr << "Error deleting directory: " << e.what() << std::endl;
  }
}
 
void copyAndRenameFile(const std::string& sourcePath, const std::string& destPath) {
  try {
    // コピー先の親ディレクトリを作成(存在しない場合)
    std::filesystem::create_directories(std::filesystem::path(destPath).parent_path());
    // ファイルをコピー
    std::filesystem::copy_file(sourcePath, destPath, std::filesystem::copy_options::overwrite_existing);
    std::cout << "File copied from " << sourcePath << " to " << destPath << std::endl;
  }
  catch (const std::filesystem::filesystem_error& e) {
    std::cerr << "Error copying file: " << e.what() << std::endl;
  }
}
 
int main(int argc, char* argv[]) {
 
  if (argc < 2 || argv[1] == nullptr) {
    std::cerr << "起動引数1のブラウザの種別の指定がありません。edge or chrome" << std::endl;
    return 1;
  }
 
  if (argc < 3 || argv[2] == nullptr) {
    std::cerr << "起動引数2のWindowsユーザ名の指定がありません。" << std::endl;
    return 1;
  }
  std::string browser = argv[1];
  std::string userId = argv[2];
  std::string baseDir = "C:\\Users\\" + userId + "\\AppData\\Local\\SeleniumBasic";
 
  std::string version;
  std::wstring driverUrl;
  std::string tempDir;
  std::string outputPath;
 
  try {
    if (browser == "chrome") {
      version = GetVersion("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe");
      std::wstring driverUrl = L"https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_" + std::wstring(version.begin(), version.end());
      tempDir = "chromedriver_temp";
      outputPath = baseDir + "\\chromedriver.exe";
      //DownloadAndExtract(driverUrl, tempDir, baseDir + "\\chromedriver.exe");
    }
    else if (browser == "edge") {
      version = GetVersion("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe");
      driverUrl = L"https://msedgedriver.azureedge.net/" + std::wstring(version.begin(), version.end()) + L"/edgedriver_win64.zip";
      tempDir = "edgedriver_temp";
      outputPath = baseDir + "\\edgedriver.exe";
      //DownloadAndExtract(driverUrl, tempDir, baseDir + "\\edgedriver.exe", "msedgedriver.exe");
    }
    else {
      std::cerr << "Unsupported browser: edge or chrome" << browser << std::endl;
      return 1;
    }
     
 
    std::string versionFilePath = GetVersionFilePath(browser, version);
 
    if (IsVersionDownloaded(versionFilePath)) {
      std::cout << "Version " << version << " is already downloaded. Skipping download." << std::endl;
    }
    else {
      Download(driverUrl, tempDir);
      std::cout << "Download and extraction complete for version: " << version << std::endl;
      SaveVersionFile(versionFilePath);
      std::string zipPath;
      std::string FixFilename;
      std::string driverDir;
      zipPath = tempDir + "\\driver.zip";
      driverDir = tempDir + "\\driver";
      extractZip(zipPath, driverDir);
 
      copyAndRenameFile(driverDir + (browser == "edge" ? "\\msedgedriver.exe" : "\\chromedriver.exe"), outputPath);
 
      FixFilename = browser + "driver_" + std::string(version.begin(), version.end()) + ".zip";
      renameFile(tempDir, "driver.zip", FixFilename);
      deleteDirectory(driverDir);
    }
  }
  catch (const std::exception& ex) {
    std::cerr << "Error: " << ex.what() << std::endl;
    return 1;
  }
 
  return 0;

 

VBA Edge制御 導入に戻る。