How to open the “Active Directory Users and Computers” object properties dialog from c#?

谁说胖子不能爱 提交于 2021-01-29 17:28:10

问题


Is there a way to call this dialog from c#?

I traced the apis, but non of the calls seems to call the dialog. Dsuiext.dll sounds very promissing, but there I foud just a LDAP browser.


回答1:


This Microsoft sample provides the expected result. You pass an ADS path as parameter and it calls the property window.

PropSheetHost.exe "LDAP://CN=user,DC=MyDomain,DC=MyTldDomain"

It is important that it is case sensitive, so "ldap://.." doesn't work. The code is definitely not designed to get called multiple times before terminating, so it is probably the best way to use the exe without changes like that:

ProcessStartInfo startInfo = new ProcessStartInfo();        
startInfo.FileName = @"PropSheetHost.exe";
startInfo.Arguments = @"LDAP://CN=user,DC=MyDomain,DC=MyTldDomain";
Process.Start(startInfo);

I wrote a wrapper to call it directly from C# and corrected the error what I found. Since I haven't programmed C for nearly 30 years, I am grateful for any hint if the implementation is incorrect. All changes are explained and marked with //MW: .... This works in my code, but you can open only one windows at a time and need to close it before opening another window.

The entry point:

__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
}

The original implementation doesn't unregister a class. Therefore it fails when it's used multiple times. These are my changes in PropSheetHost.cpp to fix that.

//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
}

... and the call from 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;
    }
}

For the translation of the Windows Error Codes I use this class.



来源:https://stackoverflow.com/questions/65521606/how-to-open-the-active-directory-users-and-computers-object-properties-dialog

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!