[英]How to marshal a structure in .NET to be used by native code?
I'm trying to write a DLL in .NET that can be called from a C++ executable. 我正在尝试在.NET中编写一个可以从C ++可执行文件调用的DLL。 The executable expects a specific DLL to exist in its folder and expects a specific function name to be exported for it to consume.
该可执行文件期望在其文件夹中存在特定的DLL,并期望导出特定的函数名称以供其使用。 I'm using info from this Unmanaged Exports page to do it.
我正在使用“ 非托管导出”页面中的信息来执行此操作。
I have the following struct in C++ which I have to accept when the .NET function is called: 我在C ++中具有以下结构,在调用.NET函数时必须接受以下结构:
#pragma pack(4)
typedef struct sFMSelectorData
{
// sizeof(sFMSelectorData)
int nStructSize;
// game version string as returned by AppName() (ie. in the form "Thief 2 Final 1.19")
const char *sGameVersion;
// supplied initial FM root path (the FM Selector may change this)
char *sRootPath;
int nMaxRootLen;
// buffer to copy the selected FM name
char *sName;
int nMaxNameLen;
// set to non-zero when selector is invoked after game exit (if requested during game start)
int bExitedGame;
// FM selector should set this to non-zero if it wants to be invoked after game exits (only done for FMs)
int bRunAfterGame;
// optional list of paths to exclude from mod_path/uber_mod_path in + separated format and like the config
// vars, or if "*" all mod paths are excluded (leave buffer empty for no excludes)
// the specified exclude paths work as if they had a "*\" wildcard prefix
char *sModExcludePaths;
int nMaxModExcludeLen;
} sFMSelectorData;
But I haven't the slightest clue how to marshal everything. 但是我丝毫不知道如何封送一切。 Here's my structure currently.
这是我目前的结构。 You can see I've been trying to experiment.
您可以看到我一直在尝试。 If I remove all the marshalling attributes, I get the MessageBox when the C++ code calls the function (below), but the data in the variables of the structure are not what's expected.
如果删除所有编组属性,则当C ++代码调用该函数时(以下),我将收到MessageBox,但结构变量中的数据不是预期的。 When I attempt to add marshalling attributes like this example, the C++ code crashes and terminates.
当我尝试像本示例一样添加编组属性时,C ++代码崩溃并终止。 I was trying to match the
#pragma pack(4)
from the C++ structure layout, but not sure how to fiddle with the strings to make them compatible with what I guess are pointers in the C++ struct. 我试图从C ++结构布局中匹配
#pragma pack(4)
,但不确定如何摆弄字符串以使其与C ++结构中的指针兼容。 Also, I'm guessing that the <FieldOffset(0)>
attributes refers to the byte index of that variable within the struct. 另外,我猜想
<FieldOffset(0)>
属性引用了该变量在结构中的字节索引。 I had to stop there and decided to post this question. 我不得不停在那里,决定发表这个问题。
<StructLayout(LayoutKind.Sequential, Pack:=4)>
Public Structure sFMSelectorData
' sizeof(sFMSelectorData)
Dim nStructSize As Integer
' game version string as returned by AppName() (ie. in the form "Thief 2 Final 1.19")
<MarshalAs(UnmanagedType.LPStr)>
Dim sGameVersion As String
' supplied initial FM root path (the FM Selector may change this)
<MarshalAs(UnmanagedType.LPStr)>
Dim sRootPath As String
Dim nMaxRootLen As Integer
' buffer to copy the selected FM name
<MarshalAs(UnmanagedType.LPStr)>
Dim sName As String
Dim nMaxNameLen As Integer
' set to non-zero when selector Is invoked after game exit (if requested during game start)
Dim bExitedGame As Integer
' FM selector should set this to non-zero if it wants to be invoked after game exits (only done for FMs)
Dim bRunAfterGame As Integer
' optional list of paths to exclude from mod_path/uber_mod_path in + separated format And Like the config
' vars, Or if "*" all Mod paths are excluded (leave buffer empty for no excludes)
' the specified exclude paths work as if they had a "*\" wildcard prefix
<MarshalAs(UnmanagedType.LPStr)>
Dim sModExcludePaths As String
Dim nMaxModExcludeLen As Integer
End Structure
So the C++ code is calling this .NET function: 因此,C ++代码正在调用此.NET函数:
<DllExport(CallingConvention:=CallingConvention.Cdecl, ExportName:="SelectFM")>
Public Function SelectFM(<MarshalAs(UnmanagedType.Struct)> ByRef data As sFMSelectorData) As Int32
Select Case MsgBox("Start the game?", MsgBoxStyle.Question Or MsgBoxStyle.YesNo, data.nStructSize)
Case MsgBoxResult.Yes : Return eFMSelReturn.kSelFMRet_Cancel
Case MsgBoxResult.No : Return eFMSelReturn.kSelFMRet_ExitGame
End Select
Return eFMSelReturn.kSelFMRet_Cancel
End Function
It does what I want. 它可以满足我的要求。 When I click Yes in the
MessageBox
, the game starts. 当我在
MessageBox
单击是时,游戏开始。 When I click No, the game closes. 当我单击否时,游戏关闭。 But I need to use the data that's supposed to be populated into the structure.
但是我需要使用应该填充到结构中的数据。 I'm not there yet.
我还没在那里。
Here's what the documentation says 这就是文档所说的
An FM selector is a separate library (DLL) containing a utility, usually a UI based application, that lists the available FMs and lets the user pick which one to run.
FM选择器是一个单独的库(DLL),其中包含一个实用程序,通常是一个基于UI的应用程序,该实用程序列出了可用的FM,并让用户选择要运行的FM。 A selector could range from a simple list box with the FM names to a full blown manager with extended info, last played timestamps, sorting/filtering etc.
选择器的范围可以从带有FM名称的简单列表框到具有扩展信息,最近播放的时间戳,排序/过滤等功能的功能齐全的管理器。
The default name for the selector is "FMSEL.DLL", but can be configured with the "fm_selector" cam_mod.ini var.
选择器的默认名称是“ FMSEL.DLL”,但可以使用“ fm_selector” cam_mod.ini var进行配置。
Exports
出口商品
The DLL only needs to have a single symbol exported "SelectFM", which is a function in the form of:
DLL仅需要导出“ SelectFM”单个符号,该符号的形式为:
int __cdecl SelectFM(sFMSelectorData *data);
The following return values are defined:
定义了以下返回值:
0 = 'data->sName'
is expected to contain the selected FM name, if string is empty it means no FM 1 = cancel and exit game0 = 'data->sName'
应该包含选定的FM名称,如果字符串为空,则表示没有FM 1 =取消并退出游戏Any other value is interpreted as cancel-and-continue, the game will start using the
cam_mod.ini
based active FM if defined, otherwise it will run without any FM.任何其他值都将解释为“取消并继续”,游戏将使用基于
cam_mod.ini
的活动FM(如果已定义)开始运行,否则它将在没有任何FM的情况下运行。Data types
资料类型
#pragma pack(4) typedef struct sFMSelectorData { // sizeof(sFMSelectorData) int structSize; // game version string as returned by AppName() (ie. in the form "Thief 2 Final 1.19") const char *sGameVersion; // supplied initial FM root path (the FM selector may change this) char *sRootPath; int nMaxRootLen; // buffer to copy the selected FM name char *sName; int nMaxNameLen; // set to non-zero when selector is invoked after game exit (if requested during game start) int bExitedGame; // FM selector should set this to non-zero if it wants to be invoked after game exits (only done for FMs) int bRunAfterGame; // optional list of paths to exclude from mod_path/uber_mod_path in + separated format and like the config // vars, or if "*" all mod paths are excluded (leave buffer empty for no excludes) // the specified exclude paths work as if they had a "*\\" wildcard prefix char *sModExcludePaths; int nMaxModExcludeLen; // language setting for FM (set by the FM selector when an FM is selected), may be empty if FM has no // language specific resources // when 'bForceLanguage' is 0 this is used to ensure an FM runs correctly even if it doesn't support // the game's current language setting (set by the "language" config var) // when 'bForceLanguage' is 1 this is used to force a language (that must be supported by the FM) other // than the game's current language char *sLanguage; int nLanguageLen; int bForceLanguage; } sFMSelectorData; #pragma pack() typedef enum eFMSelReturn { kSelFMRet_OK = 0, // run selected FM 'data->sName' (0-len string to run without an FM) kSelFMRet_Cancel = -1, // cancel FM selection and start game as-is (no FM or if defined in cam_mod.ini use that) kSelFMRet_ExitGame = 1 // abort and quit game } eFMSelReturn; typedef int (__cdecl *FMSelectorFunc)(sFMSelectorData*);
Hoping some bilingual C++/.NET guru can help me out. 希望一些双语的C ++ /。NET专家可以帮助我。
With DllImport
this structure would be 使用
DllImport
此结构将是
<StructLayout(LayoutKind.Sequential, Pack:=4)>
Public Structure sFMSelectorData
Public nStructSize As Integer
<MarshalAs(UnmanagedType.LPStr)>
Public sGameVersion As String
' supplied initial FM root path (the FM Selector may change this)
<MarshalAs(UnmanagedType.LPStr)>
Public sRootPath As String
Public nMaxRootLen As Integer
' buffer to copy the selected FM name
<MarshalAs(UnmanagedType.LPStr)>
Public sName As String
Public nMaxNameLen As Integer
' set to non-zero when selector Is invoked after game exit (if requested during game start)
Public bExitedGame As Integer
' FM selector should set this to non-zero if it wants to be invoked after game exits (only done for FMs)
Public bRunAfterGame As Integer
' optional list of paths to exclude from mod_path/uber_mod_path in + separated format And Like the config
' vars, Or if "*" all Mod paths are excluded (leave buffer empty for no excludes)
' the specified exclude paths work as if they had a "*\" wildcard prefix
<MarshalAs(UnmanagedType.LPStr)>
Public sModExcludePaths As String
Public nMaxModExcludeLen As Integer
End Structure
Whether that works depends on exactly what that DllExport does. 是否可行取决于DllExport的确切功能。
For .Net applications, you can use C++/CLI and you may find more information on MSDN . 对于.Net应用程序,可以使用C ++ / CLI,并且可以在MSDN上找到更多信息。 I have used C++/CLI in the past with great success for calling C++ objects in C#;
我过去曾经使用C ++ / CLI在C#中调用C ++对象取得了巨大的成功。 although, later we used SWIG for this purpose, as we needed for Java, Python, and R too.
但是,稍后我们将SWIG用于此目的,因为我们也需要Java,Python和R。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.