0

I have developed a Out-Of-Proc-COM-Server in C++ with Visual Studio 2010 to avoid the 64-bit vs. 32-bit problem in Shellextensions (http://blog.mattmags.com/2007/06/30/accessing-32-bit-dlls-from-64-bit-code/).

I described the Interfaces like here (http://msdn.microsoft.com/en-us/library/ms686605%28v=VS.85%29.aspx) in a IDL-File:

import "unknwn.idl";
[
 object,
 uuid("xx"),
 helpstring("IShellServerx86-Interface")
]
interface IShellServerx86 : IUnknown 
{
   HRESULT ShowFileInfo([in]BSTR file, [out]BSTR* htmlFile, [in]BSTR pathChar);
};

This file generates me a Proxy/Stub-DLL which I also registered to use the Standard Marshaller methods. If I call now

IShellServerx86* pShellServer = NULL;
CoCreateInstance(__uuidof(CShellServerx86), NULL, CLSCTX_LOCAL_SERVER,
                 __uuidof(IShellServerx86), (void**)&pShellServer);

the server is created and I can call the method

HRESULT CShellServerx86::ShowFileInfo(BSTR file, BSTR* htmlFile, BSTR pathChar)

and with the created parameters (client-side):

BSTR filebstr = ::SysAllocString(A2OLE(file));
BSTR pathBstr = ::SysAllocString(A2OLE(pathChar));
BSTR htmlFileBstr = ::SysAllocString(A2OLE(""));

In the client the BSTR's are correctly generated but when the COM-method is called (he finds it!) and I debug into the dllhost.exe, the parameters are invalid like the wrong encoding is chosen. I tried for whole project to set "Unicode" but nothing changes.

Have I forgotten any settings or should I try other data types for marshalling?

Thank you for help in advance.

EDIT:

The implementation of the client is:

int CShellWrapperx64Module::ShowFileInfo(IN const char* file, 
                                                    OUT VARIANT &htmlFile,
                                                    IN const char* pathChar)
{...
    ::CoInitialize(NULL);
    IShellServerx86* pShellServer = NULL
    hr = ::CoCreateInstance(__uuidof(CShellServerx86), NULL, 
                           CLSCTX_LOCAL_SERVER, __uuidof(IShellServerx86),
                           (void**)&pShellServer);
    BSTR filebstr = ::SysAllocString(A2OLE(file));
    BSTR pathBstr = ::SysAllocString(A2OLE(pathChar));
    BSTR htmlFileBstr = ::SysAllocString(A2OLE(""));
    //Call method of Server
    hr = pShellServer->ShowFileInfo(filebstr, &htmlFileBstr, pathBstr);
    ::CoUninitialize();
    VariantInit(&htmlFile);
    htmlFile.vt = VT_BSTR;
    htmlFile.bstrVal = htmlFileBstr;
}

The server method is declared as following:

HRESULT CShellServerx86::ShowFileInfo(BSTR file, BSTR* htmlFile, BSTR pathBSTR)
{...
 //TODO
}

In the server and client methods the debugger recognize the BSTR-strings as wchar_t*-arrays. But the content for example for the string "file" in the server method is something like: 0x02546e80 "㤈榧".

The encoding is for all projects (client/server) set to Multibyte-Encoding (Visual Studio).

EDIT2:

The server is declared as follwed:

class IShellServerx86 : public IUnknown {
  public:

  virtual HRESULT ShowFileInfo(BSTR file, BSTR* htmlFile, BSTR pathChar) = 0;

};

Implementation of the interface:

//CoClass from Interface (Implementation)
class CShellServerx86 : public IShellServerx86 {
 public:
  CShellServerx86();
  virtual ~CShellServerx86();
  //inherited from IUnknown
  ULONG STDMETHODCALLTYPE AddRef(void);
  ULONG STDMETHODCALLTYPE Release(void);
  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv);

  HRESULT ShowFileInfo(BSTR file, BSTR* htmlFile, BSTR pathChar);

 protected:
  ULONG m_uRefCount;
};

... and class-factory class CShellServerx86ClassFactory : public IClassFactory { public: CShellServerx86ClassFactory(); ~CShellServerx86ClassFactory();

 //inherited methods from IUnknown
 ULONG STDMETHODCALLTYPE AddRef(void);
 ULONG STDMETHODCALLTYPE Release(void);
 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv);

 //inherited methods from IClassFactory
 HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, 
                                          REFIID riid, void** ppv);
 HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock);

protected: ULONG m_uRefCount; };

GetClass-Method from the DLL:

STDAPI DllGetClassObject ( REFCLSID rclsid, REFIID riid, void** ppv ) {
  if (!::InlineIsEqualGUID(rclsid, __uuidof(CShellServerx86)) ) {
     return CLASS_E_CLASSNOTAVAILABLE;
  }
  *ppv = NULL;
  CShellServerx86ClassFactory* pShellServerFac;
  pShellServerFac = new CShellServerx86ClassFactory;
  if (pShellServerFac == NULL) {
     return E_OUTOFMEMORY;
  }
  pShellServerFac->AddRef();
  HRESULT hr = pShellServerFac->QueryInterface(riid, ppv);
  pShellServerFac->Release();
  return hr;

}

Tobias
  • 41
  • 4
  • 1
    BSTR has only one encoding, utf16. You leave plenty of opportunity to get the A2OLE() conversion done wrong, it critically depends on the default code page. Improve your question by documenting how you concluded that there's an encoding problem. – Hans Passant Aug 08 '11 at 16:14
  • The problem is most likely in the ShowFileInfo() implementation. Could you please show the code that assigns the `htmlFile` parameter? – sharptooth Aug 09 '11 at 06:01
  • I'd eliminate the A2OLE/Multibyte encoding stuff altogether. `const char* file` especially doesn't make sense on Windows; paths are UTF-16. – MSalters Aug 09 '11 at 10:16
  • I tried to migrate the project to UNICODE but nothing changes. – Tobias Aug 09 '11 at 14:38

1 Answers1

1

First you have to inspect what A2OLE produces in your case and whether that's suitable input for SysAllocString().

Then you have to implement that //TODO - it's the callee responsibility to properly build values of out parameters. You'll have to do something like this:

HRESULT CShellServerx86::ShowFileInfo(BSTR file, BSTR* htmlFile, BSTR pathBSTR)
{
    if( htmlFile == 0 ) {
       return E_POINTER;
    }
    // do useful stuff, generate the string for the htmlFile, then
    *htmlFile = SysAllocString( TheStringForHtmlFileParameter );
    return S_OK;
}

Also you're leaking a BSTR in the caller:

BSTR htmlFileBstr = ::SysAllocString(A2OLE(""));
//Call method of Server
hr = pShellServer->ShowFileInfo(filebstr, &htmlFileBstr, pathBstr);

will lose the BSTR passed as second parameter since a new BSTR will be created by the callee. Instead just initialize it to a null pointer:

BSTR htmlFileBstr = 0;
//Call method of Server
hr = pShellServer->ShowFileInfo(filebstr, &htmlFileBstr, pathBstr);

Also you leak all BSTRs anyway since you don't call SysFreeString() when done. Either call SysFreeString() on each BSTR you own or better use a wrapper class like ATL::CComBSTR or _bstr_t.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • Hello, I inspected the A2OLE-Macro and the BSTR-strings on client side are initialized correctly. I also modified the server methods like in your suggestion (BSTR* htmlfile). The method call and parameters from the client side are correct. But when the server starts the method still the parameters are incorrect. The string pointer the server returns back is also incorrect marshalled. Only the HRESULT return value is correct. I am really confused? – Tobias Aug 09 '11 at 13:16
  • I added the implementation of the class factory (EDIT2) to could exclude any implementation errors. – Tobias Aug 09 '11 at 13:36
  • I currently noticed that the client searches for the Interface ICallFactory and I return E_INVALIDARG. Should this Interface be implemented? – Tobias Aug 09 '11 at 13:43
  • One another question: The "HKCR\CLSID\\InProcServer32"-Standard-Entry has to point to the COM-Server or to the proxy/stub-DLL? – Tobias Aug 09 '11 at 14:17
  • @Tobias: Entries under `HKCR\CLSID` refer to class ids, not interface ids and they should store path of the server DLL, not proxy-stub DLL. – sharptooth Aug 09 '11 at 14:21
  • OK, then I was right but Microsoft is not very clearly about this: [link](http://msdn.microsoft.com/en-us/library/ms686606%28v=VS.85%29.aspx) because they demanded in the fourth point of the condition that "InProcServer32" points to proxy/stub-DLL. – Tobias Aug 09 '11 at 14:27
  • @Tobias: That's just wrong - proxy/stubs are registered under `HKCR\CLSID\Interface`, so I guess that's just a typo. – sharptooth Aug 10 '11 at 05:50
  • OK, but I'm still wondering, why the parameters are not right marshalled? The Proxy/Stub-DLL was found. Is there any possibility to debug it? – Tobias Aug 10 '11 at 07:50
  • 1
    @Tobias: I seriously doubt proxy/stub is debuggable - there's lots of generated code inside. In your case you could use Automation marshaller - your interface is Automation-compatible and you could use Automation proxy/stub by adding `oleautomation` tag in IDL and registering the typelib - http://stackoverflow.com/questions/1636148/any-sense-in-marking-an-iunknown-derived-interface-as-dual-in-idl/1741981#1741981 – sharptooth Aug 10 '11 at 08:15