I'm trying to implement the IOpenControlPanel interface, which is not documented in sites like pinvoke.net, so for this task I built the definitions from scratch as I think they should be, then I tried to manually retrieve the interface CLSID from registry, which seems to be D11AD862-66DE-4DF4-BF6C-1F5621996AF1
, and a Class that implements that inferface, which seems to be 06622D85-6856-4460-8DE1-A81921B41C4B
.
The problem is that in the following code If I call GetCurrentView
function I don't get the expected value, and a call to Open
function does nothing (I'm using a proper canonical name like Microsoft.DefaultPrograms
as explained in this MSDN article from this list of canonical names.)
Dim cp As New COpenControlPanel
Dim view As ControlPanelView
DirectCast(cp, IOpenControlPanel).GetCurrentView(view)
DirectCast(cp, IOpenControlPanel).Open("Microsoft.DefaultPrograms", "", Nothing)
So, I think that my definitions are wrong in some way, I need help to fix it.
These are the the definitions:
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# (online translation):
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);
}
}
Your interface definition is wrong because you did not defined methods in the same order as MSDN does (in fact, names are not important, what's important is the interface methods layout: matching binary signatures in the correct order). The order must be exactly what's defined in .h files available with the Windows SDK, not what MSDN displays - this is actually misleading :-). In this case, the header file is Shobjidl.h. This is how it's defined in 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;
};
There are multiple equivalent definition in .NET, C#, but here is one that should work:
[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();
}