The app I'm working on needs to handle files with very long file/path names. It's a .Net 4.6 application so I've implemented the pre-4.6.2 workaround to allow the \\?\ syntax as outlined here and here.
This is the code I'm using to enable the feature (I can't modify the app.config so this has to be set in code):
var type = Type.GetType("System.AppContext");
if (type != null)
{
AppContext.SetSwitch("Switch.System.IO.UseLegacyPathHandling", false);
AppContext.SetSwitch("Switch.System.IO.BlockLongPaths", false);
var switchType = Type.GetType("System.AppContextSwitches");
if (switchType != null)
{
// We also have to reach into System.AppContextSwitches and manually update the cached private versions of these properties (don't ask me why):
var legacyField = switchType.GetField("_useLegacyPathHandling", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
legacyField?.SetValue(null, (Int32)(-1)); // <- caching uses 0 to indicate no value, -1 for false, 1 for true.
var blockingField = switchType.GetField("_blockLongPaths", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
blockingField?.SetValue(null, (Int32)(-1)); // <- caching uses 0 to indicate no value, -1 for false, 1 for true.
}
}
This works (yay!) on all the machines we've tested on, except one (boo!). The machine in question is a Windows 10 Pro installation, like the others, and has the same registry settings in the [Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem] namespace.
The error message on this particular machine is:
The given path format is not supported
The one difference we can see on that machine is that when looking at a very long file in Windows File Explorer, the 'Location' field uses the \\?\ syntax in the r-click > Properties menu.
I'm guessing that there's some registry key that is causing both that difference in File Explorer, and the failure of my fix, but somewhere other than the FileSystem namespace mentioned above.
Has anyone encountered a similar issue, or have an idea of other registry areas that might be relevant?
You can set those AppContext switches on a machine-wide basis via the registry if you don't want to set them in each App.config file individually:
These settings will affect all .NET apps that don't specify a different value in their App.config file. That is, the registry setting only changes the default value, which can still be overridden with app-specific values by specifying <AppContextSwitchOverrides value="..." />
EnableLongPath.reg :
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AppContext]
"Switch.System.IO.BlockLongPaths"="false"
"Switch.System.IO.UseLegacyPathHandling"="false"
C:\>regedit.exe EnableLongPath.reg
Related
I'm trying to use this registry hack I found online:
;Disables F1 key - Help and Support - in Windows 10
;Ramesh Srinivasan, Winhelponline.com
[HKEY_CURRENT_USER\SOFTWARE\Classes\Typelib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0\win32]
#=""
[HKEY_CURRENT_USER\SOFTWARE\Classes\Typelib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0\win64]
#=""
When I run it as a .reg command via Windows Explorer and watch the registry with regedit, it works as intended. Removing it is another registry file that simply removes the \0 subkey (and win32 and 64 with it). I'm trying to emulate this function with C# in a Winform using .net CORE:
private void CheckF1()
{
// Registry data from ;Ramesh Srinivasan, Winhelponline.com
RegistryKey F1key = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Classes\TypeLib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0");
// EGADS! It's active!
if (F1key == null)
{
fckF1RestoreBtn.Enabled = false;
fckF1KillBtn.Enabled = true;
fckF1Status.Text = "That creepy bugger is waiting and watching.";
}
else
{
fckF1RestoreBtn.Enabled = true;
fckF1KillBtn.Enabled = false;
fckF1Status.Text = "The F1-Help function had been put in it's place.";
}
}
private void fckF1KillBtn_Click(object sender, EventArgs e)
{
Registry.CurrentUser.CreateSubKey(#"SOFTWARE\Classes\Typelib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0\win32");
Registry.CurrentUser.CreateSubKey(#"SOFTWARE\Classes\Typelib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0\win64");
CheckF1();
}
private void fckF1RestoreBtn_Click(object sender, EventArgs e)
{
Registry.CurrentUser.DeleteSubKeyTree(#"SOFTWARE\Classes\Typelib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0");
CheckF1();
}
Weirdly the code "sees" a setting and responds like it should. Even to the point that whichever toggle position it was in on close it remembers when I load the file again. It's almost like it's playing along to screw with me. Regardless, when I watch the registry, none of my code has any actual effect though by all appearances it seems to work otherwise (it doesn't actually of course because the registry change isn't happening).
NOTE: I have already updated my manifest file for the project to include elevated permissions:
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Here's the breakpoint on the test statement to see if the subkeys are there that shows they are:
Meanwhile the registry location for the exact path shown in the debug doesn't have the 1.0\0 path at all:
I don't know how the code is reading phantom values. Someone closed my previous question pointing to another answer that didn't have any effect (Registry key deleted but still getting value from registry c#):
"Prefer 32 bit" was never checked for my project in the first place
Modifying my code as recommended had no effect
var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64)
.OpenSubKey(#"SOFTWARE\WOW6432Node\Classes\TypeLib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0");
Adding "Wow64Node" to the path makes no difference in execution.
EXPECTED BEHAVIOR
Checking to see if the HKEY_CURRENT_USER\SOFTWARE\Classes\Typelib{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0 path is present should return null if the \0 path is not present
Adding and removing the subkeys should show in the registry
So apparently HKEY_CURRENT_USERS is an alias. When the above code runs, it updates in HKEY_USERS under the specific logged in user. There's a question that talks about this behavior here: write registry to hkey_current_user instead of hkey_users
That said, the code appears to work, it's just that the registry doesn't update HKCU like when you run .reg commands. To verify it was working, I'd run the toggle that kills the keys then click them in Regedit and it would say they didn't exist. When I toggled back, I could click on them. So basically, it works (not sure if it required the "using" blocks as others suggested, but I see no reason to take them out).
Now my problem is that it points to the admin user and NOT the regular user because I'm running it and regedit as admin. It took forever to determine this based on running regedit as user in one case and admin in another. Bottom line, this won't work and I'll probably end up running .reg files in the command line instead.
I'm trying to update SDMPackageXML property of an AppModel application through C# code. SDMPackageXML is an XML property. I've to update only one node named AutoInstall in the
SDMPackageXML XML property. Here is my code:
ObjectGetOptions opt = new ObjectGetOptions(null, System.TimeSpan.MaxValue, true);
var path = new ManagementPath("SMS_Application.CI_ID=16777568");
ManagementObject obj = new ManagementObject(scope, path, opt);
obj.Get();
foreach (PropertyData property in obj.Properties)
{
if (property.Name == "SDMPackageXML")
{
//change the property value. Set AutoInstall to true
XmlDocument xml = new XmlDocument();
xml.LoadXml(property.Value.ToString());
var autoInstallTag = xml.GetElementsByTagName("AutoInstall");
autoInstallTag[0].InnerText = "false";
property.Value = xml.OuterXml;
}
}
obj.Put();
The problem is that obj.Put(); updates nothing on the SCCM server. Can someone help me please?
So similar to what I talked about in this answer the main problem here is that Microsoft uses a special method to serialize their XML. The deserialization still works with using the default classes but to serialize again there is no documentation as to how to (I'm pretty sure it is possible but I am not knowledgeable enough to do it)
Instead of documentation they provide wrapper classes for this which are shipped with the SCCM Console (Located in the bin directory of the Installation folder of the Console).
In this case this would be Microsoft.ConfigurationManagement.ApplicationManagement.dll. Unlike in powershell where the dependencies in the same path seem to be loaded as well you seem also to have to reference at least Microsoft.ConfigurationManagement.ApplicationManagement.TaskSequenceInstaller.dll as well.
There are also further dlls with names like Microsoft.ConfigurationManagement.ApplicationManagement.MsiInstaller.dll present however at least in my tests the two above were the only ones needed, but if you notice the deserialization failing with "InvalidPropertyException" errors you might need the dll matching your specific application type.
With those two dlls referenced you can write something like this (note I deserialized using the dll as well because why not if it is already loaded and it creates a nice application object to directly modify the properties. This is however technically not necessary. You could deserialize like in your example and only use the serialization part.
ManagementObject obj = new ManagementObject(#"\\<siteserver>\root\SMS\site_<sitecode>:SMS_Application.CI_ID=<id>");
Microsoft.ConfigurationManagement.ApplicationManagement.Application app = Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer.DeserializeFromString(obj["SDMPackageXML"].ToString(), true);
app.AutoInstall = true;
obj["SDMPackageXML"] = Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer.SerializeToString(app, true);
obj.Put();
Now one thing to keep in mind is that is can be a little tricky referencing the applications by their CI_ID because if you update the application the id for the currently valid version of the app changes (the old id still can be used to reference the older revision). So if you change the application gotten using the ID and then change it back with the same ID it will look like only the first change worked. I don't know if this is problematic for you (If you just get all IDs then change every application only once it should not matter) but if it does you can search for the application using their name plus isLatest = 'true' in the WQL query to always get the current one.
I'm registering extended verbs for all video file types on my system by doing something like this:
foreach (var ext in FileTypes.VideoTypes)
{
var progId = Registry.GetValue($#"HKEY_CLASSES_ROOT\.{ext}", null, null);
if (progId == null)
{
continue;
}
Registry.SetValue(
$#"HKEY_CURRENT_USER\SOFTWARE\Classes\{progId}\shell\dlsub",
null,
"Download subtitle");
Registry.SetValue(
$#"HKEY_CURRENT_USER\SOFTWARE\Classes\{progId}\shell\dlsub\command",
null,
#"""D:\myapp.exe"" ""%1""");
}
Resulting in something like this (mpeg_auto_file for mkv):
[HKEY_CLASSES_ROOT\mpg_auto_file\shell\dlsub]
#="Download subtitle"
[HKEY_CLASSES_ROOT\mpg_auto_file\shell\dlsub\command]
#="\"D:\\myapp.exe\" \"%1\""
and mplayerc.mp4 for mp4:
[HKEY_CLASSES_ROOT\mplayerc.mp4\shell\dlsub]
#="Download subtitle"
[HKEY_CLASSES_ROOT\mplayerc.mp4\shell\dlsub\command]
#="\"D:\\myapp.exe\" \"%1\""
The problem is that the registered verb shows up for mkv (and a couple of other filetypes), but the context menus for the other file types (like mp4) are unaffected. Adding my verb to HKEY_CLASSES_ROOT\*\shell does work for these filetypes, but this is obviously not what I want!
Any ideas on the difference between these filetypes? Perhaps it has something to do with the registered ProgID (all mplayerc types do not seem to work...).
The default value (aka ProgId) that can be found for specific file extension doesn't always point to correct class (the one that will affect menu entries). During my tests, even on fresh copy of Windows 10 - WMP11.AssocFile.AVI is a ProgId for .avi file extension, however when added entry in WMP11.AssocFile.AVI\Shell\ (either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER) it didn't affect menu at all. I was about to give up but then i found that HKEY_CLASSES_ROOT\.avi\OpenWithProgids store few more values, including WMP11.AssocFile.AVI but also other that starts with word App, i.e. AppX6eg8h5sxqq90pv53845wmnbewywdqq5h. By editing Shell\ for that one, i was able to add menu entry. The downside was - it also affected other file extensions that are somehow linked with AppX6eg8h5sxqq90pv53845wmnbewywdqq5h. I couldn't accept solution that would force me to iterate through all classes found in OpenWithProgids, plus my menu entry had to be shown only for very specific file extensions and not the other. I decided to go with HKEY_CLASSES_ROOT\*\shellex and DLLs that create menu entry dynamically, based on clicked file type (check for extension).
On Windows, I have a C# assembly that is COM visible. It references other assemblies to control an application in the machine. It works fine.
However, under Apache Web Server and using CGI, it doesn't work. After doing some debuging, I found out that the problem is that, while running under Apache's CGI, the environment variables SYSTEMROOT and SYSTEMDRIVE, which aparently are needed by the referenced assemblies, are not loaded.
I can configure Apache to pass those environemtn variables too, but before doing so, I'd really like to know if there's some command I can put on my C# COM visible assembly to make it load environment variables as if it was, let's say, the SYSTEM user or something like that, so it doesn't have to relay on the environment passed by the starting application.
How do you force loading an existent system environment variable in C#, when IT IS NOT SET in the current process (or it was process-deleted by the launching process)?
Thanks in advance for any suggestions!
EDIT 1 - ADDED INFO: Just to make it more clear (as I see in the current answers it's not so clear): Apache intendedly deletes a lot of environment variables for CGI processes. It's not that Apache cannot see them, it can, but it won't pass them to CGI processes.
This should do the trick:
Environment.GetEnvironmentVariable("variable", EnvironmentVariableTarget.Machine);
I did a small test and it is working:
//has the value
string a = Environment.GetEnvironmentVariable("TMP");
Environment.SetEnvironmentVariable("TMP", null);
//does not have has the value
a = Environment.GetEnvironmentVariable("TMP");
//has the value
a = Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Machine);
SOLUTION: Marco's answer was great and technically answered my question - except that I found out that the environment variables SYSTEMROOT and SYSTEMDRIVE are not really set in the registry where all environment variables are set, so, the chosen answer works for all variables except those two, which I specified in the OP.
SYSTEMROOT is defined on the registry in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot, and apparently (after more research), SYSTEMDRIVE is generated as a substring of SYSTEMDRIVE.
So, to get SYSTEMDRIVE and SYSTEMROOT from registry and load them into the environment:
using Microsoft.Win32;
namespace MySpace
{
public class Setup
{
public Setup()
{
SetUpEnvironment();
}
private void SetUpEnvironment()
{
string test_a = Environment.GetEnvironmentVariable("SYSTEMDRIVE", EnvironmentVariableTarget.Process);
string test_b = Environment.GetEnvironmentVariable("SYSTEMROOT", EnvironmentVariableTarget.Process);
if (test_a == null || test_a.Length == 0 || test_b == null || test_b.Length == 0)
{
string RegistryPath = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
string SYSTEMROOT = (string) Registry.GetValue(RegistryPath, "SystemRoot", null);
if (SYSTEMROOT == null)
{
throw new System.ApplicationException("Cannot access registry key " + RegistryPath);
}
string SYSTEMDRIVE = SYSTEMROOT.Substring(0, SYSTEMROOT.IndexOf(':') + 1);
Environment.SetEnvironmentVariable("SYSTEMROOT", SYSTEMROOT, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("SYSTEMDRIVE", SYSTEMDRIVE, EnvironmentVariableTarget.Process);
}
}
}
}
Then you can just call Setup setup = new Setup(); from other classes. And that's it. :-)
Environment.GetEnvironmentVariable
see reference here.
e.g.
Environment.CurrentDirectory = Environment.GetEnvironmentVariable("windir");
DirectoryInfo info = new DirectoryInfo(".");
lock(info)
{
Console.WriteLine("Directory Info: "+info.FullName);
}
Are the variables set as system wide?
If they are not, that is what you need to do, otherwise create user variables for the user the COM is running under.
Thank you. I cannot state with any certainty that this has once and for all driven a stake through the heart of the vampire, but amazingly enough, the error has disappeared (for now). The odd thing is that access to the statement
Environment.GetEnvironmentVariable("variable", EnvironmentVariableTarget.Machine);
is a real oddity in the debugger. It does not show up in Intellisense and does not even appear to fire, which leads me to suspect, which you all knew already, that this is some sort of magic runtime object Environment that has no instantiation in the debugger but also can be benignly jumped over. Oh well.
Oh and I should mention that after you see that error, you will note oddities in your Windows OS, which is worrisome. In particular, you will see, if you try to use the Control Panel /System/Advanced Properties (whatever) that it cannot load the dialog for the environment variables any more, indicating that %windir% has been seriously hosed (compromised) across all applications. Bad bad bad....
Im developing a custom browser solution with .net's Webbrowser control.
To disable the IE-Compatibilty View, I set the registry entry
Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION:
[Sreenshot regedit] http://zbirk.mirk.at/browserreg.png "Screenshot"
i tried to use the values: dword=8000,dword=8888,dword=9000, but the webbrowser control seems to ignore these reg entries.
Maybe someone had this problems too and may help me.
The WebBrowser control definately DOES respect these keys.
Remember that while taskman may show application.exe in the name column, if you are debugging the exe name is application.vshost.exe
So in my application sI just attempt to create the key every time the app runs. If it fails to create it (because it already exists) then I continue running, if it creates the key then I inform the user that they need to restart the application.
ensure that you are not running within vshost
the app name would be different ie appname.vshost.exe
Thx for your reply, now its working.
Her is my working peace of code:
public void setIEcomp()
{
String appname = Process.GetCurrentProcess().ProcessName+".exe";
RegistryKey RK8 = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION",RegistryKeyPermissionCheck.ReadWriteSubTree);
int value9 = 9999;
int value8 = 8888;
Version ver = webBrowser1.Version;
int value = value9;
try
{
string[] parts = ver.ToString().Split('.');
int vn = 0;
int.TryParse(parts[0], out vn);
if (vn != 0)
{
if (vn == 9)
value = value9;
else
value = value8;
}
}
catch
{
value = value9;
}
//Setting the key in LocalMachine
if (RK8 != null)
{
try
{
RK8.SetValue(appname, value, RegistryValueKind.DWord);
RK8.Close();
}
catch(Exception ex)
{
//MessageBox.Show(ex.Message);
}
}
}
I too could not see that FEATURE_BROWSER_EMULATION made any difference in my application.
I was testing the FEATURE_BROWSER_EMULATION functionality by manually editing the registry with regedit. Nothing I did made any difference. My hosted page was still failing on any new-ish JavaScript and could not load external libraries.
I found my mistake:
I was editing the 64-bit view of the registry with regedit. My app was running as a 32-bit app and looking at the 32-bit view of the registry. That's why my changes to the registry seemed to have no impact on my application. By the way, the WPF project template defaults to "Prefer 32-bit."
Manually editing with regedit within the Wow6432Node key worked:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULATION
Of course, setting the DWORD value programmatically within your application will also work, since your 32-bit application will edit within the Wow6432Node.
An older post and solution is no longer accurate.
Running procmon and watching for FEATURE_BROWSER_EMULATION shows the following registry variables actually checked. This was for WINWORD.exe but other than that - take your pick...
HKU\S-1-5-21-[my-sid-paws-off]\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION\WINWORD.EXE
HKU\S-1-5-21-[my-sid-paws-off]\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION*
HKLM\SOFTWARE\Microsoft\Office\ClickToRun\REGISTRY\MACHINE\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION(Default)
HKLM\SOFTWARE\Microsoft\Office\ClickToRun\REGISTRY\MACHINE\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION\WINWORD.EXE
HKLM\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION\WINWORD.EXE
HKLM\SOFTWARE\Microsoft\Office\ClickToRun\REGISTRY\MACHINE\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION*
HKLM\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION*