如何从托管代码中使用win32'IOpenControlPanel'接口?

时间:2016-05-06 07:46:34

标签: c# .net vb.net winapi pinvoke

我正在尝试实施 IOpenControlPanel 界面,该界面没有在 pinvoke.net 这样的网站中记录,所以对于这个任务,我建立了我认为它们应该是从头开始的定义,然后我尝试从注册表中手动检索接口CLSID(似乎是D11AD862-66DE-4DF4-BF6C-1F5621996AF1),以及一个实现该接口的类,它似乎是06622D85-6856-4460-8DE1-A81921B41C4B

问题是在下面的代码中如果我调用GetCurrentView函数我没有得到预期的值,并且对Open函数的调用什么都不做(我使用的是正确的规范名称与 this MSDN article this list of canonical names 中解释的Microsoft.DefaultPrograms类似。)

Dim cp As New COpenControlPanel
Dim view As ControlPanelView
DirectCast(cp, IOpenControlPanel).GetCurrentView(view)
DirectCast(cp, IOpenControlPanel).Open("Microsoft.DefaultPrograms", "", Nothing)

所以,我认为我的定义在某种程度上是错误的,我需要帮助来解决它。

这些是定义:

VB.Net:

Friend NotInheritable Class NativeMethods

    Enum ControlPanelView As Integer
        Classic = 0
        Category = 1
    End Enum

    <ComImport()>
    <Guid("06622D85-6856-4460-8DE1-A81921B41C4B")>
    Class COpenControlPanel
    End Class

    <ComImport>
    <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    <Guid("D11AD862-66DE-4DF4-BF6C-1F5621996AF1")>
    Public Interface IOpenControlPanel

        <PreserveSig()>
        Function Open(<MarshalAs(UnmanagedType.BStr)> ByVal name As String,
                      <MarshalAs(UnmanagedType.BStr)> ByVal page As String,
                                                      ByVal punkSite As IntPtr
        ) As Integer ' HResult

        <PreserveSig()>
        Function GetPath(<MarshalAs(UnmanagedType.BStr)> ByVal name As String,
                       <MarshalAs(UnmanagedType.LPWStr)> ByVal path As StringBuilder,
                                                         ByVal bufferSize As Integer
        ) As Integer ' HResult

        <PreserveSig()>
        Function GetCurrentView(ByRef refView As ControlPanelView
        ) As Integer ' HResult

    End Interface

End Class

C#(在线翻译):

internal sealed class NativeMethods {

    public enum ControlPanelView : int {
        Classic = 0,
        Category = 1
    }

    [ComImport()]
    [Guid("06622D85-6856-4460-8DE1-A81921B41C4B")]
    class COpenControlPanel {}

    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("D11AD862-66DE-4DF4-BF6C-1F5621996AF1")]
    public interface IOpenControlPanel
    {

        [PreserveSig()]
        int Open([MarshalAs(UnmanagedType.BStr)] string name, 
                 [MarshalAs(UnmanagedType.BStr)] string page, 
                 IntPtr punkSite);

        [PreserveSig()]
        int GetPath([MarshalAs(UnmanagedType.BStr)] string name, 
                    [MarshalAs(UnmanagedType.LPWStr)] StringBuilder path, 
                    int bufferSize);

        [PreserveSig()]
        int GetCurrentView(ref ControlPanelView refView);

    }
}

1 个答案:

答案 0 :(得分:4)

您的接口定义错误,因为您没有按照与MSDN相同的顺序定义方法(实际上,名称并不重要,接口方法布局的重要性是:按正确的顺序匹配二进制签名) 。订单必须正好是Windows SDK中可用的.h文件中定义的内容,而不是MSDN显示的内容 - 这实际上是误导性的:-)。在这种情况下,头文件是Shobjidl.h。这是它在C / C ++中的定义:

MIDL_INTERFACE("D11AD862-66DE-4DF4-BF6C-1F5621996AF1")
IOpenControlPanel : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE Open( 
        /* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszName,
        /* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszPage,
        /* [unique][in] */ __RPC__in_opt IUnknown *punkSite) = 0;

    virtual HRESULT STDMETHODCALLTYPE GetPath( 
        /* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszName,
        /* [size_is][string][out] */ __RPC__out_ecount_full_string(cchPath) LPWSTR pszPath,
        /* [in] */ UINT cchPath) = 0;

    virtual HRESULT STDMETHODCALLTYPE GetCurrentView( 
        /* [out] */ __RPC__out CPVIEW *pView) = 0;

};

.NET,C#中有多个等效定义,但这里应该有一个:

    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("D11AD862-66DE-4DF4-BF6C-1F5621996AF1")]
    public interface IOpenControlPanel
    {
        [PreserveSig]
        int Open([MarshalAs(UnmanagedType.LPWStr)] string name,
                 [MarshalAs(UnmanagedType.LPWStr)] string page,
                                                   IntPtr punkSite);
        [PreserveSig]
        int GetPath([MarshalAs(UnmanagedType.LPWStr)] string name,
                    [MarshalAs(UnmanagedType.LPWStr)] StringBuilder refPath,
                                                      int bufferSize);

        // if you remove PreserveSig, you can return the [out] param directly
        // note in this case, the function could throw instead of returning an error int like with PreserveSig
        ControlPanelView GetCurrentView();
    }