Should we have something with the External application to properly register the event?
I also tried putting two breakpoints one inside the start module and other inside the Export module.
the first responded and waited for me to continue and the next didn't respond(hope did not run the line)
Also,I had manually tried coping the addin file to the addin location to avoid any post build event error but still doesnt seem to work.
could you tell me what I am I doing wrong here.
Here is the code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.DB.Events;
using System.IO;
namespace UserDataSheet
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class UserDataSheetclass : IExternalApplication
{
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
try
{
// Register event.
application.ControlledApplication.DocumentOpened += new EventHandler<Autodesk.Revit.DB.Events.DocumentOpenedEventArgs>(ExportLog);
return Result.Succeeded;
}
catch (Exception)
{
return Result.Failed;
}
}
public void ExportLog(object sender, DocumentOpenedEventArgs args)
{
var doc = sender as Document;
var isFamilyDoc = doc.IsFamilyDocument;
// variables to use
string RevitUserName = "";
DateTime OpenTime = new DateTime();
string localUserName = "";
string filename = "";
string filepath = "";
string content = "";
if (isFamilyDoc == false)
{
var IsloggedIn = Autodesk.Revit.ApplicationServices.Application.IsLoggedIn;
if (IsloggedIn == true )//&& doc.IsModelInCloud == true)
{
// use variables
filepath = doc.PathName;
filename = doc.Title;
RevitUserName = doc.Application.Username;
OpenTime = DateTime.Now;
localUserName = System.Environment.MachineName;
}
content = string.Format("Date and time : {0} \n Revit UserName : {1} \n Local PC UserName : {2} \n FileName : {3} \n FilePath : {4} "
, OpenTime.ToString(), RevitUserName, localUserName, filename, filepath);
TaskDialog.Show("Model Open Writer info", "user and file details : \n " + content);
}
var writefilepath = Path.GetTempPath();
var Writefile = writefilepath + "//records.txt";
FileStream fs = new FileStream(Writefile, FileMode.Append);
StreamWriter writer = new StreamWriter(fs);
writer.Write(content);
writer.Close();
File.OpenRead(Writefile);
}
}
}
First of all, you can completely remove the TransactionMode and RegenerationOption. The latter is completely obsolete and does nothing at all anywhere whatsoever. The former is only useful when declaring an external command. It is useless and ignored in the context of an external application.
Secondly, to address your question: you can set a breakpoint in the beginning of OnStartup. If the breakpoint is not hit, your add-in is not being loaded at all. Probably something is wrong with your add-in registration, e.g., in the add-in manifest *.addin file.
Go back to square one, i.e., work through the getting started material and the developer guide instructions on registering and loading a Revit add-in.
If the breakpoint in OnStartup is hit, then your add-in is loading correctly, which means that the add-in manifest *addin file is OK. So, you do not need to worry about that. The VisibilityMode tag is not used for external applications, by the way.
Thanks, Jeremy It worked
Firstly I apologies for adding this as answer( I don't know how to add codes in comment)
It worked when I deleted my Addin file and recreated it may be I had made some mistake in it.
meanwhile I have copied the following code from examples and used it,honestly I did't understand this line of the code.
"public void ExportLog(object sender, DocumentOpenedEventArgs args)"
can you point to a right source that explains this part. I have three questions here :
what object type is sender and args are they of type application?
How do I add a 3rd parameter to this method say I want the user to input a string to name the file the data is copied to.
Can I do this
var newEvent = new EventHandler<Autodesk.Revit.DB.Events.DocumentOpenedEventArgs>(ExportLog);
instead of
application.ControlledApplication.DocumentOpened += new EventHandler<Autodesk.Revit.DB.Events.DocumentOpenedEventArgs>(ExportLog);
why does all example use += is this to register the event every time a new instance of Revit is opened?
Thanks for your help.
You can see the class of sender yourself by setting a breakpoint at the beginning of ExportLog and looking in the debugger.
No, you cannot modify the signature of the event handler. It is predetermined by the Revit API.
Yes.
It sounds to me as if you might save some time and effort for yourself by learning a bit more about the basics of C# and .NET programming in general before continuing to tackle this task.
Related
I am new at Selenium and came up with an issue - how to take and save screenshot into the specific folder. I am using Selenium+C#+NUnit bond.
Have read many information on this but most of them is on - how to capture screenshot and add it to html file. But this is not what I need.
I need the screenshot file to be save into a folder so when I'm running Pipeline in AzureDevOps the "Tests results" block contain this screenshot as well and display it.
I was using this part of the code. The test runs and fails, but no screenshot was make
[OneTimeTearDown]
public void OneTimeTearDown()
{
if (TestContext.CurrentContext.Result.Outcome != ResultState.Failure)
{
var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
var filePath = "pathToTheFolder\\Screenshots\\";
screenshot.SaveAsFile(filePath, Png);
}
}
Maybe someone can help on this and maybe share the knowledge and the code as well)
Thank you all!
Try
using System.Drawing; //add this using statement
[OneTimeTearDown]
public void OneTimeTearDown()
{
if (TestContext.CurrentContext.Result.Outcome != ResultState.Failure)
{
var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
var filePath = "pathToTheFolder\\Screenshots\\";
screenshot.SaveAsFile(filePath + System.Drawing.Imaging.ImageFormat.Png);
}
}
This tutorial looks like what you need
Found a way how to reach out my goal - to save screenshots for failed tests in a bin folder of the project and attach them to the Pipeline "Test Result" section in further.
This is the final workable code. I have removed it from [OneTimeTearDown] and set it to [TearDown]. And also have added attachment with the last row, that allows to sent screenshots to Pipelines "Test result" block.
This is my Base.cs file where I am locating IWebDriver, all Setup and TearDown methods
using E2E_Tests.TestData;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium;
using WebDriverManager.DriverConfigs.Impl;
using OpenQA.Selenium.Chrome;
using System.Configuration;
using NUnit.Framework.Interfaces;
[TearDown]
public void TearDown()
{
TakeScreenshotDefaultImageFormat();
driver.Close();
}
public void TakeScreenshotDefaultImageFormat()
{
if (TestContext.CurrentContext.Result.Outcome == ResultState.Error)
{
var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
var screenshotDirectoryPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Screenshots\\");
if (!Directory.Exists(screenshotDirectoryPath))
{
Directory.CreateDirectory(screenshotDirectoryPath);
}
var currentDate = DateTime.Now;
var filePath = $"{screenshotDirectoryPath}{TestContext.CurrentContext.Test.Name}_{currentDate.ToString("yyyy.MM.dd-HH.mm.ss")}.png";
screenshot.SaveAsFile(filePath);
TestContext.AddTestAttachment(filePath);
}
}
i just started to develop applications for AutoCAD 2016. I want to load my dLLs into a separate AppDomain, so that i don't have to restart ACAD all the time.
After a lot of research and trying i ended up with a pipeline solution
using System.Addin and System.Addin.Contract.
I use only interfaces and standardclasses for the Views Contract and Adapters like in this example here.
This is my addin containing one methode to write Hello into Acad's Editor and a second methode for drawing a line.
using System.AddIn;
using CADAddinView;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
namespace CADAddIn
{
[AddIn("cadAddIn", Version = "1.0.0.0")]
public class CADAddIn : ICADAddinView
{
public void drawLine()
{
Document acDoc = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument;
Database acCurDb = acDoc.Database;
using (DocumentLock acLckDoc = acDoc.LockDocument())
{
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
DBObject blkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead);
BlockTable acBlkTbl = blkTbl as BlockTable;
BlockTableRecord acBlkTblRec = (BlockTableRecord)acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
Polyline acPoly = new Polyline();
acPoly.SetDatabaseDefaults();
acPoly.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0);
acPoly.AddVertexAt(0, new Point2d(100, 100), 0, 0, 0);
acBlkTblRec.AppendEntity(acPoly);
acTrans.AddNewlyCreatedDBObject(acPoly, true);
acTrans.Commit();
}
}
}
public void sayHello()
{
Editor ed = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage("Hello");
}
}
}
this is my HostApplication:
using System.AddIn.Hosting;
using System.Windows.Forms;
using CADHostView;
using System;
using System.Collections.ObjectModel;
using Autodesk.AutoCAD.Runtime;
namespace CADHost
{
public class CADHost
{
[CommandMethod("sayHello")]
public static void sayHello()
{
string addInPath = Environment.CurrentDirectory + "\\Pipeline";
string[] warnings = AddInStore.Update(addInPath);
foreach (string warning in warnings)
{
MessageBox.Show(warning);
}
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(ICADHostView), addInPath);
if (tokens.Count == 0)
{
MessageBox.Show("No AddIn found.");
}
else
{
AddInToken cadToken = tokens[0];
ICADHostView cadApp = cadToken.Activate<ICADHostView>(AddInSecurityLevel.Host);
cadApp.sayHello();
}
}
[CommandMethod("drawLine")]
public static void drawLine()
{
string addInPath = Environment.CurrentDirectory + "\\Pipeline";
string[] warnings = AddInStore.Update(addInPath);
foreach (string warning in warnings)
{
MessageBox.Show(warning);
}
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(ICADHostView), addInPath);
if (tokens.Count == 0)
{
MessageBox.Show("No AddIn found.");
}
else
{
AddInToken cadToken = tokens[0];
ICADHostView cadApp = cadToken.Activate<ICADHostView>(AddInSecurityLevel.Host);
cadApp.drawLine();
}
}
}
}
Both of the two applications reference to three standard-Dlls from Acad:
accoremgd.dll, acdbmgd.dll, acmgd.dll.
In both projects these dlls have the option local copy false.
If i start then i get an Exception, where the programm cannot find the file "accoremgd.dll" and Acad crashes.
So i tried to set the Option local copy true only for the Addin.
Now it works for the "sayHello"-Methode.
but i get an invalide cast exception when acBlkTbl is initialised.
Would be great if someone has the last steps for me to make this work.
Also great would be a working example must not be made with the Addinsystem
i only want to make this work for not restarting acad all the time^^
Thank you for your help
matthias
I don't believe a separate AppDomain will work, when you call AutoCAD object types it will go to the main AppDomain and get messed up...
As just want to edit your code and don't restart, you'll be better with Edit & Continue feature (available since VC2013 on AutoCAD 2015, I believe).
This is not supported. AutoCAD is a very old and complex program and most of the AutoCAD API objects cannot be used in remote fashion.
Please read:
http://through-the-interface.typepad.com/through_the_interface/2008/09/tired-of-not-be.html
http://forums.autodesk.com/t5/net/netload-is-there-a-net-unload-command/td-p/2404002
https://www.theswamp.org/index.php?topic=38675.0
In the #3, you can see that the AutoCAD development team confirmed that there are some global variables which will prevent working this way.
I gave up my tries to solve this problem. My current "best" solution is to load dlls at the start of AutoCAD. At least i don't have to netload every dll.
If someone has a better solution feel free to tell me^^ Thanks to all that answered. matthias
I want to track the files which are opened by the user, and select them by one particular extension. If the opened file has that extension, then I want to assign it's file path to a variable for further processing. Example applications are very cpu demanding. Is there an easy, efficient way to do that?
System wide monitoring of file-->open events (including network drives, thumb drives, etc) would require you to write a FS filter driver.
Since you have access to the machine, and you definitely need system wide access, you could simply write a simple app that will be associated with the Powerpoint extensions, perform the copy, then open Powerpoint using the filepath as a command line argument. It would look similar to the following:
using System;
using System.Windows;
using System.Diagnostics;
using System.IO;
namespace WpfApplication1
{
internal class MainWindow : Window
{
public MainWindow()
{ }
[STAThread()]
static void Main(string[] args)
{
if (args.Length == 0)
{
// [ show error or print usage ]
return;
}
if (!File.Exists(args[0]))
{
// [ show error or print usage ]
return;
}
// Perform the copy
FileInfo target = new FileInfo(args[0]);
string destinationFilename = string.Format("X:\\ExistingFolder\\{0}", target.Name);
File.Copy(target.FullName, destinationFilename);
// You may need to place the filename in quotes if it contains spaces
string targetPath = string.Format("\"{0}\"", target.FullName);
string powerpointPath = "[FullPathToPowerpointExecutable]";
Process powerpointInstance = Process.Start(powerpointPath, targetPath);
// This solution is using a wpf windows app to avoid
// the flash of the console window. However if you did
// wish to display an accumulated list then you may choose
// to uncomment the following block to display your UI.
/*
Application app = new Application();
app.MainWindow = new MainWindow();
app.MainWindow.ShowDialog();
app.Shutdown(0);
*/
Environment.Exit(0);
}
}
}
Hope this helps.
I'm trying to create a simple test app to take photos in Android, using Xamarin. When I get this app working (or so I hope), i'll use the code in a real app that I'm working on. I'm using the following recipe from Xamarin as my basis:
http://docs.xamarin.com/recipes/android/other_ux/camera_intent/take_a_picture_and_save_using_camera_app/
The major difference is that I need to store images locally, and not on the SD card. I'm able to successfully take a picture (with the Android simulator). I can see the file in the file structure using ADB and can successfully copy and open the file on my PC. However, I'm unsuccessfull in accessing the file in the app, probably due to user rights.
Please note that I was successfull in creating my own .txt files, and reading them back using either System.IO and Java.IO.
Please review the following code. My app crashes when using "System.IO.File.ReadAllText" and gives me "Access to the path "/data/data/CameraAppDemo.CameraAppDemo/files/photo.jpg" is denied.". And whatever I try (absolute, relative paths, uri's), objBitmap is always null.
ADB says that "photo.jpg" has -rwxrwx--- rights, and though I'm not entirely sure, I think that should be more than sufficient
On the other hand, maybe the intent still has a lock on "photo.jpg"? Or something else is going on...
And one final note, I'm using System.IO.File.ReadAllText just for testing purposes. I experimented with stream readers as well, but with the same result. Also, though I believe this step is unnecessary, I enabled "WriteExternalStore" in the Manifest
namespace CameraAppDemo
{
using System;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.OS;
using Android.Provider;
using Android.Widget;
using Java.IO;
using Environment = Android.OS.Environment;
using Uri = Android.Net.Uri;
[Activity(Label = "Camera App Demo", MainLauncher = true)]
public class MainActivity : Activity
{
private File _file;
private string _basePath;
private ImageView _imageView;
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
var objBitmap = BitmapFactory.DecodeFile(_file.AbsolutePath) ;
Console.WriteLine ("objBitmap = null : " + (objBitmap == null).ToString ());
var strOutput = System.IO.File.ReadAllText (FileManager.BasePath + "/photo.jpg");
Console.WriteLine (strOutput);
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
FileManager.SetupFolderStructure();
if (IsThereAnAppToTakePictures())
{
Button button = FindViewById<Button>(Resource.Id.myButton);
_imageView = FindViewById<ImageView>(Resource.Id.imageView1);
button.Click += TakeAPicture;
}
}
private bool IsThereAnAppToTakePictures()
{
Intent intent = new Intent(MediaStore.ActionImageCapture);
IList<ResolveInfo> availableActivities = PackageManager.QueryIntentActivities(intent, PackageInfoFlags.MatchDefaultOnly);
return availableActivities != null && availableActivities.Count > 0;
}
private void TakeAPicture(object sender, EventArgs eventArgs)
{
System.IO.Directory.Delete (FileManager.BasePath, true);
_basePath = FileManager.BasePath;
_file = new Java.IO.File (_basePath, "photo.jpg");
Intent intent = new Intent(MediaStore.ActionImageCapture);
intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(_file));
StartActivityForResult(intent, 0);
}
}
}
//Part of the FileManager class:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Android.Graphics;
namespace CameraAppDemo
{
public class FileManager
{
public static string BasePath {
get {
var libraryPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
if (Directory.Exists (libraryPath) == false) {
Directory.CreateDirectory (libraryPath);
}
return libraryPath;
}
}
}
}
==== Edit ====
It seems that I'm simply not able to read the file. As an ex-webdeveloper, I'm fairly new to programming for mobile, let alone the combo of C# and Java and I'm still learning a lot.
Anyway, I added the following lines:
Console.WriteLine("Setting file :" + _file.SetReadable (true));
Console.WriteLine("Can read :" + _file.CanRead());
Both cases return False. I can't read the file, and I am unable to give read access.
So, any ideas? Is this by design? Can I tell the Intent for taking images to give me read access, or is there another workaround?
If everything fails, I'm hoping to workaround the problem by saving to the SD card first and then copying the file to the local filesystem. But that's something I rather would not do; I can't guarantee that the end users have an SD card, and the pictures should not be deleted by accident.
I am using the following code under ASP.NET 4.0 framework to obtain the version of MSI file from a web app:
string strVersion = "";
try
{
Type InstallerType;
WindowsInstaller.Installer installer;
InstallerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
installer = (WindowsInstaller.Installer)Activator.CreateInstance(InstallerType);
WindowsInstaller.Database db = installer.OpenDatabase(strMSIFilePath, 0);
WindowsInstaller.View dv = db.OpenView("SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'");
WindowsInstaller.Record record = null;
dv.Execute(record);
record = dv.Fetch();
strVersion = record.get_StringData(1).ToString();
dv.Close();
//db.Commit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(dv);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(db);
}
catch
{
//Failed
strVersion = "";
}
It works fine except that when the code finishes running it holds an internal MSI file handle so when I try to move or rename the MSI file I get the error that the file is still in use. This continues until I actually navigate away from the ASPX page that calls the method above.
My question is, I obviously didn't close some handle or object in the code above. But what could that be?
PS. I'm testing it in a development IDE from VS2010.
EDIT: Edited the code like it should be after Adriano's suggestion. Thanks!
The COM object has not been released (it should be auto-released when it goes out of scope but in .NET this doesn't work really well). Because it does not implement the IDisposable interface you can't call its Dispose() method and you can't use it inside an using statement. You have to explicitly call Marshal.FinalReleaseComObject. For example:
try
{
// Your stuffs
}
finally
{
dv.Close();
Marshal.FinalReleaseComObject(dv);
Marshal.FinalReleaseComObject(db);
}
Moreover note that you do not really need a call to the Commit() method because you didn't make any change but just a query.
FWIW, you should be using Windows Installer XML (WiX) Deployment Tools Foundation (DTF). It's an FOSS project from Microsoft that can be found on CodePlex. It has MSI interop libraries with classes that are very similar to the COM classes but implement IDisosable and use P/Invoke instead of COM behind the scenes. There is even support for Linq to MSI if you want. And the full source code is available.
DTF is the gold standard for MSI interop in a .NET world. Here are two examples:
using System;
using System.Linq;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Linq;
namespace ConsoleApplication3
{
class Program
{
const string DATABASE_PATH = #"C:\FOO..MSI";
const string SQL_SELECT_PRODUCTVERSION = "SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'";
static void Main(string[] args)
{
using (Database database = new Database(DATABASE_PATH, DatabaseOpenMode.ReadOnly))
{
Console.WriteLine(database.ExecuteScalar(SQL_SELECT_PRODUCTVERSION).ToString());
}
using (QDatabase database = new QDatabase(DATABASE_PATH, DatabaseOpenMode.ReadOnly))
{
var results = from property in database.Properties where property.Property == "ProductVersion" select property.Value;
Console.WriteLine(results.AsEnumerable<string>().First());
}
}
}
}
try to Dispose the Objects.
dv.Dispose();
db.Dispose();