CreoOtkPluging/CreoUtilities.cpp
sladro a02b68c7e3 feat: add CreoUtilities class for working directory management
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>
2025-10-13 16:03:39 +08:00

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