.NET Framework Bookmark and Share   
 index > Common Language Runtime > DLL import and error
 

DLL import and error

I am trying to import a method from a C++ dll into a C# application, but I keep getting this error: "Attempted to read or write protected memory . This is often an indication that other memory is corrupt"

The method in the C++ dll looks like this:
int __declspec (dllexport) GetSpecificInterface(IObjectWithSite* object, IDocument4* pDoc)
{
	IServiceProvider* pSite;
	HRESULT hr = object->GetSite(IID_IServiceProvider,(void**)&pSite);
	hr = pSite->QueryService(SID_Document,IID_IDocument4,(void**)&pDoc);
	pSite->Release();
	//pObj->Release();
	return hr;
}
And the call from C# is this:
            IDocument4 document4 = null;
IntPtr objectAfp = Marshal.GetComInterfaceForObject(Objects[0], typeof(IObjectWithSite));
IntPtr objectDoc = IntPtr.Zero;
try {
int hr = GetSpecificInterface(objectAfp, objectDoc);
}
catch (Exception ex)
{
throw ex;
} document4 = (IDocument4)Marshal.GetObjectForIUnknown(objectDoc);
the method is declare din # like this:
        [DllImport("CPlusPlusWrapper.dll", CharSet = CharSet.Auto, EntryPoint = "?GetSpecificInterface@@YAHPAUIObjectWithSite@@PAUIDocument4@@@Z",
         CallingConvention = CallingConvention.StdCall)]
        private static extern int GetSpecificInterface(object site, object doc);


The error appears when
object->GetSite(IID_IServiceProvider,(void**)&pSite);
is called, and what is even stranger is that on another project a similar code works just fine.

OanaMarina
It is quite difficult for me to follow an invalid memory address by looking at pieces of code only, so I hope that you will be able to find the solution, if you needed.

But you say that the above C# code works, only that you are leaving something referenced. I believe it is "objServProv = IntPtr.Zero;". I believe you should not just get rid of this guy. I believe that you need to realease the interface: Marshal.Release(objServProv);

Remember that even though you got an interface through an IntPtr, it doesn't mean that you can just discard it as if it were a mere number. It points to a COM interface and you must release it before you discard it. Look for others like this in your code.

Marshal.GetObjectForIUnknown() probably increases the COM reference count too, but I am guessing that the returned object will call Release() by itself. It wouldn't hurt double-checking, though.

Remember the rule: If you duplicate an interface pointer, you must call AddRef().
MCP
  • Marked As Answer byOanaMarina Thursday, September 17, 2009 6:09 AM
  •  
webJose
In the "GetSpecificInterface" function
- make sure object parameter is valid, then do an AddRef to make sure the object will be kept around while you're using it. Do a "object->Release" when you're done
- change the second "pDoc" parameter to be a "IDocument4 **" since it's an out parameter (it needs to be passed by reference not value)

Hope this helps. Also using ATL classes will make the code more robust and easier to maintain, a lot of issues will be automatically be taken care of :

int __declspec (dllexport) GetSpecificInterface(IObjectWithSite* object, IDocument4** pDoc)
{
	CComQIPtr<IObjectWithSite> spObject(object);
        if (!spObject.p || !pDoc) {
             return E_INVALIDARG;
 	}

        HRESULT hr = S_OK;

        *pDoc = NULL;

  	CComPtr<IServiceProvider> spProvider;
        if (SUCCEEDED(hr = spObject->GetSite(IID_IServiceProvider, (LPVOID *)&spProvider))) {
              CComPtr<IDocument4> spDoc;

             if (SUCCEEDED(hr = spProvider->QueryService(SID_Document, IID_Document, (LPVOID *)&spDoc))) {
                  *pDoc = spDoc.Detach();
              }
        }        

        return hr;
}


Paul
p.b.a
I did what you suggested and no change. I get the same error, and I don't know why but it doesn't even stop at the breakpoint set in the C++ code.
OanaMarina
A quick glance at your C++ declaration and the dll import, I can tell that you have a calling convention mismatch. By default, C++ function calls are cdecl, but you stated in the DllImport attribute a calling convention of StdCall. Either change the calling convention in the DllImportAttribute attribute, or prepend your C++ function with __stdcall.
MCP
webJose
I changed the calling convention and still I have the same error...
OanaMarina
I inspected your declaration of the GetSpecificInterface() function in C#. You are using object instead of the interface types. Is there a reason for this? Normally, one would import a type library to get interface definitions (such as IDocument4), and then declare the function to use those types:

[DllImport("CPlusPlusWrapper.dll", CharSet = CharSet.Auto, EntryPoint = "?GetSpecificInterface@@YAHPAUIObjectWithSite@@PAUIDocument4@@@Z")]
private static extern int GetSpecificInterface(IObjectWithSite site, ref IDocument4 doc);


Also note the use of the ref keyword above. It is necessary so you can pass a pointer to a pointer of an interface, which is what you need to do.

Now, for the particular variable holding IObjectWithSite parameter, you might want to declare it as IntPtr instead of IObjectWithSite, since you get it as an IntPtr. The IDocument4 one, however, would need to be declared as IDocument4.

IDocument4 objectDoc = null;
try
{
    int hr = GetSpecificInterface(objectAfp, ref objectDoc);
}

Note the use of the ref keyword again in the call. It is necessary.
MCP
webJose
Hi OanaMarina, is webJose's last reply resolve the issue?

Thanks,
Eric
Please remember to mark helpful replies as answers and unmark them if they provide no help.
eryang
Unfortunately no :(.
The C++ method looks like this now:

int __declspec (dllexport) GetSpecificInterface(IObjectWithSite* object, IDocument4** pDoc)
{
	CComQIPtr<IObjectWithSite> spObject(object);
        if (!spObject.p || !pDoc) {
             return E_INVALIDARG;
 	}

        HRESULT hr = S_OK;
        *pDoc = NULL;

  	CComPtr<IServiceProvider> spProvider;
        if (SUCCEEDED(hr = spObject->GetSite(IID_IServiceProvider, (LPVOID *)&spProvider))) {
              CComPtr<IDocument4> spDoc;
			  if (SUCCEEDED(hr = spProvider->QueryService(SID_Document, IID_IDocument4, (LPVOID *)&spDoc))) {
                   *pDoc = spDoc.Detach();
             }
        }        
        return hr;

	//IServiceProvider* pSite;
	//HRESULT hr = object->GetSite(IID_IServiceProvider,(void**)&pSite);
	//hr = pSite->QueryService(SID_Document,IID_IDocument4,(void**)&pDoc);
	//pSite->Release();
	////pObj->Release();
	//return hr;
}
and the C# one like this:
  [DllImport("CPlusPlusWrapper.dll", CharSet = CharSet.Auto, EntryPoint = "?GetSpecificInterface@@YAHPAUIObjectWithSite@@PAPAUIDocument4@@@Z",
         CallingConvention = CallingConvention.Cdecl)]
        private static extern int GetSpecificInterface(object site,ref IDocument4 doc);
the call from C# is this one:
 IDocument4 document4 = null;
            IntPtr objectAfp = Marshal.GetComInterfaceForObject(Objects[0], typeof(IObjectWithSite));
            try
            {
                int hr = GetSpecificInterface(objectAfp,ref document4);
            }
            catch (Exception ex)
            {
                throw ex;
            }
and I still get the same error...


OanaMarina
Which line is failing? This one:

object->GetSite(IID_IServiceProvider,(void**)&pSite);
? Or is it another one? If it is the above, then most likely

Marshal.GetComInterfaceForObject(Objects[0], typeof(IObjectWithSite));

is not returning the right pointer. Do you have a way to verify this? Are you certain that Objects[0] contain a truly COM object that implements the IObjectWithSite interface?

MCP
webJose
I am sure that Object[0] is ok.

If I write the following code in C# it works, but somewhere something is not released (although I release all com objects on my side) and the application is not closed.
Guid SID_Document = new Guid(0x7f27feeb, 0x57c2, 0x40f9, 0xbf, 0xb5, 0x3d, 0x85, 0x5a, 0xf8, 0x1a, 0xd7);
Guid IID_IDocument4 = new Guid(0x87f9204d, 0x2bcc, 0x45d7, 0x9b, 0x0d, 0x2d, 0x50, 0x65, 0x76, 0xea, 0x45);

object atollReference = GetAtollInterface();
IDocument4 document4 = (IDocument4)GetSpecificInterface(SID_Document, IID_IDocument4, atollReference);

  private Object GetAtollInterface()
        {
            return (Objects[0] as OtherObjectWithSite).Site;
        }

        private Object GetSpecificInterface(Guid serviceID, Guid interfaceID, Object serviceProvider)
        {
            Guid IID_IServiceProvider = new Guid("6d5140c1-7436-11ce-8034-00aa006009fa");
            OtherObjectWithSite pSite = (OtherObjectWithSite)serviceProvider;
            IntPtr objServProv = IntPtr.Zero;
            pSite.GetSite(ref IID_IServiceProvider, out objServProv);
            Microsoft.VisualStudio.OLE.Interop.IServiceProvider srv = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(objServProv);
            pSite = null;
            objServProv = IntPtr.Zero;
            IntPtr objIntPtr = IntPtr.Zero;
            srv.QueryService(ref serviceID, ref interfaceID, out objIntPtr);
            Object retInterface = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(objIntPtr);
            objIntPtr = IntPtr.Zero;
            srv = null;
            return retInterface;
        }
If I write the folowing code in the same
IObjectWithSite* pObj;
			m_ppUnk[i]->QueryInterface(IID_IObjectWithSite, (void**)&pObj);
			IServiceProvider* pSite;
			HRESULT hr = pObj->GetSite(IID_IServiceProvider,(void**)&pSite);
			Atoll::IDocument4* pDoc;
			hr = pSite->QueryService(SID_Document,Atoll::IID_IDocument4,(void**)&pDoc);

			Atoll::IApplication* pApp;
			pDoc->get_Application(&pApp);

			pApp->Release();

			pDoc->Release();

			pSite->Release();

			pObj->Release();

module, but wrote in C++ it also works fine, no hanging.


So I tried tocal the code that works from C#, and I get that memory error.
OanaMarina
It is quite difficult for me to follow an invalid memory address by looking at pieces of code only, so I hope that you will be able to find the solution, if you needed.

But you say that the above C# code works, only that you are leaving something referenced. I believe it is "objServProv = IntPtr.Zero;". I believe you should not just get rid of this guy. I believe that you need to realease the interface: Marshal.Release(objServProv);

Remember that even though you got an interface through an IntPtr, it doesn't mean that you can just discard it as if it were a mere number. It points to a COM interface and you must release it before you discard it. Look for others like this in your code.

Marshal.GetObjectForIUnknown() probably increases the COM reference count too, but I am guessing that the returned object will call Release() by itself. It wouldn't hurt double-checking, though.

Remember the rule: If you duplicate an interface pointer, you must call AddRef().
MCP
  • Marked As Answer byOanaMarina Thursday, September 17, 2009 6:09 AM
  •  
webJose
Oh my god, can't believe I wasted days for such a small thing. I released the objServProv and the rest of IntPtr and it works. No more hanging, no more C++. Thanks a lot!!!
OanaMarina

You can use google to search for other answers

Custom Search

More Threads

• CAS Policy Problem
• Security issue
• Loading Dll's
• System.TypeLoadException error!
• GAC Confusion Story
• Namespaces VS. References
• What is the purpose of having static method in Value types (struct)?
• "Invalid Operation Exception was unhandled" error in VB Express 2005
• Module loading question
• How to Run .NET Executable on a Network Drive?