c# IDsObjectPickerCredentials fail - c#

I have to set credentials to Active-Directory-Object-Picker (IDsObjectPicker) on c#.
But I can't force the IDsObjectPickerCredentials to work.
I've made same on c++ (msdn example) and it works good.
I used "headers" from here ComInterop.cs and StructsFlags.cs
Please tell me what I'm doing wrong. How to call IDsObjectPickerCredentials.SetCredentials
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Tulpep.ActiveDirectoryObjectPicker;
namespace cred
{
class Program
{
static void Main(string[] args)
{
string szTargetComputer = #"10.0.9.115";
string szUser = #"TST\test";
string szPassword = #"123qazWSX";
DSObjectPicker picker = new DSObjectPicker();
IDsObjectPicker ipicker = (IDsObjectPicker)picker;
int res = InitObjectPicker(ipicker, szTargetComputer);
if (res == (int)HRESULT.S_OK)
{
try
{
// !!! HERE !!!
IDsObjectPickerCredentials cred = (ipicker as IDsObjectPickerCredentials);
res = cred.SetCredentials(szUser, szPassword);
// c++ like variant
// res = InitCredentials(ipicker, szUser, szPassword);
if (res != (int)HRESULT.S_OK) Console.WriteLine("SetCredentials Fail : 0x{0}", res.ToString("X4")); // On Win32 I get 0x80070057 - looks like E_INVALIDARG
IntPtr hwndParent = Process.GetCurrentProcess().MainWindowHandle;
IDataObject dataObj = null;
int hresult = ipicker.InvokeDialog(hwndParent, out dataObj);
Console.WriteLine(hresult == (int)HRESULT.S_OK ? "OK" : "Cancel");
Console.ReadKey();
}
finally
{
Marshal.ReleaseComObject(ipicker);
}
}
}
[ComImport, Guid("E2D3EC9B-D041-445A-8F16-4748DE8FB1CF"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IDsObjectPickerCredentials
{
[PreserveSig()]
int SetCredentials(
[In, MarshalAs(UnmanagedType.LPWStr)] string szUserName,
[In, MarshalAs(UnmanagedType.LPWStr)] string szPassword);
}
static int InitObjectPicker(IDsObjectPicker ipicker, string szTargetComputer)
{
int res = (int)HRESULT.S_FALSE;
DSOP_SCOPE_INIT_INFO[] aScopeInit = new DSOP_SCOPE_INIT_INFO[1];
DSOP_INIT_INFO InitInfo = new DSOP_INIT_INFO();
aScopeInit[0].cbSize = (uint)Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO));
aScopeInit[0].flType =
DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN |
DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN;
aScopeInit[0].FilterFlags.Uplevel.flBothModes =
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_COMPUTERS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_USERS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_WELL_KNOWN_PRINCIPALS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_BUILTIN_GROUPS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_WELL_KNOWN_PRINCIPALS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_INCLUDE_ADVANCED_VIEW;
aScopeInit[0].FilterFlags.flDownlevel =
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_COMPUTERS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_USERS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_WELL_KNOWN_PRINCIPALS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_BUILTIN_GROUPS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_WELL_KNOWN_PRINCIPALS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_INCLUDE_ADVANCED_VIEW;
IntPtr refScopeInitInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO)) * aScopeInit.Length);
try
{
// Marshal structs to pointers
for (int index = 0; index < aScopeInit.Length; index++)
{
Marshal.StructureToPtr(aScopeInit[index], (IntPtr)((int)refScopeInitInfo + index * Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO))), false);
}
InitInfo.cbSize = (uint)Marshal.SizeOf(typeof(DSOP_INIT_INFO));
InitInfo.cDsScopeInfos = (uint)aScopeInit.Length; //sizeof(aScopeInit)/sizeof(DSOP_SCOPE_INIT_INFO);
InitInfo.aDsScopeInfos = refScopeInitInfo;
InitInfo.flOptions = DSOP_INIT_INFO_FLAGS.DSOP_FLAG_MULTISELECT;
InitInfo.pwzTargetComputer = szTargetComputer;
res = ipicker.Initialize(ref InitInfo);
}
finally
{
Marshal.FreeHGlobal(refScopeInitInfo);
}
return res;
}
static int InitCredentials(IDsObjectPicker ipicker, string User, string Password)
{
IntPtr ptr = IntPtr.Zero;
Guid IID_IDsObjectPickerCredentials = new Guid("E2D3EC9B-D041-445A-8F16-4748DE8FB1CF");
IntPtr pIUnk = Marshal.GetIUnknownForObject(ipicker);
int hr = Marshal.QueryInterface(pIUnk, ref IID_IDsObjectPickerCredentials, out ptr);
if (hr == HRESULT.S_OK)
{
try
{
IDsObjectPickerCredentials cred = (IDsObjectPickerCredentials)Marshal.GetObjectForIUnknown(ptr);
hr = cred.SetCredentials(User, Password);
if (hr != HRESULT.S_OK)
{
System.Diagnostics.Debugger.Break(); // Fail
return (int)HRESULT.S_FALSE;
}
}
catch (Exception ex)
{
return (int)HRESULT.S_FALSE;
}
finally
{
Marshal.Release(ptr);
}
}
return (int)HRESULT.S_OK;
}
}
}

The IDsObjectPickerCredentials interface derives from the IDsObjectPicker. Your sample actually calls the Initialize method with a username and a password instead of SetCredentials.
Here is a correct declaration:
[ComImport, Guid("e2d3ec9b-d041-445a-8f16-4748de8fb1cf"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IDsObjectPickerCredentials
{
[PreserveSig]
int Initialize(ref DSOP_INIT_INFO pInitInfo);
[PreserveSig]
int InvokeDialog(IntPtr HWND, out IDataObject lpDataObject);
[PreserveSig]
int SetCredentials(string userName, string password);
}
Please note that instead of deriving from the IDsObjectPicker, the code replicates its methods, in the same order. This is necessary for .NET COM Interop.
You don't need that manual QueryInterface/Release calls, var cred = (IDsObjectPickerCredentials)iobjectpicker; is enough.
Also I found that the SetCredentials should be called before the Initialize method.

Related

C# - How to Authenticate with server dinamically

I need to check if service "Advice" has its status running on said server. I made a method that does just that:
public static bool CheckServicesFromServer(string pServicos)
{
ServiceController service = new ServiceController();
List<string> Servicos = pServicos.Split(',').Select(p => p.Trim()).ToList();
if (Config.BaseLogin == BasesSistema.QualityLogin)
service.MachineName = "quality";
if (Config.BaseLogin == BasesSistema.TS02Login)
service.MachineName = "ts02";
if (Config.BaseLogin == BasesSistema.TS03Login)
service.MachineName = "ts03";
if (Config.BaseLogin == BasesSistema.LocalHost)
service.MachineName = Environment.MachineName;
try
{
foreach (var item in Servicos)
{
service.ServiceName = item;
if ((service.Status.Equals(ServiceControllerStatus.Stopped)) || (service.Status.Equals(ServiceControllerStatus.StopPending)))
{
File.AppendAllText(StatusLog.StatusLocation, "O servico " + service.ServiceName + " está parado. a Regra não será gerada.");
throw new Exception();
}
if (service.Status.Equals(ServiceControllerStatus.Running))
{
File.AppendAllText(StatusLog.StatusLocation, "O serviço " + service.ServiceName + "está rodando perfeitamente.");
}
}
}
catch (Exception e)
{
Log.WriteErrorLog(e.Message);
throw new Exception(e.Message);
}
return true;
}
The thing is, when I run the test, it says "Access Denied", and throws an exception. When I add my user (The user from the computer which is running the application) as an Adm at the server, it runs fine.
Is there a way to authenticate my computer so it can have permission to access the server and check its service status?
Thanks to Krik, I found the solution. It's as simple as addind this class:
using System;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Security.Permissions;
public class ImpersonateUser
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
String lpszUsername,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
private static IntPtr tokenHandle = new IntPtr(0);
private static WindowsImpersonationContext impersonatedUser;
// If you incorporate this code into a DLL, be sure to demand that it
// runs with FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Impersonate(string domainName, string userName, string password)
{
//try
{
// Use the unmanaged LogonUser function to get the user token for
// the specified user, domain, and password.
const int LOGON32_PROVIDER_DEFAULT = 0;
// Passing this parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
tokenHandle = IntPtr.Zero;
// ---- Step - 1
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(
userName,
domainName,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle); // tokenHandle - new security token
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
// ---- Step - 2
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
// ---- Step - 3
{
impersonatedUser = newId.Impersonate();
}
}
}
// Stops impersonation
public void Undo()
{
impersonatedUser.Undo();
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
internal void Impersonate()
{
throw new NotImplementedException();
}
}
And I Just had to call it like this:
public static bool CheckServicesFromServer(string pServicos)
{
ImpersonateUser Iu = new ImpersonateUser();
ServiceController service = new ServiceController();
List<string> Servicos = pServicos.Split(',').Select(p => p.Trim()).ToList();
if (Config.BaseLogin == BasesSistema.QualityLogin)
service.MachineName = "quality";
if (Config.BaseLogin == BasesSistema.TS02Login)
service.MachineName = "ts02";
if (Config.BaseLogin == BasesSistema.TS03Login)
service.MachineName = "ts03";
if (Config.BaseLogin == BasesSistema.LocalHost)
service.MachineName = Environment.MachineName;
Iu.Impersonate(Config.Dominio, Config.LoginMaster, Config.SenhaMaster);
try
{
foreach (var item in Servicos)
{
service.ServiceName = item;
if ((service.Status.Equals(ServiceControllerStatus.Stopped)) || (service.Status.Equals(ServiceControllerStatus.StopPending)))
{
Flag = true;
File.AppendAllText(StatusLog.StatusLocation, "O servico " + service.ServiceName + " está parado. a Regra não será gerada. <br />");
throw new Exception();
}
if (service.Status.Equals(ServiceControllerStatus.Running))
{
File.AppendAllText(StatusLog.StatusLocation, "O serviço " + service.ServiceName + " está rodando perfeitamente. <br />");
}
}
Iu.Undo();
}
catch
{
Iu.Undo();
Log.WriteErrorLog("Não é possível abrir o Gerenciador de Controle de Serviços no Computador '" + service.MachineName + "'. <br />");
return false;
}
return true;
}
My Config Class holds the information of the Domain, User and PassWord, and it worked perfectly.

Microsoft CAPI returns NTE_BADKEYSET while using SHA256

I have my private key in HSM and i am using Microsoft CAPI (SignerSignEx) to sign a file . If am able to generate a sha1 signature successfully ,but if i try to generate a sha256 signature using the same cert and same key on my hsm SignerSignEx() returns -2146893802 . (NTE_BAD_KEYSET) . I am not sure what is going wrong . My private key is stored in a CSP container on the HSM .
void main(string filePath, X509Certificate2 crossSigningCert, string timestampUrl)
{
UInt32 signatureAlgId = 0x0000800c //CALG_SHA_256 Signature Algorithm
X509Certificate2 cert = PKCSWrapper.GetCertificateFromHSM(SignCertLabel);
uint dwFlags = 0x0;
IntPtr pSignerCert = IntPtr.Zero;
IntPtr pSubjectInfo = IntPtr.Zero;
IntPtr pSignatureInfo = IntPtr.Zero;
IntPtr pProviderInfo = IntPtr.Zero;
pSignerCert = CreateSignerCertStruct(cert, crossSigningCert);
pSubjectInfo = CreateSubjectInfoStruct(filePath);
pSignatureInfo = CreateSignerSignatureInfoStruct(signatureAlgId);
pProviderInfo = CreateProviderInfoStruct(cert);
SIGNER_CONTEXT signerContext;
int hResult = SignerSignEx(
dwFlags,
pSubjectInfo,
pSignerCert,
pSignatureInfo,
pProviderInfo,
timestampUrl,
IntPtr.Zero,
IntPtr.Zero,
out signerContext
);
}
private IntPtr CreateSubjectInfoStruct(string filePath){
IntPtr assemblyFilePtr = Marshal.StringToHGlobalUni(filePath);
SIGNER_FILE_INFO fileInfo = new SIGNER_FILE_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_FILE_INFO)),
pwszFileName = assemblyFilePtr,
hFile = IntPtr.Zero
};
SIGNER_SUBJECT_INFO subjectInfo = new SIGNER_SUBJECT_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_SUBJECT_INFO)),
pdwIndex = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)))
};
subjectInfo.Union1 = new SIGNER_SUBJECT_INFO.SubjectChoiceUnion
{
pSignerFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_FILE_INFO)))
};
UInt32 SIGNER_SUBJECT_FILE = 0x1;
var index = 0;
Marshal.StructureToPtr(index, subjectInfo.pdwIndex, false);
subjectInfo.dwSubjectChoice = SIGNER_SUBJECT_FILE; //SIGNER_SUBJECT_FILE
Marshal.StructureToPtr(fileInfo, subjectInfo.Union1.pSignerFileInfo, false);
IntPtr pSubjectInfo = Marshal.AllocHGlobal(Marshal.SizeOf(subjectInfo));
Marshal.StructureToPtr(subjectInfo, pSubjectInfo, false);
return pSubjectInfo;
}
private IntPtr CreateProviderInfoStruct(X509Certificate2 cert){
ICspAsymmetricAlgorithm key = (ICspAsymmetricAlgorithm)cert.PrivateKey;
const int PVK_TYPE_KEYCONTAINER = 2;
const uint AT_SIGNATURE = 2;
if (key == null){
return IntPtr.Zero;
}
SIGNER_PROVIDER_INFO providerInfo = new SIGNER_PROVIDER_INFO{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_PROVIDER_INFO)),
pwszProviderName = Marshal.StringToHGlobalUni(key.CspKeyContainerInfo.ProviderName),
dwProviderType = (uint)key.CspKeyContainerInfo.ProviderType,
dwKeySpec = AT_SIGNATURE/* AT_SIGNATURE */,
dwPvkChoice = PVK_TYPE_KEYCONTAINER /* The private key information is a key container.*/,
Union1 = new SIGNER_PROVIDER_INFO.SignerProviderUnion
{
pwszKeyContainer = Marshal.StringToHGlobalUni(key.CspKeyContainerInfo.KeyContainerName)
},
};
IntPtr pProviderInfo = Marshal.AllocHGlobal(Marshal.SizeOf(providerInfo));
Marshal.StructureToPtr(providerInfo, pProviderInfo, false);
return pProviderInfo;
}
private IntPtr CreateSignerSignatureInfoStruct(UInt32 signatureAlgId){
const UInt32 ATTRIBUTES = 0x0; // SIGNER_NO_ATTR
SIGNER_SIGNATURE_INFO signatureInfo = new SIGNER_SIGNATURE_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_SIGNATURE_INFO)),
algidHash = signatureAlgId,
dwAttrChoice = ATTRIBUTES,
pAttrAuthCode = IntPtr.Zero,
psAuthenticated = IntPtr.Zero,
psUnauthenticated = IntPtr.Zero
};
IntPtr pSignatureInfo = Marshal.AllocHGlobal(Marshal.SizeOf(signatureInfo));
Marshal.StructureToPtr(signatureInfo, pSignatureInfo, false);
return pSignatureInfo;
}
private IntPtr CreateSignerCertStruct(X509Certificate2 cert, X509Certificate2 crossCertifcate ){
UInt32 SIGNER_CERT_STORE = 0x2;
SIGNER_CERT signerCert = new SIGNER_CERT
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_CERT)),
dwCertChoice = SIGNER_CERT_STORE,
Union1 = new SIGNER_CERT.SignerCertUnion
{
pCertStoreInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_CERT_STORE_INFO)))
},
hwnd = IntPtr.Zero
};
IntPtr pCertContext = CertCreateCertificateContext(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
cert.GetRawCertData(),
cert.GetRawCertData().Length);
IntPtr pHCertStore = IntPtr.Zero;
uint certPolicy = 0;
if (crossCertifcate != null)
{
pHCertStore = CreateCrossCertificateStore(crossCertifcate);
certPolicy = (UInt32)CERT_POLICY.SIGNER_CERT_POLICY_STORE;
}
SIGNER_CERT_STORE_INFO certStoreInfo = new SIGNER_CERT_STORE_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_CERT_STORE_INFO)),
pSigningCert = pCertContext,
dwCertPolicy = (UInt32)CERT_POLICY.SIGNER_CERT_POLICY_CHAIN| certPolicy,
hCertStore = pHCertStore
};
Marshal.StructureToPtr(certStoreInfo, signerCert.Union1.pCertStoreInfo, false);
IntPtr pSignerCert = Marshal.AllocHGlobal(Marshal.SizeOf(signerCert));
Marshal.StructureToPtr(signerCert, pSignerCert, false);
return pSignerCert;
}
You cannot create a full CMS with a SHA-256 and CSP on windows.
The only way to do it, is to manually construct the CMS structure and sign the SHA-256 directly with a key.
If you use RSACryptoServiceProvider instance and SHA-256 it should work.
Also, if you use a CNG provider, your code will work.

Does Marshal.PtrToStringAnsi not working in windows service?

I have written a dll which call windows spooler api to get printer name and printer driver name, it works fine in console application. However, I got nothing when I call the dll in windows services application.
Here is my dll source code(i.e. printerUtility):
PRINTER_ENUM_LOCAL = 0x00000002,
PRINTER_ENUM_CONNECTIONS = 0x00000004,
public ArrayList getAllLocalPrinterInfo(LogWriter logger)
{
ArrayList printerInfoList = getAllLocalPrinterInfoV2(PrinterEnumFlags.PRINTER_ENUM_LOCAL | PrinterEnumFlags.PRINTER_ENUM_CONNECTIONS);
foreach (PrinterInfoV2 printerInfo in printerInfoList)
{
logger.write("In dll Printer Name pointer:" + printerInfo.pPrinterName+"\nIn dll Printer Name:" + Marshal.PtrToStringAnsi(printerInfo.pPrinterName));
logger.write("In dll Driver Name pointer:" + printerInfo.pDriverName+"\nIn dll Driver Name:" + Marshal.PtrToStringAnsi(printerInfo.pDriverName));
}
//return allLocalPrinterInfo;
return printerInfoList;
}
public ArrayList getAllLocalPrinterInfoV2(PrinterEnumFlags Flags)
{
return enumPrinters(Flags, 2);
}
private ArrayList enumPrinters(PrinterEnumFlags Flags, UInt32 level)
{
bool result;
ArrayList printerInfo = new ArrayList();
UInt32 returned, needed = 0;
UInt32 flags = Convert.ToUInt32(Flags);
//find required size for the buffer
result = EnumPrinters(flags, null, level, IntPtr.Zero, 0, out needed, out returned);
if (Marshal.GetLastWin32Error() != Convert.ToUInt32(CredUIReturnCodes.ERROR_INSUFFICIENT_BUFFER))
{
throw new Exception("EnumPrinters 1 failure, error code=" + Marshal.GetLastWin32Error());
}
else
{
IntPtr buffer = Marshal.AllocHGlobal((int)needed);
result = EnumPrinters(flags, null, level, buffer, needed, out needed, out returned);
if (result)
{
if ((level > 0) || (level < 5))
{
Type type = typeof(PrinterInfoV1);
switch (level)
{
case 1:
type = typeof(PrinterInfoV1);
break;
case 2:
type = typeof(PrinterInfoV2);
break;
case 3:
type = typeof(PrinterInfoV3);
break;
case 4:
type = typeof(PrinterInfoV4);
break;
}
int offset = buffer.ToInt32();
int increment = Marshal.SizeOf(type);
for (int i = 0; i < returned; i++)
{
printerInfo.Add(Marshal.PtrToStructure(new IntPtr(offset), type));
offset += increment;
}
Marshal.FreeHGlobal(buffer);
return printerInfo;
}
else
{
Marshal.FreeHGlobal(buffer);
throw new Exception("The value of level is out of range");
}
}
else
{
Marshal.FreeHGlobal(buffer);
throw new Exception("EnumPrinters 2 failure, error code=" + Marshal.GetLastWin32Error());
}
}
}
[DllImport("winspool.drv", SetLastError = true)]
static extern bool EnumPrinters([InAttribute()] UInt32 Flags,
[InAttribute()] string pPrinterName,
[InAttribute()] UInt32 Level,
[OutAttribute()] IntPtr pPrinter,
[InAttribute()] UInt32 cbBuf,
[OutAttribute()] out UInt32 pcbNeeded,
[OutAttribute()] out UInt32 pcReturned);
The LogWriter Source code:
public class LogWriter
{
EventLog eventLog = null;
public LogWriter(System.Diagnostics.EventLog elog)
{
this.eventLog = elog;
}
public void write(String msg)
{
if (eventLog != null)
{
eventLog.WriteEntry(msg);
}
}
}
Here is services source code:
protected override void OnStart(string[] args)
{
serviceStatus.dwWaitHint = 100000;
serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
printerMonitorList = new ArrayList();
try
{
ArrayList printerInfoList = printerUtility.getAllLocalPrinterInfo(logger);
foreach (PrinterInfoV2 printerInfo in printerInfoList)
{
logger.write("In service Printer Name pointer:" + printerInfo.pPrinterName+"\nIn service Printer Name:" + Marshal.PtrToStringAnsi(printerInfo.pPrinterName));
logger.write("In service Driver Name pointer:" + printerInfo.pDriverName+"\nIn service Driver Name:" + Marshal.PtrToStringAnsi(printerInfo.pDriverName));
}
serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
logger.write(this.ServiceName + " Has Been Started");
}
catch (Exception err)
{
logger.write("Print Alert Service cannot be started:"+err.Message);
}
In event viewer, I got an empty string for both printer name and printer driver name.
However, I found that the dll return a pointer for both printer name and printer driver name. Therefore, I suspect whether the Marshal.PtrToStringAnsi function not working in window services environment.

Using COM component fails as admin

I have a .NET wrapper over a native dll.
I try to register the component only for current user based on this : link
This runs fine, except if I right click the executable and say "Run as admin.." the tool will fail that it cannot find the class.
This is the code I use, is there something I am missing , I think it might be related to :
link
Thanks!
public int Register(ServerAction Action, string ServerName)
{
int hr = 0;
string szFunction = (Action == ServerAction.Register) ? "DllRegisterServer" : "DllUnregisterServer";
logger.Info("FileReader->Try to " + szFunction + " " + ServerName);
try
{
IntPtr hMod = LoadLibrary(ServerName);
if (hMod.ToInt32() != 0)
{
IntPtr registerDllServer_ProcAddress = GetProcAddress(hMod, szFunction);
DllRegisterServerInvoker pfnDllProc = (DllRegisterServerInvoker)Marshal.GetDelegateForFunctionPointer(registerDllServer_ProcAddress, typeof(DllRegisterServerInvoker));
if (pfnDllProc != null)
{
if (isUserAdminAndProcessElevated())
{
logger.Info("User is admin and process is elevated!");
}
else
{
logger.Info("User is not admin!");
hr = OverrideClassesRoot(HKEY_CURRENT_USER, "Software\\Classes");
}
}
hr = pfnDllProc();
pfnDllProc = null;
FreeLibrary(hMod);
}
else
{
hr = this.GetHresultFromWin32();
}
}
catch
{
Console.WriteLine("FileReader->" + szFunction + " " + ServerName + " FAILED!");
}
string errorMessage = new Win32Exception(hr).Message;
logger.Fatal(errorMessage);
return hr;
}
private Int32 OverrideClassesRoot(UIntPtr hKeyBase, string OverrideKey)
{
UIntPtr hkey = new UIntPtr();
int l = RegOpenKeyA(hKeyBase, OverrideKey, ref hkey);
if (l == 0)
{
l = RegOverridePredefKey(HKEY_CLASSES_ROOT, hkey);
RegCloseKey(hkey);
}
return this.GetHresultFromWin32();
}
Still haven't figured this out but I fixed it with : MSDN link

How do I use OpenFileDialog to select a folder?

I was going to use the following project: https://github.com/scottwis/OpenFileOrFolderDialog
However, there's a problem: it uses the GetOpenFileName function and OPENFILENAME structure. OPENFILENAME has the member named templateID, which is the identifier for dialog template. And the project contains the res1.rc file and the templated dialog init, too. But I couldn't figure out how to attach this file to my C# project.
Is there a better way to use an OpenFileDialog to select folders?
Basically you need the FolderBrowserDialog class:
Prompts the user to select a folder. This class cannot be inherited.
Example:
using(var fbd = new FolderBrowserDialog())
{
DialogResult result = fbd.ShowDialog();
if (result == DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath))
{
string[] files = Directory.GetFiles(fbd.SelectedPath);
System.Windows.Forms.MessageBox.Show("Files found: " + files.Length.ToString(), "Message");
}
}
If you work in WPF you have to add the reference to System.Windows.Forms.
you also have to add using System.IO for Directory class
As a note for future users who would like to avoid using FolderBrowserDialog, Microsoft once released an API called the WindowsAPICodePack that had a helpful dialog called CommonOpenFileDialog, that could be set into a IsFolderPicker mode. The API is available from Microsoft as a NuGet package.
This is all I needed to install and use the CommonOpenFileDialog. (NuGet handled the dependencies)
Install-Package Microsoft.WindowsAPICodePack-Shell
For the include line:
using Microsoft.WindowsAPICodePack.Dialogs;
Usage:
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = "C:\\Users";
dialog.IsFolderPicker = true;
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
MessageBox.Show("You selected: " + dialog.FileName);
}
There is a hackish solution using OpenFileDialog where ValidateNames and CheckFileExists are both set to false and FileName is given a mock value to indicate that a directory is selected.
I say hack because it is confusing to users about how to select a folder. They need to be in the desired folder and then just press Open while file name says "Folder Selection."
This is based on Select file or folder from the same dialog by Denis Stankovski.
OpenFileDialog folderBrowser = new OpenFileDialog();
// Set validate names and check file exists to false otherwise windows will
// not let you select "Folder Selection."
folderBrowser.ValidateNames = false;
folderBrowser.CheckFileExists = false;
folderBrowser.CheckPathExists = true;
// Always default to Folder Selection.
folderBrowser.FileName = "Folder Selection.";
if (folderBrowser.ShowDialog() == DialogResult.OK)
{
string folderPath = Path.GetDirectoryName(folderBrowser.FileName);
// ...
}
Here is a pure C# version, nuget-free, that should work with all versions of .NET (including .NET Core, .NET 5, WPF, Winforms, etc.) and uses Windows Vista (and higher) IFileDialog interface with the FOS_PICKFOLDERS options so it has the nice folder picker Windows standard UI.
I have also added WPF's Window type support but this is optional, the WPF-marked lines can just be removed.
usage:
var dlg = new FolderPicker();
dlg.InputPath = #"c:\windows\system32";
if (dlg.ShowDialog() == true)
{
MessageBox.Show(dlg.ResultPath);
}
code:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows; // for WPF support
using System.Windows.Interop; // for WPF support
public class FolderPicker
{
public virtual string ResultPath { get; protected set; }
public virtual string ResultName { get; protected set; }
public virtual string InputPath { get; set; }
public virtual bool ForceFileSystem { get; set; }
public virtual string Title { get; set; }
public virtual string OkButtonLabel { get; set; }
public virtual string FileNameLabel { get; set; }
protected virtual int SetOptions(int options)
{
if (ForceFileSystem)
{
options |= (int)FOS.FOS_FORCEFILESYSTEM;
}
return options;
}
// for WPF support
public bool? ShowDialog(Window owner = null, bool throwOnError = false)
{
owner ??= Application.Current.MainWindow;
return ShowDialog(owner != null ? new WindowInteropHelper(owner).Handle : IntPtr.Zero, throwOnError);
}
// for all .NET
public virtual bool? ShowDialog(IntPtr owner, bool throwOnError = false)
{
var dialog = (IFileOpenDialog)new FileOpenDialog();
if (!string.IsNullOrEmpty(InputPath))
{
if (CheckHr(SHCreateItemFromParsingName(InputPath, null, typeof(IShellItem).GUID, out var item), throwOnError) != 0)
return null;
dialog.SetFolder(item);
}
var options = FOS.FOS_PICKFOLDERS;
options = (FOS)SetOptions((int)options);
dialog.SetOptions(options);
if (Title != null)
{
dialog.SetTitle(Title);
}
if (OkButtonLabel != null)
{
dialog.SetOkButtonLabel(OkButtonLabel);
}
if (FileNameLabel != null)
{
dialog.SetFileName(FileNameLabel);
}
if (owner == IntPtr.Zero)
{
owner = Process.GetCurrentProcess().MainWindowHandle;
if (owner == IntPtr.Zero)
{
owner = GetDesktopWindow();
}
}
var hr = dialog.Show(owner);
if (hr == ERROR_CANCELLED)
return null;
if (CheckHr(hr, throwOnError) != 0)
return null;
if (CheckHr(dialog.GetResult(out var result), throwOnError) != 0)
return null;
if (CheckHr(result.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out var path), throwOnError) != 0)
return null;
ResultPath = path;
if (CheckHr(result.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEEDITING, out path), false) == 0)
{
ResultName = path;
}
return true;
}
private static int CheckHr(int hr, bool throwOnError)
{
if (hr != 0)
{
if (throwOnError)
Marshal.ThrowExceptionForHR(hr);
}
return hr;
}
[DllImport("shell32")]
private static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IShellItem ppv);
[DllImport("user32")]
private static extern IntPtr GetDesktopWindow();
#pragma warning disable IDE1006 // Naming Styles
private const int ERROR_CANCELLED = unchecked((int)0x800704C7);
#pragma warning restore IDE1006 // Naming Styles
[ComImport, Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")] // CLSID_FileOpenDialog
private class FileOpenDialog
{
}
[ComImport, Guid("42f85136-db7e-439c-85f1-e4075d135fc8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IFileOpenDialog
{
[PreserveSig] int Show(IntPtr parent); // IModalWindow
[PreserveSig] int SetFileTypes(); // not fully defined
[PreserveSig] int SetFileTypeIndex(int iFileType);
[PreserveSig] int GetFileTypeIndex(out int piFileType);
[PreserveSig] int Advise(); // not fully defined
[PreserveSig] int Unadvise();
[PreserveSig] int SetOptions(FOS fos);
[PreserveSig] int GetOptions(out FOS pfos);
[PreserveSig] int SetDefaultFolder(IShellItem psi);
[PreserveSig] int SetFolder(IShellItem psi);
[PreserveSig] int GetFolder(out IShellItem ppsi);
[PreserveSig] int GetCurrentSelection(out IShellItem ppsi);
[PreserveSig] int SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);
[PreserveSig] int GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName);
[PreserveSig] int SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
[PreserveSig] int SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);
[PreserveSig] int SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
[PreserveSig] int GetResult(out IShellItem ppsi);
[PreserveSig] int AddPlace(IShellItem psi, int alignment);
[PreserveSig] int SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
[PreserveSig] int Close(int hr);
[PreserveSig] int SetClientGuid(); // not fully defined
[PreserveSig] int ClearClientData();
[PreserveSig] int SetFilter([MarshalAs(UnmanagedType.IUnknown)] object pFilter);
[PreserveSig] int GetResults([MarshalAs(UnmanagedType.IUnknown)] out object ppenum);
[PreserveSig] int GetSelectedItems([MarshalAs(UnmanagedType.IUnknown)] out object ppsai);
}
[ComImport, Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IShellItem
{
[PreserveSig] int BindToHandler(); // not fully defined
[PreserveSig] int GetParent(); // not fully defined
[PreserveSig] int GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
[PreserveSig] int GetAttributes(); // not fully defined
[PreserveSig] int Compare(); // not fully defined
}
#pragma warning disable CA1712 // Do not prefix enum values with type name
private enum SIGDN : uint
{
SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000,
SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,
SIGDN_FILESYSPATH = 0x80058000,
SIGDN_NORMALDISPLAY = 0,
SIGDN_PARENTRELATIVE = 0x80080001,
SIGDN_PARENTRELATIVEEDITING = 0x80031001,
SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001,
SIGDN_PARENTRELATIVEPARSING = 0x80018001,
SIGDN_URL = 0x80068000
}
[Flags]
private enum FOS
{
FOS_OVERWRITEPROMPT = 0x2,
FOS_STRICTFILETYPES = 0x4,
FOS_NOCHANGEDIR = 0x8,
FOS_PICKFOLDERS = 0x20,
FOS_FORCEFILESYSTEM = 0x40,
FOS_ALLNONSTORAGEITEMS = 0x80,
FOS_NOVALIDATE = 0x100,
FOS_ALLOWMULTISELECT = 0x200,
FOS_PATHMUSTEXIST = 0x800,
FOS_FILEMUSTEXIST = 0x1000,
FOS_CREATEPROMPT = 0x2000,
FOS_SHAREAWARE = 0x4000,
FOS_NOREADONLYRETURN = 0x8000,
FOS_NOTESTFILECREATE = 0x10000,
FOS_HIDEMRUPLACES = 0x20000,
FOS_HIDEPINNEDPLACES = 0x40000,
FOS_NODEREFERENCELINKS = 0x100000,
FOS_OKBUTTONNEEDSINTERACTION = 0x200000,
FOS_DONTADDTORECENT = 0x2000000,
FOS_FORCESHOWHIDDEN = 0x10000000,
FOS_DEFAULTNOMINIMODE = 0x20000000,
FOS_FORCEPREVIEWPANEON = 0x40000000,
FOS_SUPPORTSTREAMABLEITEMS = unchecked((int)0x80000000)
}
#pragma warning restore CA1712 // Do not prefix enum values with type name
}
result:
Strange that so much answers/votes, but no one add the following code as an answer:
using (var opnDlg = new OpenFileDialog()) //ANY dialog
{
//opnDlg.Filter = "Png Files (*.png)|*.png";
//opnDlg.Filter = "Excel Files (*.xls, *.xlsx)|*.xls;*.xlsx|CSV Files (*.csv)|*.csv"
if (opnDlg.ShowDialog() == DialogResult.OK)
{
//opnDlg.SelectedPath -- your result
}
}
Sounds to me like you're just after the FolderBrowserDialog.
Here is another solution, that has all the source available in a single, simple ZIP file.
It presents the OpenFileDialog with additional windows flags that makes it work like the Windows 7+ Folder Selection dialog.
Per the website, it is public domain: "There’s no license as such as you are free to take and do with the code what you will."
Article: .NET Win 7-style folder select dialog (http://www.lyquidity.com/devblog/?p=136)
Source code: http://s3downloads.lyquidity.com/FolderSelectDialog/FolderSelectDialog.zip
Archive.org links:
Article: https://web.archive.org/web/20180823181552/http://www.lyquidity.com/devblog/?p=136
Source code: https://web.archive.org/web/20180823181632/http://s3downloads.lyquidity.com/FolderSelectDialog/FolderSelectDialog.zip
Take a look at the Ookii Dialogs libraries which has an implementation of a folder browser dialog for Windows Forms and WPF respectively.
Ookii.Dialogs.WinForms
https://github.com/augustoproiete/ookii-dialogs-winforms
Ookii.Dialogs.Wpf
https://github.com/augustoproiete/ookii-dialogs-wpf
Im new to C# and came across this thread just now.
This might be of interest to those that are new to the language as well.
Change design of FolderBrowserDialog
The answer given by Simon Mourier would be the best answer considering the OP question. It does not involve NuGET package so there will not be any dependency issue in the future for selecting folder method.
If you encountered error related to "...is not available in C# 7.3", just add <LangVersion>8.0</LangVersion> to your .csproj (testing with Visual Studio does not produce any error when build and run)
If you can't change the project language then just replace owner
??= Application.Current.MainWindow
with
owner = owner ?? Application.Current.MainWindow
This is how I use the folder browser dialog in Small Visual Basic. This code solves the non-selected initial folder issue and also selects the folder from the clipboard or the registry (if any), and if the folder is deleted, it goes up through parents until selecting an existing folder. This makes using the dialog very comfortable.
Note that I am sending 4 tabs because I show the Create new folder button, but if you hide it, use two tabs only.
Public Shared Function OpenFolderDialog(initialFolder As String) As String
Dim folder = GetInitialFolder(initialFolder)
If folder = "" Then folder = GetInitialFolder(System.Windows.Clipboard.GetText())
If folder = "" Then folder = GetInitialFolder(GetSetting("sVB", "OpenFolder", "LastFolder", ""))
Dim th As New Threading.Thread(
Sub()
Threading.Thread.Sleep(300)
System.Windows.Forms.SendKeys.SendWait("{TAB}{TAB}{TAB}{TAB}{RIGHT}")
End Sub)
th.Start()
Try
Dim dlg As New System.Windows.Forms.FolderBrowserDialog With {
.Description = "Select a folder:",
.SelectedPath = folder
}
If dlg.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
SaveSetting("sVB", "OpenFolder", "LastFolder", dlg.SelectedPath)
OpenFolderDialog = dlg.SelectedPath
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Function
Private Shared Function GetInitialFolder(folder As String) As String
Try
If folder <> "" Then
If IO.File.Exists(folder) Then
folder = Path.GetDirectoryName(folder)
Else
Do
If Directory.GetDirectoryRoot(folder) = folder OrElse Directory.Exists(folder) Then
Exit Do
End If
folder = Path.GetDirectoryName(folder)
Loop
End If
End If
Catch
folder = ""
End Try
Return folder
End Function
this should be the most obvious and straight forward way
using (var dialog = new System.Windows.Forms.FolderBrowserDialog())
{
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
if(result == System.Windows.Forms.DialogResult.OK)
{
selectedFolder = dialog.SelectedPath;
}
}

Categories