如何从 C# 打开 "Active Directory Users and Computers" 对象属性对话框?
How to open the "Active Directory Users and Computers" object properties dialog from c#?
有没有办法从 C# 调用此对话框?
我跟踪了 api,但没有一个调用似乎调用了对话框。 Dsuiext.dll 听起来很有前途,但我只找到了一个 LDAP 浏览器。
这 Microsoft sample 提供了预期的结果。您传递一个 ADS 路径作为参数,它调用 属性 window.
PropSheetHost.exe "LDAP://CN=user,DC=MyDomain,DC=MyTldDomain"
区分大小写很重要,因此“ldap://..”不起作用。代码绝对不是设计为在终止前被多次调用,所以它可能是使用 exe 而不进行这样的更改的最佳方式:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = @"PropSheetHost.exe";
startInfo.Arguments = @"LDAP://CN=user,DC=MyDomain,DC=MyTldDomain";
Process.Start(startInfo);
我编写了一个包装器以直接从 C# 调用它并更正了我发现的错误。由于我已经将近 30 年没有编写 C 语言,如果实现不正确,我很感激任何提示。所有更改均已解释并标有 //MW: ...
。 这在我的代码中有效,但您一次只能打开一个 windows,并且需要在打开另一个 window.
之前将其关闭
入口点:
__declspec(dllexport) HRESULT __stdcall CallPropSheetHost(const char* ldapPath)
{
TCHAR szTemp[MAX_ADSPATH_CHARS];
LPWSTR pwszADsPath = NULL;
HRESULT hr = E_FAIL; // MW: move before "if" and preset
CoInitialize(NULL);
{
//MW: copy the parameter
_tcsncpy_s(szTemp, ARRAYSIZE(szTemp), ldapPath, MAX_ADSPATH_CHARS - 1);
}
DWORD dwChars = lstrlen(szTemp) + 1;
pwszADsPath = new WCHAR[dwChars];
if (pwszADsPath)
{
HINSTANCE hInstance = NULL;
HWND hwndConsole = GetConsoleWindow();
if (hwndConsole)
{
hInstance = (HINSTANCE)(LONG_PTR)GetWindowLongPtr(hwndConsole, GWLP_HINSTANCE);
}
CPropSheetHost* pHost = new CPropSheetHost(hInstance);
LocalToWideChar(pwszADsPath, dwChars, szTemp, dwChars);
// Hold a reference count for the CPropSheetHost object.
pHost->AddRef();
hr = pHost->SetObject(pwszADsPath);
if (FAILED(hr))
{
goto ExitMain;
}
//MW: My implmentation returns E_Fail when the registration fails
hr = pHost->Run();
if (FAILED(hr))
{
pHost->Release();
goto ExitMain;
}
//Release the CPropSheetHost object. Other components may still hold a
//reference to the object, so this cannot just be deleted here. Let
//the object delete itself when all references are released.
pHost->Release();
}
ExitMain:
if (pwszADsPath)
{
delete pwszADsPath;
return hr; //MW: return th HRESULT
}
CoUninitialize();
return hr; //MW: return th HRESULT
}
原始实现不会注销 class。因此,它在多次使用时会失败。这些是我在 PropSheetHost.cpp
中的更改以解决此问题。
//MW: new method
void CPropSheetHost::_UnregisterWndClass()
{
UnregisterClass(m_szHiddenWindowClass, m_hInst);
}
//MW: added a HRESULT and calling of "_UnregisterWndClass"
HRESULT CPropSheetHost::Run()
{
if (!m_spADObject.p)
{
return E_FAIL; //MW: added a return value
}
// Create the hidden window.
m_hwndHidden = _CreateHiddenWindow();
if (!m_hwndHidden)
{
return E_FAIL; //MW: added a return value
}
/*
Display the proeprty sheet. This is a modal call and will not return
until the property sheet is dimissed.
*/
_CreatePropertySheet();
// Destroy the hidden window.
DestroyWindow(m_hwndHidden);
//WM: Unregister the class; this call was missing
_UnregisterWndClass();
return ERROR_SUCCESS; //MW: added a return value
}
...以及来自 C# 的调用:
using System;
using System.Runtime.InteropServices;
using System.Windows;
const int MAX_ADSPATH_CHARS = 2047;
[DllImport("PropSheetHost.dll", EntryPoint = "CallPropSheetHost", CallingConvention = CallingConvention.Cdecl)]
private static extern int CallPropSheetHost(string ldapPath);
///CAUTION:
/// * This call is modal and won't return until the called window is closed
/// * You can open only one window at a time. Trying opening a second window before closing the the first one fails
public static int Win32PropSheetHost(string distinguishedName, string serverOrDomain = null)
{
if (string.IsNullOrEmpty(distinguishedName)) throw new ArgumentNullException("EXC262: the distinguished name must not be null nor empty");
//<----------
/// Caution: "LDAP" must be uppercase!
string ldapPath = string.IsNullOrEmpty(serverOrDomain)
? $"LDAP://{ distinguishedName }"
: $"LDAP://{ serverOrDomain }/{ distinguishedName }";
if (ldapPath.Length > MAX_ADSPATH_CHARS) throw new ArgumentException($"EXC263: the complete lds path must not be longer than { MAX_ADSPATH_CHARS } characters (current path: \"{ ldapPath }\")");
//<----------
try
{
return CallPropSheetHost(ldapPath);
}
catch (DllNotFoundException ex)
{
/// Could't find a dll, mos likely our propsheethost.dll
return ResultWin32.ERROR_DLL_NOT_FOUND;
}
}
对于 Windows 错误代码的翻译,我使用 this class。
有没有办法从 C# 调用此对话框?
我跟踪了 api,但没有一个调用似乎调用了对话框。 Dsuiext.dll 听起来很有前途,但我只找到了一个 LDAP 浏览器。
这 Microsoft sample 提供了预期的结果。您传递一个 ADS 路径作为参数,它调用 属性 window.
PropSheetHost.exe "LDAP://CN=user,DC=MyDomain,DC=MyTldDomain"
区分大小写很重要,因此“ldap://..”不起作用。代码绝对不是设计为在终止前被多次调用,所以它可能是使用 exe 而不进行这样的更改的最佳方式:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = @"PropSheetHost.exe";
startInfo.Arguments = @"LDAP://CN=user,DC=MyDomain,DC=MyTldDomain";
Process.Start(startInfo);
我编写了一个包装器以直接从 C# 调用它并更正了我发现的错误。由于我已经将近 30 年没有编写 C 语言,如果实现不正确,我很感激任何提示。所有更改均已解释并标有 //MW: ...
。 这在我的代码中有效,但您一次只能打开一个 windows,并且需要在打开另一个 window.
入口点:
__declspec(dllexport) HRESULT __stdcall CallPropSheetHost(const char* ldapPath)
{
TCHAR szTemp[MAX_ADSPATH_CHARS];
LPWSTR pwszADsPath = NULL;
HRESULT hr = E_FAIL; // MW: move before "if" and preset
CoInitialize(NULL);
{
//MW: copy the parameter
_tcsncpy_s(szTemp, ARRAYSIZE(szTemp), ldapPath, MAX_ADSPATH_CHARS - 1);
}
DWORD dwChars = lstrlen(szTemp) + 1;
pwszADsPath = new WCHAR[dwChars];
if (pwszADsPath)
{
HINSTANCE hInstance = NULL;
HWND hwndConsole = GetConsoleWindow();
if (hwndConsole)
{
hInstance = (HINSTANCE)(LONG_PTR)GetWindowLongPtr(hwndConsole, GWLP_HINSTANCE);
}
CPropSheetHost* pHost = new CPropSheetHost(hInstance);
LocalToWideChar(pwszADsPath, dwChars, szTemp, dwChars);
// Hold a reference count for the CPropSheetHost object.
pHost->AddRef();
hr = pHost->SetObject(pwszADsPath);
if (FAILED(hr))
{
goto ExitMain;
}
//MW: My implmentation returns E_Fail when the registration fails
hr = pHost->Run();
if (FAILED(hr))
{
pHost->Release();
goto ExitMain;
}
//Release the CPropSheetHost object. Other components may still hold a
//reference to the object, so this cannot just be deleted here. Let
//the object delete itself when all references are released.
pHost->Release();
}
ExitMain:
if (pwszADsPath)
{
delete pwszADsPath;
return hr; //MW: return th HRESULT
}
CoUninitialize();
return hr; //MW: return th HRESULT
}
原始实现不会注销 class。因此,它在多次使用时会失败。这些是我在 PropSheetHost.cpp
中的更改以解决此问题。
//MW: new method
void CPropSheetHost::_UnregisterWndClass()
{
UnregisterClass(m_szHiddenWindowClass, m_hInst);
}
//MW: added a HRESULT and calling of "_UnregisterWndClass"
HRESULT CPropSheetHost::Run()
{
if (!m_spADObject.p)
{
return E_FAIL; //MW: added a return value
}
// Create the hidden window.
m_hwndHidden = _CreateHiddenWindow();
if (!m_hwndHidden)
{
return E_FAIL; //MW: added a return value
}
/*
Display the proeprty sheet. This is a modal call and will not return
until the property sheet is dimissed.
*/
_CreatePropertySheet();
// Destroy the hidden window.
DestroyWindow(m_hwndHidden);
//WM: Unregister the class; this call was missing
_UnregisterWndClass();
return ERROR_SUCCESS; //MW: added a return value
}
...以及来自 C# 的调用:
using System;
using System.Runtime.InteropServices;
using System.Windows;
const int MAX_ADSPATH_CHARS = 2047;
[DllImport("PropSheetHost.dll", EntryPoint = "CallPropSheetHost", CallingConvention = CallingConvention.Cdecl)]
private static extern int CallPropSheetHost(string ldapPath);
///CAUTION:
/// * This call is modal and won't return until the called window is closed
/// * You can open only one window at a time. Trying opening a second window before closing the the first one fails
public static int Win32PropSheetHost(string distinguishedName, string serverOrDomain = null)
{
if (string.IsNullOrEmpty(distinguishedName)) throw new ArgumentNullException("EXC262: the distinguished name must not be null nor empty");
//<----------
/// Caution: "LDAP" must be uppercase!
string ldapPath = string.IsNullOrEmpty(serverOrDomain)
? $"LDAP://{ distinguishedName }"
: $"LDAP://{ serverOrDomain }/{ distinguishedName }";
if (ldapPath.Length > MAX_ADSPATH_CHARS) throw new ArgumentException($"EXC263: the complete lds path must not be longer than { MAX_ADSPATH_CHARS } characters (current path: \"{ ldapPath }\")");
//<----------
try
{
return CallPropSheetHost(ldapPath);
}
catch (DllNotFoundException ex)
{
/// Could't find a dll, mos likely our propsheethost.dll
return ResultWin32.ERROR_DLL_NOT_FOUND;
}
}
对于 Windows 错误代码的翻译,我使用 this class。