c# - 从 COM 调用托管 API

标签 c# c com com-interop

我是学习 COM 互操作的新手,从我发现的所有示例中,它们都是从 C# 转向 C/C++。我正在尝试 C 到 C# 并返回。

我有一个 C 程序,它通过使用 COM 互操作来调用 C# DLL。我有一个 IDL 文件,它公开 C# 函数以允许 C 程序调用它。我能够通过创建具有相同变量的类并指定 [StructLayout(LayoutKind.Sequential)](仅使用 blittable 类型)将结构传递到 C# DLL 中。

问题:如何在 C 结构中传递变量并允许 C# DLL 修改结构中的变量并将其传递回调用 C 程序? 我可以传入一个指向我的 C 结构的指针并且 C# 能够修改其内容吗?

请注意以下示例: 如果我删除请求的接口(interface)原型(prototype)和函数调用中的“ref”,则程序仅接受结构作为输入并成功继续。通过使用“ref”,程序在调用 C# 函数 Request() 时崩溃。

更新完整示例

C# 3 files (main.cs, ocStruct.cs, guid.cs)
<小时/>
//main.cs
using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

namespace OC
{
    //--------------------------------
    //  Interface protoype
    //--------------------------------    
   [Guid(OC.Guid.IID)]
   public interface IOC
       {
       int Request (ref ocInfo formatInfo);
       }

    [ClassInterface(ClassInterfaceType.None)]

    [Guid(OC.Guid.CLSID)]
    public class OCClass1: IOC
        {
        public OCClass1()
            {
            }

        //--------------------------------
        //  Request()
        //--------------------------------
        public int Request(ref ocInfo Info)
            {
            int rc = 1; //Return code
            try
                {
                Info.req_type = 2;
                }
            catch (Exception e)
                {
                Info.bstr_error = e.Message;
                Info.bstr_stacktrace = e.StackTrace;
                goto exit;
                }

            rc = 0;//Success

            exit:
               return (rc);
           }
        }
    }
<小时/>
//ocStruct.cs
using System;
using System.Runtime.InteropServices;

namespace OC
{
    [StructLayout(LayoutKind.Sequential)]
    public struct ocInfo
        {
                                            public int    req_type;
        [MarshalAs(UnmanagedType.BStr)]     public String bstr_filepath;
        [MarshalAs(UnmanagedType.BStr)]     public String bstr_newfilepath;
        [MarshalAs(UnmanagedType.BStr)]     public String bstr_error;
        [MarshalAs(UnmanagedType.BStr)]     public String bstr_stacktrace;
        }
}
<小时/>
//ocGuid.cs
namespace OC
   {
   public sealed class Guid
       {
       //Class ID
       public const String CLSID  = "12345678-90aA-BCDE-1234-5607890ABCDE";

       //Interface ID
       public const String IID    = "12345678-90aB-BCDE-1234-5607890ABCDE";

       //Type Library
       private const String TLB    = "12345678-90aC-BCDE-1234-5607890ABCDE";
       }//End of class
   }//End of namespace

C 3 文件(test.c、sscce.h、ocStruct.h)

<小时/>
//test.c
#include <windows.h>
#include "sscce.h"


static const IID OC_CLSID = {0x12345678,0x90aA,0xBCDE,{0x12,0x34,0x56,0x07,0x89,0x0A,0xBC,0xDE}};
static const IID OC_IID   = {0x12345678,0x90aB,0xBCDE,{0x12,0x34,0x56,0x07,0x89,0x0A,0xBC,0xDE}};

BSTR BSTR_ALLOC(wchar_t* wstr)
{
byte*          bptr = NULL;
unsigned long  wlen; 

   wlen = (unsigned long)wcslen(wstr) * sizeof(wchar_t);

   if  ((bptr = (byte *)malloc(wlen + sizeof(unsigned long) + sizeof(wchar_t))) == NULL)
       {
       goto exit;
       }

   memcpy(bptr,
          &wlen,
          sizeof(ULONG));

   bptr += sizeof(ULONG);

   memcpy(bptr,
          wstr,
          wlen);
exit:
   return ((BSTR)bptr);
}

void BSTR_FREE(BSTR bstr)
{
byte*   bptr = NULL;
int     rc = 1;

   bptr = (byte*)bstr;
   bptr -= sizeof(unsigned long);

   free(bptr);
   bptr = NULL;

   return;
}


int main(int argc, char* argv[])
{
HRESULT     hr;
IOC        *ocInterface;
OCINFO      Info;
int         rc; //return code

   hr = CoInitializeEx(NULL,
                       COINIT_MULTITHREADED);
   if  (FAILED(hr))
       {
       goto exit;
       }
   hr = CoCreateInstance(&OC_CLSID,
                         NULL,
                         CLSCTX_INPROC_SERVER,
                         &OC_IID,
                         &ocInterface);
   if  (FAILED(hr))
       {
       goto exit;
       }

   memset(&Info,
          0x00,
          sizeof(OCINFO));

   Info.req_type = 1;
   Info.bstr_filepath = BSTR_ALLOC(L"C:\\test\\readme.txt");

   ocInterface->lpVtbl->Request(ocInterface,
                                &Info,
                                &rc);

   BSTR_FREE(Info.bstr_filepath);
exit:
    return (0);
}
<小时/>

sscce.h

/* this ALWAYS GENERATED file contains the definitions for the interfaces */


 /* File created by MIDL compiler version 6.00.0366 */
/* at Wed Apr 16 15:38:43 2014
 */
/* Compiler settings for sscce.idl:
    Oicf, W1, Zp8, env=Win32 (32b run)
    protocol : dce , ms_ext, c_ext, robust
    error checks: allocation ref bounds_check enum stub_data 
    VC __declspec() decoration level: 
         __declspec(uuid()), __declspec(selectany), __declspec(novtable)
         DECLSPEC_UUID(), MIDL_INTERFACE()
*/
//@@MIDL_FILE_HEADING(  )

#pragma warning( disable: 4049 )  /* more than 64k source lines */


/* verify that the <rpcndr.h> version is high enough to compile this file*/
#ifndef __REQUIRED_RPCNDR_H_VERSION__
#define __REQUIRED_RPCNDR_H_VERSION__ 475
#endif

#include "rpc.h"
#include "rpcndr.h"

#ifndef __RPCNDR_H_VERSION__
#error this stub requires an updated version of <rpcndr.h>
#endif // __RPCNDR_H_VERSION__

#ifndef COM_NO_WINDOWS_H
#include "windows.h"
#include "ole2.h"
#endif /*COM_NO_WINDOWS_H*/

#ifndef __sscce_h__
#define __sscce_h__

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

/* Forward Declarations */ 

#ifndef __IOC_FWD_DEFINED__
#define __IOC_FWD_DEFINED__
typedef interface IOC IOC;
#endif  /* __IOC_FWD_DEFINED__ */


#ifndef __OCClass1_FWD_DEFINED__
#define __OCClass1_FWD_DEFINED__

#ifdef __cplusplus
typedef class OCClass1 OCClass1;
#else
typedef struct OCClass1 OCClass1;
#endif /* __cplusplus */

#endif  /* __OCClass1_FWD_DEFINED__ */


/* header files for imported files */
#include "oaidl.h"
#include "ocidl.h"
#include "ocStruct.h"

#ifdef __cplusplus
extern "C"{
#endif 

void * __RPC_USER MIDL_user_allocate(size_t);
void __RPC_USER MIDL_user_free( void * ); 

#ifndef __IOC_INTERFACE_DEFINED__
#define __IOC_INTERFACE_DEFINED__

/* interface IOC */
/* [helpstring][oleautomation][dual][version][uuid][object] */ 


EXTERN_C const IID IID_IOC;

#if defined(__cplusplus) && !defined(CINTERFACE)

    MIDL_INTERFACE("12345678-90aB-BCDE-1234-5607890ABCDE")
    IOC : public IDispatch
    {
    public:
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE Request( 
            /* [out][in] */ POCINFO Info,
            /* [retval][out] */ long *pRetVal) = 0;

    };

#else   /* C style interface */

    typedef struct IOCVtbl
    {
        BEGIN_INTERFACE

        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 
            IOC * This,
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void **ppvObject);

        ULONG ( STDMETHODCALLTYPE *AddRef )( 
            IOC * This);

        ULONG ( STDMETHODCALLTYPE *Release )( 
            IOC * This);

        HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )( 
            IOC * This,
            /* [out] */ UINT *pctinfo);

        HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )( 
            IOC * This,
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo **ppTInfo);

        HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )( 
            IOC * This,
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID *rgDispId);

        /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )( 
            IOC * This,
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS *pDispParams,
            /* [out] */ VARIANT *pVarResult,
            /* [out] */ EXCEPINFO *pExcepInfo,
            /* [out] */ UINT *puArgErr);

        /* [id] */ HRESULT ( STDMETHODCALLTYPE *Request )( 
            IOC * This,
            /* [out][in] */ POCINFO Info,
            /* [retval][out] */ long *pRetVal);

        END_INTERFACE
    } IOCVtbl;

    interface IOC
    {
        CONST_VTBL struct IOCVtbl *lpVtbl;
    };



#ifdef COBJMACROS


#define IOC_QueryInterface(This,riid,ppvObject) \
    (This)->lpVtbl -> QueryInterface(This,riid,ppvObject)

#define IOC_AddRef(This)    \
    (This)->lpVtbl -> AddRef(This)

#define IOC_Release(This)   \
    (This)->lpVtbl -> Release(This)


#define IOC_GetTypeInfoCount(This,pctinfo)  \
    (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo)

#define IOC_GetTypeInfo(This,iTInfo,lcid,ppTInfo)   \
    (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo)

#define IOC_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
    (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId)

#define IOC_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)   \
    (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr)


#define IOC_Request(This,Info,pRetVal)  \
    (This)->lpVtbl -> Request(This,Info,pRetVal)

#endif /* COBJMACROS */


#endif  /* C style interface */



/* [id] */ HRESULT STDMETHODCALLTYPE IOC_Request_Proxy( 
    IOC * This,
    /* [out][in] */ POCINFO Info,
    /* [retval][out] */ long *pRetVal);


void __RPC_STUB IOC_Request_Stub(
    IRpcStubBuffer *This,
    IRpcChannelBuffer *_pRpcChannelBuffer,
    PRPC_MESSAGE _pRpcMessage,
    DWORD *_pdwStubPhase);



#endif  /* __IOC_INTERFACE_DEFINED__ */



#ifndef __OC_LIBRARY_DEFINED__
#define __OC_LIBRARY_DEFINED__

/* library OC */
/* [helpstring][version][uuid] */ 


EXTERN_C const IID LIBID_OC;

EXTERN_C const CLSID CLSID_OCClass1;

#ifdef __cplusplus

class DECLSPEC_UUID("12345678-90aA-BCDE-1234-5607890ABCDE")
OCClass1;
#endif
#endif /* __OC_LIBRARY_DEFINED__ */

/* Additional Prototypes for ALL interfaces */

unsigned long             __RPC_USER  BSTR_UserSize(     unsigned long *, unsigned long            , BSTR * ); 
unsigned char * __RPC_USER  BSTR_UserMarshal(  unsigned long *, unsigned char *, BSTR * ); 
unsigned char * __RPC_USER  BSTR_UserUnmarshal(unsigned long *, unsigned char *, BSTR * ); 
void                      __RPC_USER  BSTR_UserFree(     unsigned long *, BSTR * ); 

/* end of Additional Prototypes */

#ifdef __cplusplus
}
#endif

#endif

IDL 1 文件 (sscce.idl)

<小时/>
import "oaidl.idl";
import "ocidl.idl";

import "ocStruct.h";

//Interface header
    [
    object,                                    
    uuid(12345678-90aB-BCDE-1234-5607890ABCDE),
    version(1.0),                              
    dual,                                      
    oleautomation,                             
    helpstring("OC interface")          
    ]

//Interface body
interface IOC : IDispatch
    {
    [id(1)]    HRESULT Request([in,out] POCINFO Info,
                               [out, retval] long* pRetVal);
    };


//Type Library information
    [
    uuid(12345678-90aC-BCDE-1234-5607890ABCDE),
    version(1.0),
    helpstring("oc type library")
    ]
library OC
    {
    importlib("stdole2.tlb");

        [
        uuid(12345678-90aA-BCDE-1234-5607890ABCDE),
        version(1.0),
        helpstring("oc class")
        ]

    coclass OCClass1
        {
        [default] interface IOC;
        };
    };
<小时/>
//ocStruct.h
typedef struct _ocInfo
{
    INT                     req_type;             
    BSTR                    bstr_filepath;   
    BSTR                    bstr_newfilepath;
    BSTR                    bstr_error;      
    BSTR                    bstr_stacktrace;   
}OCINFO, *POCINFO;

最佳答案

假设我理解正确,您需要传递结构的地址,而不是按值传递结构。

所以 C 代码变成:

MYINFO     Info;
my_interface->lpVtbl->Request(interface_ptr,
                              &Info,
                              &rc);

IDL 变为:

[id(4)]    HRESULT Request([in, out] MYINFO* Info,
                           [out, retval] long* pRetVal);

C# 函数变为:

public int Request(ref My_info Info)

更新1

C 代码对我来说看起来很奇怪。为什么要传递 interface_ptr 而不是 my_interface

更新2

您的最新编辑删除了原始答案引用的所有代码。这令人失望。

您最新更新的明显错误是您没有创建真正的 BSTR 实例。它们是通过调用 SysAllocString 和相关函数来创建的。

关于c# - 从 COM 调用托管 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23069744/

相关文章:

c - 什么范围的机器的二进制兼容性?

python - python 的 win32com.client.dispatch() 无法识别已注册的 COM 对象

.net - Powershell-将COM对象类型转换为字符串

c# - 分别托管 API 和 IdentityServer4 主机(C#、.NET CORE)有哪些优势?

c# - 如何使用路由属性绑定(bind) WebAPI GET 请求中的请求模型?

C - 从数组(结构数组)中删除除平均成绩最高的 10 名学生之外的学生

c# - 通过 Windows 进程句柄访问 COM 对象

c# - 捕获 Outlook 2013 发送事件

c# - 如何在不使用PrinterSettings的情况下更改打印机的纸张尺寸?

c - 静态变量的地址在运行时改变