回答編集履歴

1

自動マーシャラはダメ

2024/09/11 12:30

投稿

matukeso
matukeso

スコア1677

test CHANGED
@@ -1,6 +1,49 @@
1
- variantはデフォルトマーシャリングでObject型にできるので
1
+ ~~variantはデフォルトマーシャリングでObject型にできるので
2
2
  object o=“some”;
3
3
  …GetField(…,ref o);
4
- みたいなコードでいけないですか?
4
+ みたいなコードでいけないですか?~~
5
5
 
6
6
  https://learn.microsoft.com/ja-jp/dotnet/framework/interop/default-marshalling-for-objects
7
+ VT_BSTR|VT_BYREFなVARIANTは、それを通すマーシャリングがないのでダメですね。。
8
+
9
+ なので、GetFieldで自動のobject->variantマーシャラが動かないように、IntPtrとかカスタムVARIANT構造体とかを渡せるようにpDataの型を変更する必要があって、自動生成の場合は一手間ですね。
10
+ まず_netMSDBServiceのクラスがtlbimpで自動生成されているばあい、VS2022なら「逆コンパイルされたソースへのナビゲーション機能」で、定義に移動すれば(逆コンパイルされた)ソースがでてくるので、これをベースにすればいいとおもいます。順序が重要なので、interfaceを全部コピーする必要があります。
11
+
12
+ ```csharp:自動ComInterop.cs
13
+ [ComImport]
14
+ [Guid("XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXX")]
15
+ public interface _SDBServiceClass
16
+ {(略)
17
+ bool GetField([In][MarshalAs(UnmanagedType.BStr)] string Key, [In][MarshalAs(UnmanagedType.BStr)] string NetworkName = "", [In][MarshalAs(UnmanagedType.BStr)] string FieldName = "", [In] int FieldNo = 0,
18
+ [In][Out][MarshalAs(UnmanagedType.Struct)] ref object pData);
19
+ }
20
+ ```
21
+ みたいな定義がでてくるとおもうので、これをコピーして、名前だけちょっとかえて、GetFieldの宣言をかえます。(Guidは同じ)
22
+ ```csharp:変造Interop.cs
23
+ [ComImport]
24
+ [Guid("XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXX")]
25
+ public interface _SDBServiceClass2 {
26
+ (略)
27
+ bool GetField([In][MarshalAs(UnmanagedType.BStr)] string Key, [In][MarshalAs(UnmanagedType.BStr)] string NetworkName = "", [In][MarshalAs(UnmanagedType.BStr)] string FieldName = "", [In] int FieldNo = 0,
28
+ [In][Out] ref VARIANT pData);
29
+ }
30
+ [StructLayout(LayoutKind.Sequential)]
31
+ public struct VARIANT
32
+ {
33
+ public ushort vt;
34
+ public ushort r0;
35
+ public ushort r1;
36
+ public ushort r2;
37
+ public IntPtr ptr0;
38
+ public IntPtr ptr1;
39
+ }
40
+ ```
41
+
42
+ 呼び出しはこんなかんじ。解放処理は省略しますが、ちゃんとVariantInitとか呼んだ方がいいかもだけど。
43
+ ```csharp: user.cs
44
+ VARIANT pData;
45
+ pData.vt = 8 | 16384; //VT_BSTR | VT_BYREF
46
+ pData.ptr0 =Marshal.AllocHGlobal( IntPtr.Size);
47
+ _SDBServiceClass2 _netMSDBService2= (_SDBServiceClass2 )_netMSDBService ;
48
+ _netMSDBService2.GetField(key, networkName, "TAG", 0, ref pData);
49
+ string data = Marshal.PtrToStringBSTR( Marshal.ReadIntPtr(pData.ptr0) );