As I stated I am working on an add in that causes whatever it is interacting with to crash when ran, the code checks for the correct state: will create a pdf, and move it and another file to a drawing folder. This works, but after it completes, whatever is used to change state crashes, it seems to work perfectly once, but not again. Windows file explorer crashes in any state change, and solidworks crashes after the full code runs. The code has changed a lot as I try and fix the issue, so I will include the first code I got working.
It is based off of the solidworks reference code that runs at state change, and I can include the dll file if for some reason someone wants to troubleshoot that instead of just looking over code.
I assume I am releasing something I am not supposed to, or the opposite, but I cannot figure it out, any tips are appreciated.
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text
Public Class DrawingCheckInHandler
Implements IEdmAddIn5
' --- CONFIGURATION CONSTANTS ---
Private Const DRAWINGS_BASE_PATH As String = "C:\Users\default\Desktop\Drawings"
Private Const ARCHIVE_BASE_PATH As String = "C:\Users\default\Desktop\Archive"
Private Const REVISION_PROPERTY_NAME As String = "*Revision"
' --- END CONFIGURATION CONSTANTS ---
Private Const LOG_FILE_NAME As String = "PdmDrawingCheckInHandlerLog.txt"
' --- TEMPORARY: For testing with a hardcoded path ---
Private Const USE_HARDCODED_SLDDRW_PATH As Boolean = False ' SET TO True TO USE, False TO TRY PDM GetLocalPath
Private Const HARDCODED_SLDDRW_PATH As String = "C:\Users\default\Sandbox\New Folder\999-9999.SLDDRW"
' --- END TEMPORARY ---
Private Shared conversionInProgress As Boolean = False
#Region "IEdmAddIn5 Implementation"
Public Sub GetAddInInfo(ByRef poInfo As EdmAddInInfo,
ByVal poVault As IEdmVault5,
ByVal poCmdMgr As IEdmCmdMgr5) Implements IEdmAddIn5.GetAddInInfo
Try
poInfo.mbsAddInName = "Drawing PDF Processor"
poInfo.mbsCompany = "Your Company Name"
poInfo.mbsDescription = "Archives old PDF and creates new PDF during drawing check-in."
poInfo.mlAddInVersion = 1
poInfo.mlRequiredVersionMajor = 25 ' Corresponds to PDM 2017 (for IEdmFile17 features)
poInfo.mlRequiredVersionMinor = 1
poCmdMgr.AddHook(EdmCmdType.EdmCmd_PostState)
poCmdMgr.AddHook(EdmCmdType.EdmCmd_PostUnlock)
LogMessage("Add-in loaded. Hook registered for EdmCmd_PostUnlock.")
If USE_HARDCODED_SLDDRW_PATH Then
LogMessage("WARNING: USE_HARDCODED_SLDDRW_PATH is TRUE. Add-in will only process: " & HARDCODED_SLDDRW_PATH)
End If
Catch ex As Runtime.InteropServices.COMException
'MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + vbCrLf + ex.Message)
Catch ex As Exception
LogMessage(\$"Error in GetAddInInfo: {ex.ToString()}")
'MessageBox.Show(\$"Error initializing add-in: {ex.Message}", "Add-in Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Public Sub OnCmd(ByRef poCmd As EdmCmd, ByRef ppoData As EdmCmdData()) Implements IEdmAddIn5.OnCmd
LogMessage("OnCmd method ENTERED.")
Dim vault As IEdmVault5 = Nothing
Try
If poCmd.mpoVault Is Nothing Then
LogMessage("OnCmd: poCmd.mpoVault is Nothing. Exiting.")
Return
End If
vault = TryCast(poCmd.mpoVault, IEdmVault5)
If vault Is Nothing Then
LogMessage("Vault is null after TryCast. Exiting.")
Return
End If
LogMessage(\$"Connected to PDM Vault: {vault.Name}. OnCmd: {poCmd.meCmdType.ToString()}.")
If ppoData Is Nothing OrElse ppoData.Length = 0 Then
LogMessage("ppoData is empty. Exiting.")
Return
End If
LogMessage(\$"ppoData contains {ppoData.Length} item(s).")
Select Case poCmd.meCmdType
Case EdmCmdType.EdmCmd_PostState
LogMessage("OnCmd: EdmCmd_PostState MET.")
Dim affectedFileNames As String = ""
For Each affectedFile As EdmCmdData In ppoData
Dim newStateName As String = affectedFile.mbsStrData2
LogMessage(\$"File '{affectedFile.mbsStrData1}' new state: '{newStateName}'")
If newStateName IsNot Nothing AndAlso newStateName.Trim().ToUpper() = "REV IN PROCESS" Then
affectedFileNames += affectedFile.mbsStrData1 + vbCrLf
' Your PDF processing logic
Dim fileId As Integer = affectedFile.mlObjectID1
Dim parentFolderIdFromCmdData As Integer = affectedFile.mlObjectID3
If fileId > 0 Then
Dim fileAsObject As Object = Nothing
Try
fileAsObject = vault.GetObject(EdmObjectType.EdmObject_File, fileId)
Dim baseFileNameForLog As String = affectedFile.mbsStrData1
ProcessDrawingFile(fileAsObject, parentFolderIdFromCmdData, vault, baseFileNameForLog)
Catch fileEx As Exception
LogMessage(\$"Error processing file ID {fileId}: {fileEx.ToString()}")
'MessageBox.Show(\$"Error for file ID {fileId}: {fileEx.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Finally
If fileAsObject IsNot Nothing Then
Marshal.ReleaseComObject(fileAsObject)
fileAsObject = Nothing
End If
End Try
Else
LogMessage(\$"Invalid FileID {fileId}. Skipping.")
End If
End If
' Show if this is a top node in the transition
Try
Dim cmdNode As IEdmCmdNode = TryCast(affectedFile.mpoExtra, IEdmCmdNode)
If cmdNode IsNot Nothing Then
Dim topNode As Boolean = cmdNode.GetProperty(EdmCmdNodeProp.EdmCmdNode_IsTopNode)
LogMessage(\$"File '{affectedFile.mbsStrData1}' is top node? {topNode}")
End If
Catch ex As Exception
LogMessage("Error checking top node: " & ex.Message)
End Try
Next
If affectedFileNames.Length > 0 Then
poCmd.mpoVault.MsgBox(poCmd.mlParentWnd, affectedFileNames + " has been released.")
End If
Case Else
poCmd.mpoVault.MsgBox(poCmd.mlParentWnd, "An unknown command type was issued.")
End Select
Catch ex As Runtime.InteropServices.COMException
'MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + vbCrLf + ex.Message)
Catch ex As Exception
LogMessage(\$"Critical error in OnCmd: {ex.ToString()}")
'MessageBox.Show(\$"PDM Add-in Error: {ex.Message}", "Critical Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Finally
LogMessage("OnCmd EXITED (Finally).")
' Do NOT release vault here; it is managed by PDM, not your code.
If vault IsNot Nothing Then
Marshal.ReleaseComObject(vault)
End If
End Try
End Sub
#End Region
#Region "Core Logic"
Private Sub ProcessDrawingFile(ByVal fileAsBaseObject As Object, ByVal parentFolderIdFromCmdData As Integer, ByVal vault As IEdmVault5, ByVal initialPdmFileNameAttempt As String)
Dim edmFile17 As IEdmFile17 = Nothing
Dim edmFileFolder As IEdmFolder5 = Nothing
Dim actualParentFolderIdToUse As Integer = parentFolderIdFromCmdData
Dim pdmDrawingFileName As String = initialPdmFileNameAttempt
Dim localDrawingFilePath As String = ""
Try
LogMessage(\$"ProcessDrawingFile for PDM file (initial name attempt): {initialPdmFileNameAttempt}. Initial CmdData.ParentFolderID: {parentFolderIdFromCmdData}")
Try
edmFile17 = TryCast(fileAsBaseObject, IEdmFile17)
If edmFile17 IsNot Nothing Then
pdmDrawingFileName = edmFile17.Name
LogMessage(\$"Successfully cast to IEdmFile17: {pdmDrawingFileName}.")
Try
edmFileFolder = TryCast(edmFile17.Folder, IEdmFolder5)
If edmFileFolder IsNot Nothing Then
actualParentFolderIdToUse = edmFileFolder.ID
LogMessage(\$"Using ParentFolderID from IEdmFile17.Folder: {actualParentFolderIdToUse} (Name: '{edmFileFolder.Name}')")
Else
LogMessage(\$"IEdmFile17.Folder was Nothing. Using CmdData.ParentFolderID: {parentFolderIdFromCmdData}")
actualParentFolderIdToUse = parentFolderIdFromCmdData
End If
Catch exMissing As MissingMemberException
LogMessage(\$"MissingMemberException for IEdmFile17.Folder: {exMissing.Message}. Interop issue. Using CmdData.ParentFolderID: {parentFolderIdFromCmdData}")
actualParentFolderIdToUse = parentFolderIdFromCmdData
Catch exFolder As Exception
LogMessage(\$"Exception on IEdmFile17.Folder: {exFolder.Message}. Using CmdData.ParentFolderID: {parentFolderIdFromCmdData}")
actualParentFolderIdToUse = parentFolderIdFromCmdData
End Try
Else
LogMessage(\$"Could not cast base file object to IEdmFile17. Using initial name '{initialPdmFileNameAttempt}' and CmdData.ParentFolderID.")
actualParentFolderIdToUse = parentFolderIdFromCmdData
End If
Catch castEx As Exception
LogMessage(\$"Exception casting to IEdmFile17 or accessing its members: {castEx.Message}. Using initial values.")
actualParentFolderIdToUse = parentFolderIdFromCmdData
End Try
' Always try to get a valid folder ID for the file
If edmFile17 IsNot Nothing Then
Dim edmFile5 As IEdmFile5 = TryCast(edmFile17, IEdmFile5)
If edmFile5 IsNot Nothing Then
Dim foundFolderId = GetValidFolderIdForFile(edmFile5, vault)
If foundFolderId <> 0 Then
actualParentFolderIdToUse = foundFolderId
LogMessage(\$"Found valid folder ID for file: {actualParentFolderIdToUse}")
Else
LogMessage("Could not find a valid folder for the file. Using CmdData.ParentFolderID: " & parentFolderIdFromCmdData)
actualParentFolderIdToUse = parentFolderIdFromCmdData
End If
End If
End If
If Not pdmDrawingFileName.ToUpper().EndsWith(".SLDDRW") Then
LogMessage(\$"PDM File '{pdmDrawingFileName}' is not SLDDRW. Skipping.")
Return
End If
Dim drawingFileNameWithoutExtension As String = Path.GetFileNameWithoutExtension(pdmDrawingFileName)
If USE_HARDCODED_SLDDRW_PATH Then
localDrawingFilePath = HARDCODED_SLDDRW_PATH
LogMessage(\$"USING HARDCODED SLDDRW PATH: {localDrawingFilePath}")
If Path.GetFileName(HARDCODED_SLDDRW_PATH).ToUpper() <> pdmDrawingFileName.ToUpper() Then
LogMessage(\$"WARNING: Hardcoded file name '{Path.GetFileName(HARDCODED_SLDDRW_PATH)}' differs from PDM file name '{pdmDrawingFileName}'.")
End If
If Not File.Exists(localDrawingFilePath) Then
LogMessage(\$"CRITICAL: Hardcoded SLDDRW file does not exist: {localDrawingFilePath}. Cannot convert to PDF.")
'MessageBox.Show(\$"Hardcoded SLDDRW file not found: {localDrawingFilePath}", "File Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End If
ElseIf edmFile17 IsNot Nothing Then
LogMessage(\$"Attempting PDM GetLocalPath for '{pdmDrawingFileName}' using ParentFolderID: {actualParentFolderIdToUse}.")
Try
localDrawingFilePath = edmFile17.GetLocalPath(actualParentFolderIdToUse)
If String.IsNullOrEmpty(localDrawingFilePath) OrElse Not File.Exists(localDrawingFilePath) Then
LogMessage(\$"GetLocalPath failed or path invalid: '{localDrawingFilePath}'. Attempting GetSpecificVersions.")
Dim ppoFiles(0) As EdmSelItem
ppoFiles(0).mlDocID = edmFile17.ID
ppoFiles(0).mlProjID = actualParentFolderIdToUse
vault.GetSpecificVersions(1, ppoFiles, IntPtr.Zero, EdmGetCmdFlags.Egcf_Nothing)
localDrawingFilePath = edmFile17.GetLocalPath(actualParentFolderIdToUse)
If String.IsNullOrEmpty(localDrawingFilePath) OrElse Not File.Exists(localDrawingFilePath) Then
LogMessage(\$"GetLocalPath still failed after GetSpecificVersions: '{localDrawingFilePath}'. Cannot convert.")
'MessageBox.Show(\$"Could not get PDM local path for '{pdmDrawingFileName}'.", "PDM Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End If
End If
LogMessage(\$"PDM GetLocalPath successful: {localDrawingFilePath}")
Catch exPath As Exception
LogMessage(\$"Exception during PDM GetLocalPath for '{pdmDrawingFileName}': {exPath.Message}. Cannot convert.")
'MessageBox.Show(\$"Error getting PDM local path for '{pdmDrawingFileName}': {exPath.Message}", "PDM Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End Try
Else
LogMessage(\$"Cannot get local path: IEdmFile17 object is null (or cast failed) and not using hardcoded path. Cannot convert.")
'MessageBox.Show(\$"Could not obtain IEdmFile17 object to get path for '{pdmDrawingFileName}'. Check COM references for EPDMLibLib and ensure PDM client is working.", "Internal Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End If
Dim drawingSubfolderName As String = GetFolderNameFromDrawing(drawingFileNameWithoutExtension)
If String.IsNullOrEmpty(drawingSubfolderName) OrElse drawingSubfolderName.StartsWith("UNKNOWN") Then
LogMessage(\$"Could not determine folder for '{drawingFileNameWithoutExtension}'. Skipping.")
'MessageBox.Show(\$"Could not map folder for '{drawingFileNameWithoutExtension}'.", "Folder Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return
End If
Dim targetDrawingFolderPath As String = Path.Combine(DRAWINGS_BASE_PATH, drawingSubfolderName)
Dim targetArchiveFolderPath As String = Path.Combine(ARCHIVE_BASE_PATH, drawingSubfolderName)
Try
If Not Directory.Exists(DRAWINGS_BASE_PATH) Then LogMessageAndThrow("Base drawing path missing.", DRAWINGS_BASE_PATH)
If Not Directory.Exists(ARCHIVE_BASE_PATH) Then LogMessageAndThrow("Base archive path missing.", ARCHIVE_BASE_PATH)
If Not Directory.Exists(targetDrawingFolderPath) Then Directory.CreateDirectory(targetDrawingFolderPath)
If Not Directory.Exists(targetArchiveFolderPath) Then Directory.CreateDirectory(targetArchiveFolderPath)
LogMessage(\$"Directories ensured: Drawings='{targetDrawingFolderPath}', Archive='{targetArchiveFolderPath}'")
Catch dirEx As Exception
LogMessage(\$"Error creating directories: {dirEx.Message}")
'MessageBox.Show(\$"Error creating directories: {dirEx.Message}", "Dir Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End Try
Dim oldPdfPathInDrawingFolder As String = Path.Combine(targetDrawingFolderPath, drawingFileNameWithoutExtension & ".pdf")
Dim revision As String = "0"
Dim enumVar As IEdmEnumeratorVariable5 = Nothing
If edmFile17 IsNot Nothing Then
Try
enumVar = TryCast(edmFile17.GetEnumeratorVariable(), IEdmEnumeratorVariable5)
If enumVar IsNot Nothing Then
Dim revisionObj As Object = Nothing
If enumVar.GetVar(REVISION_PROPERTY_NAME, "@", revisionObj) AndAlso revisionObj IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(revisionObj.ToString()) Then
revision = revisionObj.ToString().Trim()
ElseIf enumVar.GetVar(REVISION_PROPERTY_NAME, "", revisionObj) AndAlso revisionObj IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(revisionObj.ToString()) Then
revision = revisionObj.ToString().Trim()
Else
LogMessage(\$"Revision var '{REVISION_PROPERTY_NAME}' empty/not found for '{pdmDrawingFileName}'. Default: '{revision}'.")
End If
LogMessage(\$"Revision for '{pdmDrawingFileName}': '{revision}'.")
Else
LogMessage(\$"IEdmEnumeratorVariable5 null for '{pdmDrawingFileName}'. Default revision: '{revision}'.")
End If
Catch revEx As Exception
LogMessage(\$"Error getting revision for '{pdmDrawingFileName}': {revEx.ToString()}. Default: '{revision}'.")
Finally
If enumVar IsNot Nothing Then
Marshal.ReleaseComObject(enumVar)
enumVar = Nothing ' Good practice
End If
End Try
Else
LogMessage(\$"edmFile17 is null, cannot get revision. Default: '{revision}'.")
End If
Dim archivedPdfName As String = \$"{drawingFileNameWithoutExtension}_REV{revision}.pdf"
Dim archivedPdfPath As String = Path.Combine(targetArchiveFolderPath, archivedPdfName)
If File.Exists(oldPdfPathInDrawingFolder) Then
Try
If File.Exists(archivedPdfPath) Then
Dim count = 1
Dim tempBaseName = Path.GetFileNameWithoutExtension(archivedPdfName)
Dim tempExt = Path.GetExtension(archivedPdfName)
Dim tempArchivedPdfPath = Path.Combine(targetArchiveFolderPath, \$"{tempBaseName}_{count}{tempExt}")
While File.Exists(tempArchivedPdfPath)
count += 1
tempArchivedPdfPath = Path.Combine(targetArchiveFolderPath, \$"{tempBaseName}_{count}{tempExt}")
End While
archivedPdfPath = tempArchivedPdfPath
LogMessage(\$"Archive '{Path.GetFileName(archivedPdfName)}' exists. Saving as '{Path.GetFileName(archivedPdfPath)}'.")
End If
File.Copy(oldPdfPathInDrawingFolder, archivedPdfPath, True)
LogMessage(\$"Archived '{oldPdfPathInDrawingFolder}' to '{archivedPdfPath}'.")
Catch archiveEx As Exception
LogMessage(\$"Error archiving PDF for '{drawingFileNameWithoutExtension}': {archiveEx.ToString()}")
'MessageBox.Show(\$"Error archiving PDF: {archiveEx.Message}", "Archive Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
Else
LogMessage(\$"No existing PDF at '{oldPdfPathInDrawingFolder}' to archive for '{drawingFileNameWithoutExtension}'.")
End If
Dim newPdfPath As String = Path.Combine(targetDrawingFolderPath, drawingFileNameWithoutExtension & ".pdf")
If ConvertSldDrwToPdf(localDrawingFilePath, newPdfPath) Then
LogMessage(\$"Successfully converted '{pdmDrawingFileName}' (from '{localDrawingFilePath}') to '{newPdfPath}'.")
Dim successMsg As New StringBuilder()
successMsg.AppendLine(\$"PDF for '{drawingFileNameWithoutExtension}' created: {newPdfPath}")
If File.Exists(archivedPdfPath) AndAlso oldPdfPathInDrawingFolder <> archivedPdfPath Then
successMsg.AppendLine(\$"Previous version archived: {archivedPdfPath}")
ElseIf Not File.Exists(oldPdfPathInDrawingFolder) Then
successMsg.AppendLine("No previous PDF found to archive.")
ElseIf File.Exists(oldPdfPathInDrawingFolder) AndAlso Not File.Exists(archivedPdfPath) Then
successMsg.AppendLine("Note: Archiving of previous PDF may have failed.")
End If
'MessageBox.Show(successMsg.ToString(), "Process Successful", MessageBoxButtons.OK, MessageBoxIcon.Information)
Try
'If File.Exists(newPdfPath) Then System.Diagnostics.Process.Start(newPdfPath)
Catch openEx As Exception
LogMessage(\$"Error auto-opening new PDF '{newPdfPath}': {openEx.Message}")
End Try
Else
LogMessage(\$"Failed to convert '{pdmDrawingFileName}' (from '{localDrawingFilePath}') to PDF.")
'MessageBox.Show(\$"Failed to convert '{pdmDrawingFileName}' to PDF. Check logs.", "PDF Conversion Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
Catch exMain As Exception
LogMessage(\$"Major exception in ProcessDrawingFile for PDM file '{pdmDrawingFileName}': {exMain.ToString()}")
Finally
If edmFileFolder IsNot Nothing Then
Marshal.ReleaseComObject(edmFileFolder)
edmFileFolder = Nothing ' Good practice
End If
If edmFile17 IsNot Nothing Then
Marshal.ReleaseComObject(edmFile17)
edmFile17 = Nothing ' Good practice
End If
End Try
End Sub
Private Sub LogMessageAndThrow(message As String, pathDetail As String)
Dim fullMessage = \$"{message} Path: '{pathDetail}'"
LogMessage(fullMessage)
'MessageBox.Show(fullMessage, "Configuration Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Throw New DirectoryNotFoundException(fullMessage)
End Sub
Private Function GetFolderNameFromDrawing(dwgNameWithoutExtension As String) As String
If String.IsNullOrWhiteSpace(dwgNameWithoutExtension) Then
LogMessage("GetFolderNameFromDrawing: Input drawing name is null or whitespace.")
Return "UNKNOWN_FOLDER_EMPTY_NAME"
End If
If dwgNameWithoutExtension.Length >= 3 AndAlso dwgNameWithoutExtension.StartsWith("574") Then
Return "5 MOTORS"
ElseIf dwgNameWithoutExtension.Length > 0 Then
Dim firstChar As Char = dwgNameWithoutExtension(0)
Select Case firstChar
Case "0"c : Return "0 CUSTOMER INFO"
Case "1"c : Return "1 COMPLETE FAN"
Case "2"c : Return "2 INLET-OUTLET"
Case "3"c : Return "3 HOUSING"
Case "4"c : Return "4 WHEELS"
Case "5"c : Return "5 DRIVE TRAIN"
Case "6"c : Return "6 ACCESSORIES"
Case "7"c : Return "7 BASE"
Case "8"c : Return "8 HARDWARE"
Case "9"c : Return "9 MATERIAL"
Case Else
LogMessage(\$"GetFolderNameFromDrawing: Drawing name '{dwgNameWithoutExtension}' does not match known prefix for folder mapping.")
Return "UNKNOWN_FOLDER_UNMATCHED_PREFIX"
End Select
Else
LogMessage(\$"GetFolderNameFromDrawing: Drawing name '{dwgNameWithoutExtension}' is too short for prefix mapping.")
Return "UNKNOWN_FOLDER_SHORT_NAME"
End If
End Function
Private Function ConvertSldDrwToPdf(ByVal sldDrwFilePath As String, ByVal pdfOutputPath As String) As Boolean
Dim sldWorksApp As SldWorks = Nothing
Dim drawingDoc As ModelDoc2 = Nothing
Dim modelExt As ModelDocExtension = Nothing
Dim errors As Integer = 0
Dim warnings As Integer = 0
Dim success As Boolean = False
Dim createdNewSwInstance As Boolean = False
Dim openedByAddin As Boolean = False ' To track if this function opened the document
Dim docNameForClosing As String = sldDrwFilePath ' <-- Move declaration here
If conversionInProgress Then
LogMessage("Another conversion is in progress, skipping")
Return False
End If
conversionInProgress = True
If String.IsNullOrEmpty(sldDrwFilePath) Then
LogMessage("ConvertSldDrwToPdf: sldDrwFilePath is null or empty. Cannot convert.")
Return False
End If
If Not File.Exists(sldDrwFilePath) Then
LogMessage(\$"ConvertSldDrwToPdf: Source SLDDRW file does not exist: '{sldDrwFilePath}'. Cannot convert.")
Return False
End If
Try
LogMessage(\$"Starting PDF conversion for: {sldDrwFilePath} to {pdfOutputPath}")
' Step 1: Get or Create SolidWorks Application Instance
Try
sldWorksApp = TryCast(Marshal.GetActiveObject("SldWorks.Application"), SldWorks)
If sldWorksApp Is Nothing Then
Throw New COMException("No active SolidWorks instance found")
End If
createdNewSwInstance = False
LogMessage("Successfully attached to existing SolidWorks instance")
Catch ex As Exception
LogMessage(\$"Creating new SolidWorks instance: {ex.Message}")
Try
sldWorksApp = CType(CreateObject("SldWorks.Application"), SldWorks)
If sldWorksApp Is Nothing Then
LogMessage("Failed to create SolidWorks instance")
Return False
End If
sldWorksApp.Visible = True ' Changed to True to prevent background crashes
createdNewSwInstance = True
LogMessage("Created new SolidWorks instance")
Catch createEx As Exception
LogMessage(\$"Failed to create SolidWorks instance: {createEx.Message}")
Return False
End Try
End Try
If sldWorksApp Is Nothing Then
LogMessage("SolidWorks application object is null after attempting to get/create. Cannot proceed.")
Return False
End If
' At the start of ConvertSldDrwToPdf
If createdNewSwInstance Then
sldWorksApp.CommandInProgress = True ' Prevent other operations
End If
' Step 2: Open the Drawing Document
Dim alreadyOpenDoc As ModelDoc2 = sldWorksApp.GetOpenDocumentByName(sldDrwFilePath)
If alreadyOpenDoc IsNot Nothing Then
LogMessage("Drawing is already open in SolidWorks. Using existing document reference.")
drawingDoc = alreadyOpenDoc
openedByAddin = False ' We didn't open it, so we shouldn't close it
Else
LogMessage(\$"Opening drawing: {sldDrwFilePath}")
Dim openErrors As Integer = 0
Dim openWarnings As Integer = 0
drawingDoc = CType(sldWorksApp.OpenDoc6(sldDrwFilePath,
swDocumentTypes_e.swDocDRAWING,
swOpenDocOptions_e.swOpenDocOptions_Silent Or swOpenDocOptions_e.swOpenDocOptions_ReadOnly,
"", openErrors, openWarnings), ModelDoc2)
If drawingDoc IsNot Nothing Then
LogMessage(\$"Drawing opened successfully by add-in. SW OpenDoc Errors: {openErrors} ({GetOpenErrorDescription(openErrors)}), Warnings: {openWarnings} ({GetOpenWarningDescription(openWarnings)})")
openedByAddin = True ' We opened it, so we are responsible for closing it
docNameForClosing = drawingDoc.GetPathName() ' Get the exact path name from SW
If String.IsNullOrEmpty(docNameForClosing) Then docNameForClosing = sldDrwFilePath
Else
LogMessage(\$"Failed to open drawing '{sldDrwFilePath}'. SW OpenDoc Errors: {openErrors} ({GetOpenErrorDescription(openErrors)}), Warnings: {openWarnings} ({GetOpenWarningDescription(openWarnings)})")
' No need to return false yet, finally block will handle SW app release
End If
End If
' Step 3: Process the Document if Opened/Found
If drawingDoc IsNot Nothing Then
' Verify document is fully loaded
If drawingDoc.GetSaveFlag() Then
LogMessage("Document is in an unsaved state, waiting...")
System.Threading.Thread.Sleep(1000)
End If
modelExt = CType(drawingDoc.Extension, ModelDocExtension)
If modelExt Is Nothing Then
LogMessage("Failed to get ModelDocExtension from the drawing. Cannot save as PDF.")
Else
Dim outputDir As String = Path.GetDirectoryName(pdfOutputPath)
If Not Directory.Exists(outputDir) Then
Try
Directory.CreateDirectory(outputDir)
LogMessage(\$"Created output directory: {outputDir}")
Catch dirEx As Exception
LogMessage(\$"Error creating output directory '{outputDir}': {dirEx.Message}. Cannot save PDF.")
success = False ' Mark as failed, proceed to cleanup
End Try
End If
If success OrElse Directory.Exists(outputDir) Then ' Proceed if directory exists or was created
If File.Exists(pdfOutputPath) Then
Try
File.Delete(pdfOutputPath)
LogMessage(\$"Deleted existing PDF at: {pdfOutputPath}")
Catch delEx As Exception
LogMessage(\$"Could not delete existing PDF '{pdfOutputPath}': {delEx.Message}. SaveAs might overwrite or fail.")
End Try
End If
LogMessage(\$"Attempting to save as PDF: {pdfOutputPath}")
Dim saveErrors As Integer = 0
Dim saveWarnings As Integer = 0
success = modelExt.SaveAs(pdfOutputPath,
swSaveAsVersion_e.swSaveAsCurrentVersion,
swSaveAsOptions_e.swSaveAsOptions_Silent Or swSaveAsOptions_e.swSaveAsOptions_Copy,
Nothing, saveErrors, saveWarnings)
If success Then
LogMessage(\$"Successfully saved PDF. SW SaveAs Errors: {saveErrors} ({GetSaveErrorDescription(saveErrors)}), Warnings: {saveWarnings} ({GetOpenWarningDescription(saveWarnings)})")
Else
LogMessage(\$"Failed to save PDF (SaveAs returned false). SW SaveAs Errors: {saveErrors} ({GetSaveErrorDescription(saveErrors)}), Warnings: {saveWarnings} ({GetOpenWarningDescription(saveWarnings)})")
End If
End If ' End if Directory.Exists
End If ' End if modelExt is not nothing
Else
LogMessage(\$"Drawing document was not available for '{sldDrwFilePath}'. PDF conversion skipped.")
success = False
End If
Catch ex As Exception
LogMessage(\$"CRITICAL EXCEPTION in ConvertSldDrwToPdf: {ex.ToString()}")
success = False
Finally
LogMessage("ConvertSldDrwToPdf: Entering Finally block for cleanup.")
If modelExt IsNot Nothing Then
Try
Marshal.ReleaseComObject(modelExt)
LogMessage("Released ModelDocExtension.")
Catch exRCO As Exception
LogMessage(\$"Error releasing ModelDocExtension: {exRCO.Message}")
End Try
modelExt = Nothing
End If
If drawingDoc IsNot Nothing Then
If openedByAddin Then
Try
' Give SW a moment to finish any operations
System.Threading.Thread.Sleep(1000)
If sldWorksApp IsNot Nothing Then
' Don't force close if document is dirty
If Not drawingDoc.GetSaveFlag() Then
sldWorksApp.CloseDoc(docNameForClosing)
LogMessage(\$"Closed document: {docNameForClosing}")
Else
LogMessage("Document has unsaved changes, skipping close")
End If
End If
Catch ex As Exception
LogMessage(\$"Error closing document: {ex.Message}")
End Try
End If
Try
Marshal.ReleaseComObject(drawingDoc)
drawingDoc = Nothing
Catch ex As Exception
LogMessage(\$"Error releasing drawingDoc: {ex.Message}")
End Try
End If
If sldWorksApp IsNot Nothing Then
If createdNewSwInstance Then
Try
' Give SW time to finish operations
System.Threading.Thread.Sleep(2000)
' Don't exit if there are open documents
Dim openDocs As Object = sldWorksApp.GetDocuments()
If openDocs Is Nothing OrElse CType(openDocs, Object()).Length = 0 Then
sldWorksApp.ExitApp()
LogMessage("Exited SolidWorks instance")
Else
LogMessage("SolidWorks has open documents, skipping exit")
End If
Catch ex As Exception
LogMessage(\$"Error during SolidWorks cleanup: {ex.Message}")
Finally
Try
Marshal.ReleaseComObject(sldWorksApp)
sldWorksApp = Nothing
Catch ex As Exception
LogMessage(\$"Error releasing SolidWorks object: {ex.Message}")
End Try
End Try
Else
LogMessage("SolidWorks instance was pre-existing, not exiting it from add-in.")
End If
If createdNewSwInstance Then
GC.Collect()
GC.WaitForPendingFinalizers()
LogMessage("GC.Collect and GC.WaitForPendingFinalizers called after releasing created SW instance.")
End If
End If
conversionInProgress = False
LogMessage("ConvertSldDrwToPdf: Exiting Finally block.")
End Try
Return success
End Function
' Helper function for SaveAs errors (stub - enhance as needed)
Private Function GetSaveErrorDescription(errorCode As Integer) As String
If errorCode = 0 Then Return "No error."
' You might need to find the correct enum for SolidWorks save errors
' For example, if it was swFileSaveError_e:
' Try
' Return CType(errorCode, swFileSaveError_e).ToString()
' Catch
' Return \$"Unknown SolidWorks file save error code (cast failed): {errorCode}"
' End Try
Return \$"SolidWorks SaveAs Error Code: {errorCode}"
End Function
' Helper function for OpenDoc6 errors
Private Function GetOpenErrorDescription(errorCode As Integer) As String
If errorCode = 0 Then Return "No error."
Try
Return CType(errorCode, swFileLoadError_e).ToString() ' swFileLoadError_e is a valid enum
Catch
Return \$"Unknown SolidWorks file load error code (cast failed): {errorCode}"
End Try
End Function
' Your existing GetOpenWarningDescription function should remain:
' Private Function GetOpenWarningDescription(warningCode As Integer) As String
' If warningCode = 0 Then Return "No warning."
' Try
' Return CType(warningCode, swFileLoadWarning_e).ToString()
' Catch
' Return \$"Unknown SolidWorks file load warning code: {warningCode}"
' End Try
' End Function
Private Function GetOpenWarningDescription(warningCode As Integer) As String
If warningCode = 0 Then Return "No warning."
Try
Return CType(warningCode, swFileLoadWarning_e).ToString()
Catch
Return \$"Unknown SolidWorks file load warning code: {warningCode}"
End Try
End Function
' Add this helper function to your class
Private Function GetValidFolderIdForFile(edmFile As IEdmFile5, vault As IEdmVault5) As Integer
Dim pos As Object = edmFile.GetFirstFolderPosition()
If pos IsNot Nothing Then
Dim folder As IEdmFolder5 = Nothing
Try
folder = edmFile.GetNextFolder(pos)
If folder IsNot Nothing Then
Return folder.ID
End If
Finally
If folder IsNot Nothing Then
Marshal.ReleaseComObject(folder)
folder = Nothing ' Good practice
End If
End Try
End If
Return 0
End Function
#End Region
#Region "Logging"
Private Sub LogMessage(message As String)
Dim logFilePath As String = ""
Try
Dim tempPath As String = Path.GetTempPath()
If String.IsNullOrEmpty(tempPath) OrElse Not Directory.Exists(tempPath) Then
tempPath = "C:\Temp" ' Fallback
If Not Directory.Exists(tempPath) Then Directory.CreateDirectory(tempPath)
End If
logFilePath = Path.Combine(tempPath, LOG_FILE_NAME)
Using writer As StreamWriter = File.AppendText(logFilePath)
writer.WriteLine(\$"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} - {message}")
End Using
Catch ex As Exception
System.Diagnostics.Debug.WriteLine(\$"Logging Error to '{logFilePath}': {ex.Message} | Original Msg: {message}")
End Try
End Sub
#End Region
End Class