ComRegisterFunction not worked - windows

i'll try to implement IShellIconOverlayIdentifier interface for setting icon overlays.
But code in ComRegisterFunction not worked:
[ComRegisterFunction]
public static void Register(Type t)
{
RegistryKey rk = Registry.LocalMachine.CreateSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\"
+ t.Name + #"\");
rk.SetValue(string.Empty, t.GUID.ToString("B").ToUpper());
rk.Close();
ShellInterop.SHChangeNotify(0x08000000, 0, IntPtr.Zero, IntPtr.Zero);
}
After registration threre is no my key in Registry. If i call the same code from othere application it’s work correct. But with Regasm not worked.

Related

How can I use EnvDTE as a part of a VS2015 project/solution in order to change its own settings without using VS extensions?

Update: I've developed my own PowerShell solution (in the answers), but I'd still like to know if anyone knows of a NuGet package that mimics the behavior of the abandoned StudioShell project.
I'm trying to use EnvDTE to make some problematic settings persistent (no matter what future devs do) in my Visual Studio 2015 solutions/projects using only pre/post-build scripts, NuGet packages, or some built-in feature of VS. (I'll refer to these settings/configuration changes as "tweaks" from here on)
The end goals are:
No external tools/extensions required - Whatever I use to make these changes should be a standard part of Visual Studio 2015 and/or should be portable with the solution, or it should be reasonable to expect it to be part of recent Windows operating systems (Windows 7+).
In other words, future developers who inherit my work should be able to pull the solution from source control and build it without needing to install VS extension "X", make change "Y" in their build environment, or change "Z" in their user specific project settings.
Discoverable - The component/tool/method used to make the tweaks should be exposed somewhere in the Visual Studio interface without digging further than the solution tree (including context menus, project properties, etc.) - so the automated tweaks can be altered or disabled without too much digging.
I don't mind the GUI element just being a link to some code. I expect the future devs can figure out the code, but they need to be able to easily discover that this code is responsible for maintaining the EnvDTE tweaks.
For example, custom MSBuild tasks could potentially be considered a "built-in" solution, but those custom tasks are completely hidden to the average developer in my office. (Pre/Post build events are borderline hidden too, but at least they are in the GUI where devs can run across those by accident on occasion)
So, does anyone know of a NuGet package (StudioShell may have been perfect, but it looks like that project has been abandoned and there's no support for VS2015) or some other solution that would give me access to a DTE object bound to the current instance of VS from within a pre/post-build process? Or better yet, a method of using EnvDTE to tweak things when the solution/project is loaded?
Specific tweaks
The specific settings I'm trying to tweak from EnvDTE are not really relevant in my opinion, but since I know someone will ask, here are the tweaks I'm currently trying to get in place during solution/project load or at least before the build/debug process starts.
Set the startup project - I'm surprised this bug still plagues me, but despite having already set the startup project in the solution, inevitably some other dev pulls down my solution and ends up with the wrong project set as the startup project which messes with the dependencies/build order and breaks the build.
Configure user/environment specific project settings (ie. settings stored in <project>.user files) that are known to be generic enough for all build environments to use.
More specifically, I want to set the Project Properties -> Web -> Start Action configuration items in a web project. For normal web projects I understand why these settings are user specific because who knows what browsers the dev might have. But, I have a web hosted WCF service with test scripts nested under the project directory - so project debugging should always be able to be done running those scripts using relative paths known at build time even when the path to the solution/project changes between environments.
Here's the post-build event PowerShell solution I've come up with (a workaround for users running in a Restricted execution policy is at the end of this answer).
1) Create a PowerShell script containing the following:
Param(
[Parameter(Mandatory=$True,Position=1)]
[string] $projPath,
[Parameter(Mandatory=$True,Position=2)]
[string] $debugScriptPath
)
# Setup new data types and .NET classes to get EnvDTE from a specific process
Add-Type -TypeDefinition #"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text.RegularExpressions;
public static class NTDLL
{
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_BASIC_INFORMATION
{
public IntPtr Reserved1;
public IntPtr PebBaseAddress;
public IntPtr Reserved2_0;
public IntPtr Reserved2_1;
public IntPtr UniqueProcessId;
public UIntPtr ParentUniqueProcessId;
}
public static UInt32 GetParentProcessID(IntPtr handle)
{
PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
Int32 returnLength;
UInt32 status = NtQueryInformationProcess(handle, IntPtr.Zero, ref pbi, Marshal.SizeOf(pbi), out returnLength);
if (status != 0)
return 0;
return pbi.ParentUniqueProcessId.ToUInt32();
}
[DllImport("ntdll.dll")]
private static extern UInt32 NtQueryInformationProcess(IntPtr processHandle, IntPtr processInformationClass, ref PROCESS_BASIC_INFORMATION processInformation, Int32 processInformationLength, out Int32 returnLength);
}
public static class OLE32
{
[DllImport("ole32.dll")]
public static extern Int32 CreateBindCtx(UInt32 reserved, out IBindCtx ppbc);
}
public class VisualStudioProcBinder
{
public static Object GetDTE(int processId)
{
Regex VSMonikerNameRegex = new Regex(#"!?VisualStudio\.DTE([\.\d]+)?:" + processId);
object runningObject = null;
IBindCtx bindCtx = null;
IRunningObjectTable rot = null;
IEnumMoniker enumMonikers = null;
try
{
Marshal.ThrowExceptionForHR(OLE32.CreateBindCtx(0, out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out enumMonikers);
IMoniker[] moniker = new IMoniker[1];
IntPtr numberFetched = IntPtr.Zero;
while (enumMonikers.Next(1, moniker, numberFetched) == 0)
{
IMoniker runningObjectMoniker = moniker[0];
string name = null;
try
{
if (runningObjectMoniker != null)
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
catch (UnauthorizedAccessException)
{
// Do nothing, there is something in the ROT that we do not have access to.
}
if (!string.IsNullOrEmpty(name) && VSMonikerNameRegex.IsMatch(name))
{
Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
break;
}
}
}
finally
{
if (enumMonikers != null)
Marshal.ReleaseComObject(enumMonikers);
if (rot != null)
Marshal.ReleaseComObject(rot);
if (bindCtx != null)
Marshal.ReleaseComObject(bindCtx);
}
return runningObject;
}
}
"#
# Get the devenv.exe process that started this pre/post-build event
[Diagnostics.Process] $dteProc = [Diagnostics.Process]::GetCurrentProcess();
while ($dteProc -and $dteProc.MainModule.ModuleName -ne 'devenv.exe')
{
#Write-Host "$(${dteProc}.Id) = $(${dteProc}.MainModule.ModuleName)";
try { $dteProc = [Diagnostics.Process]::GetProcessById([NTDLL]::GetParentProcessID($dteProc.Handle)); }
catch { $_; $dteProc = $null; }
}
# Get dteCOMObject using the parent process we just located
$dteCOMObject = [VisualStudioProcBinder]::GetDTE($dteProc.Id);
# Get the project directory
$projDir = Split-Path $projPath -Parent;
# If the script path does not exist on its own - try using it relative to the project directory
if (!(Test-Path $debugScriptPath)) {
$debugScriptPath = "${projDir}\${debugScriptPath}";
}
#####################################################
# Finally, tweak the project
#####################################################
if ($dteCOMObject) {
# Get the project reference from DTE
$dteProject = $dteCOMObject.Solution.Projects | ? { $_.FileName -eq $projPath } | Select-Object -First 1;
# Set this project as the startup project
$startupProj = $dteCOMObject.Solution.Properties["StartupProject"].Value;
if ($startupProj -ne $dteProject.Name) {
$dteCOMObject.Solution.Properties["StartupProject"].Value = $dteProject.Name;
}
# Get the external debug program and arguments currently in use
$debugProg = $dteProject.Properties['WebApplication.StartExternalProgram'].Value;
$debugArgs = $dteProject.Properties['WebApplication.StartCmdLineArguments'].Value;
# If an external debug program is not set, or it is set to cmd.exe /C "<file path>"
# and "file path" points to a file that doesn't exist (ie. project path has changed)
# then correct the program/args
if (!$debugProg -or ($debugProg -eq $env:ComSpec -and $debugArgs -match '^\s*/C\s+("?)([^"]+)\1$'-and !(Test-Path $Matches[2]))) {
if (!$debugProg) { $dteProject.Properties['WebApplication.DebugStartAction'].Value = 2; } # 2 = run external program
$dteProject.Properties['WebApplication.StartExternalProgram'].Value = $env:ComSpec; # run cmd.exe
# pass "<project dir>\Testing\Debug.cmd" as the program to run from cmd.exe
$dteProject.Properties['WebApplication.StartCmdLineArguments'].Value = "/C `"${debugScriptPath}`"";
}
# Release our COM object reference
[Runtime.InteropServices.Marshal]::ReleaseComObject($dteCOMObject) | Out-Null;
}
2) Call the PowerShell script from your project post-build like:
powershell.exe -File "$(ProjectDir)script.ps1" "$(ProjectPath)" "$(ProjectDir)Testing\Debug.cmd"
The first parameter (after -File) is the path to the script you created in step 1, the second parameter is the path to the project being built, and the third parameter (which your script will probably not have unless you're trying to do exactly what I am) is the path to the batch file/script that to be configured to run when debugging with an external program.
Workaround for users limited to running under a Restricted execution policy
(ie. powershell.exe -Command "Set-ExecutionPolicy Unrestricted" does not work)
If PowerShell is locked into the Restricted execution policy you will not be able to run a PowerShell script either using powershell.exe -File script.ps1 commands or through dot-sourcing methods like powershell.exe -Command ". .\script.ps1". However, I've discovered that you can read a script into a variable and then run Invoke-Expression $ScriptContent. (Seems odd to me that this works, but it does)
The workaround consists of:
1) Create a PowerShell script using the same content from above, but exclude the Param(...) lines at the top.
2) Call the PowerShell script from your project post-build like:
powershell -Command "& { $projPath='$(ProjectPath)'; $debugScriptPath='$(ProjectDir)Testing\Debug.cmd'; Get-Content '$(ProjectDir)script.ps1' -Encoding String | ? { $_ -match '^^\s*[^^#].*$' } | %% { $VSTweaks += $_ + """`r`n"""; }; Invoke-Expression $VSTweaks; } }"
This reads contents of script.ps1 into a variable named $VSTweaks (skipping lines that are only comments - ie. digital signature lines which cause problems in some scenarios), and then runs the script contents with Invoke-Expression. The values that were passed into $projPath and $debugScriptPath through parameters in the original script are now set at the beginning of the call in powershell.exe -Command "& { $projPath ...}. (If they are not set then the script will fail)
NOTE: Because the VS project post-build event contents are executed as a Windows batch file you have to escape a lot of characters that are special to Windows batch files. That explains some of the confusing character combinations in the script
% = %%
^ = ^^
" = """ (I'm honestly not sure why this is required, but it seems to be)
Tip
I've started wrapping the entire PowerShell call (<PowerShell commands> in the example below) in try catch statements in order to make any PowerShell errors show up in the Visual Studio "Error List..." view when a build fails.
powershell -Command "& { try { <PowerShell commands> } catch { Write-Host "post-build : PowerShell error $^($_.Exception.HResult^) : $_"; exit $_.Exception.HResult; } }"
The resulting error message in VS looks like this:
You can run a VBScript file (.vbs) with code like this:
Dim dte
Set dte = GetObject(, "VisualStudio.DTE")
dte.ExecuteCommand("Help.About")
While I was trying to find a solution I briefly looked into making my own .NET PowerShell cmdlet that would return the EnvDTE.DTE object for the instance of VS that started the PowerShell script.
The code is ugly, but it works and it met my requirements for the most part (more on that in the notes at the end). So, I thought I'd go ahead and include it as an answer as well just in case someone prefers this method or needs a starting point for making their own DTE cmdlet.
Usage Example:
PS C:\> Import-Module .\GetDTECmdlet.dll;
PS C:\> $dte = Get-DTE | Select-Object -First 1;
PS C:\> $dte = Get-DTE -ProcID 8547 | Select-Object -First 1;
PS C:\> $dte = Get-DTE -FromAncestorProcs | Select-Object -First 1;
PS C:\> $dte.ExecuteCommand('Help.About');
PS C:\> [Runtime.InteropServices.Marshal]::ReleaseComObject($dte); | Out-Null;
Cmdlet Parameters:
-ProcID <int>: int = the process ID (PID) of a running instance of Visual studio to get the DTE from
-FromAncestorProcs: If this switch is specified then the Get-DTE will limit its search of DTE objects to Visual Studio processes higher up in the process tree (ie. parent/ancestor processes) from the PowerShell session that called it.
Source Code:
(Make sure your project references System.Management.Automation.dll and envdte.dll)
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Management.Automation;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace VSAutomation
{
[Cmdlet(VerbsCommon.Get, "DTE")]
[OutputType(typeof(EnvDTE.DTE))]
public class GetDTECmdlet : Cmdlet, IDisposable
{
private Int32 procID = -1;
private IBindCtx bindCtx = null;
private IRunningObjectTable rot = null;
private IEnumMoniker monikerEnumerator = null;
private IMoniker[] moniker = new IMoniker[1];
private ProcCollection matchingProcs = new ProcCollection();
[Parameter]
public SwitchParameter FromAncestorProcs { get; set; }
[Parameter]
public Int32 ProcID { get { return procID; } set { procID = value; } }
protected override void BeginProcessing()
{
base.BeginProcessing();
Marshal.ThrowExceptionForHR(OLE32.CreateBindCtx(0, out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out monikerEnumerator);
}
protected override void ProcessRecord()
{
base.ProcessRecord();
Regex VSMonikerNameRegex = new Regex(#"^!?VisualStudio\.DTE([\.\d]+)?:(?<PID>\d+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
// Get a list of ancestor PIDs if the results should be limited based on ancestor processes
if (FromAncestorProcs.IsPresent)
{
try
{
using (Process thisProc = Process.GetCurrentProcess())
{
Process proc = thisProc;
Int32 parentProcID;
while ((parentProcID = NTDLL.GetParentProcessID(proc.Handle)) != 0)
{
proc = Process.GetProcessById(parentProcID);
matchingProcs.Add(new ROTProc(proc));
}
}
}
catch { }
}
// Loop through the running objects and find a suitable DTE
while (monikerEnumerator.Next(1, moniker, IntPtr.Zero) == 0)
{
Object runningObject;
IMoniker runningObjectMoniker = moniker[0];
if (!FromAncestorProcs.IsPresent && ProcID == -1)
{
// Returning all DTE objects from running processes
//Only return each object once
if (!matchingProcs.Contains(runningObjectMoniker))
{
Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
EnvDTE.DTE dte = runningObject as EnvDTE.DTE;
if (dte != null && !matchingProcs.Contains(dte))
{
matchingProcs.Add(new ROTProc(dte, runningObjectMoniker));
WriteObject(runningObject);
}
}
continue;
}
// Returning only DTE objects from ancestor processes or a specific process
Match nameMatch;
String name = null;
try
{
if (runningObjectMoniker != null)
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
catch (UnauthorizedAccessException)
{
// Do nothing, there is something in the ROT that we do not have access to.
}
if (String.IsNullOrEmpty(name))
continue;
nameMatch = VSMonikerNameRegex.Match(name);
if (!nameMatch.Success)
continue;
if (ProcID != -1)
{
if (Int32.Parse(nameMatch.Groups["PID"].Value) != ProcID)
continue;
//Found a match for the specified process ID - send it to the pipeline and quit enumerating
Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
if (runningObject is EnvDTE.DTE)
{
WriteObject(runningObject);
return;
}
}
// collect DTE objects so that they can be returned in order from closest ancestor to farthest ancestor in the event that VS launched VS which launched MSBUild ...
ROTProc ancestorProc = matchingProcs.GetByProcId(Int32.Parse(nameMatch.Groups["PID"].Value));
if (ancestorProc == null)
continue;
Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
ancestorProc.DTE = runningObject as EnvDTE.DTE;
}
if (!FromAncestorProcs.IsPresent)
return;
for (Int32 i = 0; i < matchingProcs.Count; i++)
if (matchingProcs[i].DTE != null) WriteObject(matchingProcs[i].DTE);
}
protected override void EndProcessing()
{
base.EndProcessing();
Dispose();
}
protected override void StopProcessing()
{
base.StopProcessing();
Dispose();
}
public void Dispose()
{
if (monikerEnumerator != null)
{
Marshal.ReleaseComObject(monikerEnumerator);
monikerEnumerator = null;
}
if (rot != null)
{
Marshal.ReleaseComObject(rot);
rot = null;
}
if (bindCtx != null)
{
Marshal.ReleaseComObject(bindCtx);
bindCtx = null;
}
if (matchingProcs != null)
{
matchingProcs.Dispose();
matchingProcs = null;
}
}
private class ROTProc : IDisposable
{
public Process Proc = null;
public EnvDTE.DTE DTE = null;
public IMoniker Moniker = null;
public IntPtr COMPtr = IntPtr.Zero;
public ROTProc(Process Proc, EnvDTE.DTE DTE = null, IMoniker Moniker = null)
{
this.Proc = Proc;
this.DTE = DTE;
this.Moniker = Moniker;
if (DTE != null)
COMPtr = Marshal.GetComInterfaceForObject(DTE, typeof(EnvDTE._DTE));
}
public ROTProc(EnvDTE.DTE DTE, IMoniker Moniker) : this(null, DTE, Moniker) { }
public void Dispose()
{
if (Proc != null)
{
try { Proc.Dispose(); }
catch (ObjectDisposedException) { }
Proc = null;
}
if (COMPtr != IntPtr.Zero)
{
try { Marshal.Release(COMPtr); }
catch { }
COMPtr = IntPtr.Zero;
}
}
}
private class ProcCollection : System.Collections.CollectionBase, IDisposable
{
public ROTProc this[Int32 index]
{
get { return InnerList[index] as ROTProc; }
set { InnerList[index] = value; }
}
public Int32 Add(ROTProc p)
{
return InnerList.Add(p);
}
public Boolean Contains(IMoniker Moniker)
{
if (Moniker == null)
return false;
foreach (ROTProc p in this)
if (p != null && Moniker.IsEqual(p.Moniker) == 0) return true;
return false;
}
public Boolean Contains(EnvDTE.DTE DTE)
{
if (DTE == null)
return false;
foreach (ROTProc p in this)
{
if (p != null && (
Marshal.Equals(DTE, p.DTE) ||
Marshal.GetComInterfaceForObject(DTE, typeof(EnvDTE._DTE)) == p.COMPtr))
{
return true;
}
}
return false;
}
public ROTProc GetByProcId(Int32 ProcId)
{
foreach (ROTProc p in this)
if (p != null && p.Proc != null && p.Proc.Id == ProcId) return p;
return null;
}
public void Dispose()
{
foreach (ROTProc p in this)
{
try { if (p != null) p.Dispose(); }
catch (ObjectDisposedException) { }
}
}
}
}
#region Supporting interop classes
public static class OLE32
{
[DllImport("ole32.dll")]
public static extern Int32 CreateBindCtx(UInt32 reserved, out IBindCtx ppbc);
}
public static class NTDLL
{
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_BASIC_INFORMATION
{
public IntPtr Reserved1;
public IntPtr PebBaseAddress;
public IntPtr Reserved2_0;
public IntPtr Reserved2_1;
public IntPtr UniqueProcessId;
public IntPtr ParentUniqueProcessId;
}
public static Int32 GetParentProcessID(IntPtr handle)
{
PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
Int32 returnLength;
UInt32 status = NtQueryInformationProcess(handle, IntPtr.Zero, ref pbi, Marshal.SizeOf(pbi), out returnLength);
if (status != 0)
return 0;
return pbi.ParentUniqueProcessId.ToInt32();
}
[DllImport("ntdll.dll")]
private static extern UInt32 NtQueryInformationProcess(IntPtr processHandle, IntPtr processInformationClass, ref PROCESS_BASIC_INFORMATION processInformation, Int32 processInformationLength, out Int32 returnLength);
}
#endregion Supporting interop classes
}
NOTE: I still prefer the single PowerShell script solution to this one (posted in another answer). But that's mainly because of the Restricted execution policy that is enforced in my environment. Because of that, even when I was using this cmdlet to do the heavy lifting of getting the DTE reference, the rest of my PowerShell code had to be compressed into an ugly, batch escaped, hard to understand, single line of code (ie powershell.exe -Command " ... %% ^^ ... ").
Even as I was writing it I found it hard to follow. So, I knew any future developers picking up the project would have been cursing me if they ever had to tweak it.
While trying to find a way to get around the "ugly one-liner" problem, I discovered the method of using Get-Content and Invoke-Expression to "psuedo-source" PowerShell scripts. Once I had that routine down I decided I'd rather have a single PowerShell script rather than a project to make a cmdlet and a PowerShell script using it.

Does the Phoenix framework support Windows authentication?

Our web apps are currently in C# running on Windows and IIS. We rely heavily on the Windows authentication scheme that is included in this environment. With Windows authentication enabled we can detect the identity of the connected user and perform authorization on what screens and operation they are able to use.
If I set up a Phoenix web application will it be possible to detect the identity of the connected user based on their current Windows login? If not is there an easy to use replacement for the Windows authentication?
I just did this over the weekend. Yes, it is possible. You have to use the HttpPlatformHandler add-on for IIS to make it work. HttpPlatformHandler has the forwardWindowsAuthToken configuration setting that you can use to forward the Windows user token for the authenticated user from IIS to your Phoenix application which is running as a child process. You have to use NIFs to process the token and get the Windows username or SID. As you'll note from the docs, you need to call CloseHandle to release the Windows user token for each request.
(I apologize in advance if the code below is not up to best practices. I'm new to Elixir and am actively trying to learn how to write better code. This was also from a hacking session trying to figure out the solution this weekend, so it's also not necessarily polished either.)
To do this, I packaged everything into a custom plug that I could put into the pipeline (I removed Logger statements to compress the size of the example):
defmodule MyApp.WindowsAuthentication do
import Plug.Conn
require Logger
#on_load :load_nifs
def load_nifs do
if match? {:win32, _}, :os.type do
:erlang.load_nif("./priv/windows_authentication", 0)
else
:ok
end
end
def init(options), do: options
def call(conn, _options) do
if match? {:win32, _}, :os.type do
case get_req_header(conn, "x-iis-windowsauthtoken") do
[token_handle_string] ->
# token_handle_string is a hex string
token_handle = String.to_integer(token_handle_string, 16)
case do_get_windows_username(token_handle) do
{:ok, {domain_name, username}} ->
conn = assign(conn, :windows_user, {domain_name, username})
error ->
Logger.error IO.inspect(error)
end
do_close_handle(token_handle)
[] ->
Logger.debug "X-IIS-WindowsAuthToken was not present"
end
end
conn
end
def do_get_windows_username(_token_handle) do
raise "do_get_windows_username/1 is only available on Windows"
end
def do_close_handle(_handle) do
raise "do_close_handle/1 is only available on Windows"
end
end
The C source code for the NIFs is below:
#include <Windows.h>
#include <erl_nif.h>
static const char* error_atom = "error";
static const char* invalid_token_handle_atom = "invalid_token_handle";
static const char* ok_atom = "ok";
static const char* win32_error_atom = "win32_error";
#define MAX_NAME 256
static HANDLE get_user_token(ErlNifEnv *env, ERL_NIF_TERM token) {
HANDLE token_handle;
if (!enif_get_ulong(env, token, (unsigned long *)&token_handle)) {
return NULL;
}
return token_handle;
}
static ERL_NIF_TERM make_win32_error_tuple(ErlNifEnv* env, DWORD error_code) {
return enif_make_tuple2(
env,
enif_make_atom(env, error_atom),
enif_make_ulong(env, error_code)
);
}
static ERL_NIF_TERM make_invalid_token_handle_error(ErlNifEnv* env) {
return enif_make_tuple2(
env,
enif_make_atom(env, error_atom),
enif_make_atom(env, invalid_token_handle_atom)
);
}
static ERL_NIF_TERM do_get_windows_username(ErlNifEnv* env, int argc, ERL_NIF_TERM argv[]) {
HANDLE token_handle;
DWORD token_user_length;
PTOKEN_USER token_user;
DWORD last_error;
WCHAR username[MAX_NAME];
DWORD username_length = MAX_NAME;
WCHAR domain_name[MAX_NAME];
DWORD domain_name_length = MAX_NAME;
size_t converted_chars;
char converted_username[MAX_NAME * 2];
char converted_domain_name[MAX_NAME * 2];
errno_t err;
BOOL succeeded;
SID_NAME_USE sid_name_use;
token_handle = get_user_token(env, argv[0]);
if (!token_handle) {
return make_invalid_token_handle_error(env);
}
if (!GetTokenInformation(token_handle, TokenUser, NULL, 0, &token_user_length)) {
last_error = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER != last_error) {
return make_win32_error_tuple(env, last_error);
}
}
token_user = (PTOKEN_USER)malloc(token_user_length);
if (!GetTokenInformation(token_handle, TokenUser, token_user, token_user_length, &token_user_length)) {
free(token_user);
return make_win32_error_tuple(env, GetLastError());
}
succeeded = LookupAccountSidW(
NULL,
token_user->User.Sid,
username,
&username_length,
domain_name,
&domain_name_length,
&sid_name_use);
if (!succeeded) {
free(token_user);
return make_win32_error_tuple(env, GetLastError());
}
err = wcstombs_s(&converted_chars, converted_username, 512, username, username_length);
err = wcstombs_s(&converted_chars, converted_domain_name, 512, domain_name, domain_name_length);
free(token_user);
return enif_make_tuple2(
env,
enif_make_atom(env, ok_atom),
enif_make_tuple2(
env,
enif_make_string(env, converted_domain_name, ERL_NIF_LATIN1),
enif_make_string(env, converted_username, ERL_NIF_LATIN1)
)
);
}
static ERL_NIF_TERM do_close_handle(ErlNifEnv* env, int argc, ERL_NIF_TERM argv[]) {
HANDLE token_handle;
token_handle = get_user_token(env, argv[0]);
if (!token_handle) {
return make_invalid_token_handle_error(env);
}
if (!CloseHandle(token_handle)) {
return make_win32_error_tuple(env, GetLastError());
}
return enif_make_atom(env, ok_atom);
}
static ErlNifFunc nif_functions[] = {
{ "do_close_handle", 1, do_close_handle },
{ "do_get_windows_username", 1, do_get_windows_username }
};
ERL_NIF_INIT(
Elixir.MyApp.WindowsAuthentication,
nif_functions,
NULL,
NULL,
NULL,
NULL
)
You can compile the C code using the 64-bit Visual Studio C++ tools (open the x64 VS command prompt). I tried this out with the new VS2017 tools. Put the DLL in the priv directory of your application.
cl /LD /I "C:\Program Files\erl-8.2\erts-8.2\include" /DDEBUG windows_authentication.c advapi32.lib
To run the plug, add it to your pipeline in web/router.ex:
pipeline :browser do
plug :accepts, ["html"]
plug MyApp.WindowsAuthentication
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
The end result of this is that conn.assigns.windows_user will contain a tuple of the form {domain_name, username} that has the Windows domain username for the authenticated user.
Note: When I was trying this, I found CPU and memory leak issues from erl.exe when running as a child process of IIS. I'm still trying to figure that out in case you see it. I posted a question about it here.
I'll probably release this as a library on hex.pm when I've cleaned it up and fixed the memory/CPU issue, but for now, here's the code that will let you use Windows authentication with Phoenix.

How to make Xamarin.Mac app "Open at Login"?

I have a Xamarin.Mac app that needs to open automatically at login.
How do I have my application get this setting without having to manually click on it?
I can give you a hint how to do it programmatically.
For this approach you need to make use of calls to native libraries via DllImport.
Following code will give you an idea how to proceed:
//needed library
const string DllName = "/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices";
static LSSharedFileList ()
{
dllHandle = Dlfcn.dlopen (DllName, 0);
kLSSharedFileListSessionLoginItems = Dlfcn.GetStringConstant (dllHandle, "kLSSharedFileListSessionLoginItems");
kLSSharedFileListItemLast = Dlfcn.GetStringConstant (dllHandle, "kLSSharedFileListItemLast");
}
[DllImport(DllName)]
public static extern IntPtr LSSharedFileListCreate (
IntPtr inAllocator,
IntPtr inListType,
IntPtr listOptions);
[DllImport(DllName, CharSet=CharSet.Unicode)]
public static extern void CFRelease (
IntPtr cf
);
[DllImport(DllName)]
public extern static IntPtr LSSharedFileListInsertItemURL (
IntPtr inList,
IntPtr insertAfterThisItem,
IntPtr inDisplayName,
IntPtr inIconRef,
IntPtr inURL,
IntPtr inPropertiesToSet,
IntPtr inPropertiesToClear);
And here the actual snippet:
public static void EnableLogInItem ()
{
IntPtr pFileList = IntPtr.Zero;
IntPtr pItem = IntPtr.Zero;
try
{
pFileList = LSSharedFileListCreate (
IntPtr.Zero,
kLSSharedFileListSessionLoginItems,
IntPtr.Zero);
pItem = LSSharedFileListInsertItemURL (
pFileList,
kLSSharedFileListItemLast,
IntPtr.Zero,
IntPtr.Zero,
NSBundle.MainBundle.BundleUrl.Handle,
IntPtr.Zero,
IntPtr.Zero);
}
finally
{
CFRelease (pItem);
CFRelease (pFileList);
}
}
Please keep in mind that it is not the whole solution, it is just a snippet to put an app in the login items list. Of course you have to handle errors, check for IntPtr.Zero after every call and so on, but this should give you an idea how it works.
Hope that helps!
Since the LSSharedFileList library is not supported on Xamarin.Mac you have to create a dylib with Xcode and bind it in your Xamarin.Mac application.
1) Create a Dylib project on Xcode. Add This function:
-(BOOL)AddLoginItem:(NSString *) AppPath{
// Your have to send this string as argument(i.e: on a textbox, write: /Applications/Calculator.app/)
// Get Login Items
LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if (loginItems) {
NSLog(#"[DEBUG]Your Application Path:%#",AppPath);
// Covert String to CFURLRef (It adds "file://" to your itemUrl1)
CFURLRef appUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:(NSString*) AppPath];
NSLog(#"[DEBUG] appUrl:%#", appUrl);
// Now we add the requested Login Item
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemLast, NULL, NULL, appUrl, NULL, NULL);
// Confirm that your path was correct and that you got a valid Login Item Reference
NSLog(#"[DEBUG]Item:%#", itemRef);
if (itemRef) CFRelease(itemRef);
// Your item just got added to the user's Login Items List
CFRelease(loginItems);
return true;
}
return false;
}
2) Create a Cocoa project with an TextBox, a Push button and their Actions and Outlets controls in it.
Use this link to help you with the C# bindings from a objective-c DLL.
3) On your Xamarin.Mac project, in MainWindow.cs, add the following code and make the necessary changes to fit on your code.
Don't forget to add your .Net Assembly reference, created on the previous link and also your: using DLLloginItem; line.
// Some variables
private string TestItemName;
private bool rem;
// Button Click
partial void AddItemButton (Foundation.NSObject sender) {
LoginItemsDLL loginItem = new LoginItemsDLL();
// Enter this string on your TextBox TxtName /Applications/Calculator.app/
TestItemName = TxtName.StringValue;
rem=loginItem.AddLoginItem(TestItemName);
Console.WriteLine(rem);
}
In order to get your application path entered, use another function that just receives the application name and returns its path into AddLoginItem argument. Hope it helps!

How to install a desktop shortcut (to a batch file) from a WiX-based installer that has "Run as Administrator" enabled?

I'm installing a desktop shortcut (to a batch file) from a WiX-based installer -- how do I automatically configure this shortcut with the "Run as Administrator" setting enabled? The target OS is Windows Server 2008 R2, and the installer is running with elevated priveleges.
Update:
Thanks to the link provided by #Anders, I was able to get this working. I needed to do this in a C# CustomAction, so here is the C# version of the code:
namespace CustomAction1
{
public class CustomAction1
{
public bool MakeShortcutElevated(string file_)
{
if (!System.IO.File.Exists(file_)) { return false; }
IPersistFile pf = new ShellLink() as IPersistFile;
if (pf == null) { return false; }
pf.Load(file_, 2 /* STGM_READWRITE */);
IShellLinkDataList sldl = pf as IShellLinkDataList;
if (sldl == null) { return false; }
uint dwFlags;
sldl.GetFlags(out dwFlags);
sldl.SetFlags(dwFlags | 0x00002000 /* SLDF_RUNAS_USER */);
pf.Save(null, true);
return true;
}
}
[ComImport(), Guid("00021401-0000-0000-C000-000000000046")]
public class ShellLink { }
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("45e2b4ae-b1c3-11d0-b92f-00a0c90312e1")]
interface IShellLinkDataList
{
void AddDataBlock(IntPtr pDataBlock);
void CopyDataBlock(uint dwSig, out IntPtr ppDataBlock);
void RemoveDataBlock(uint dwSig);
void GetFlags(out uint pdwFlags);
void SetFlags(uint dwFlags);
}
}
I'm guessing you would need a custom action and call the COM interfaces on your own. Query IShellLink (or IPersistFile?) for IShellLinkDataList, then: IShellLinkDataList->SetFlags(orgFlagsFromGetFlags|SLDF_RUNAS_USER);
Edit: Raymond has full sample code on his blog

How to tell if .NET code is being run by Visual Studio designer

I am getting some errors thrown in my code when I open a Windows Forms form in Visual Studio's designer. I would like to branch in my code and perform a different initialization if the form is being opened by designer than if it is being run for real.
How can I determine at run-time if the code is being executed as part of designer opening the form?
if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
{
// Design time logic
}
To find out if you're in "design mode":
Windows Forms components (and controls) have a DesignMode property.
Windows Presentation Foundation controls should use the IsInDesignMode attached property.
The Control.DesignMode property is probably what you're looking for. It tells you if the control's parent is open in the designer.
In most cases it works great, but there are instances where it doesn't work as expected. First, it doesn't work in the controls constructor. Second, DesignMode is false for "grandchild" controls. For example, DesignMode on controls hosted in a UserControl will return false when the UserControl is hosted in a parent.
There is a pretty easy workaround. It goes something like this:
public bool HostedDesignMode
{
get
{
Control parent = Parent;
while (parent!=null)
{
if(parent.DesignMode) return true;
parent = parent.Parent;
}
return DesignMode;
}
}
I haven't tested that code, but it should work.
The most reliable approach is:
public bool isInDesignMode
{
get
{
System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
bool res = process.ProcessName == "devenv";
process.Dispose();
return res;
}
}
The most reliable way to do this is to ignore the DesignMode property and use your own flag that gets set on application startup.
Class:
public static class Foo
{
public static bool IsApplicationRunning { get; set; }
}
Program.cs:
[STAThread]
static void Main()
{
Foo.IsApplicationRunning = true;
// ... code goes here ...
}
Then just check the flag whever you need it.
if(Foo.IsApplicationRunning)
{
// Do runtime stuff
}
else
{
// Do design time stuff
}
I had the same problem in Visual Studio Express 2013. I tried many of the solutions suggested here but the one that worked for me was an answer to a different thread, which I will repeat here in case the link is ever broken:
protected static bool IsInDesigner
{
get { return (Assembly.GetEntryAssembly() == null); }
}
The devenv approach stopped working in VS2012 as the designer now has its own process. Here is the solution I am currently using (the 'devenv' part is left there for legacy, but without VS2010 I am not able to test that though).
private static readonly string[] _designerProcessNames = new[] { "xdesproc", "devenv" };
private static bool? _runningFromVisualStudioDesigner = null;
public static bool RunningFromVisualStudioDesigner
{
get
{
if (!_runningFromVisualStudioDesigner.HasValue)
{
using (System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess())
{
_runningFromVisualStudioDesigner = _designerProcessNames.Contains(currentProcess.ProcessName.ToLower().Trim());
}
}
return _runningFromVisualStudioDesigner.Value;
}
}
/// <summary>
/// Are we in design mode?
/// </summary>
/// <returns>True if in design mode</returns>
private bool IsDesignMode() {
// Ugly hack, but it works in every version
return 0 == String.CompareOrdinal(
"devenv.exe", 0,
Application.ExecutablePath, Application.ExecutablePath.Length - 10, 10);
}
System.Diagnostics.Debugger.IsAttached
It's hack-ish, but if you're using VB.NET and when you're running from within Visual Studio My.Application.Deployment.CurrentDeployment will be Nothing, because you haven't deployed it yet. I'm not sure how to check the equivalent value in C#.
using (System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess())
{
bool inDesigner = process.ProcessName.ToLower().Trim() == "devenv";
return inDesigner;
}
I tried the above code (added a using statement) and this would fail on some occasions for me. Testing in the constructor of a usercontrol placed directly in a form with the designer loading at startup. But would work in other places.
What worked for me, in all locations is:
private bool isDesignMode()
{
bool bProcCheck = false;
using (System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess())
{
bProcCheck = process.ProcessName.ToLower().Trim() == "devenv";
}
bool bModeCheck = (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime);
return bProcCheck || DesignMode || bModeCheck;
}
Maybe a bit overkill, but it works, so is good enough for me.
The success in the example noted above is the bModeCheck, so probably the DesignMode is surplus.
You check the DesignMode property of your control:
if (!DesignMode)
{
//Do production runtime stuff
}
Note that this won't work in your constructor because the components haven't been initialized yet.
When running a project, its name is appended with ".vshost".
So, I use this:
public bool IsInDesignMode
{
get
{
Process p = Process.GetCurrentProcess();
bool result = false;
if (p.ProcessName.ToLower().Trim().IndexOf("vshost") != -1)
result = true;
p.Dispose();
return result;
}
}
It works for me.
I'm not sure if running in debug mode counts as real, but an easy way is to include an if statement in your code that checkes for System.Diagnostics.Debugger.IsAttached.
If you created a property that you don't need at all at design time, you can use the DesignerSerializationVisibility attribute and set it to Hidden. For example:
protected virtual DataGridView GetGrid()
{
throw new NotImplementedException("frmBase.GetGrid()");
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int ColumnCount { get { return GetGrid().Columns.Count; } set { /*Some code*/ } }
It stopped my Visual Studio crashing every time I made a change to the form with NotImplementedException() and tried to save. Instead, Visual Studio knows that I don't want to serialize this property, so it can skip it. It only displays some weird string in the properties box of the form, but it seems to be safe to ignore.
Please note that this change does not take effect until you rebuild.
We use the following code in UserControls and it does the work. Using only DesignMode will not work in your app that uses your custom user controls as pointed out by other members.
public bool IsDesignerHosted
{
get { return IsControlDesignerHosted(this); }
}
public bool IsControlDesignerHosted(System.Windows.Forms.Control ctrl)
{
if (ctrl != null)
{
if (ctrl.Site != null)
{
if (ctrl.Site.DesignMode == true)
return true;
else
{
if (IsControlDesignerHosted(ctrl.Parent))
return true;
else
return false;
}
}
else
{
if (IsControlDesignerHosted(ctrl.Parent))
return true;
else
return false;
}
}
else
return false;
}
Basically the logic above boils down to:
public bool IsControlDesignerHosted(System.Windows.Forms.Control ctrl)
{
if (ctrl == null) return false;
if (ctrl.Site != null && ctrl.Site.DesignMode) return true;
return IsControlDesignerHosted(ctrl.Parent);
}
If you are in a form or control you can use the DesignMode property:
if (DesignMode)
{
DesignMode Only stuff
}
I found the DesignMode property to be buggy, at least in previous versions of Visual Studio. Hence, I made my own using the following logic:
Process.GetCurrentProcess().ProcessName.ToLower().Trim() == "devenv";
Kind of a hack, I know, but it works well.
System.ComponentModel.Component.DesignMode == true
To solve the problem, you can also code as below:
private bool IsUnderDevelopment
{
get
{
System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
if (process.ProcessName.EndsWith(".vshost")) return true;
else return false;
}
}
Here's another one:
//Caters only to thing done while only in design mode
if (App.Current.MainWindow == null){ // in design mode }
//Avoids design mode problems
if (App.Current.MainWindow != null) { //applicaiton is running }
After testing most of the answers here, unfortunately nothing worked for me (VS2015).
So I added a little twist to JohnV's answer, which didn't work out of the box, since DesignMode is a protected Property in the Control class.
First I made an extension method which returns the DesignMode's Property value via Reflection:
public static Boolean GetDesignMode(this Control control)
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static;
PropertyInfo prop = control.GetType().GetProperty("DesignMode", bindFlags);
return (Boolean)prop.GetValue(control, null);
}
and then I made a function like JohnV:
public bool HostedDesignMode
{
get
{
Control parent = Parent;
while (parent != null)
{
if (parent.GetDesignMode()) return true;
parent = parent.Parent;
}
return DesignMode;
}
}
This is the only method that worked for me, avoiding all the ProcessName mess, and while reflection should not be used lightly, in this case it did all the difference! ;)
EDIT:
You can also make the second function an extension method like this:
public static Boolean IsInDesignMode(this Control control)
{
Control parent = control.Parent;
while (parent != null)
{
if (parent.GetDesignMode())
{
return true;
}
parent = parent.Parent;
}
return control.GetDesignMode();
}
For WPF (hopefully this is useful for those WPF people stumbling upon this question):
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
}
GetIsInDesignMode requires a DependencyObject. If you don't have one, just create one.
/// <summary>
/// Whether or not we are being run from the Visual Studio IDE
/// </summary>
public bool InIDE
{
get
{
return Process.GetCurrentProcess().ProcessName.ToLower().Trim().EndsWith("vshost");
}
}
Here's a flexible way that is adaptable to where you compile from as well as whether or not you care which mode you're in.
string testString1 = "\\bin\\";
//string testString = "\\bin\\Debug\\";
//string testString = "\\bin\\Release\\";
if (AppDomain.CurrentDomain.BaseDirectory.Contains(testString))
{
//Your code here
}

Resources