I want to open an OBJ file in Unity using C# during runtime when I press L. However, the file dialog always opens twice. After I selected the first file, I get a new dialog. Whatever file I select the first and second time, both files are opened and displayed.
I have:
void Update ()
{
if (Input.GetKeyDown(KeyCode.L)) {
LoadObj();
//StartCoroutine(ExecuteAfterTime(5));
}
}
IEnumerator ExecuteAfterTime(float time)
{
yield return new WaitForSeconds(time);
}
void LoadObj()
{
string path = EditorUtility.OpenFilePanel("Load scan file", "", "obj");
\\Open the file and display it
}
I tried:
Add a time (see commented code above) -- did not work
Add a boolean that allows to execute LoadObj() only when the previous file is opened -- did not work
Added EditorUtility.OpenFilePanel() before or after yield return new WaitForSeconds(time) -- in the latter case, there is the delay of 5 seconds, but it still opens twice in both cases.
I searched for other solutions, but did not find anything else.
I work on a Windows machine and use Unity 2018. The aim is to open a single file at a time, but it must be possible to open another file, say, after a few minutes.
Any idea how to prevent the dialog from opening twice?
As #Eshan Mohammadi suggested, I checked if I only had a single instance of the code. I deleted the entire file, created a new one and added it to my GameObject. However, only then I noticed that this particular GameObject is copied, including the code, of course.
So, I chaned the way the GameObject was copied and problem solved.
I want to set a cache limit for my C# program so I decided to use the Project Properties>Settings function of Visual Studio [2015] to do so.
I had some help and was told to enter this.
My settings I want are as follows:
Folder Path- C:\SysApp
Size Limit- 150MB
Amount to Delete- 149MB
For the sizeLimit and toDelete sections I need to know what unit (ie. bytes, megabytes, kilobytes...) they're in so I can convert them to what I listed above.
I was also told that
If you change the settings value in the program you need to save the new values before exiting the application. This is done with Properties.Settings.Default.Save();. This command creates a .config file with your values.
I need to know where in my coding to insert the Properties.Settings.Default.Save(); command.
Screenshots would be very helpful. Thanks.
The is no possibility to store metadata like units into default settings. You have to define the unit (kB, MB,...) the user should enter or store it as a string (e.g. 150MB) and parse it yourself.
The Save method must be called after setting the values (example):
Properties.Settings.Default.sizeLimit = 150000
Properties.Settings.Default.Save();
If you only want to read the settings (see comments below) change the scope of the settings from "User" to "Application" and read the settings in your program like this:
class Program {
void main(string args[]) {
String folderPath = Properties.Setings.Default.folder;
int folderSizeLimit = Properties.Setings.Default.sizeLimit;
int amountToDelete = Properties.Setings.Default.toDelete;
DeleteOldFilesIfOverFolderLimit(folderPath, folderSizeLimit, amountToDelete);
}
private private void DeleteOldFilesIfOverFolderLimit(string folderPath,
long folderSizeLimit,
long amountToDelete)
...... from other post .....
}
}
SCENARIO
I manage and organize many files during the day, the SendTo is the most used feature that I use on Windows.
PROBLEM
By default, when the user clicks an item/link of the contextmenu to send the files, the O.S does not show any kind of advise/notifier indicating that the files are copying to the selected destination.
I consider it a very wrong design issue because for big files its OK ...a progressbar will be shown, but if the files are to small it will not show any progressbar/visual indicator so is not possible to ensure that the files are copied (without manual effort) because I'm human and I could click outside the SendTo contextmenu by error.
So, I would like to develop a personal mini-tool that will help me to optimize my time showing me a notifier window wherever on the screen when I send/copy files using the SendTo feature from the contextmenu, and only the SendTo feature.
QUESTION
In just simple words, I want to detect a copy/send operation from SendTo menu to ensure that the click was done properly on the menu item (and not outside the menu), by also providing additional basic info such as the source folder, the destination folder, and the amount of files or the filepaths.
Any ideas to start developing this tool in the right direction?.
I will be grateful for a code example in C# or else VB.Net, preferably this last.
APPROACH
Since I don't know how to start doing this I mean which could be the easiest or the efficient way to intercept those SendTo calls, firstly I thought to hook the CopyFile or CopyFileEx API functions, but they do not provide the information that I need because that function will be called in any kind of copy operation and not only when I use the SendTo Feature, so I'm lost.
I'm not sure if I should investigate more about internal calls, or maybe investigate more about the windows contextmenu itself instead of messing with function hooks and ugly things that I could avoid.
My main idea is to develop a hidden WinForms (or else a windows service) that stays in background waiting for when I use the SendTo feature (when I click on an item of the SendTo menu) and then show any kind of visual indicator on the screen to ensure that I properly clicked that menu-item and maybe inform about the amount of files that I'm moving and where I'm moving them.
RESEARCH
Here is a code example that I think it demostrates how to instantiate the SendTo com object to create your own?, but its written in c++ and I'm not sure if the example is helpful because my intention is not to replace the SendTo menu but I'll keep this useful info here it it serves for something else:
How to add(enable) standard "Send To" context menu option in a namespace extension
The KNOWNFOLDERID constants docs gives some useful info about the SendTo folder, again I'm not sure if this could help maybe for a read/access monitoring approach?, I just keep the info here:
GUID: {8983036C-27C0-404B-8F08-102D10DCFD74}
Default Path: %APPDATA%\Microsoft\Windows\SendTo
Legacy Default Path: %USERPROFILE%\SendTo
In the Shell Extension Handlers docs there is a Copy hook handler which I don't know if it has relation with the SendTo's COM component and if that could help me in some way,
the same ignorance for IContextMenu::InvokeCommand method reference which maybe I could intercept it to identify a SendTo invocation?
By the moment I feel like flying blind.
I recently found this A managed "Send To" menu class but again its a example written in C/C++ (I think is the same source before) which I don't understand at all and again I'm not sure if that could help me because I repeat that replacing SendTo is not what I have in mind (just because I don't know how to properly do it avoiding all possible risks, I prefer to still let Windows logic copy/send the files, I just want to detect the copy operation to retrieve info)
EXPECTED RESULTS AND USAGE
Step 1:
Select a random file and use the SendTo menu (in my language, Spanish, the command name is 'Enviar a')
Step 2:
Let the .net application's logic (working in background) intercept the SendTo operation to retrieve info.
(I only need help with this step)
Step 3:
Display the info somewhere over the screen to ensure that the SendTo operation was performed, to ensure that I properly clicked the SendTo item (My Link).
(That popup is just a simulation, I don't know any way to retrieve all that info)
It's really simple to do once you understand what SendTo really does, and it doesn't involves COM or shell extensions at all. Basically, the send to menu is populated with the content of the SendTo folder of the user profile (C:\Users\\AppData\Roaming\Microsoft\Windows\SendTo by default in Windows 6.x).
When clicked, if the option is a shortcut to a folder it will copy the files there, but if there is a shortcut to a program (or a program executable itself) it will run that program, passing the paths of the selected files as command-line arguments.
From there, it's really trivial to make some program that simply takes paths as arguments, present some kind of notification and then copies the files or do whatever you want with them.
A quick and dirty example could be as follow (in C#, but could be done with anything else really):
private static void Main(string[] args)
{
if(MessageBox.Show("Are you sure you want to copy files?", "Copy files", MessageBoxButtons.YesNo) == DialogResult.No) return;
foreach (string file in args)
File.Copy(file, Path.Combine("c:\\temp", Path.GetFileName(file));
}
This just ask for confirmation for copying a bunch of files. Note that this really doesn't "intercepts" the send to menu, but rather handles it completely, so it's the program responsability to do any meaningful action. A more serious implementation could use the built-in Windows copy dialog and display some screen with progress or anything else, that's up to your needs.
It could also take some more parameters on the command line. When you place a shortcut in the SendTo folder, the destination could add some more parameters that will be passed as the first ones (before the file names). For example the destination of the shortcut can read c:\program files\copyfiles.exe c:\temp to pass the destination folder instead of hardcoding. The called program must then interpret the first parameter as the destination path and subsequent ones as the source files.
I've had to do something like this before. You don't even have to intercept the SendTo() function, you only need to make sure the the file has arrived. How about FileSystemWatcher if it's on the same computer?
You could use a watcher to watch before you send it, then, if the file successfully arrives at it's destination, you can display a successful message, and then kill the watcher.
Code Example
// Create a FileSystemWatcher property.
FileSystemWatcher fsw { get; set; }
// So we can set the FileToWatch within WatchFilesBeforeTransfer().
private string FileToWatch { get; set; }
private void WatchFilesBeforeTransfer(string FileName, string DestinationFolder)
{
fsw = new FileSystemWatcher();
fsw.Path = DestinationFolder;
FileToWatch = FileName;
// Only if you need support for multiple directories. Code example note included.
fsw.InclueSubdirectories = true;
// We'll be searching for the file name and directory.
fsw.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName
// If it's simply moving the file to another location on the computer.
fsw.Renamed += new RenamedEventHandler(FileRenamed);
// If it was copied, not moved or renamed.
fsw.Created += new FileSystemEventHandler(FileCreated);
fsw.EnableRaisingEvents = true;
}
// If the file is just renamed. (Move/Rename)
private void FileRenamed(Object source, RenamedEventArgs e)
{
// Do something.
// Note that the full filename is accessed by e.FullPath.
if (e.Name == FileToWatch)
{
DisplaySuccessfulMessage(e.Name);
KillFileWatcher();
}
}
// If creating a new file. (Copied)
private void FileCreated(Object source, FileSystemEventArgs e)
{
// Do something.
// Note that the full filename is accessed by e.FullPath.
if (e.Name == FileToWatch)
{
DisplaySuccessfulMessage(e.Name);
KillFileWatcher();
}
}
private void KillFileWatcher()
{
fsw.Dispose();
}
You can access the desired property information (like in your popup gif) in this way:
Folder name: Path.GetDirectory(e.FullPath); (like "C:\yo\")
Full file name: e.FullPath (like "C:\yo\hey.exe")
File name: e.Name (like "hey.exe")
Non-code execution process:
Before you initiate SendTo(), create an instance of the FileSystemWatcher property, and have it watch for a specific Folder/File name combination, which should show up in the watched folder: WatchFilesBeforeTransfer(FileName, DestinationFolder).
Initiate SendTo().
File received? DisplaySuccessfulSendToMessage(), KillFileWatcher();
???
Profit.
UPDATE:
I just realized that's just for one file. If you want to check for multiple files, you could either create multiple FileWatcher instances (not recommended), or use a List<string> object, like this:
private void SendTo(List<string> FileCollection)
{
// Clear your previous FileList.
FileList.Clear();
foreach (string file in FileCollection)
{
FileList.Add(file);
}
// Rest of the code.
}
List<string> FileList { get; set; }
private void WatchFilesBeforeTransfer(string DestinationFolder)
{
// Same code as before, but delete FileToWatch.
}
private void FileRenamed(Object source, RenamedEventArgs e)
{
foreach (string file in FileList)
{
if (e.Name == file)
{
// Do stuff.
}
}
}
private void FileCreated(Object source, FileSystemEventArgs e)
{
foreach (string file in FileList)
{
if (e.Name == file)
{
// Do stuff.
}
}
}
Hope this helps!
I'm afraid this ain't that easy.
I was playing around with the FileSystemWatcher on this, but with only partly success.
Something that should definitely work are File system drivers but this looks like just too, well, look at it...
In the end it might be the easiest way to write your own shell extension to access the SendTo folder, and use this instead of the SentTo command which would give you full control.
These might be some starters:
Windows shell extensions
Shell Context Menus
Maybe I come a little bit late to answer my own question, which was published on year 2015, but it wasn't until few days ago that I became interested in this matter again, with much more experience and knowledge gained in .NET after these years to start from scratch and try to understand everything that I did not understood in the past.
I just discovered that, as #Ňɏssa Pøngjǣrdenlarp already commented in the comments box, apparently the most viable way to accomplish this would be to implement my own SendTo context menu and use IFileOperationProgressSink interface to report progress, which for this firstly I need to depend on the usage of IFileOperation interface.
The reason to use the IFileOperation interface is because it seems the only way offered in the Windows API to let the developer perform multiple file operations (copy, move, rename, create or delete) all at once within the same progress dialog UI. This is probably the interface used by the system when the user selects multiple files or directories (via the SendTo menu or just CTRL+C and CTRL+V) to move or copy them at once, because it only shows one progress dialog with all the copy operations queued in it...
So clearly IFileOperation and IFileOperationProgressSink interfaces were what I need. But from what I know there is no managed implementation of these interfaces in the .NET framework namespaces (neither in the Microsoft's WindowsAPICodePack library), which seems somewhat inexcusable to me considering that these interfaces exists since the era of Windows VISTA or even earlier, and it is an indisputable improvement being the totally opposite of any built-in members that you can think of to perform copy, move, rename, create or delete operations, like for example System.IO.File.Copy or Microsoft.VisualBasic.FileIO.FileSystem.CopyFile method. All of them only supports a single operation and only on a single progress dialog at once.
So I focused to investigate on that suggested solution, and I ended up finding a good implementation of IFileOperation and IFileOperationProgressSink interfaces in this repository:
https://github.com/misterhaan/au.Shared/tree/master/IO/Files.FileOperation
And this one which is the original implementation that I found on a old Microsoft's article which also comes with a good PDF reading to learn some things:
https://github.com/mlaily/MSDNMagazine2007-.NET-Matters-IFileOperation-in-Windows-Vista
https://learn.microsoft.com/en-us/archive/msdn-magazine/2007/december/net-matters-ifileoperation-in-windows-vista
Actually, for what I've discussed in this thread to do since year 2015, delving into the use of the IFileOperationProgressSink interface to report progress would not be necessary (only if I really want a deep progress information), since it is enough to determine that the copy was started / the call to IFileOperation.PerformOperations function was performed, and if any problems occur during the copy then it will appear in the progress dialog UI.
But of course what is still necessary is to develop a shell-extension of a custom SendTo menu to replace the one built into Windows. Shell-extensions could be developed with SharpShell library.
I develop in VB.NET language. I managed to extend and document their IFileOperation implementation and the wrapper, and updated the enums adding the newer, missing values added for Windows 7 and Windows 8.
If it can be helpful for someone, I'm going to attach the IFIleOperation and the wrapper in Vb.NET all in a new answer (because it exceeds the maximum character limit allowed for a post).
Note that in my implementation of IFileOperation interface and the wrapper class with name FileSystemOperation you will find some return types and missing classes or methods (like HRESULT return type or NativeMethods class), I just can't share all in one place, but these missing things are things that any experienced programmer will know how to resolve (eg. change HRESULT to UInteger type, and go to Pinvoke.net to find any missing method from my NativeMethods class).
UPDATE
It seems that StackOverflow doesn't allow me to add another answer, so I'll upload my implementation on PasteBin instead:
Class FileSystemOperation
https://pastebin.com/nvgLWEXu
Interface IFileOperation
https://pastebin.com/GzammHtu
Interface IFileOperationProgressSink
https://pastebin.com/jf9JjzyH
Class ComObjectDisposer(Of T As Class)
https://pastebin.com/7mPeawWr
Enum TransferSourceFlags
https://pastebin.com/V7wSSEvv
Enum FileOperationFlags
https://pastebin.com/A223w9XY
That's all.
I've managed to create my own file extension following this tutorial: http://www.codeproject.com/Articles/17023/System-File-Association
So far, it works perfectly. I've got only one thing that I can't solve.
When I double-click on a file with that extension, my program opens up. Now, I'd want to perform an action in my program. I made my way through some threads here and read that the file path is automatically passed to the startup arguments.
The problem is that no single argument is passed, also Process.GetCurrentProcess().StartInfo.FileName returned an empty string. I think this is consecutively because I don't pass any arguments when double-clicking my file.
This is my code:
var fai = new FileAssociationInfo(".extension");
if (!fai.Exists)
{
try
{
fai.Create("My Extension Program");
var pai = new ProgramAssociationInfo(fai.ProgId);
if (!pai.Exists)
{
pai.Create("My Program File",
new ProgramVerb("Open", Application.ExecutablePath);
pai.DefaultIcon = new ProgramIcon(Application.ExecutablePath);
}
}
}
As you can see I only pass the application's path to open it up. But how can I pass the file path as argument now? I've seen that e.g. the author of the article passes "%1" as argument, I tried that, too, but nothing changed.
Thanks in advance.
ProcessStartInfo.FileName usually gives you the path to your program executable itself, not the file which was clicked in Windows Explorer, so this seems the wrong thing to check in your case.
If you want to get the arguments using the current Process, then Process.GetCurrentProcess().StartInfo.Arguments should give you a string containing all the arguments passed to the program. If there are multiple arguments, you would need to parse these into separate values yourself.
But the standard, simpler way to get the arguments is to make sure the Main() method of your program has signature static void Main(string[] args){}. args is already processed into separate values for you, so it is easier to handle it here, even if you only pass it off to another class or store them in a static variable.
The %1 should ensure the clicked file is passed as the first argument (args[0]) to your program.
Well, I got it. What I had to do was creating a subkey in ClassesRoot: "ProgramName\shell\open\command". Then set a value containing the application's path and attach "%1" to it and you're done.
Hi I have a requirement in which I have to open a drawing file stored in C:\Temp Folder.
I tried the following code
public void launchacad(string pth) //pth is the path to the .DWG file
{
const string progID = "AutoCAD.Application.19.1";
const string exePath = #"C:\Program Files\Autodesk\AutoCAD 2014\acad.exe";
AcadApplication acApp = null;
try
{
acApp =
(AcadApplication)Marshal.GetActiveObject(progID);
}
catch { }
if (acApp != null)
{
MessageBox.Show("An instance of AutoCAD is already running.");
}
else
{
try
{
ProcessStartInfo psi =new ProcessStartInfo(exePath);
psi.WorkingDirectory = #"C:\Temp";
psi.Arguments = pth;
Process pr = Process.Start(psi);
pr.WaitForInputIdle();
while (acApp == null)
{
try
{
acApp =(AcadApplication)Marshal.GetActiveObject(progID);
}
catch
{
Application.DoEvents();
}
}
}
catch (Exception ex)
{
MessageBox.Show(
"Cannot create or attach to AutoCAD object: "
+ ex.Message
);
}
}
if (acApp != null)
{
acApp.Visible = true;
acApp.ActiveDocument.SendCommand("_MYCOMMAND ");
}
}
But as soon as Autocad starts it popups an error message saying Cannot find the specified drawing. When I use CMD.exe and type
"C:\Program Files\Autodesk\AutoCAD 2014\acad.exe" "C:\Temp\41 Stabd.dwg" It opens Autocad with the file(41 Stand.dwg) open.
I can't understand where I am making an error. Can someone help me out.
If still the drawing persists with problems, continue on to the next set of steps.
These can be done in any order, but have been listed in the order that Autodesk recommends makes the most sense. The file can be checked after each step. If things appear back to normal, there is no need to continue on to the rest of the steps.
Open a blank DWG and type RECOVER at the command line. Browse to the problematic file to - allow AutoCAD a chance to restore the file.
Type OVERKILL at the command line, and select all objects. Check or uncheck properties to include or ignore, then click OK.
Type DELCON at the command line, and select all objects.
Type BREP and select all objects (if there are solids or surfaces in the file)
Type -SCALELISTEDIT, then "R" for reset, then "Y" for yes.
Type FILTERS, then click on the 'delete filters' button.
The DGNPURGE tool can be run if the file size is unexpectedly very large: http://knowledge.autodesk.com/article/AutoCAD-DWG-files-unexpectedly-increase-in-file-size
Try using a different version of AutoCAD to open the drawing, such as AutoCAD 2013 vs. AutoCAD 2015 or plain AutoCAD vs. AutoCAD Architecture, etc. Try different computers if available.
Open a blank DWG, and try to attach the problematic file as an XREF. If it allows you to attach the file, try next to BIND it to the current file. If that works, run the repair steps listed above.
Use the SAVEAS command to save the DWG in an older file format. Attempt to open the newly created file.
Export the file to DXF format using the DXFOUT command. Next, open a blan DWG and use the DXFIN command to import the file just created.
< Restore the Layout tabs:
Right-Click one of the default layout tabs
Select 'From Template...'
Open the original file
Choose the layout tabs to restore. (It is recommended to do this one tab at a time, in case one or more layout tabs are corrupted)
Move drawing objects between model and paper space. You may find that only one drawing space is usable in your file, although your main concern is model space:
1. Create a new layout and if need be, create a viewport.
Use CHSPACE to move all the geometry to paper space.
Create a new drawing and use the Design Center (ADC) to move the layout from the damaged file into it.
Use CHSPACE again to move the geometry back to model space.
Restore the original layouts from the bad file using the Design Center.
Dissect the drawing. In a copy of the file, conduct a process of elimination using QSELECT to select different object types and then delete them to see if that fixes what is wrong in the file. Do PURGE All after each deletion. Eventually you should remove the problem elements and then you can choose to leave them out, copy them in again from another file, recreate them, or further troubleshoot individual items to pinpoint exactly which one is problematic. A quick start to this whole process is to delete everything in the drawing and then test it. This will quickly tell you if the issue is with a drawing object or if it is a part of the drawing database.