2

ハードウェア センサーのペアとやり取りする C# アプリケーションを作成しています。残念ながら、デバイスで公開されている唯一のインターフェイスには、Delphi で記述された提供された dll が必要です。

DLL に必要な関数を呼び出し、スタウト経由でセンサー データを返す Delphi 実行可能ラッパーを作成しています。ただし、このデータの戻り値の型は PWideChar (または PChar) であり、コマンド ラインで印刷するために ansi に変換できませんでした。

データを直接 WriteLn に渡すと、'?' が返されます。キャラクターごとに。文字の配列を調べて、Ansi 変換で一度に 1 つずつ印刷しようとすると、印刷される文字はわずかで (データは確認されます)、順序どおりに印刷されないことがよくあります。また、PWideChar を直接整数に変換してみました。「I」は 21321 に対応します。すべての変換を把握できる可能性がありますが、一部のデータには多数の値が含まれています。

dll が使用している Delphi のバージョンはわかりませんが、4 だと思います。間違いなく 7 より前です。

どんな助けでも大歓迎です!

TLDR: 印刷用に UTF-16 PWideChar を AnsiString に変換する必要があります。

適用例:

program SensorReadout;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Windows,
  SysUtils,
  dllFuncUnit in 'dllFuncUnit.pas'; //This is my dll interface.

var  state: integer;
     answer: PChar;
     I: integer;
     J: integer;
     output: string;
     ch: char;

begin
  try
    for I := 0 to 9 do
    begin
      answer:= GetDeviceChannelInfo_HSI(1, Ord('a'), I, state); //DLL Function, returns a PChar with the results.  See example in comments.

      if state = HSI_NO_ERRORCODE then
      begin
        output:= '';
        for J := 0 to length(answer) do
        begin
          ch:= answer[J]; //This copies the char. Originally had an AnsiChar convert here.
          output:= output + ch;
        end;
        WriteLn(output);
      end;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn(I);
end.`

問題は、PAnsiChar が DLL をソースとする関数の戻り値の型である必要があることでした。

4

4 に答える 4

2

に変換するPWideCharにはAnsiString:

function WideCharToAnsiString(P: PWideChar): AnsiString;
begin
  Result := P;
end;

このコードは、UTF-16、null で終わる PWideChar から に変換しますAnsiString。出力に疑問符が表示される場合は、入力が UTF-16 ではないか、ANSI コードページでエンコードできない文字が含まれています。

私の推測では、実際に起こっていることは、Delphi DLL が Unicode 以前の Delphi で作成されているため、ANSI テキストを使用しているということです。PCharしかし今、別の意味を持つポスト Unicode Delphi からそれにリンクしようとしています。ロブはあなたの他の質問でこれを説明したと確信しています. そのため、DLL のインポートを宣言することで簡単に修正できPAnsiCharますPChar。このような:

function GetDeviceChannelInfo_HSI(PortNumber, Address, ChNumber: Integer;
  var State: Integer): PAnsiChar; stdcall; external DLL_FILENAME;

そして、これを行うと、上で説明したのと同様の方法で文字列変数に割り当てることができます。

吸収する必要があるのは、古いバージョンの Delphi では、PCharが のエイリアスであることですPAnsiChar。現代の Delphi では、 のエイリアスですPWideChar。その不一致は、あなたが報告するすべてを説明します。


Delphi ラッパーを DLL に記述し、stdout を介して C# アプリと通信することは、非常に迂遠なアプローチだと思います。C# コードから直接 DLL を p/invoke するだけです。これは不可能だと思われるかもしれませんが、それは非常に簡単です。

[DllImport(@"mydll.dll")]
static extern IntPtr GetDeviceChannelInfo_HSI(
    int PortNumber, 
    int Address, 
    int ChNumber,
    ref int State
);

次のように関数を呼び出します。

IntPtr ptr = GetDeviceChannelInfo_HSI(Port, Addr, Channel, ref State);

関数が UTF-16 文字列を返す場合 (疑わしいと思われます)、次のIntPtrように変換できます。

string str = Marshal.PtrToStringUni(ptr);

または、実際にANSI文字列である可能性が非常に高い場合は、次のようにします。

string str = Marshal.PtrToStringAnsi(ptr);

そしてもちろん、DLL を呼び出して、返された文字列ポインターの割り当てを解除する必要があります。これは、ヒープに割り当てられていることを前提としています。

于 2012-04-06T17:01:06.050 に答える
0

コメントについて考えを変えました-私はそれを答えにします:)

そのコードによると、「state」がコード<> HSI_NO_ERRORCODEであり、例外がない場合、初期化されていない文字列「output」がコンソールに書き込まれます。これには、誤って「S」と「4」を表示したり、一連の1つ以上の疑問符を表示したりすることが含まれます。

于 2012-04-06T18:17:39.040 に答える
0

回答 (変数) の型は PChar です。文字列変数に適した長さ関数を使用します。長さの代わりに strlen を使用します。

 for J := 0 to StrLen(answer)-1 do

また、PChar(char *) のアクセス可能な範囲は 0..n-1 です

于 2012-04-07T10:15:51.493 に答える