Implement SetWorkingDirectory method with OTK API integration: - GetCurrentDirectory() to retrieve current path - ChangeDirectory() to switch working directory - Intelligent comparison: only changes if target differs from current - Path normalization for Windows (case-insensitive, backslash handling) - Comprehensive error handling with detailed result structure Also includes minor code cleanup in CreoManager.cpp formatting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
213 lines
7.0 KiB
C++
213 lines
7.0 KiB
C++
#include "pch.h"
|
|
#include "CreoUtilities.h"
|
|
#include <windows.h>
|
|
#include <cctype>
|
|
#include <sstream>
|
|
|
|
// Set Creo working directory
|
|
ChangeDirectoryResult CreoUtilities::SetWorkingDirectory(const std::string& target_directory) {
|
|
ChangeDirectoryResult result;
|
|
result.target_directory = target_directory;
|
|
|
|
try {
|
|
// Validate input
|
|
if (target_directory.empty()) {
|
|
result.success = false;
|
|
result.error_message = "Target directory cannot be empty";
|
|
return result;
|
|
}
|
|
|
|
// Get Creo session
|
|
pfcSession_ptr session = nullptr;
|
|
try {
|
|
session = pfcGetCurrentSessionWithCompatibility(pfcC4Compatible);
|
|
} catch (...) {
|
|
session = nullptr;
|
|
}
|
|
|
|
if (!session) {
|
|
result.success = false;
|
|
result.error_message = "Failed to get Creo session. Creo may not be running.";
|
|
return result;
|
|
}
|
|
|
|
// Get current working directory
|
|
xstring current_dir_xstr;
|
|
try {
|
|
current_dir_xstr = session->GetCurrentDirectory();
|
|
result.original_directory = XStringToString(current_dir_xstr);
|
|
} catch (...) {
|
|
result.success = false;
|
|
result.error_message = "Failed to get current directory";
|
|
return result;
|
|
}
|
|
|
|
// Normalize and compare paths
|
|
std::string normalized_current = NormalizePath(result.original_directory);
|
|
std::string normalized_target = NormalizePath(target_directory);
|
|
|
|
// Check if directories are the same
|
|
if (PathsAreEqual(normalized_current, normalized_target)) {
|
|
// Already at target directory, no change needed
|
|
result.success = true;
|
|
result.directory_changed = false;
|
|
result.current_directory = result.original_directory;
|
|
result.message = "Already at target directory, no change needed";
|
|
return result;
|
|
}
|
|
|
|
// Directories are different, need to change
|
|
try {
|
|
xstring target_dir_xstr = StringToXString(target_directory);
|
|
session->ChangeDirectory(target_dir_xstr);
|
|
} catch (...) {
|
|
result.success = false;
|
|
result.error_message = "Failed to change directory (directory may not exist or is invalid)";
|
|
result.current_directory = result.original_directory;
|
|
return result;
|
|
}
|
|
|
|
// Verify the change by reading current directory again
|
|
try {
|
|
xstring new_dir_xstr = session->GetCurrentDirectory();
|
|
result.current_directory = XStringToString(new_dir_xstr);
|
|
} catch (...) {
|
|
result.current_directory = target_directory; // Assume success if we can't verify
|
|
}
|
|
|
|
// Check if change was successful
|
|
std::string normalized_new = NormalizePath(result.current_directory);
|
|
if (PathsAreEqual(normalized_new, normalized_target)) {
|
|
result.success = true;
|
|
result.directory_changed = true;
|
|
result.message = "Working directory changed successfully";
|
|
} else {
|
|
result.success = false;
|
|
result.directory_changed = false;
|
|
result.error_message = "Directory change operation completed but verification failed";
|
|
}
|
|
|
|
} catch (const std::exception& e) {
|
|
result.success = false;
|
|
result.error_message = std::string("Unexpected exception: ") + e.what();
|
|
result.current_directory = result.original_directory;
|
|
} catch (...) {
|
|
result.success = false;
|
|
result.error_message = "Unexpected unknown exception occurred";
|
|
result.current_directory = result.original_directory;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Convert xstring to std::string
|
|
std::string CreoUtilities::XStringToString(const xstring& xstr) {
|
|
try {
|
|
// Check for null xstring
|
|
if (xstr.IsNull()) {
|
|
return "";
|
|
}
|
|
|
|
std::wstring wstr(xstr);
|
|
if (wstr.empty()) {
|
|
return "";
|
|
}
|
|
|
|
// Length check to prevent memory issues
|
|
if (wstr.length() > 32767) { // Windows API limit
|
|
return "";
|
|
}
|
|
|
|
// Use WideCharToMultiByte for proper UTF-8 encoding conversion
|
|
int wstr_len = static_cast<int>(wstr.length());
|
|
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr_len, NULL, 0, NULL, NULL);
|
|
if (size_needed <= 0 || size_needed > 65535) { // Safety boundary check
|
|
return "";
|
|
}
|
|
|
|
std::string result(size_needed, 0);
|
|
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr_len, &result[0], size_needed, NULL, NULL);
|
|
|
|
return result;
|
|
} catch (...) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
// Convert std::string to xstring
|
|
xstring CreoUtilities::StringToXString(const std::string& str) {
|
|
try {
|
|
if (str.empty()) {
|
|
return xstring();
|
|
}
|
|
|
|
// Length check to prevent memory issues
|
|
if (str.length() > 65535) { // Reasonable length limit
|
|
return xstring();
|
|
}
|
|
|
|
// Validate input string for invalid characters
|
|
for (char c : str) {
|
|
if (c == '\0' && &c != &str.back()) { // Null character in the middle
|
|
return xstring();
|
|
}
|
|
}
|
|
|
|
// Use MultiByteToWideChar for proper UTF-8 decoding conversion
|
|
int str_len = static_cast<int>(str.length());
|
|
int size_needed = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.c_str(), str_len, NULL, 0);
|
|
if (size_needed <= 0 || size_needed > 32767) { // Safety boundary check
|
|
return xstring();
|
|
}
|
|
|
|
std::wstring wstr(size_needed, 0);
|
|
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.c_str(), str_len, &wstr[0], size_needed);
|
|
|
|
return xstring(wstr.c_str());
|
|
} catch (...) {
|
|
return xstring();
|
|
}
|
|
}
|
|
|
|
// Normalize path for comparison
|
|
std::string CreoUtilities::NormalizePath(const std::string& path) {
|
|
if (path.empty()) {
|
|
return "";
|
|
}
|
|
|
|
std::string normalized = path;
|
|
|
|
// Replace forward slashes with backslashes (Windows standard)
|
|
std::replace(normalized.begin(), normalized.end(), '/', '\\');
|
|
|
|
// Remove trailing backslash if present (except for root paths like "C:\")
|
|
if (normalized.length() > 3 && normalized.back() == '\\') {
|
|
normalized.pop_back();
|
|
}
|
|
|
|
// Convert to lowercase for case-insensitive comparison (Windows)
|
|
std::transform(normalized.begin(), normalized.end(), normalized.begin(),
|
|
[](unsigned char c) { return std::tolower(c); });
|
|
|
|
return normalized;
|
|
}
|
|
|
|
// Compare two paths for equality
|
|
bool CreoUtilities::PathsAreEqual(const std::string& path1, const std::string& path2) {
|
|
// Both empty
|
|
if (path1.empty() && path2.empty()) {
|
|
return true;
|
|
}
|
|
|
|
// One empty, one not
|
|
if (path1.empty() || path2.empty()) {
|
|
return false;
|
|
}
|
|
|
|
// Compare normalized paths
|
|
std::string norm1 = NormalizePath(path1);
|
|
std::string norm2 = NormalizePath(path2);
|
|
|
|
return norm1 == norm2;
|
|
}
|