none
VisualBasic2010配布用アプリケーション RRS feed

  • 質問

  • Visual Basic2010を使ってFormアプリケーションを作成して、WEB上で無料公開をしています。

    公開してから数年たちますが、今回は配布用アプリケーションに問題が生じました。使っていますPCのOSは、Win8.1です。

    問題点は、アプリケーションの動作確認がVisual Basic2010で出来て、配布用アプリケーションも完成し、インストールした後、画面上に出来たアイコンから動作確認をすると、c:¥ProgramFiles(x86)の中にある「ABC.sdf」にアクセスできないというエラーメッセージが出ます。そこで、その「ABC.sdf」のプロパティを開いてセキュリティを見ますと、「AllApplicationPackages」と「制限されたすべてのアプリケーションパッケジ」と「Users(ABC180)」という項目では、「読み取りと実行」と「読み取り」しかアクセスが許可されていません。「フルコントロール」とか「変更」とか「書き込み」はアクセスが許可されていないのです。

    ちなみに、配布用アプリケーションの「ABC.sdf」ではなく、プログラムで作成されたProgramFiles(x86)以外のところにある「ABC.sdf」のプロパティを見ますと、「System」と「Administrations」と「ABC180」しか項目は無いのですが、すべてアクセス許可のチェックが入っています。これら3項目はc:¥ProgramFiles(x86)の中の「ABC.sdf」にも出てきます。但し「ABC180」は、「Users(ABC180)」と名前が変わっています。「System」と「Administrations」は、すべてに許可のチェックが入っていますが、「Users(ABC180)」は「読み取りと実行」と「読み取り」しか許可のチェックはありません。

    配布用アプリケーションを作動させるには、c:¥ProgramFiles(x86)の中にある「ABC.sdf」のプロパティからアクセスを許可するに変更すれば良いのですが、今までそのような煩わしい作業をしなくても、画面上のアイコンをクリックするだけでソフトが動いていたのに、使いが手が悪くなったという印象です。ソフトの利用者にできるだけ作業負担を軽減するためにも、以前のような配布用アプリケーションにできないか思考中です。

    良い対応策がございましたら、お教えください。また、私の書き方が不十分で分かりづらいところがございましたら、ご指摘ください。

    2019年7月21日 3:53

すべての返信

  • そもそもC:\Program FilesやC:\Program Files(x86)はセキュリティのために読み取り専用です。インストール/アップデート/アンインストールのタイミング以外では書き込みを行うべきではありません。ABC.sdfの配置場所を見直すべきです。

    Environment.GetFolderPath Methodを使用すると適切なフォルダーが得られます。Environment.SpecialFolder.ApplicationData辺りが適切と思います。

    2019年7月21日 5:34
  • Program Files に書けない(書くべきではない)のはすでに指摘されているとおりとして、この話は 10 年以上前から出ている事柄なので、最近変わった話ではないですよ。
    認識ずれのないようにご注意ください。

    たとえば、初回起動時に Program Files にある ABC.sdf をユーザーごとの Application Data にフォルダーを作った上でコピーし、そこで読み書きするという手段も考えられます。

    2019年7月21日 7:10
  • 佐祐理様、アドバイスをありがとうございます。

    アプリケーションの開発から配布用アプリケーションの作成まで、すべてVisualBasic2010で行っています。市販されていますVisualBasic2010の本を読んで、VisualBasic2010のインストーラを使って配布用アプリケーションを作成しています。

    インストール後のアプリケーション動作確認作業で、「データベースファイルのアクセスが許可されていません。 FileName=c:¥ProguramuFiles(x86)¥......¥ABC.sdf 」というエラーメッセージが出て、ProguramuFiles(x86)フォルダーの中にあるABC.sdfファイルにアクセス許可がないのでアプリケーションが動かないと分かりました。

    ABC.sdfの配置場所の見直しということの意味が理解できません。すべて、ファイルの設置はVisualBasic2010のインストーラが行っているものなので、私が勝手に動かせるものなのでしょうか。

    2019年7月21日 7:25
  • Azulean様、アドバイスをありがとうございます。

    頂いたアドバイスの中で「ユーザーごとの Application Dataに...」とありますが、このアプリケーションは不特定多数の全国の方が使われていまして、ユーザーごとにフォルダーを作ることはできません。

    また、このアプリケーションは何度も同じ手順を踏んで、バージョンアップをしています。それまで1度もこのような現象は生じていません。原因が分からないのが残念です。

    2019年7月21日 12:03
  • 前にうまく動いていた理由は知りませんが、Program Files に配置したファイルは書き換え不可能 です。
    基本的にインストーラーでは「初期値」となるファイルだけを配置し、実行しているユーザーアカウントごとのフォルダーにユーザーごとの設定を置くべきとされています。

    おそらく、以前動いていたのは「互換性のためのフォルダーリダイレクト」機能のおかげでしょうけれども、なぜそれが効かなくなったのかはわかりません。
    前と同じ動きをしようとすると、その互換性の効果を失うことになった、何らかの変更を諦めてもらうことになるので、現実的ではないでしょう。
    (.NET のバージョンを変えたとか、Visual Studio のバージョンを変えたとか、マニフェストファイルを追加したとか)

    >ユーザーごとにフォルダーを作ることはできません。
    なぜですか?
    「1 つのパソコンを多人数で使用し、設定を共有したい」というなら理解できますが、そうでないなら、ユーザーごとで良いはずです。
    佐祐理さんや私が書いている「ユーザーごと」というのは「ファイルパスを固定する」という意味ではなく、実行しているアカウントごとのフォルダーを(A さんなら A、B さんなら B というように)取得して、環境に合わせて動的に振る舞いなさいということです。
    参考:http://blog.livedoor.jp/akf0/archives/50896163.html

    インストーラーで何か工夫するのではなく、あなたのアプリケーションのコードで工夫してください。
    sdf にアクセスする部分を相対パス(アプリケーションと同じフォルダー)ではなく、動的に決定した絶対パス(C:\Users\(略)など)にすることです。

    考えられる手順例
    1.起動時に、Environment.GetFolderPath で実行しているアカウントの設定を保存できるフォルダーを取得する
    2.そのフォルダーとアプリ名を組み合わせたフォルダーを決める(Path.Combine でパスを連結する)
    3.そのフォルダーが存在しないか、そのフォルダーに sdf ファイルがないならフォルダーを作って、アプリと同じフォルダーから sdf ファイルをコピーする(Application.StartupPath、Directory.CreateDirectory、File.Copy など)
    4.そのコピー先の sdf ファイルを読み書きするようにアプリのコードを修正する

    2019年7月21日 13:22
  • Azulean様 丁寧な説明をありがとうございます。

    手順例に従って作業を進めましたが、経験をしたことが無い領域に入りますので、確認をしながら進めます。

    1番は、アドバイス通り「Environment.SpecialFolder.ApplicationData」で進めました。

    2番はAppDataとアプリ名(ABC)を連結しますので、C:\Documents and Settings\All Users\Application Data¥ABCとなります。

    3番は、Cドライブのドキュメントにそのフォルダーは無いので、Directory.CreateDirectoryで作成して、ABC.sdfはFile.Copy でProgramFiles(x86)\ABCからABC.sdfをコピーします。

    4番が分かりにくいです。sdf ファイルを読み書き出来るようにコードを修正するのは、どうするのでしょうか。ABC.sdfファイルは、読込とか書き込みを気にすることなくVisual Basic2010が作成してくれていましたので、対応が分かりません。

    最後に、「Program Files に配置したファイルは書き換え不可能 です。」ということですが、セキュリティー上のことでしない方が良いということなのでしょうか。ABC.sdfのプロパティのセキュリティからアクセス許可の変更は可能でした。ソフトも作動します。でも、ソフトの利用者さんに煩雑な作業をお願いするよりも、簡単に使えるほうが良いので頑張ります。

    2019年7月22日 7:49
  • 4番が分かりにくいです。sdf ファイルを読み書き出来るようにコードを修正するのは、どうするのでしょうか。ABC.sdfファイルは、読込とか書き込みを気にすることなくVisual Basic2010が作成してくれていましたので、対応が分かりません。

    私自身はデータベース系ほとんどやらないのでピンとは来ませんが、その sdf ファイルを設定しているプロパティ、コードなど、どこかにありませんか?
    見つけられない場合は、編集 - 検索と置換 - フォルダーを指定して検索で検索する文字列にその sdf のファイル名、検索対象に「ソリューション全体」を指定してすべて検索を実行してみてください。

    最後に、「Program Files に配置したファイルは書き換え不可能 です。」ということですが、セキュリティー上のことでしない方が良いということなのでしょうか。

    改ざんされる・破壊される、あるいは誤って壊してしまうといった被害防止のほか、同じパソコンを複数のユーザーで共有している場合に他の人の設定が勝手に適用されると使い勝手が悪いなどの事情があります。

    2019年7月22日 13:09
  • システムフォルダーへの保存が原則 NG なのは、.NET 以前の古い Windows の頃から言われていたことではありますね。徐々に厳密化されていったものではありますが。

    ところで…接続文字列での指定はそもそもどうなっていますか?

    .sdf への接続文字列を app.config に書いていて、

    <add name="プロジェクト.Properties.Settings.ConnectionString"
         connectionString="Data Source=|DataDirectory|\ABC.sdf"
    providerName="Microsoft.SqlServerCe.Client.3.5"
    />

    のように、|DataDirectory| リテラル指定で記述しているのでしょうか。それともフルパスでの指定ですか?

    |DataDirectory| での指定だとしたら、それは ClickOnce 配置ではユーザーごとのデータフォルダー(ApplicationDeployment.CurrentDeployment.DataDirectory) を指しますが、そうでないデスクトップアプリでは、アプリケーションの exe のある場所(たとえば Program Files の配下)が指し示されるかと思います。

    このパスを変更したい場合は、事前に AppDomain.CurrentDomain.SetData("DataDirectory", データフォルダ) メソッドの指定することができます。取得は AppDomain.CurrentDomain.GetData("DataDirectory") メソッドです。

    現状、ClickOnce 配置ではなさそうなので、恐らくは手動配置もしくは Windows インストーラーによる配置であったのだろうと想像しますが、その場合、C:\Program Files (x86) 配下に置かれるファイル群は、通常の権限では読み取り専用になると認識しています。それが従来書き込めていたのだとすれば、担当者が初回導入時に設定を変更していたか…あるいは最初のインストーラーに、権限付与のための仕組みを組み込んでいたのではないでしょうか。

    .

    なお蛇足ですが、自分の場合はプログラムの更新時に .sdf が置き換えられると困るため、インストーラーに含めている .sdf は、あくまでも初期配置用のテンプレートという位置づけにしておき、「アプリケーションで編集する .sdf」は、それは別に容易する運用にしていました。

    たとえば、最小サイズの .sdf をリソースに保持しておき(あるいはファイルコンテンツとして配置)、それを初回起動時(あるいはメンテンスリセット時)に、Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) のサブフォルダー等、アプリ用のデータフォルダー先に対して、File.WriteAllBytes あるいは File.Copy で配置しておき、そちらを読み書きさせるといった具合です。

    2019年7月22日 14:37
  • 魔界の仮面弁士様

    アドバイスをありがとうございます。昔から一度、魔界の仮面弁士様のご指導が頂きたいと思っていましたので、今回はとても嬉しいです。そこで、魔界の仮面弁士様の方法で挑戦してみようと思っています。

    公開していますアプリは、Formアプリですので、Form1の冒頭でEnvironment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)を記述してアプリ用のフォルダーを作り、そこにABC.sdfをコピーする。そして、C:\ProgramFile(x86)\ABC\ABC.sdfは、初期配置用とする。

    コピーされたABC.sdfは、c:\ProgramData\......\ABC\ABC.sdfに配置される。

    ProgramFile(x86)\ABC\ABC.sdfは、アクセス制限があるが、ProgramData\......\ABC\ABC.sdfはアクセス制限がないので、読み書きが自由にできますが、ProgramFile(x86)\ABCにあるプログラムファイルがProgramData\......\ABC\ABC.sdfを読みにいってくれるのでしょうか。VisualBasic2010では、サーバーエクスプローラでABC.sdfの接続をセットするところがありますが、それはプログラム中のことで、ソフト起動時に接続先を書き込めるのでしょうか。要は、C:\ProgramFile(x86)\ABC\ABC.sdfは飾りとなり、c:\ProgramData\......\ABC\ABC.sdfをProgramFile(x86)\ABCにあるプログラムの対象となるファイルにする手法を知りたいのです。



    2019年7月23日 13:28