I'm taking my first steps in COM but I keep getting System.AccessViolationException which is one of those exceptions which tells you nothing of value. I'm trying to create an IShellItem with SHCreateItemFromParsingName. I copied the definition of the interface from another project so I know there is nothing wrong with it, the problem is most likely in my function definition/call, which I wrote myself for learning purposes.
I want to call the unmanaged function without it returning the IShellItem interface but instead passing a reference of it to the last argument in the call.
The function declaration:
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
internal static extern uint SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IBindCtx pbc, Guid riid, out IShellItem ppv);
The function call:
IShellItem file;
SHCreateItemFromParsingName(#"C:\file.txt", null, typeof(IShellItem).GUID, out file);
I find IDL to be rather cryptic, but my reasoning is:
HRESULT = uint
[in] PCWSTR = [MarshalAs(UnmanagedType.LPWStr)] string
[in, optional] IBindCtx = IBindCtx
[in] REFIID = Guid
[out] void = out IShellItem
SHCreateItemFromParsingName function
The problem comes from the REFIID type parameter. It's not a GUID (a 16 bytes struct), but a GUID reference (REFIID), a pointer (so 4 or 8 bytes depending on process bitness).
So you could define the method like this, with a ref keyword (so the struct would be passed by reference, as a pointer):
internal static extern uint SHCreateItemFromParsingName(
[MarshalAs(UnmanagedType.LPWStr)] string pszPath,
IBindCtx pbc,
ref Guid riid,
out IShellItem ppv);
But I recommend this way which is easier to use and avoids creating/copying GUIDs all around (you will call it the same way as you do):
internal static extern uint SHCreateItemFromParsingName(
[MarshalAs(UnmanagedType.LPWStr)] string pszPath,
IBindCtx pbc,
[MarshalAs(UnmanagedType.LPStruct)] Guid riid,
out IShellItem ppv);
Related
In C++, calling this function is just as simple as:
CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, NULL, NULL, (PFN_CERT_ENUM_SYSTEM_STORE)addr);
Addr would just be the base address of where the function resides
In C#, you can't just pass in an address, and will have to pass in a function, which is not what I want in this case. Below is a snippet of my code
public delegate bool CertEnumSystemStoreCallback([In, MarshalAs(UnmanagedType.LPWStr)] String pvSystemStore, uint dwFlags, ref CERT_SYSTEM_STORE_INFO pStoreInfo, uint pvReserved, [In, MarshalAs(UnmanagedType.LPWStr)] String pvArg);
[DllImport("crypt32.dll", CharSet = CharSet.Unicode)]
public static extern uint CertEnumSystemStore(uint dwFlags, uint pvSystemStoreLocationPara, String pvArg, CertEnumSystemStoreCallback pfnEnum);
[StructLayout(LayoutKind.Sequential)]
public struct CERT_SYSTEM_STORE_INFO
{
uint cbSize;
}
CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, 0, null, (CertEnumSystemStoreCallback)addr);
I get a type conversion error that tells me it can't cast type InPtr to type CertEnumSystemStoreCallback.
What can I do to just pass an address containing the function instead of having to supply the function name itself?
First: I'm sorry if the title is wrong. I'm not sure how to name my problem.
In my C API I have a function:
MYAPI_STATUS SetParam(void *hInst, unsigned long param, void *value);
This function accepts different types of pointers depending on param type. Like this:
SetParam(hInst, 1, (void*)"somevalue");
int x = 55;
SetParam(hInst, 2, &x);
I'm just writing a wrapper/binding in C# and I have a problem.
[DllImport("myapi", CallingConvention = CallingConvention.Cdecl]
public static extern uint SetParam(IntPtr hInst, uint paramCode, IntPtr paramValue);
What's the best way to replicate behaviour from C? So the function would look like:
public static uint SetParam(IntPtr hInst, uint paramCode, ref object paramValue);
or possibly:
public static uint SetParam(IntPtr hInst, uint paramCode, object paramValue);
I solved it by marshalling manually first checking type of object if the object is string then I use Marshal.StringToHGlobalAnsi if it's something else then I marshall differently based on what I need.
If someone has any better solution feel free to write :)
The * sign in C programming means give parameter by reference, so this code is not match:
public static uint SetParam(IntPtr hInst, uint paramCode, object paramValue);
Because it gives parameter by value.
This code is very similar to what you want:
public static uint SetParam(IntPtr hInst, uint paramCode, ref object paramValue);
But there is a bit difference. When you using ref before a parameter you have to initialize it before sending to the method, but by using out you don't have this limitation for passing it. So I think the best possible match will be this code:
public static uint SetParam(IntPtr hInst, uint paramCode, out object paramValue);
I have defined the following class using COM to use the IGroupPolicyObject using C#.NET:
[ComImport, Guid("EA502722-A23D-11d1-A7D3-0000F87571E3")]
public class GPClass
{
// The C# compiler will add a parameterless constructor that we will call // to create an instance of the COM coclass.
}
[ComImport, Guid("EA502723-A23D-11d1-A7D3-0000F87571E3"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IGroupPolicyObject
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords")]
void New(
[MarshalAs(UnmanagedType.LPWStr)] string domainName,
[MarshalAs(UnmanagedType.LPWStr)] string displayName,
uint flags);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
void OpenDsgpo(
[MarshalAs(UnmanagedType.LPWStr)] string path,
uint flags);
void OpenLocalMachineGpo(
uint flags);
void OpenRemoteMachineGpo(
[MarshalAs(UnmanagedType.LPWStr)] string computerName,
uint flags);
void Save(
[MarshalAs(UnmanagedType.Bool)] bool machine,
[MarshalAs(UnmanagedType.Bool)] bool add,
[MarshalAs(UnmanagedType.LPStruct)] Guid extension,
[MarshalAs(UnmanagedType.LPStruct)] Guid app);
void Delete();
void GetName(
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder name,
int maxLength);
void GetDisplayName(
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder name,
int maxLength);
void SetDisplayName(
[MarshalAs(UnmanagedType.LPWStr)] string name);
void GetPath(
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder path,
int maxPath);
void GetDSPath(
uint section,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder path,
int maxPath);
void GetFileSysPath(
uint section,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder path,
int maxPath);
IntPtr GetRegistryKey(uint section);
uint GetOptions();
void SetOptions(
uint options,
uint mask);
void GetMachineName(
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder name,
int maxLength);
uint GetPropertySheetPages(
out IntPtr pages);
}
The problem is when I try to use the IGroupPolicyObject as follows I get an InvalidCastException:
GPClass gpClass = new GPClass();
IGroupPolicyObject comGroupPolicyObject = (IGroupPolicyObject)gpClass;
Exception I get is:
Unable to cast COM object of type 'ConfigureRemoteSources.GPClass' to interface type 'ConfigureRemoteSources.IGroupPolicyObject'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{EA502722-A23D-11D1-A7D3-0000F87571E3}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
Any ideas on how to solve this?
Thanks
You can find it back with Regedit.exe, HKCR\CLSID\{EA502722-A23D-11D1-A7D3-0000F87571E3}\InProcServer32 key. Which contains the ThreadModel value, it is set to "Apartment". That means that the coclass is not thread-safe, it must be called from a Single-Threaded Apartment. You'll recognize the acronym, that's what STA means in [STAThread].
There's commonly also a key in HKCR\Interface that declares the proxy/stub DLL that marshals an interface call across apartments. But that's missing. Which is what the error message really means, COM created a separate thread to give the component a safe home but then it couldn't find a way to marshal the call. Microsoft just didn't bother, this coclass is normally used from MMC by running the group policy editor, gpedit.msc. You must provide a similar safe home for this non-threadsafe component, an STA thread that pumps a message loop. The UI thread of a GUI program. You took care of STA with the attribute, probably not of the message loop. You might get away with it, if you notice deadlock then you didn't.
It seems that this article has something clean but I wasn't able to get that working though. I got the issue solved by adding [STAThread] to the main method. (using System.Threading;)
I've got a COM object wtitten on c#, and i'm using it in a c++ dll.
In c++ the COM object is imported with #import derective
The problem is that when the call is made from a c++ dll to any function of the COM object i'm getting a runtime check error:
Run-Time Check Failure #0 - The value of ESP was not properly saved
across a function call. This is usually a result of calling a
function declared with one calling convention with a function pointer
declared with a different calling convention.
In the com object wrapper generated by c++ import directive, all functions are declared as __stdcall
OK, it looks like I have found the solution:
I have declated In/Out/MarshalAs attributes for all methods in the C# COM interface:
[Guid("EEB4C1AE-4DB2-4bdb-86D4-A429B27496A3")]
public interface IAXFarCards
{
[DispId(1)]
void InitDbConnection([In, MarshalAs(UnmanagedType.BStr)] string connectionString);
[DispId(2)]
[return: MarshalAs(UnmanagedType.I4)]
int GetCardInfo(
[In, MarshalAs(UnmanagedType.BStr)] string card,
[In, MarshalAs(UnmanagedType.VariantBool)] bool isTemplate,
[In, MarshalAs(UnmanagedType.I4)] int cashDeskId,
[Out, MarshalAs(UnmanagedType.VariantBool)] out bool isActive,
[Out, MarshalAs(UnmanagedType.I4)] out int discountNumber,
[Out, MarshalAs(UnmanagedType.I8)] out Int64 amount,
[In, Out, MarshalAs(UnmanagedType.BStr)] ref string ownerName,
[In, Out, MarshalAs(UnmanagedType.I4)] ref int clientId
);
I have a dll which accepts a struct that contains a pointer to a function to do a callback.
How can I get an IntPtr to a function of my application to build the struct?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class OPERATION {
public uint OperationID;
public IntPtr Context;
public IntPtr Callback; -> How to pass this?
}
Here is the delegate accepting the OPERATION struct
public delegate void MY_CALLBACK([In] OPERATION operation, [In] uint msgId, [In] IntPtr msgDataPtr);
use Marshal.GetFunctionPointerForDelegate
Maybe the Marshal.GetFunctionPointerForDelegate method may help you.