2016-09-16 5 views
0

私はwinapi-rs経由でC FFI経由でWindows APIを利用するプログラムを持っています。関数の1つは、文字列へのポインタへのポインタが出力パラメータとして期待されます。この関数は、結果をこの文字列に格納します。私はこの文字列にタイプWideCStringの変数を使用しています。可変参照を渡すか、FFIのコンテキストで変数の所有権を移すべきですか?

可変機能のrefを安全に文字列に(ちょうど安全でないブロック内の)この関数に渡すことはできますか?(と.from_raw()のような機能を使用して、変数の所有権をC関数?

どちらのバージョンもコンパイルして動作しますが、私は直接的な方法で不利益を買っているのかどうか疑問に思っています。

私のコードの関連する行は、.into_raw.from_rawです。

let mut widestr: WideCString = WideCString::from_str("test").unwrap(); //this is the string where the result should be stored 
let mut security_descriptor_ptr: winnt::LPWSTR = widestr.into_raw(); 

let rtrn3 = unsafe { 
    advapi32::ConvertSecurityDescriptorToStringSecurityDescriptorW(sd_buffer.as_mut_ptr() as *mut std::os::raw::c_void, 
            1, 
            winnt::DACL_SECURITY_INFORMATION, 
            &mut security_descriptor_ptr, 
             ptr::null_mut()) 

}; 

if rtrn3 == 0 { 
    match IOError::last_os_error().raw_os_error() { 
     Some(1008) => println!("Need to fix this errror in get_acl_of_file."), // Do nothing. No idea, why this error occurs 
     Some(e) => panic!("Unknown OS error in get_acl_of_file {}", e), 
     None => panic!("That should not happen in get_acl_of_file!"), 
    } 
} 

let mut rtr: WideCString = unsafe{WideCString::from_raw(security_descriptor_ptr)}; 

description of this parameter in MSDNは言う:

ヌル - 末端セキュリティ記述子の文字列へのポインタを受け取る変数へのポインタ。文字列形式の説明については、Security Descriptor String Formatを参照してください。返されたバッファを解放するには、LocalFree関数を呼び出します。

私は変数の値を変更することを期待しています。定義ごとに - 私が所有権を移転していることを意味しないのですか?

+1

再びそれを呼び出す、あなたは割り当てる必要があるバイト数を返します。この場合Cの文字列へのポインタに? 'WCHAR **'?関数はこれと何をすると言うでしょうか? MSDNで検索できるように、呼び出す関数を教えてください。 –

+0

@Shepmaster実際には、Cサイドの機能が何を望んでいるのかまだ分かりませんから。 –

+1

ショートバージョン、いくつかのコードを表示します。 –

答えて

3

私は関数が変数の値を変更することを期待しています。定義ごとに - 私が所有権を移転していることを意味しないのですか?

いいえオーナーシップについて考えてみるには、あなたがそれを完了したときに値を破壊する責任があります。

文書予想所有権ルールですが、時には単語が斜めであるか、ある程度の外部知識を持っていることもあります。この特定の関数はこう言います。

返されたバッファを解放するには、LocalFree関数を呼び出します。

これは、ConvertSecurityDescriptorToStringSecurityDescriptorWが何らかの種類の割り当てを実行し、それをユーザーに返すことを意味します。関数宣言をチェックアウト、あなたはまた、「アウト」パラメータであるとして、彼らはそのパラメータを文書化することがわかります。

_Out_ LPTSTR    *StringSecurityDescriptor, 

することは、なぜそれがこのように行われていますか?呼び出し元は、文字列を格納するために割り当てるメモリの量を知らないので、!

通常、の初期化されていないメモリへの参照を関数に渡します。この関数は、それを初期化する必要があります。

これはコンパイルされていますが、実際にそれを呼び出すために十分なコンテキストを提供しなかったので、それが動作するかどうか誰が知っている:

extern crate advapi32; 
extern crate winapi; 
extern crate widestring; 

use std::{mem, ptr, io}; 
use winapi::{winnt, PSECURITY_DESCRIPTOR}; 
use widestring::WideCString; 

fn foo(sd_buffer: PSECURITY_DESCRIPTOR) -> WideCString { 
    let mut security_descriptor = unsafe { mem::uninitialized() }; 

    let retval = unsafe { 
     advapi32::ConvertSecurityDescriptorToStringSecurityDescriptorW(
      sd_buffer, 
      1, 
      winnt::DACL_SECURITY_INFORMATION, 
      &mut security_descriptor, 
      ptr::null_mut() 
     ) 
    }; 

    if retval == 0 { 
     match io::Error::last_os_error().raw_os_error() { 
      Some(1008) => println!("Need to fix this errror in get_acl_of_file."), // Do nothing. No idea, why this error occurs 
      Some(e) => panic!("Unknown OS error in get_acl_of_file {}", e), 
      None => panic!("That should not happen in get_acl_of_file!"), 
     } 
    } 

    unsafe { WideCString::from_raw(security_descriptor) } 
} 

fn main() { 
    let x = foo(ptr::null_mut()); 
    println!("{:?}", x); 
} 
[dependencies] 
winapi = { git = "https://github.com/nils-tekampe/winapi-rs/", rev = "1bb62e2c22d0f5833cfa9eec1db2c9cfc2a4a303" } 
advapi32-sys = { git = "https://github.com/nils-tekampe/winapi-rs/", rev = "1bb62e2c22d0f5833cfa9eec1db2c9cfc2a4a303" } 
widestring = "*" 

を直接あなたの質問に答える:

refへの変更可能なrefを(安全でないブロック内の)この関数の文字列に渡すことはできますか、むしろ.into_raw()や.from_rのような機能を使うべきですか変数の所有権をC関数に移すaw()?

どちらもありません。この関数は、文字列へのポインタを渡すことを期待していません。に文字列を挿入できる場所へのポインタを渡す必要があります。

私もちょうど私の例では(私はそれを理解限り)、widestr変数はC言語の関数によって上書きれることは決してありませんことをご説明した後に実現。データへの参照は上書きされますが、データ自体は上書きされません。

WideCString::from_str("test")によって割り当てられたメモリが完全にリークしている可能性があります。関数呼び出し後にそのポインタへの参照がないためです。

これは、C(WinAPI)関数が(最初にサイズを返す2ステップアプローチに従わないと)バッファを常に割り当てるという一般的なルールですか?

私はC APIまたはさえCのAPIの内側との間任意の一般的なルールがあるとは思いません。特にAPI面が非常に多いMicrosoftほど大きな会社ではそれぞれの方法のドキュメントを読む必要があります。これは、Cを書くことをスローグのように感じさせる絶え間ないドラッグの一部です。

このような関数に初期化されていないメモリを引き渡すのは、何とか私にとって奇妙に感じます。

実際には、関数がそれを初期化するという保証はないためです。実際には、失敗した場合に初期化するのは無駄なので、おそらく失敗します。 Rustにはより良い解決策があるように思われるもう一つのことです。


あなたはlast_os_errorのようなものを呼び出す前に、関数呼び出し(例えばprintln!)を行うべきではありません注意。これらの関数呼び出しは、最後のエラーの値を変更する可能性があります。


その他のWindows APIは、実際に多段階プロセスを必要とする - あなたはNULLで関数を呼び出し、それは、あなたがポインタ何

+0

ありがとう!あなたのコードはコンパイルされて正常に動作します。私はまた、あなたの説明の後で、私の例で(私が理解している限り)私の例では、widestr変数はC関数によって上書きされることはないことを認識しました。それはデータ自体ではなく、データ自体の参照を上書きします:-( C(WinAPI)関数は常にバッファを自分で割り当てます(最初にサイズを返す2ステップアプローチに従わない場合)。 – Norbert

+0

これに関連する質問をすることができますか?(別のトピックを開く必要があるかどうかを教えてください)。ケースについては、私は助けができませんが、どういうわけか、私はこのような関数に初期化されていないメモリを渡すことが奇妙に感じます。私はWindows API関数を2回呼び出す必要があります(最初に必要なバッファサイズを返す)。使用する最良のデータ型は何ですか?私はVectorを使って実験しましたが、あなたのanwerの哲学に従って、 alloc :: heap :: allocateのような関数を使ってFFIのメモリを単にアドレス指定する方が良いでしょう。 – Norbert

関連する問題