2016-10-14 11 views
3

この小さなC#テストDLLはUnmanagedExports(NuGetパッケージとして入手)を使って書かれていますが正常に動作しています。しかし、どうすればVBAラッパー関数でSplit()でなければならない文字列を返すのではなく、すぐにString()配列を返すことができるのだろうかと思います。VBAのC#dllの文字列[]を返す

つまり、注目点はメソッドGetFilesWithExtension()です。 DLLの他のメソッドは、正しいエンコーディングで文字列を渡す方法を考えながら作成した小さなテストです。

DLLはx64と.NET 4.5.2をターゲットにしていますが、x86用にもビルドすることができます(したがって、VBAの関数宣言を変更する必要があります)。

C#クラスライブラリ(TestDll.dll):

using System; 
using System.IO; 
using System.Linq; 
using System.Runtime.InteropServices; 
using RGiesecke.DllExport; 

namespace TestDll 
{ 
    public class Class1 
    { 
     [DllExport(nameof(Addition), CallingConvention.StdCall)] 
     public static int Addition(int a, int b) 
     { 
      return a + b + 100; 
     } 


     [DllExport(nameof(LinqAddition), CallingConvention.StdCall)] 
     public static int LinqAddition(int a, int b) 
     { 
      return new int[] {a, b, 1, 4, 5, 6, 7, 8 }.Sum(); 
     } 

     [DllExport(nameof(LinqAdditionString), CallingConvention.StdCall)] 
     [return: MarshalAs(UnmanagedType.AnsiBStr)] 
     public static string LinqAdditionString(int a, int b) 
     { 
      return new int[] { a, b, 1, 4, 5, 6, 7, 8 }.Sum() + ""; 
     } 

     [DllExport(nameof(GetFilesWithExtension), CallingConvention.StdCall)] 
     [return: MarshalAs(UnmanagedType.AnsiBStr)] 
     public static string GetFilesWithExtension([MarshalAs(UnmanagedType.AnsiBStr)] string folderPath, [MarshalAs(UnmanagedType.AnsiBStr)] string extension, bool includeSubdirectories) 
     { 
      //Debug 
      //File.WriteAllText(@"C:\Users\johanb\Source\Repos\TestDll\output.txt", $"folderPath: {folderPath}, extension: {extension}, includeSubdirectories: {includeSubdirectories}"); 
      try 
      { 
       if (!Directory.Exists(folderPath)) 
        return ""; 

       extension = extension.Trim('.'); 

       return string.Join(";", 
        Directory.GetFiles(folderPath, "*.*", 
          includeSubdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) 
         .Where(
          f => 
           Path.GetExtension(f)? 
            .Trim('.') 
            .Equals(extension, StringComparison.InvariantCultureIgnoreCase) ?? false) 
         .ToArray()); 
      } 
      catch (Exception ex) 
      { 
       return ex.ToString(); 
      } 
     } 
    } 
} 

(エクセルでテスト)VBAモジュール:私はかなりのExcelにオブジェクトを返さない飽きない可能性があり

Attribute VB_Name = "TestDll" 
Option Explicit 

Public Declare PtrSafe Function Addition Lib "C:\Users\johanb\Source\Repos\TestDll\TestDll\bin\Debug\TestDll.dll" (ByVal a As Long, ByVal b As Long) As Long 
Public Declare PtrSafe Function LinqAddition Lib "C:\Users\johanb\Source\Repos\TestDll\TestDll\bin\Debug\TestDll.dll" (ByVal a As Long, ByVal b As Long) As Long 
Public Declare PtrSafe Function LinqAdditionString Lib "C:\Users\johanb\Source\Repos\TestDll\TestDll\bin\Debug\TestDll.dll" (ByVal a As Long, ByVal b As Long) As String 
Public Declare PtrSafe Function GetFilesWithExt Lib "C:\Users\johanb\Source\Repos\TestDll\TestDll\bin\Debug\TestDll.dll" Alias "GetFilesWithExtension" (ByVal folderPath As String, ByVal extension As String, ByVal includeSubdirs As Boolean) As String 

Sub Test() 
    Dim someAddition As Long 
    Dim someLinqAddition As Long 
    Dim someLinqAdditionAsString As String 
    Dim files() As String 
    Dim i As Long 

    someAddition = Addition(5, 3) 
    Debug.Print someAddition 

    someLinqAddition = LinqAddition(5, 3) 
    Debug.Print someLinqAddition 

    someLinqAdditionAsString = LinqAdditionString(5, 3) 
    Debug.Print someLinqAddition 

    files = GetFilesWithExtension("C:\Tradostest\Project 4", "sdlxliff", True) 
    For i = 0 To UBound(files) 
     Debug.Print files(i) 
    Next i 

End Sub 

Function GetFilesWithExtension(folderPath As String, extension As String, includeSubdirs As Boolean) As String() 
    GetFilesWithExtension = Split(GetFilesWithExt(folderPath, extension, includeSubdirs), ";") 
End Function 
+0

VBAが 'string()'を返すと宣言されているinterop関数を処理する方法を覚えていませんが(実際には存在しないので)、 'out'パラメータで配列を返して、 TLBが存在するCOMシナリオの 'string()'戻り型を残してしまい、そのようなことは自然に機能します。 – GSerg

+0

ああ、良いアイデア。他の応答がない場合は、outパラメータで試してみます。 GSergに感謝します。 – Jbjstam

+0

VBA intは32ビットbtwではなく16ビットです。 –

答えて

0

作品は、参照渡しで前後にオブジェクトを渡すだけで正常に動作します。何らかの理由で、私はキーワードrefをoutの代わりに使用しなければなりませんでした。さもなければ、Excelはクラッシュします。

文字列のエンコーディングを正しくするには、UnmanagedType.AnsiBstrを使用する必要がありましたが、文字列配列の場合、それをオブジェクトとして宣言し、実行時にメソッドの開始。

using System; 
using System.IO; 
using System.Linq; 
using System.Runtime.InteropServices; 
using RGiesecke.DllExport; 

namespace TestDll 
{ 
    public class FolderHandling 
    { 
     [DllExport(nameof(GetFilesByExtensions), CallingConvention.StdCall)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static bool GetFilesByExtensions(
      ref object arrayOfFiles,         //out doesn't work 
      [MarshalAs(UnmanagedType.AnsiBStr)] string folderPath, 
      object extensions,          //type safety breaks it..somehow 
      [MarshalAs(UnmanagedType.Bool)] bool includeSubdirectories) 
     { 
      try 
      { 
       if (!Directory.Exists(folderPath)) 
       { 
        arrayOfFiles = new[] { $"Parameter {nameof(folderPath)} ({folderPath}) is not a folder" }; 
        return false; 
       } 

       if (!(extensions is string[])) 
       { 
        arrayOfFiles = new[] { $"Parameter {nameof(extensions)} is not a string array" }; 
        return false; 
       } 

       var exts = ((string[])extensions).Select(e => e.Trim('.').ToLowerInvariant()).ToArray(); 

       var files = Directory.GetFiles(folderPath, "*.*", 
         includeSubdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) 
        .Where(f => exts.Contains(Path.GetExtension(f)?.Trim('.').ToLowerInvariant() ?? ";;;")) 
        .ToArray(); 


       //normalize ANSI just in case 
       General.NormalizeANSI(ref files); 

       arrayOfFiles = files; 

       return true; 
      } 
      catch (Exception ex) 
      { 
       arrayOfFiles = new[] { "Exception: " + ex }; 
       return false; 
      } 
     } 
    } 
} 


using System; 
using System.Globalization; 
using System.IO; 
using System.Linq; 
using System.Reflection; 
using System.Text; 

namespace TestDll 
{ 
    static class General 
    { 
     public static void NormalizeANSI(ref string[] files) 
     { 
      for (int i = 0; i < files.Length; i++) 
      { 
       files[i] = string.Concat(files[i].Normalize(NormalizationForm.FormD).Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)); 
      } 
     } 
    } 
} 

私は、ユーザーのシステムフォルダに置くか、COMに登録する必要がないように、私は、のLoadLibrary()を使用して、以下のようにExcelで私のDLLを使用することができます。 FreeLibrary()を使用する利点は、Excelを終了せずにC#プロジェクトを再コンパイルできることです。

Public Declare PtrSafe Function GetFilesByExtensions Lib "TestDll.dll" (ByRef filesRef, ByVal folderPath As String, ByVal extensions, ByVal includeSubdirs As Boolean) As Boolean 

Private Declare PtrSafe Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long 
Private Declare PtrSafe Function LoadLibraryA Lib "kernel32" (ByVal lpLibFileName As String) As Long 

Private Function LoadLibrary(dllName As String) As Long 
    Dim path As String 
    path = ThisWorkbook.path & "\" & dllName 
    LoadLibrary = LoadLibraryA(path) 
End Function 

Sub TestFolderFiltering() 
    Dim files() As String 
    Dim i As Long 
    Dim moduleHandle As Long 

    On Error GoTo restore 

    moduleHandle = LoadLibrary("TestDll.dll") 

    If GetFilesByExtensions(files, "C:\Tradostest\Project 4", Split("sdlxliff", ";"), True) Then 
     For i = 0 To UBound(files) 
      Debug.Print " - " & files(i) 
     Next i 
    Else 
     Debug.Print "ERROR: " & files(0) 
    End If 

restore: 
    If moduleHandle <> 0 Then 
     Call FreeLibrary(moduleHandle) 
    End If 

End Sub 

このようにDLLをVBAからCOMオブジェクトを渡すと、標準のMicrosoftの相互運用ライブラリやNetOfficeを使用してそれらを処理することも可能である、と私はでVBA文字列の配列をフィルタリングする方法を記述するために管理してきましたあなたがGitLabにプロジェクト全体を見つけることができます

If FilterStringArray(myArr, "s => s.ToUpperInvariant().Equals(s, StringComparison.CurrentCulture)") Then 
    For i = 0 To UBound(myArr) 
     Debug.Print " - " & myArr(i) 
    Next i 
Else 
    Debug.Print "ERROR: " & myArr(0) 
End If 

:それは多くの人々のために便利になるかもしれないように聞こえるC#のラムダ式の文字列表現。

関連する問題