Passing C# array of COM objects to VB6 - c#

I'm trying to pass .NET array to COM VB6 library. I have an object which is COM wrapper of VB6 object. It has method with the following signature:
[MethodImpl(MethodImplOptions.InternalCall,
MethodCodeType = MethodCodeType.Runtime)]
void AddEx([MarshalAs(UnmanagedType.Struct)] object vSafeArrayOfItems);
but when I call it I get an ArgumentException with the following message:
Value does not fall within the expected range.
The type of exception and its description doesn't even depend on passed element.
Does anybody know how to go around this issue?
UPD: I removed .NET wrapper assemblies and referrenced source .COM libraries. No changes had happened.

You would need to mirror the VB6 struct format:
[StructLayout(LayoutKind.Sequential)]
public struct myStruct {
type1 member1;
type2 member2;
}
To import the function you would have to do:
[DllImport("dllname.dll")]
public static extern void AddEx(IntPtr paramName);
You can easily use the following functions to perform struct <-> IntPtr conversions:
myStruct struct = Marshal.PtrToStructure(paramName, typeof(myStruct));
// do stuff
Marshal.StructureToPtr(struct, paramName, false);
Edit: I misread what you wanted to do. But this is a starter for doing the interop.
The argument exception comes from trying to send a reference type as a value type. (object is a class, structs are handled differently)
If you want to pass an array you would do:
void AddEx([MarshalAs(UnmanagedType.LPArray)] ref myStruct[] param);

I think you could write the external method declaration like the following:
[DllImport...
public static extern void AddEx(YourType[] paramName);
//or like the following:
public static extern unsafe void AddEx(YourType * paramName);

Related

Is it safe to pass a struct with "ref this" to native code

I am currently integrating the Steamworks SDK into my game and have a couple of methods which require to pass a struct as a pointer, for example:
// Public interface method declared by the library
S_API bool SteamAPI_SteamNetworkingIPAddr_IsLocalHost( SteamNetworkingIPAddr* self );
// C# implementation
[StructLayout(LayoutKind.Explicit, Size = 18, Pack = 1)]
public struct SteamNetworkingIPAddr {
// My current usage of "ref this"
public bool IsLocalhost => NativeMethods.SteamAPI_SteamNetworkingIPAddr_IsLocalHost(ref this);
// How another library solves it
public bool IsLocalhost {
get {
SteamNetworkingIPAddr copy = this;
NativeMethods.SteamAPI_SteamNetworkingIPAddr_IsLocalHost(ref self);
}
}
private static class NativeMethods {
[DllImport(SteamAPI.LIBRARY_NAME, EntryPoint = "SteamAPI_SteamNetworkingIPAddr_IsLocalHost", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool SteamAPI_SteamNetworkingIPAddr_IsLocalHost(ref SteamNetworkingIPAddr self);
}
}
I am a bit concerned about passing this with ref to the native method because I've seen other implementations which create a copy of the struct before passing it but I could not find something which officially says that it's either safe or unsafe to do so.
So, my question is - should I make a copy or keep my solution?
In a instance method or property of a struct, the implicit this parameter is a ref managed reference. So any changes to the struct do mutate (change) the struct passed in.
Therefore, when you call the native function, you are passing an actual reference to your own struct. So it is possible for your caller to see these changes, if they have passed in a reference to their own struct. But depending on how your caller makes this call, there may be a defensive copy anyway.
For example:
var isLocal = SomeClass.IPAddrProperty.IsLocalhost
this will create a copy of the struct, and any changes will disappear.
Whereas this:
var ipAddr = SomeClass.IPAddrProperty;
var isLocal = ipAddr.IsLocalhost;
SomeClass.IPAddrProperty = ipAddr;
means that the results are copied back.

How do I go about instantiating a COM Object in C# by CLSID?

Forgive me if my terminology is off, as this is somewhat uncharted territory for me.
I have a program which needs to create a FolderShortcut. Microsoft has documentation on how to create it in C++, and I'm trying to translate the directions to C#. The instructions state that the CoCreateInstance function needs to be called with CLSID_FolderShortcut as a parameter, which I infer to mean that it's instantiating a COM object. The CLSID for this object is {0AFACED1-E828-11D1-9187-B532F1E9575D}.
I've tried adding a reference to Shell32.dll from the COM tab, but the FolderShortcut object does not show up in Intellisense (maybe it's not in the typelib?). I also thought about doing a DLLImport, but, of course, that only gives me access to functions, not objects.
What do I need to do to get access to this object in .Net?
If you do not want to import the classes during compile time, as Simon Mourier describes it is also possible to do some late binding to the COM objects, using Activator.
If you've got the ProgID of your object, get the type using
Type comType = Type.GetTypeFromProgID("MyProg.ProgId");
otherwise you can get the type by it's CLSID:
Type comType =
Type.GetTypeFromCLSID(new Guid("0AFACED1-E828-11D1-9187-B532F1E9575D"));
Using this type, you are now able to create an instance of the coclass, using Activator.CreateInstance:
var instance = Activator.CreateInstance(comType);
Basicly you can now invoke methods using Type.InvokeMember. This only works if the object implements the IDispatch interface.
However for your specific example you should be able to cast the instance to System.Runtime.InteropServices.ComTypes.IPersistFile, which results in an call to QueryInterface for COM objects. Using this interface you can easily access the members of IPersistFile.
You may continue here with further reading.
Here is a piece of code that allows you to create a folder shortcut. The CoCreateInstance can (in general) be replaced by declaring a simple class decorated with the Guid attribute with the required CLSID, and the ComImport attribute. The new call will do the COM magic automatically. With this code, you don't even need a Shell32 reference (or you can reuse the IShellLink declaration from there if you prefer).
Usage:
static void Main(string[] args)
{
CreateFolderShortcut(#"c:\temp", Path.GetFullPath("Shortcut to Temp"));
}
Code:
public static void CreateFolderShortcut(string path, string shortcutPath)
{
CreateFolderShortcut(path, shortcutPath, null);
}
public static void CreateFolderShortcut(string path, string shortcutPath, string comment)
{
if (path == null)
throw new ArgumentNullException("path");
IShellLink link = (IShellLink)new ShellLinkFolder();
if (comment != null)
{
link.SetDescription(comment);
}
link.SetPath(path);
IPersistFile file = (IPersistFile)link;
file.Save(shortcutPath, false);
}
[ComImport]
[Guid("00021401-0000-0000-C000-000000000046")]
private class ShellLink
{
}
[ComImport]
[Guid("0AFACED1-E828-11D1-9187-B532F1E9575D")]
private class ShellLinkFolder
{
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
private interface IShellLink
{
void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags);
void GetIDList(out IntPtr ppidl);
void SetIDList(IntPtr pidl);
void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
void GetHotkey(out short pwHotkey);
void SetHotkey(short wHotkey);
void GetShowCmd(out int piShowCmd);
void SetShowCmd(int iShowCmd);
void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
void Resolve(IntPtr hwnd, int fFlags);
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
Did you try to add a new reference:
Open Solution Explorer
Expand the C# project
Right click on the references node and add a new COM reference
It sounds like that you have tried adding your COM using the "Add a new COM reference".
These are the things that I'd try:
What I'd do first is to make sure that your COM DLL is registered in your computer. If not then register it then try to add it again using the COM tab.
Are you running on a 64bit machine? Try to also make sure that your project properties is set to AnyCPU for it to be able to read the 32bit COM.
Make sure that you have the interop equivalent of the DLL that you are trying to access. It is usually named like "Interop.YourDLLName.dll". Add a reference to that DLL and that should work.

How do I call a function in a C++ Dll from C# that has void* callback and object parameter

I am trying to create a wrapper to a C dll and I am trying to call a function that has takes a callback function, receives an object as a pointer that is passed back.
The .h file delares
extern int SetErrorHandler(void (*handler) (int, const char*, void*),
void *data_ptr);
The handler is a callback function that is called when an error occurs and the data_ptr is any object (state) that is passed back to you, in the case of my app that will just be this (current object).
I am able to call functions in a dll that uses marshalled constant types like simple types strings, ints etc. But I cant figure out how to just marshall a pointer to a C# object that is the state.
In order to pass the object reference to the C function from what I have find by searching here and otherwise it seems that I need a structure type to be able to marshall to the function so I created a struct to hold my object:
[StructLayout(LayoutKind.Sequential)]
struct MyObjectState
{
public object state;
}
EDIT: I tried to put an attribute: [MarshalAs(UnmanagedType.Struct, SizeConst = 4)] on the public object state property, but this produces the same error, so I removed it, doesnt seem it would work anyway.
The struct contains a single object property to hold any object for the callback function.
I declared the delegate in C# as follows:
delegate void ErrorHandler(int errorCode, IntPtr name, IntPtr data);
Then I declared the import function in C# as follows:
[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int SetErrorHandler handler, IntPtr data);
Then I created a callback function in my C# code:
void MyErrorHandler(int errorCode, IntPtr name, IntPtr data)
{
var strName = Marshal.PtrToStringAnsi(name);
var state = new MyObjectState();
Marshal.PtrToStructure(data, state);
Console.WriteLine(strName);
}
I am able to call the library function as follows:
var state = new MyObjectState()
{
state = this
};
IntPtr pStruct = Marshal.AllocHGlobal(Marshal.SizeOf(state));
Marshal.StructureToPtr(state, pStruct, true);
int ret = SetErrorHandler(MyErrorHandler, pStruct);
The call works and the callback function is called but I am unable to access the data in the callback function and when i try Marshal.PtrToStructure I get an error:
The structure must not be a value class.
I did a lot of searching here and found various things on Marshall and void* but nothing has helped me to get this to work
Thanks.
You are making this more complicated than it needs to be. Your C# client does not need to use the data_ptr parameter because a C# delegate already has a built in mechanism for maintaining the this pointer.
So you can simply pass IntPtr.Zero to the delegate. Inside your error handler delegate you just ignore the value of data_ptr since this will be available.
In case you don't follow this description, here's a short program to illustrate what I mean. Note how MyErrorHandler is an instance method that acts as the error handler, and can modify instance data.
class Wrapper
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void ErrorHandler(int errorCode, string name, IntPtr data);
[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int SetErrorHandler(ErrorHandler handler, IntPtr data);
void MyErrorHandler(int errorCode, string name, IntPtr data)
{
lastError = errorCode;
lastErrorName = name;
}
public Wrapper()
{
SetErrorHandler(MyErrorHandler, IntPtr.Zero);
}
public int lastError { get; set; }
public string lastErrorName { get; set; }
}
class Program
{
static void Main(string[] args)
{
Wrapper wrapper = new Wrapper();
}
}
There may very well be a way to do this, but I gave up a long time ago. The solution I've come up with is slightly hackish, but it's very effective and works with everything I've thrown at it:
C# -> Managed C++ -> Native calls
Doing it this way you end up writing a small wrapper in managed C++, which is kind of a pain, but I found to be more capable and less painful than all of that marshaling code.
Honestly though I'm kind of hoping that someone gives a non-evasive answer, I've struggled with this for quite a while myself.

AccessViolationException when marshaling a C++ struct to C# class

I'm trying to wrap a C++ library (libclang) into a C# wrapper, using PInvoke.
Everything was shiny, until I tried to call a C++ method that returns a struct.
I did everything by the book, but when this method gets called, I get AccessViolationException.
Now, I read that it's probably because there's something messed up with the memory image of the objects. I checked and doublechecked if I've put all the arrtibutes and what nots everywhere but the Exception won't go away. (I've been looking at this code for hours, so I may have missed some things, you guys don't).
The C++ part (not my code, but I'm sure that it works):
CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
/* Parse the hell out of a lot of data, an then make a string
In the end, string gets wrapped in a custom struct: CXString.
*/
return createCXString(Out.str(), true);
}
Here's CXString:
typedef struct {
void *data;
unsigned private_flags;
} CXString;
So I have my C# classes to represent the wrapped C++ originals:
public class Diagnostic
{
private IntPtr _nativeObject;
internal IntPtr NativeObject { get { return _nativeObject; } }
[DllImport("libclang.dll", EntryPoint = "clang_formatDiagnostic")]
[return: MarshalAs(UnmanagedType.LPStruct)]
private static extern CXString FormatDiagnostic(IntPtr diag, uint options);
public Diagnostic(IntPtr native)
{
_nativeObject = native;
}
public string GetString(DiagnosticDisplayOptions options = DiagnosticDisplayOptions.DisplaySourceLocation)
{
var cxstr = FormatDiagnostic(_nativeObject, (uint)options); //<-- I get the exception here
return cxstr.GetString();
}
}
The functions I need are also implemented in a C taste (global) so, I can make an impression of OO in my C# classes, but in fact I store the IntPtr representations of the C++ objects (_nativeObject). So I'm pretty sure, that the object stored in the _nativeObject is in fact an CXDiagnostic (I got the reference returned from another function of the same library).
The actual object that I'm trying to use with FormatDiagnostic method gets initialized from the constructor of another wrapper class (TranslationUnit):
public TranslationUnit(/*lots of params to init the unit*/)
{
//... there are some int conversions and initialization failsafe codes here
//this is a property of TranslationUnit
Diagnostics = new List<Diagnostic>();
//GetDiagnosticsCount is also PInvoke to count CXDiagnostic objects related to the TranslationUnit
var dgCnt = (int)GetDiagnosticsCount(_nativeObject);
for (int i = 0; i < dgCnt; i++)
{
//GetDiagnostic is also PInvoke, gets the CXDiagnostic at the given index
var diag_ptr = GetDiagnostic(_nativeObject, (uint)i);
Diagnostics.Add(new Diagnostic(diag_ptr));
}
//up until now, all the calls seem to work
//I get the expected count of diagnostics and none of the other
//PInvoke calls throw exceptions. They use IntPtrs, but none of them
//use structs.
}
So as the MSDN tutorial suggested, I've made a C# class to Marshal the CXString struct into, and it looks like this:
[StructLayout(LayoutKind.Sequential)]
public class CXString
{
public IntPtr data;
public uint private_flags;
}
I get the AccessViolationException when the code reaches FormatDiagnostic call.
Any tips where it could have gone wrong?
EDIT:
CXDiagnostic is a pointer type in the original C++ code:
typedef void *CXDiagnostic;
I think marshalling to LPStruct is not proper here and the CXString class needs to be struct.
Try the following code:
public class Diagnostic
{
...
[DllImport("libclang.dll", EntryPoint = "clang_formatDiagnostic")]
private static extern CXString FormatDiagnostic(IntPtr diag, uint options);
...
}
[StructLayout(LayoutKind.Sequential)]
public struct CXString
{
public IntPtr data;
public uint private_flags;
}
Also, you should take care of the calling convention (StdCall by default, but for example pure C use Cdecl) and the struct byte alignment.

C# Interop: Out params that can also be null

Consider the following DllImport:
[DllImport("lulz.so")]
public static extern int DoSomething(IntPtr SomeParam);
This is actually referencing a C style function like this:
int DoSomething(void* SomeParam);
Consider that SomeParam is an "out" param, but can also be NULL. The C function behaves differently if the param is NULL. So I would probably want:
[DllImport("lulz.so")]
public static extern int DoSomething(out IntPtr SomeParam);
But, if I make it an out param in my import, I cannot pass it NULL, i.e. I can't do this:
int retVal = DoSomething(IntPtr.Zero)
What are my options here?
If you're trying to pass a value, then out is not the right keyword; change it to ref. You'll still need to explicitly pass a variable, but it can be a null reference.
For example...
[DllImport("lulz.so")]
public static extern int DoSomething(ref IntPtr SomeParam);
You can then call it like this:
IntPtr retVal = IntPtr.Zero;
DoSomething(ref retVal);
However
What is telling you that it needs to be either out or ref? Passing an IntPtr as out or ref is really akin to passing a double pointer. It would actually seem more appropriate to pass the parameter as an IntPtr.
The typical procedure is either to allocate the necessary memory in managed code and pass an IntPtr representing that allocated memory, or IntPtr.Zero to represent a null pointer. You do not need to pass the IntPtr as out or ref in order to send data back to .NET; you only need to do that if the function you're calling would actually change the pointer's address.
I don't understand what the problem is....
This runs:
private void button2_Click(object sender, EventArgs e) {
object bar;
Method(out bar);
bar = IntPtr.Zero;
Method(out bar);
}
private void Method(out object foo) {
foo = null;
}
What's the intention of passing NULL? Is it intended to call the method as usual, but to simply not set the output parameter?
In that case, I think I'd just wrap the extern method with an overload in C#. That overload (without the out parameter) would be like this:
public void DoSomething()
{
IntPtr dummy;
DoSomething(out dummy);
}
I ran into this once. I ended up marshaling the pointer myself (see Marshal Members for the library functions to do so).
Personally, I'd import this function twice, first time with 'out' parameter, second with 'in'.
[DllImport("lulz.so")]
public static extern int DoSomething(out IntPtr SomeParam);
// call as DoSomethingWithNull(IntPtr.Zero)
[DllImport("lulz.so", EntryPoint="DoSomething")]
public static extern int DoSomethingWithNull(IntPtr SomeParam);
This will solve your problem and will make code more readable.

Categories