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>
This commit is contained in:
sladro 2025-10-13 16:03:39 +08:00
parent d5f87e5457
commit a02b68c7e3
5 changed files with 271 additions and 3 deletions

View File

@ -499,9 +499,9 @@ std::string CreoManager::GetFileSize(const std::string& filepath) {
size.LowPart = fileInfo.nFileSizeLow;
double file_size_mb = static_cast<double>(size.QuadPart) / (1024.0 * 1024.0);
std::ostringstream oss;
oss << std::fixed << std::setprecision(1) << file_size_mb << "MB";
oss << file_size_mb << "MB";
return oss.str();
} else {
DWORD error = GetLastError();
@ -674,8 +674,9 @@ std::string CreoManager::CalculateAssemblyTotalSize(pfcModel_ptr model) {
// 转换为MB并返回
double total_mb = total_size_bytes / (1024.0 * 1024.0);
std::ostringstream oss;
oss << std::fixed << std::setprecision(1) << total_mb << "MB (from " << processed_count << " files)";
oss << total_mb << "MB (from " << processed_count << " files)";
return oss.str();
}

212
CreoUtilities.cpp Normal file
View File

@ -0,0 +1,212 @@
#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;
}

47
CreoUtilities.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
// Creo OTK headers
#include <pfcGlobal.h>
#include <pfcSession.h>
#include <wfcSession.h>
#include <pfcExceptions.h>
// Standard library headers
#include <string>
#include <algorithm>
// Working directory change result structure
struct ChangeDirectoryResult {
bool success = false;
std::string original_directory; // Original working directory before operation
std::string target_directory; // Target directory requested by user
std::string current_directory; // Actual working directory after operation
bool directory_changed = false; // Whether directory was actually changed
std::string message; // Success message or informational text
std::string error_message; // Error details if operation failed
};
// Creo Utilities - Common utility functions for Creo operations
class CreoUtilities {
public:
// Set Creo working directory
// Only changes directory if target is different from current directory
// Parameters:
// target_directory - The desired working directory path
// Returns:
// ChangeDirectoryResult with detailed operation status
static ChangeDirectoryResult SetWorkingDirectory(const std::string& target_directory);
private:
// Private constructor - this is a static utility class
CreoUtilities() = delete;
~CreoUtilities() = delete;
CreoUtilities(const CreoUtilities&) = delete;
CreoUtilities& operator=(const CreoUtilities&) = delete;
// Helper methods for internal use
static std::string XStringToString(const xstring& xstr);
static xstring StringToXString(const std::string& str);
static std::string NormalizePath(const std::string& path);
static bool PathsAreEqual(const std::string& path1, const std::string& path2);
};

View File

@ -209,6 +209,7 @@ ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ClCompile Include="AuthManager.cpp" />
<ClCompile Include="ComponentChildrenManager.cpp" />
<ClCompile Include="CreoManager.cpp" />
<ClCompile Include="CreoUtilities.cpp" />
<ClCompile Include="GeometryAnalyzer.cpp" />
<ClCompile Include="HierarchyStatisticsAnalyzer.cpp" />
<ClCompile Include="HttpRouter.cpp" />
@ -240,6 +241,7 @@ ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ClInclude Include="ComponentChildrenManager.h" />
<ClInclude Include="Config.h" />
<ClInclude Include="CreoManager.h" />
<ClInclude Include="CreoUtilities.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="GeometryAnalyzer.h" />
<ClInclude Include="HierarchyStatisticsAnalyzer.h" />

View File

@ -93,6 +93,9 @@
<ClCompile Include="ComponentChildrenManager.cpp">
<Filter>源文件\src\creo</Filter>
</ClCompile>
<ClCompile Include="CreoUtilities.cpp">
<Filter>源文件\src\utils</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="MFCCreoDll.def">
@ -178,6 +181,9 @@
<ClInclude Include="ComponentChildrenManager.h">
<Filter>源文件\src\creo</Filter>
</ClInclude>
<ClInclude Include="CreoUtilities.h">
<Filter>源文件\src\utils</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="MFCCreoDll.rc">