Re: Registration-free COM client

2016-06-29 Thread Thalamus via Digitalmars-d-learn

On Wednesday, 29 June 2016 at 19:21:50 UTC, John wrote:

On Wednesday, 29 June 2016 at 14:51:10 UTC, Thalamus wrote:

[...]


A little research reveals that C# COM servers don't export 
DllGetClassObject, or any of the COM server plumbing. 
"Registering for COM interop" merely adds the registry entries 
- which is the step you're trying to avoid by loading the DLL 
dynamically. Then you'd run regasm on it to generate a type 
library. 
https://msdn.microsoft.com/en-us/library/aa645738(v=vs.71).aspx


Thanks John! Yes, I did forget to add : IUnknown. I would have 
caught that, but I appreciate you pointing that out. Thanks also 
for the link to the regasm article.


Re: Registration-free COM client

2016-06-29 Thread John via Digitalmars-d-learn

On Wednesday, 29 June 2016 at 14:51:10 UTC, Thalamus wrote:
I was hoping there would be a code-only solution, and I'm glad 
to see one is possible. It isn't quite working for me yet, 
though. I can get the HINSTANCE in the CoLoadLibrary call, but 
GetProcAddress for DllGetClassObject fails, with 
ERROR_PROC_NOT_FOUND.


I've never written a COM server before (only clients), so I 
must be building the C# assembly incorrectly. I'm just not sure 
how. Everything across the board is built as 64-bit, and I've 
selected "Register for COM interop". The C# is pretty simple:


A little research reveals that C# COM servers don't export 
DllGetClassObject, or any of the COM server plumbing. 
"Registering for COM interop" merely adds the registry entries - 
which is the step you're trying to avoid by loading the DLL 
dynamically. Then you'd run regasm on it to generate a type 
library. 
https://msdn.microsoft.com/en-us/library/aa645738(v=vs.71).aspx


Re: Registration-free COM client

2016-06-29 Thread John via Digitalmars-d-learn

On Wednesday, 29 June 2016 at 17:55:27 UTC, John wrote:

On Wednesday, 29 June 2016 at 14:51:10 UTC, Thalamus wrote:


and the D code (mostly unchanged from your example):

interface ITestInterface
{
int Identifier();
}


ITestInterface needs to derive from IUnknown.


I realise that doesn't address your question, but the other thing 
is I'm pretty sure C# will transform the ITestInterface 
definition into something more like this:


  interface ITextInterface : IUnknown {
HRESULT Identifier(int* result);
  }

And when it comes to calling it:

  int result;
  checkHR(instance.Indentifier());


Re: Registration-free COM client

2016-06-29 Thread John via Digitalmars-d-learn

On Wednesday, 29 June 2016 at 14:51:10 UTC, Thalamus wrote:


and the D code (mostly unchanged from your example):

interface ITestInterface
{
int Identifier();
}


ITestInterface needs to derive from IUnknown.



Re: Registration-free COM client

2016-06-29 Thread Thalamus via Digitalmars-d-learn

On Tuesday, 28 June 2016 at 02:30:56 UTC, thedeemon wrote:
To load load a COM object from a given file (DLL or AX) without 
having it registered, just load the file with CoLoadLibrary() 
and use its DllGetClassObject() function to get IClassFactory 
which will give you any kind of object in this library.
Here's how I do it (in my case the requested object has 
"IBaseFilter" COM interface):


Thanks to both of you!

I was hoping there would be a code-only solution, and I'm glad to 
see one is possible. It isn't quite working for me yet, though. I 
can get the HINSTANCE in the CoLoadLibrary call, but 
GetProcAddress for DllGetClassObject fails, with 
ERROR_PROC_NOT_FOUND.


I've never written a COM server before (only clients), so I must 
be building the C# assembly incorrectly. I'm just not sure how. 
Everything across the board is built as 64-bit, and I've selected 
"Register for COM interop". The C# is pretty simple:


[ComVisible(true)]
[Guid("B99B6E64-966C-4655-B633-D0108F4DE909")]
public interface ITestInterface
{
int Identifier();
}

[ComVisible(true)]
[Guid("0F8BDA67-D6CF-42D0-A62D-E19BD7864362")]
[ClassInterface(ClassInterfaceType.None)]
public class TestClass : ITestInterface
{
public int Identifier()
{
return 42;
}
}

and the D code (mostly unchanged from your example):

interface ITestInterface
{
int Identifier();
}


ITestInterface GetInstance()
{
import std.exception;
import core.sys.windows.com;
GUID iid = { 0xB99B6E64, 0x966C, 0x4655, [0xB6, 0x33, 0xD0, 
0x10, 0x8F, 0x4D, 0xE9, 0x09]};
GUID clsid = { 0x0F8BDA67, 0xD6CF, 0x42D0, [0xA6, 0x2D, 0xE1, 
0x9B, 0xD7, 0x86, 0x43, 0x62]};


HINSTANCE hinst = 
CoLoadLibrary(cast(wchar*)r"managed\bin\test.dll".toUTF16z, 0); 
//CoFreeLibrary will need to be called later.

enforce!COMException(hinst);

LPFNGETCLASSOBJECT fnGetClassObject = 
cast(LPFNGETCLASSOBJECT)GetProcAddress(hinst, 
"DllGetClassObject");

enforce!COMException(fnGetClassObject);

IClassFactory factory;
auto factory_iid = IID_IClassFactory;
fnGetClassObject(, _iid, 
cast(void**)).checkHR("fnGetClassObject failed");

enforce!COMException(factory);

ITestInterface instance;
factory.CreateInstance(null, , 
cast(void**)).checkHR("factory.CreateInstance");

return instance;
}

Any ideas what I'm missing?

thanks!
Gene


Re: Registration-free COM client

2016-06-28 Thread John via Digitalmars-d-learn

On Monday, 27 June 2016 at 21:17:52 UTC, Thalamus wrote:

Hi everyone,

I've succeeded in using D as a client for regular (registered) 
COM servers in the past, but in this case, I'm building the 
server as well. I would like to avoid registering it if 
possible so XCOPY-like deployment remains an option. Can a 
registration-free COM client be built in D? If so, how can the 
code be setup to consume the manifest file, or alternately, is 
there a way to just point D to the correct assembly directly in 
code?


You just deploy the manifest in the same folder as your client.


Re: Registration-free COM client

2016-06-27 Thread thedeemon via Digitalmars-d-learn

On Monday, 27 June 2016 at 21:17:52 UTC, Thalamus wrote:

Hi everyone,

I've succeeded in using D as a client for regular (registered) 
COM servers in the past, but in this case, I'm building the 
server as well. I would like to avoid registering it if 
possible so XCOPY-like deployment remains an option. Can a 
registration-free COM client be built in D? If so, how can the 
code be setup to consume the manifest file, or alternately, is 
there a way to just point D to the correct assembly directly in 
code?


To load load a COM object from a given file (DLL or AX) without 
having it registered, just load the file with CoLoadLibrary() and 
use its DllGetClassObject() function to get IClassFactory which 
will give you any kind of object in this library.
Here's how I do it (in my case the requested object has 
"IBaseFilter" COM interface):


IBaseFilter createDSFilterFromFile(GUID guid, string dir, string 
fname) {

auto curdir = getcwd();
scope(exit) chdir(curdir);
chdir(dir); // in case the lib depends on other dlls nearby

	auto hinst = CoLoadLibrary( cast(wchar*) buildPath(dir, 
fname).toUTF16z, TRUE);

enforce!COMException(hinst);

	auto fnGetClassObject = cast(LPFNGETCLASSOBJECT) 
GetProcAddress(hinst, "DllGetClassObject");

enforce!COMException(fnGetClassObject);

IClassFactory factory;
auto iid = IID_IClassFactory;
	fnGetClassObject(, , 
cast(void**)).checkHR("fnGetClassObject failed");

enforce!COMException(factory);

IBaseFilter ibf;
	factory.CreateInstance(null, _IBaseFilter, 
cast(void**)).checkHR("factory.CreateInstance");

return ibf;
}


Registration-free COM client

2016-06-27 Thread Thalamus via Digitalmars-d-learn

Hi everyone,

I've succeeded in using D as a client for regular (registered) 
COM servers in the past, but in this case, I'm building the 
server as well. I would like to avoid registering it if possible 
so XCOPY-like deployment remains an option. Can a 
registration-free COM client be built in D? If so, how can the 
code be setup to consume the manifest file, or alternately, is 
there a way to just point D to the correct assembly directly in 
code?


Some background info:

The project I'm working on requires a high degree of D and C# 
interop. Getting C# to invoke D via C linkage is simple, but I've 
encountered a lot of problems the other way around.


Although it's technically possible to get pointers to C# objects 
such that the same approach could be used, doing so would require 
large portions of the code to be marked unsafe, the objects to be 
instantiated as fixed, use of GCHandle.Alloc, etc., which has a 
high dev learning curve and scalability issues.


The typical solution is to use delegates as callbacks. This 
works, but it doesn't scale well to more complex scenarios. You 
can send a struct of delegates to the D layer and use that to 
access public methods in a class, but if one of those methods 
would normally return a instance of a different class and the 
caller will need to invoke methods within that other class, this 
approach breaks down. For example, 
MyClassInstance.MyProperty.DoSomething() can't be modeled as 
MyClassInstanceDelegate.MyPropertyDelegate.DoSomethingDelegate(). 
This fails marshaling, because delegates are not blittable. 
There's very likely a way to structure complex delegate 
hierarchies that would in the end be marshalable, but the 
implementation and maintenance overhead would be sizable.


This leaves COM, which seems like it would work fine, on Windows 
anyway. (I'm not sure about Linux, but maybe some combo of Mono 
and WINE would do it? Not a high prio right now.) I'm hoping, 
though, to avoid having to register the C# COM server to keep 
things as simple as possible.


thanks!