How can the application (PdCommon.Application) be closed in C#? - c#

I have the following code snippet in C# to open a PowerDesigner Model and I would like to close / exit the PowerDesigner application instance but I do not know how?
PdCommon.Application pdApplication = null;
try{
//Creating PD Application object...
pdApplication = new PdCommon.Application();
//Opening the relevant models of a dedicated directory
String[] files = System.IO.Directory.GetFiles(ldmSourceDirectory);
foreach (String curFile in files){
if(curFile.EndsWith(".cdm")){
pdApplication.OpenModel(#curFile, PdCommon.OpenModelFlags.omf_DontOpenView);
}
}
//Doing some operations...
}finally{
if (pdApplication != null){
//Closing models
foreach (PdCDM.Model curPDModel in pdApplication.Models){
curPDModel.Close(false);
}
//But how can I close the application?
pdApplication = null;
}
}

As commented by pascal, you should close the model, the workspace, and then, kill the process. Don´t forget to add PdWSP (workspace model) reference, and System.Diagnostics using declaration. The process name may be different depending on the PD version.
PdCommon.Application pd = new PdCommon.Application();
var pdm = (PdPDM.Model)pdApplication.OpenModel(fileName);
var wsp = (PdWSP.Workspace)pdApplication.ActiveWorkspace;
pdm.Close(false);
wsp.Close(true);
pd = null;
// close powerdesigner processes
foreach(var process in Process.GetProcesses().Where(pr => pr.ProcessName == "PdShell16"))
{
process.Kill();
}

Related

How can I retrieve items and their details from the shell:AppsFolder virtual folder using c#?

I am trying to get all the items from the FOLDERID_AppsFolder, which you can access by running explorer.exe shell:appsFolder command, and their details, specifically the AppUserModelID.
I can get the name of the items using the code below but I am not sure how to get the AppUserModelID. Can I get this value somehow?
IShellItem appsFolder;
string str;
var res = ShellItemUtilities.SHGetKnownFolderItem(ShellItemUtilities.FOLDERID_AppsFolder,
0, IntPtr.Zero, typeof(IShellItem).GUID, out appsFolder);
if (res < 0) return;
try
{
var pidl = default(PIDLIST_ABSOLUTE);
foreach (var app in appsFolder.Enumerate())
{
try
{
recyleBin.GetDisplayName(2, out ptr);
// Get the actual name of the item
str = Marshal.PtrToStringUni(ptr);
}
finally
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(ptr);
ptr = IntPtr.Zero;
}
}
}
}
...
Perhaps the IShellItem::GetAttributes method is what I need but it can only retrieve the attribute that I specify through the sfgaoMask parameter and the documentation regarding the values for this parameter does not include anything related to the AppUserModelID.
And for reference, this is what the apps folder looks like:
Can you hear the crickets chirping?
I was eventually able to find a solution to this problem when I stumbled upon the Microsoft.WindowsAPICodePack-Shell nuget package which wraps all the shell commands I needed so that I don't have to P/Ivoke them. The code now becomes:
// GUID taken from https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
var FODLERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FODLERID_AppsFolder);
foreach (var app in (IKnownFolder)appsFolder)
{
string name = app.Name;
// The ParsingName property is the AppUserModelID
string appUserModelID = app.ParsingName; // or app.Properties.System.AppUserModel.ID
ImageSource icon = app.Thumbnail.MediumBitmapSource;
}
The ShellObject object actually contains a whole lot more proeprties available through it's Properties.System props.
In case you are wondering why I am casting the appsFolder to a ShellObject at instantiation only to cast it back to an IKnownFolder when enumerating, that's because the API code pack actually comes with a ShellObjectWatcher which takes a ShellObject and monitors it for changes. If a new app is installed and it is listed in this virtual folder, the watcher can be used to monitor for this:
ShellObjectWatcher sow = new ShellObjectWatcher(appFolder, false);
sow.AllEvents += (s, e) => DoWhatever();
sow.Start();

The file being use by another process when i add it programatically by SharpSvn

We are using SharpSvn to add SolidWorks files programatically to SVN tortoise.
When file is open in SolidWorks, i want to add it to SVN by code without closing file.
I used code below
var SvnResult = new SvnResult();
var FullPath = SvnHelper.FileCombine(FileName);
try
{
var SvnArg = new SvnAddArgs();
SvnArg.Force = true;
SvnArg.Depth = SvnDepth.Infinity;
//
SvnClient.Add(FullPath, SvnArg);
SvnResult.Message = "Success.";
SvnResult.Status = true;
//
return SvnResult;
}
catch (SvnException exc)
{
SvnResult.Message = exc.Message;
SvnResult.Status = false;
return SvnResult;
}
and i get error like this :
The process cannot access the file because it is being used by another process.
How can i add it to SVN without closing file?
Regards,
We solved the problem. At first we used TortoiseSvn.exe command lines to add and commit the file but when we used to send commit command, svn Dialog form was raised. For solving this problem I install “Command Line Client Tools” from the svn setup. By installing this option you can find svn.exe under svn path “C:\Program Files\TortoiseSVN\bin”.
I add this path to Environment Variables and then use svn command lines to add and commit while file is open.
public SvnResult CommitFiles_BySVNCmd(string filePath)
{
var fullPath = SvnHelper.FileCombine(filePath);
var svnResult = new SvnResult();
try
{
// svn add command
var status = GetStatus(fullPath);
//
if (status.LocalContentStatus == SvnStatus.NotVersioned)
{
var argumentsAdd = $#"add {fullPath}";
ProcessStart(argumentsAdd);
}
// svn commit command
var argumentsCommit = $#"commit -m Commited_Automatically {fullPath}";
ProcessStart(argumentsCommit);
svnResult.Message = "Success
svnResult.Status = true;
return svnResult;
}
catch (SvnException se)
{
svnResult.Message = se.Message;
svnResult.Status = false;
return svnResult;
}
}
private void ProcessStart(string arguments)
{
var processInfo = new ProcessStartInfo("svn", arguments);
processInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
Process.Start(processInfo);
}
Best Regards,

Created PST file using Redemption API is empty when loaded in Microsoft Outlook

Environment: I have a windows console application and I am running the exe from command line.
Below is my code:
static void Main(string[] args)
{
CreatePSTUsingRedemption(args[0], args[1]);
}
private static void CreatePSTUsingRedemption(string messageFilePath, string pstPath)
{
RDOSession pstSession = new RDOSession();
RDOPstStore store = null;
store = pstSession.LogonPstStore(pstPath, 1, "combinedPST");
//actually there is a loop here to loop through each message files.
RDOMail rdo_Mail = pstSession.GetMessageFromMsgFile(messageFilePath);
rdo_Mail.CopyTo(store.IPMRootFolder);
rdo_Mail.Save();
store.Save();
completedCount++;
Console.WriteLine("FILES_PROCESSED:" + completedCount);
pstSession.Logoff();
}
Main purpose of this code is to create a single pst file combining email message(.msg) files.
Now when I run the exe, a pst file is created in the given location and its size keeps increasing as the code runs. After all the message files are processed application exists. There is no any error. Now, when I try to load this pst file in Outlook 2013. Pst is empty and its size is also reduced to 265KB every time. I don't think any process is using this pst because I can copy and move it anywhere I want. What might be the issue? Any suggestion, please?
UPDATE 1
private static void CreatePSTUsingRedemption(XmlNodeList nodelist, string pstPath)
{
System.Diagnostics.Debugger.Launch();
RDOSession pstSession = null;
RDOPstStore store = null;
RDOFolder folder = null;
RDOMail rdo_Mail = null;
try
{
pstSession = new RDOSession();
store = pstSession.LogonPstStore(pstPath, 1, Path.GetFileNameWithoutExtension(pstPath));
var enumerator = store.IPMRootFolder.Folders.GetEnumerator(); //DELETE DEFAULT FOLDERS
while (enumerator.MoveNext())
{
var defaultFolders = enumerator.Current as RDOFolder;
defaultFolders.Delete();
}
int completedCount = 0;
folder = store.IPMRootFolder;
foreach (XmlNode node in nodelist)
{
rdo_Mail = pstSession.GetMessageFromMsgFile(node["FullPath"].InnerText);
rdo_Mail.CopyTo(folder);
rdo_Mail.Save();
store.Save();
completedCount++;
Console.WriteLine("FILES_PROCESSED:" + completedCount);
}
}
finally
{
Marshal.ReleaseComObject(rdo_Mail);
Marshal.ReleaseComObject(folder);
Marshal.ReleaseComObject(store);
}
pstSession.Logoff();
Marshal.ReleaseComObject(pstSession);
GC.Collect();
}
Above is my code for the actual loop. I am loading all the email messages file path from an xml file. I still encounter same issue as above.
This is an indication that the PST store is not fully flushed to the disk and Outlook "fixes' the PST file by resetting it.
Try to explicitly release all Redemption objects first before logging off and call GC.Collect(). If you have a loop processing multiple files, release the message on each step of the loop.
private static void CreatePSTUsingRedemption(string messageFilePath, string pstPath)
{
RDOSession pstSession;
try
{
RDOPstStore store;
RDOFolder folder;
RDOMail rdo_Mail;
pstSession = new RDOSession();
store = pstSession.LogonPstStore(pstPath, 1, "combinedPST");
//actually there is a loop here to loop through each message files.
rdo_Mail = pstSession.GetMessageFromMsgFile(messageFilePath);
folder = store.IPMRootFolder;
rdo_Mail.CopyTo(folder);
rdo_Mail.Save();
store.Save();
completedCount++;
Console.WriteLine("FILES_PROCESSED:" + completedCount);
}
finally
{
Marshal.ReleaseComObject(rdo_Mail);
Marshal.ReleaseComObject(folder);
Marshal.ReleaseComObject(store);
}
pstSession.Logoff();
Marshal.ReleaseComObject(pstSession);
GC.Collect();
}

Kill Process Excel C#

I have to 2 process excel. For example:
1) example1.xlsx
2) example2.xlsx
How to kill first "example1.xlsx"?
I use this code:
foreach (Process clsProcess in Process.GetProcesses())
if (clsProcess.ProcessName.Equals("EXCEL")) //Process Excel?
clsProcess.Kill();
That kill a both.
I wanna kill just one...
Thank you.
The ProcessMainWindow Title will do it for you, it appends "Microsoft Excel - " to the name of the file:
So essentially (quick code):
private void KillSpecificExcelFileProcess(string excelFileName)
{
var processes = from p in Process.GetProcessesByName("EXCEL")
select p;
foreach (var process in processes)
{
if (process.MainWindowTitle == "Microsoft Excel - " + excelFileName)
process.Kill();
}
}
Use:
KillSpecificExcelFileProcess("example1.xlsx");
Edit: Tested and verified to work.
kd7's post is an awesome answer and works well, just two things to add,
MainWindowTitle format is - "Filename.xlsx - Excel"
If your excel document is not visible then your MainWindowTitle will be ""
using the "" for MainWindowTitle will kill all zombie excel process'.
If your current code is working, this amendment should kill the first process it finds with the name "EXCEL".
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Equals("EXCEL"))
{
clsProcess.Kill();
break;
}
}
If you want to kill a specific process, you're going to have to give a bit more information.
Excel will always be a single process, AFAIK. The same process/windows opens multiple documents inside it. What you want to do is use Excel automation to CLOSE the document you want to. Perhaps this will get you started. http://support.microsoft.com/kb/302084
Hope this helps.
Copy and paste this. Its done!
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
if (!string.IsNullOrEmpty(p.ProcessName))
{
try
{
p.Kill();
}
catch { }
}
}
You need to check file handles, that are opened by process and then kill it.
How to check which file handles process is holding: How do I get the list of open file handles by process in C#?
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Equals("EXCEL") && HasFileHandle(fileName, clsProcess))
{
clsProcess.Kill();
break;
}
}
Try getting the main window title
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Equals("EXCEL")&& clsProcess.MainWindowTitle =="example")
{
clsProcess.CloseMainWindow();
break;
}
}
just did a quick search on Google, try Process.MainWindowTitle() to get the title of the Excel process, and decide which one is that you want to kill.
I am not sure about this method, but hope this will help:
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.mainwindowtitle.aspx
Use below logic to prevent Zombie Excel processes in Task Manager
List<int> GetAllExcelProcessID()
{
List<int> ProcessID = new List<int>();
if (currentExcelProcessID == -1)
{
List<System.Diagnostics.Process> currentExcelProcessList = System.Diagnostics.Process.GetProcessesByName("EXCEL").ToList();
foreach(var item in currentExcelProcessList)
{
ProcessID.Add(item.Id);
}
}
return ProcessID;
}
int GetApplicationExcelProcessID(List<int> ProcessID1, List<int> ProcessID2)
{
foreach(var processid in ProcessID2)
{
if (!ProcessID1.Contains(processid)) { currentExcelProcessID = processid; }
}
return currentExcelProcessID;
}
void KillExcel()
{
System.Diagnostics.Process process = System.Diagnostics.Process.GetProcessById(currentExcelProcessID);
process.Kill();
}
List<int> ProcessID1 = GetAllExcelProcessID();
excel = new Excel.Application();
List<int> ProcessID2 = GetAllExcelProcessID();
currentExcelProcessID = GetApplicationExcelProcessID(ProcessID1, ProcessID2);
In the namespace section add this using statement.
using System.Diagnostics;
This example instantiated Excel with this:
_Application excel = new _Excel.Application();
This method kills the right Excel task by using the window handle.
public void Kill()
{
Int32 ExcelHwnd = excel.Hwnd;
Process[] localExcel = Process.GetProcessesByName("EXCEL");
foreach (Process Pgm in localExcel)
{
// xlMinimized keeps the screen from flashing when the user interface is made
// visible with the excel.visible needed to set the MainWindowHandle
excel.WindowState = XlWindowState.xlMinimized;
excel.Visible = true;
if ((Pgm.ProcessName == "EXCEL") && (ExcelHwnd == Pgm.MainWindowHandle.ToInt32()))
{
Pgm.Kill();
}
}
}
This worked without fail.

TFS / File Checkout from C#

I don't have a great deal of experience with TFS, other than using it for source control. I am working on a C# application that will need to modify files that are being controlled by TFS. From within my C# application, how can I check out a file that is controlled via TFS?
Thanks - Randy
You can use PendEdit to make your files writables, make your changes to it, then you add it to the pending changes, and finally check it in.
Here is some code where a folder structure is created and then checked in (Very similar to what you will need).
private static void CreateNodes(ItemCollection nodes)
{
using (var tfs = TeamFoundationServerFactory.GetServer("http://tfsserver:8080"))
{
var versionControlServer = tfs.GetService(typeof (VersionControlServer)) as VersionControlServer;
versionControlServer.NonFatalError += OnNonFatalError;
// Create a new workspace for the currently authenticated user.
var workspace = versionControlServer.CreateWorkspace("Temporary Workspace", versionControlServer.AuthenticatedUser);
try
{
// Check if a mapping already exists.
var workingFolder = new WorkingFolder("$/testagile", #"c:\tempFolder");
// Create the mapping (if it exists already, it just overides it, that is fine).
workspace.CreateMapping(workingFolder);
// Go through the folder structure defined and create it locally, then check in the changes.
CreateFolderStructure(workspace, nodes, workingFolder.LocalItem);
// Check in the changes made.
workspace.CheckIn(workspace.GetPendingChanges(), "This is my comment");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// Cleanup the workspace.
workspace.Delete();
// Remove the temp folder used.
Directory.Delete("tempFolder", true);
}
}
}
private static void CreateFolderStructure(Workspace workspace, ItemCollection nodes, string initialPath)
{
foreach (RadTreeViewItem node in nodes)
{
var newFolderPath = initialPath + #"\" + node.Header;
Directory.CreateDirectory(newFolderPath);
workspace.PendAdd(newFolderPath);
if (node.HasItems)
{
CreateFolderStructure(workspace, node.Items, newFolderPath);
}
}
}
Using the other solution gave me permission problems.
Here's an alternative way to checkout your files using tf.exe:
//Checkout file
Process proc = new Process();
proc.StartInfo =
new ProcessStartInfo(
#"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe",
string.Format("checkout \"{0}\"", fileLocation)
);
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit();
For those looking to use the first solution and resolve the permission issue you can use the following code to use the current credentials, this replaces the "TeamFoundationServerFactory.GetServer" call then use the TfsTeamProjectCollection (tmPrjColl) to get the VersionControlServer:
using Microsoft.TeamFoundation.Client;
using MTVC = Microsoft.TeamFoundation.VersionControl.Client;
using MVSC = Microsoft.VisualStudio.Services.Common;
MVSC.VssCredentials creds = new MVSC.VssCredentials(new MVSC.WindowsCredential(true));
using (TfsTeamProjectCollection tmPrjColl = new TfsTeamProjectCollection(new Uri("<source control URL>"), creds))
{
MTVC.VersionControlServer verCtrlSvr = tmPrjColl.GetService<MTVC.VersionControlServer>();
...
}

Categories